chore: sync the frontend template from 4dc033c to 8157ad4
The product was materialized from the template at `4dc033c` and has stayed on it through 43 template commits, so it was missing all three rounds of adapter remediation — including files it never had, such as the shared `abortable-operation` primitive and the `exact-snapshot` decoder that later fixes are written against. Taking only the newest round was not possible for that reason: the delta is coherent only as a whole. The product had not touched `src/adapters` at all since materialization, so the 140-file delta applied with a three-way merge and no conflicts. `package.json` was the single overlap and merged cleanly: the product owns `name`, the template contributed `check:adapter-inventory`, `check:remediation-ledger` and the image-resolve-signal type fixture. All 24 product-owned files — README, index.html, CI workflow, i18n catalog, home page, generated schemas, evidence scripts, component and visual snapshots — are byte-identical to `main`. `template.lock.json` now pins the synced revision and tree. Verified in this repository, not inherited from the template: six type projects, lint, nine gates (adapter inventory, remediation ledger, registries, diagnostics, realtime boundaries, architecture, browser file/storage boundaries, optional recipes, documentation), the production build, and 2,054 of 2,073 tests. The 19 failures are all in `tests/unit/ci-artifact-contract.test.ts` and are the same pre-existing sandbox RLIMIT, EMFILE, umask and `/tmp` permission behaviour the template records; four suites that failed once under parallel load pass in isolation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
002ba3624e
commit
4bff9ca151
@@ -50,6 +50,29 @@ export type BrowserRpcStreamFrame =
|
||||
failure: BrowserRpcTransportFailure;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* RPC-RR-01. A server stream is a physical resource, not just a sequence.
|
||||
*
|
||||
* A bare `AsyncIterable` gives the runtime no way to cancel the underlying
|
||||
* stream or to learn when it actually closed: `iterator.return()` is a request
|
||||
* a non-cooperative implementation may ignore. The runtime could then time out,
|
||||
* report the call finished, and admit a second stream for the same operation
|
||||
* while the first was still running against the server.
|
||||
*
|
||||
* The lease separates the three concerns the runtime needs:
|
||||
*
|
||||
* - `frames` is the sequence,
|
||||
* - `cancel(reason)` is a synchronous request to stop the physical stream,
|
||||
* - `waitClosed()` settles only once that stream is really closed,
|
||||
* - `streamId` names the physical stream so two leases are never confused.
|
||||
*/
|
||||
export type BrowserRpcServerStreamLease = Readonly<{
|
||||
streamId: string;
|
||||
frames: AsyncIterable<BrowserRpcStreamFrame>;
|
||||
cancel(reason: string): void;
|
||||
waitClosed(): Promise<void>;
|
||||
}>;
|
||||
|
||||
export type BrowserRpcTransport = BrowserRpcRuntimeBindingIdentity &
|
||||
Readonly<{
|
||||
invokeUnary?(
|
||||
@@ -57,9 +80,78 @@ export type BrowserRpcTransport = BrowserRpcRuntimeBindingIdentity &
|
||||
): Promise<BrowserRpcUnaryTransportResult>;
|
||||
openServerStream?(
|
||||
call: BrowserRpcTransportCall,
|
||||
): AsyncIterable<BrowserRpcStreamFrame>;
|
||||
): BrowserRpcServerStreamLease;
|
||||
}>;
|
||||
|
||||
const STREAM_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
||||
|
||||
/**
|
||||
* RPC-RR-01. Decodes a lease from own data descriptors before the runtime
|
||||
* registers it, so an accessor cannot hand the registry one object and the
|
||||
* cancellation path another.
|
||||
*/
|
||||
export function decodeServerStreamLease(
|
||||
value: unknown,
|
||||
): BrowserRpcServerStreamLease | null {
|
||||
if (value === null || typeof value !== "object") return null;
|
||||
let streamId: unknown;
|
||||
let frames: unknown;
|
||||
let cancel: unknown;
|
||||
let waitClosed: unknown;
|
||||
try {
|
||||
if (Object.getOwnPropertySymbols(value).length > 0) return null;
|
||||
const names = Object.getOwnPropertyNames(value).sort();
|
||||
const expected = ["cancel", "frames", "streamId", "waitClosed"];
|
||||
if (
|
||||
names.length !== expected.length ||
|
||||
names.some((name, index) => name !== expected[index])
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
for (const name of names) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
||||
if (!descriptor || !("value" in descriptor)) return null;
|
||||
}
|
||||
streamId = Object.getOwnPropertyDescriptor(value, "streamId")?.value;
|
||||
frames = Object.getOwnPropertyDescriptor(value, "frames")?.value;
|
||||
cancel = Object.getOwnPropertyDescriptor(value, "cancel")?.value;
|
||||
waitClosed = Object.getOwnPropertyDescriptor(value, "waitClosed")?.value;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
// RPC-02. The async-iterator lookup is a read of foreign state like any
|
||||
// other, so it happens inside the decoder's own boundary. Performing it after
|
||||
// the `try` let a throwing `Symbol.asyncIterator` getter escape this
|
||||
// function as a native `TypeError`, breaking the decoder's totality.
|
||||
let openFrames: unknown;
|
||||
try {
|
||||
if (
|
||||
typeof streamId !== "string" ||
|
||||
!STREAM_ID.test(streamId) ||
|
||||
frames === null ||
|
||||
typeof frames !== "object" ||
|
||||
typeof cancel !== "function" ||
|
||||
typeof waitClosed !== "function"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
openFrames = (frames as AsyncIterable<unknown>)[Symbol.asyncIterator];
|
||||
if (typeof openFrames !== "function") return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const iterate = (openFrames as () => AsyncIterator<BrowserRpcStreamFrame>)
|
||||
.bind(frames);
|
||||
return Object.freeze({
|
||||
streamId,
|
||||
frames: Object.freeze({
|
||||
[Symbol.asyncIterator]: iterate,
|
||||
}) as AsyncIterable<BrowserRpcStreamFrame>,
|
||||
cancel: (cancel as (reason: string) => void).bind(value),
|
||||
waitClosed: (waitClosed as () => Promise<void>).bind(value),
|
||||
});
|
||||
}
|
||||
|
||||
export function defineBrowserRpcTransport(
|
||||
transport: BrowserRpcTransport,
|
||||
): BrowserRpcTransport {
|
||||
|
||||
Reference in New Issue
Block a user