@raybase/record-mount 0.0.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +432 -2
- package/dist/body.d.ts +165 -0
- package/dist/body.d.ts.map +1 -0
- package/dist/body.js +538 -0
- package/dist/body.js.map +1 -0
- package/dist/client.d.ts +132 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +376 -0
- package/dist/client.js.map +1 -0
- package/dist/contract.d.ts +261 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +83 -0
- package/dist/contract.js.map +1 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +81 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/mount.d.ts +219 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +372 -0
- package/dist/mount.js.map +1 -0
- package/dist/testing.d.ts +149 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +764 -0
- package/dist/testing.js.map +1 -0
- package/package.json +34 -8
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The declarable client binding: an app names its record-door service
|
|
3
|
+
* binding and gets a typed client against the door's wire contract.
|
|
4
|
+
*
|
|
5
|
+
* Two refusals live here and they land at different moments, deliberately.
|
|
6
|
+
*
|
|
7
|
+
* **The address is checked at construction.** A binding name that is
|
|
8
|
+
* absent from `env`, or present but not door-shaped, is a deploy
|
|
9
|
+
* misconfiguration and there is nothing to wait for: `openRecordDoor`
|
|
10
|
+
* throws immediately. So does a malformed declared namespace.
|
|
11
|
+
*
|
|
12
|
+
* **The contract version is checked at the first verb call**, memoized per
|
|
13
|
+
* door stub — which is per isolate, because a Worker's `env` and its
|
|
14
|
+
* binding stubs are built once per isolate and handed to every request.
|
|
15
|
+
* The check cannot honestly happen at construction: a Worker constructs
|
|
16
|
+
* its handle on every request, and an RPC round trip there would price the
|
|
17
|
+
* check per request. `assertContract()` is the eager form for an app that
|
|
18
|
+
* genuinely has a boot hook and wants to pay once. Either way no verb
|
|
19
|
+
* result reaches the app before the pin has been checked, a mismatch is
|
|
20
|
+
* terminal and stays memoized, and a failure to reach the door at all
|
|
21
|
+
* clears the memo so a flap does not poison an isolate.
|
|
22
|
+
*
|
|
23
|
+
* ## What a Cloudflare RPC stub does to property checks
|
|
24
|
+
*
|
|
25
|
+
* A real service-binding stub answers EVERY property access with a callable.
|
|
26
|
+
* `typeof stub.append === "function"` is therefore true against a read door,
|
|
27
|
+
* true against a service that is not a record door at all, and true for a
|
|
28
|
+
* property nobody ever defined. Property presence carries no information
|
|
29
|
+
* there, and code that reads it is reading its own assumption back.
|
|
30
|
+
*
|
|
31
|
+
* So this module detects the stub's KIND by behavior — probing a sentinel
|
|
32
|
+
* property no door will ever serve — and takes a different route for each:
|
|
33
|
+
*
|
|
34
|
+
* - **A plain object** (a test double, an in-process fake): presence is real.
|
|
35
|
+
* The construction-time "not a record door" refusal and the read-door
|
|
36
|
+
* `writable` answer both mean what they say.
|
|
37
|
+
* - **An RPC stub**: presence means nothing, so the missing-verb refusal at
|
|
38
|
+
* construction is skipped rather than pretended, and writability comes from
|
|
39
|
+
* the door's own `version().writes` declaration. Until that reply lands,
|
|
40
|
+
* `writable` is `"unknown"` — an honest third state, not a guess — and a
|
|
41
|
+
* `TypeError` from a write verb on an undeclared door is TRANSLATED into
|
|
42
|
+
* this package's typed refusal so a consumer switches on one taxonomy
|
|
43
|
+
* rather than sniffing runtime messages.
|
|
44
|
+
*/
|
|
45
|
+
import type { RecordAppendResult, RecordCountGroup, RecordCountResult, RecordDoorVersion, RecordGetResult, RecordId, RecordListQuery, RecordListResult, RecordMaterializeEntry, RecordQuery, RecordQueryResult, RecordReverseRefsResult, RecordTransitionEvent, RecordTransitionResult } from "./contract.js";
|
|
46
|
+
export type OpenRecordDoorOptions<Env> = {
|
|
47
|
+
/** The Worker's environment — where the service binding lives. */
|
|
48
|
+
env: Env;
|
|
49
|
+
/**
|
|
50
|
+
* The binding name declared in `wrangler.jsonc`, e.g. `"STORE_DOOR"`.
|
|
51
|
+
* Named rather than passed as a stub so the refusal can say which line
|
|
52
|
+
* of configuration is wrong, and so the contract check has a stable
|
|
53
|
+
* identity to memoize against.
|
|
54
|
+
*/
|
|
55
|
+
binding: string;
|
|
56
|
+
/**
|
|
57
|
+
* The namespace this mount believes its binding carries.
|
|
58
|
+
*
|
|
59
|
+
* **This is a declaration, not a verification.** The namespace is a
|
|
60
|
+
* deploy-seam fact: it rides the binding's `props` beside the actor,
|
|
61
|
+
* where only a chair-signed `wrangler` config can write it, and no verb
|
|
62
|
+
* takes or returns a namespace — that one-wayness is what makes the
|
|
63
|
+
* address unforgeable. The client can read `version()`; it cannot read
|
|
64
|
+
* props. So what this value buys is: exactly one namespace per mount
|
|
65
|
+
* (asserted structurally — a mount holds one store), a legible name in
|
|
66
|
+
* refusals and telemetry, and a written-down claim a reviewer can diff
|
|
67
|
+
* against the deployed config. What it cannot do is catch a config that
|
|
68
|
+
* says `prod` here and something else in the props.
|
|
69
|
+
*/
|
|
70
|
+
namespace: string;
|
|
71
|
+
/**
|
|
72
|
+
* Pin a contract version other than the one this build compiles
|
|
73
|
+
* against. Present for a consumer that deliberately lags a release;
|
|
74
|
+
* absent, the client expects its own.
|
|
75
|
+
*/
|
|
76
|
+
expect?: string;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The typed client. Every verb the door serves, the taxonomy rehydrated
|
|
80
|
+
* into `RecordDoorError`, and `materialize` paging under the covers so a
|
|
81
|
+
* caller sees the stream the substrate's own signature promises.
|
|
82
|
+
*/
|
|
83
|
+
export type RecordDoorClient = {
|
|
84
|
+
/** The binding name this client was opened against. */
|
|
85
|
+
readonly binding: string;
|
|
86
|
+
/** The declared namespace. See `OpenRecordDoorOptions.namespace`. */
|
|
87
|
+
readonly namespace: string;
|
|
88
|
+
/** The contract version this client pins. */
|
|
89
|
+
readonly contract: string;
|
|
90
|
+
/**
|
|
91
|
+
* Whether the bound door carries the write verbs — and `"unknown"` when
|
|
92
|
+
* nothing observable says yet.
|
|
93
|
+
*
|
|
94
|
+
* This is a getter, and its answer sharpens as the client learns:
|
|
95
|
+
*
|
|
96
|
+
* - **Plain binding** (a double, an in-process fake): the methods are
|
|
97
|
+
* really there or really not, so this is a boolean from the first read
|
|
98
|
+
* and never changes.
|
|
99
|
+
* - **RPC stub**: `"unknown"` at construction — property presence is
|
|
100
|
+
* meaningless across the seam — and then the door's declared
|
|
101
|
+
* `version().writes` once the contract check has resolved one. A door
|
|
102
|
+
* that declares nothing (one built before the field existed) leaves this
|
|
103
|
+
* `"unknown"` forever, which is the honest answer rather than a guess.
|
|
104
|
+
*
|
|
105
|
+
* For a definite answer, `await client.assertContract()` first and then
|
|
106
|
+
* read it. `"unknown"` never blocks a write: the call goes out, and a
|
|
107
|
+
* `TypeError` from a door that turns out to lack the verb comes back as
|
|
108
|
+
* this package's typed `forbidden` refusal.
|
|
109
|
+
*/
|
|
110
|
+
readonly writable: boolean | "unknown";
|
|
111
|
+
/** The door's identity, unchecked — the one call that must work before
|
|
112
|
+
* the contract is known. */
|
|
113
|
+
version(): Promise<RecordDoorVersion>;
|
|
114
|
+
/** Check the pin now rather than at the first verb. Idempotent. */
|
|
115
|
+
assertContract(): Promise<void>;
|
|
116
|
+
get(id: RecordId): Promise<RecordGetResult>;
|
|
117
|
+
query(q: RecordQuery): Promise<RecordQueryResult>;
|
|
118
|
+
materialize(prefix: string): Promise<AsyncIterable<RecordMaterializeEntry>>;
|
|
119
|
+
list(q: RecordListQuery): Promise<RecordListResult>;
|
|
120
|
+
count(args: {
|
|
121
|
+
family?: string;
|
|
122
|
+
groupBy: RecordCountGroup;
|
|
123
|
+
}): Promise<RecordCountResult>;
|
|
124
|
+
reverseRefs(id: RecordId, opts?: {
|
|
125
|
+
limit?: number;
|
|
126
|
+
order?: "asc" | "desc";
|
|
127
|
+
}): Promise<RecordReverseRefsResult>;
|
|
128
|
+
append(body: string | Uint8Array): Promise<RecordAppendResult>;
|
|
129
|
+
transition(id: RecordId, expected: string, next: string, event: RecordTransitionEvent): Promise<RecordTransitionResult>;
|
|
130
|
+
};
|
|
131
|
+
export declare function openRecordDoor<Env>(options: OpenRecordDoorOptions<Env>): RecordDoorClient;
|
|
132
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAGH,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAEjB,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,eAAe,CAAC;AAwBvB,MAAM,MAAM,qBAAqB,CAAC,GAAG,IAAI;IACvC,kEAAkE;IAClE,GAAG,EAAE,GAAG,CAAC;IACT;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;OAaG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAEvC;iCAC6B;IAC7B,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACtC,mEAAmE;IACnE,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC,GAAG,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAA;KAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxF,WAAW,CACT,EAAE,EAAE,QAAQ,EACZ,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GAChD,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC/D,UAAU,CACR,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACpC,CAAC;AAoBF,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAiVzF"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The declarable client binding: an app names its record-door service
|
|
3
|
+
* binding and gets a typed client against the door's wire contract.
|
|
4
|
+
*
|
|
5
|
+
* Two refusals live here and they land at different moments, deliberately.
|
|
6
|
+
*
|
|
7
|
+
* **The address is checked at construction.** A binding name that is
|
|
8
|
+
* absent from `env`, or present but not door-shaped, is a deploy
|
|
9
|
+
* misconfiguration and there is nothing to wait for: `openRecordDoor`
|
|
10
|
+
* throws immediately. So does a malformed declared namespace.
|
|
11
|
+
*
|
|
12
|
+
* **The contract version is checked at the first verb call**, memoized per
|
|
13
|
+
* door stub — which is per isolate, because a Worker's `env` and its
|
|
14
|
+
* binding stubs are built once per isolate and handed to every request.
|
|
15
|
+
* The check cannot honestly happen at construction: a Worker constructs
|
|
16
|
+
* its handle on every request, and an RPC round trip there would price the
|
|
17
|
+
* check per request. `assertContract()` is the eager form for an app that
|
|
18
|
+
* genuinely has a boot hook and wants to pay once. Either way no verb
|
|
19
|
+
* result reaches the app before the pin has been checked, a mismatch is
|
|
20
|
+
* terminal and stays memoized, and a failure to reach the door at all
|
|
21
|
+
* clears the memo so a flap does not poison an isolate.
|
|
22
|
+
*
|
|
23
|
+
* ## What a Cloudflare RPC stub does to property checks
|
|
24
|
+
*
|
|
25
|
+
* A real service-binding stub answers EVERY property access with a callable.
|
|
26
|
+
* `typeof stub.append === "function"` is therefore true against a read door,
|
|
27
|
+
* true against a service that is not a record door at all, and true for a
|
|
28
|
+
* property nobody ever defined. Property presence carries no information
|
|
29
|
+
* there, and code that reads it is reading its own assumption back.
|
|
30
|
+
*
|
|
31
|
+
* So this module detects the stub's KIND by behavior — probing a sentinel
|
|
32
|
+
* property no door will ever serve — and takes a different route for each:
|
|
33
|
+
*
|
|
34
|
+
* - **A plain object** (a test double, an in-process fake): presence is real.
|
|
35
|
+
* The construction-time "not a record door" refusal and the read-door
|
|
36
|
+
* `writable` answer both mean what they say.
|
|
37
|
+
* - **An RPC stub**: presence means nothing, so the missing-verb refusal at
|
|
38
|
+
* construction is skipped rather than pretended, and writability comes from
|
|
39
|
+
* the door's own `version().writes` declaration. Until that reply lands,
|
|
40
|
+
* `writable` is `"unknown"` — an honest third state, not a guess — and a
|
|
41
|
+
* `TypeError` from a write verb on an undeclared door is TRANSLATED into
|
|
42
|
+
* this package's typed refusal so a consumer switches on one taxonomy
|
|
43
|
+
* rather than sniffing runtime messages.
|
|
44
|
+
*/
|
|
45
|
+
import { RECORD_DOOR_CONTRACT_VERSION } from "./contract.js";
|
|
46
|
+
import { recordDoorError, rehydrateRecordResult, RecordDoorError } from "./errors.js";
|
|
47
|
+
/** The namespace rule the record substrate itself enforces. */
|
|
48
|
+
const NAMESPACE_RE = /^[a-z0-9-]+$/;
|
|
49
|
+
/**
|
|
50
|
+
* A property no record door will ever serve. If reading it off the binding
|
|
51
|
+
* yields a callable, the binding answers every property access with one — it
|
|
52
|
+
* is an RPC stub, and nothing about it can be learned by looking.
|
|
53
|
+
*/
|
|
54
|
+
const RPC_PROBE = "__raybaseRecordDoorRpcProbe__";
|
|
55
|
+
/** The verbs every door carries, whichever entrypoint it is. */
|
|
56
|
+
const REQUIRED_VERBS = [
|
|
57
|
+
"version",
|
|
58
|
+
"get",
|
|
59
|
+
"query",
|
|
60
|
+
"materialize",
|
|
61
|
+
"list",
|
|
62
|
+
"count",
|
|
63
|
+
"reverseRefs"
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* The per-isolate contract memo.
|
|
67
|
+
*
|
|
68
|
+
* Keyed by the door stub itself, then by the pinned version. A Worker's
|
|
69
|
+
* binding stubs are built with `env` once per isolate, so a WeakMap on the
|
|
70
|
+
* stub memoizes for exactly the isolate's life — and two clients against
|
|
71
|
+
* two different doors cannot share an answer even when their bindings are
|
|
72
|
+
* spelled the same, which a name-keyed cache would let happen.
|
|
73
|
+
*
|
|
74
|
+
* It memoizes the SERVED VERSION, not merely "the check passed". A Worker
|
|
75
|
+
* builds a fresh client per request over one isolate-lifetime stub, so the
|
|
76
|
+
* second client and every one after it must be able to read the door's
|
|
77
|
+
* declared capabilities out of the memo. Memoizing a bare `Promise<void>`
|
|
78
|
+
* meant only the isolate's first client ever saw `version().writes`, and
|
|
79
|
+
* every later request read `"unknown"` forever.
|
|
80
|
+
*/
|
|
81
|
+
const contractChecks = new WeakMap();
|
|
82
|
+
export function openRecordDoor(options) {
|
|
83
|
+
const { env, binding, namespace } = options;
|
|
84
|
+
const expect = options.expect ?? RECORD_DOOR_CONTRACT_VERSION;
|
|
85
|
+
if (typeof binding !== "string" || binding.length === 0) {
|
|
86
|
+
throw new Error("openRecordDoor requires a binding name, e.g. binding: \"STORE_DOOR\"");
|
|
87
|
+
}
|
|
88
|
+
if (typeof namespace !== "string" || !NAMESPACE_RE.test(namespace)) {
|
|
89
|
+
throw new Error(`openRecordDoor: namespace ${JSON.stringify(namespace)} is not a legal record namespace ` +
|
|
90
|
+
"(lowercase letters, digits and hyphens). The namespace a mount declares must match the " +
|
|
91
|
+
"one its binding's props carry; nothing at runtime can check that for you.");
|
|
92
|
+
}
|
|
93
|
+
const bag = env;
|
|
94
|
+
const candidate = bag === null || bag === undefined ? undefined : bag[binding];
|
|
95
|
+
if (candidate === undefined || candidate === null || typeof candidate !== "object") {
|
|
96
|
+
throw new Error(`openRecordDoor: no service binding named ${JSON.stringify(binding)} on this Worker's env. ` +
|
|
97
|
+
"Declare it in wrangler.jsonc with the door's entrypoint and its {actor, namespace} props.");
|
|
98
|
+
}
|
|
99
|
+
const stub = candidate;
|
|
100
|
+
/**
|
|
101
|
+
* Does this binding answer every property access with a callable?
|
|
102
|
+
*
|
|
103
|
+
* A Cloudflare RPC service-binding stub does, so the probe below is a
|
|
104
|
+
* function on a real stub and `undefined` on any ordinary object. This one
|
|
105
|
+
* fact decides which of the two checks that follow are meaningful.
|
|
106
|
+
*/
|
|
107
|
+
const isRpcStub = typeof stub[RPC_PROBE] === "function";
|
|
108
|
+
if (!isRpcStub) {
|
|
109
|
+
// On a plain binding, absence is absence: a missing verb is a genuinely
|
|
110
|
+
// wrong object and saying so at construction beats a TypeError at the
|
|
111
|
+
// first call. On an RPC stub this check is skipped rather than performed
|
|
112
|
+
// and believed — every verb would "be there", including on a service
|
|
113
|
+
// that has never heard of a record door. What catches THAT case is the
|
|
114
|
+
// translation in `notARecordDoor` below, at the first call that goes out.
|
|
115
|
+
const missing = REQUIRED_VERBS.filter((verb) => typeof stub[verb] !== "function");
|
|
116
|
+
if (missing.length > 0) {
|
|
117
|
+
throw new Error(`openRecordDoor: the binding ${JSON.stringify(binding)} is not a record door — it carries no ` +
|
|
118
|
+
`${missing.join(", ")}. Check the binding's "entrypoint" in wrangler.jsonc.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Writability, as far as this client honestly knows it right now.
|
|
123
|
+
*
|
|
124
|
+
* A plain binding settles it at construction and never moves. An RPC stub
|
|
125
|
+
* starts `"unknown"` and moves exactly once, when a `version()` reply
|
|
126
|
+
* declares `writes`.
|
|
127
|
+
*/
|
|
128
|
+
let writable = isRpcStub
|
|
129
|
+
? "unknown"
|
|
130
|
+
: typeof stub.append === "function" && typeof stub.transition === "function";
|
|
131
|
+
/** Record what a `version()` reply declared, if it declared anything. */
|
|
132
|
+
const learnFrom = (served) => {
|
|
133
|
+
if (isRpcStub && typeof served.writes === "boolean")
|
|
134
|
+
writable = served.writes;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* The runtime's method-missing signal, and only for this verb's own name.
|
|
138
|
+
*
|
|
139
|
+
* workerd refuses a method the bound entrypoint does not carry with a
|
|
140
|
+
* TypeError whose message names the method — the observed shape is
|
|
141
|
+
* `The RPC receiver does not implement the method "append"`. Only that
|
|
142
|
+
* exact situation may be read as "the door does not carry this verb":
|
|
143
|
+
* any other TypeError — a broken door, a serialization failure, a
|
|
144
|
+
* mis-invocation like the extracted-`.call` form that filed
|
|
145
|
+
* fnd-2026-08-31-mount-write-call-invocation — propagates raw, because
|
|
146
|
+
* loud beats mislabeled. If workerd's wording ever shifts, this
|
|
147
|
+
* translation stops firing and consumers see the raw TypeError:
|
|
148
|
+
* fail-open to noise, never to a wrong diagnosis.
|
|
149
|
+
*/
|
|
150
|
+
const isVerbMissing = (ex, verb) => ex instanceof TypeError &&
|
|
151
|
+
ex.message.includes("does not implement") &&
|
|
152
|
+
ex.message.includes(`"${verb}"`);
|
|
153
|
+
/**
|
|
154
|
+
* The bound service does not speak this contract at all.
|
|
155
|
+
*
|
|
156
|
+
* Only reachable on an RPC stub: a plain binding without `version()` was
|
|
157
|
+
* refused at construction. There, `version`'s own method-missing TypeError
|
|
158
|
+
* (see `isVerbMissing`) is the runtime saying "no such method", which is
|
|
159
|
+
* the only signal the seam gives that the binding points at the wrong
|
|
160
|
+
* service — so it is translated into the taxonomy's own `invalid_binding`
|
|
161
|
+
* rather than propagating as a bare runtime error a consumer would have to
|
|
162
|
+
* sniff. Any other TypeError is a different defect and is left alone.
|
|
163
|
+
*/
|
|
164
|
+
const notARecordDoor = (verb, cause) => recordDoorError("invalid_binding", {
|
|
165
|
+
binding,
|
|
166
|
+
verb,
|
|
167
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
168
|
+
}, `invalid_binding: the service bound at '${binding}' does not speak the record-door contract — ` +
|
|
169
|
+
`calling '${verb}' on it found no such method. Check the binding's "service" and ` +
|
|
170
|
+
'"entrypoint" in wrangler.jsonc.');
|
|
171
|
+
/**
|
|
172
|
+
* `version()` against the stub, with the not-a-door translation. Kept
|
|
173
|
+
* separate from `rehydrateRecordResult` deliberately: only the CALL is
|
|
174
|
+
* guarded, so a typed refusal the door itself answers with propagates
|
|
175
|
+
* untouched instead of being swallowed by a catch meant for the seam.
|
|
176
|
+
*/
|
|
177
|
+
const askVersion = async () => {
|
|
178
|
+
let reply;
|
|
179
|
+
try {
|
|
180
|
+
reply = await stub.version();
|
|
181
|
+
}
|
|
182
|
+
catch (ex) {
|
|
183
|
+
// Same razor as the write verbs: only the method-missing shape that
|
|
184
|
+
// names `version` itself means "not a record door" — any other
|
|
185
|
+
// TypeError is a different defect and propagates raw.
|
|
186
|
+
if (isRpcStub && isVerbMissing(ex, "version"))
|
|
187
|
+
throw notARecordDoor("version", ex);
|
|
188
|
+
throw ex;
|
|
189
|
+
}
|
|
190
|
+
const served = rehydrateRecordResult(reply);
|
|
191
|
+
learnFrom(served);
|
|
192
|
+
return served;
|
|
193
|
+
};
|
|
194
|
+
/** The memoized contract check, resolving to the version the door served. */
|
|
195
|
+
const checked = () => {
|
|
196
|
+
let byVersion = contractChecks.get(stub);
|
|
197
|
+
if (byVersion === undefined) {
|
|
198
|
+
byVersion = new Map();
|
|
199
|
+
contractChecks.set(stub, byVersion);
|
|
200
|
+
}
|
|
201
|
+
const memo = byVersion.get(expect);
|
|
202
|
+
if (memo !== undefined)
|
|
203
|
+
return memo;
|
|
204
|
+
const check = (async () => {
|
|
205
|
+
const served = await askVersion();
|
|
206
|
+
if (served.contract !== expect) {
|
|
207
|
+
throw recordDoorError("contract_mismatch", { expected: expect, served: served.contract }, `contract_mismatch: this client pins record-door contract '${expect}' and the door bound at ` +
|
|
208
|
+
`'${binding}' serves '${served.contract}' (deployment '${served.deployment}') — refusing ` +
|
|
209
|
+
"rather than guessing which shape is live");
|
|
210
|
+
}
|
|
211
|
+
return served;
|
|
212
|
+
})();
|
|
213
|
+
byVersion.set(expect, check);
|
|
214
|
+
check.catch((ex) => {
|
|
215
|
+
// Two codes are terminal and stay memoized, because both are deploy
|
|
216
|
+
// configuration rather than weather: `contract_mismatch` (this door
|
|
217
|
+
// serves another shape) and `invalid_binding` (this binding points at
|
|
218
|
+
// a service that is not a record door). Neither resolves by being
|
|
219
|
+
// asked again inside one isolate, and re-asking would price a round
|
|
220
|
+
// trip on every verb of a permanently misconfigured Worker.
|
|
221
|
+
//
|
|
222
|
+
// Anything else — the door unreachable, the seam flapping — is the
|
|
223
|
+
// world's unreliability and must not poison the isolate.
|
|
224
|
+
const terminal = ex instanceof RecordDoorError &&
|
|
225
|
+
(ex.code === "contract_mismatch" || ex.code === "invalid_binding");
|
|
226
|
+
if (!terminal)
|
|
227
|
+
byVersion.delete(expect);
|
|
228
|
+
});
|
|
229
|
+
return check;
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* The gate every verb passes: the pin is checked, and this client has read
|
|
233
|
+
* whatever the door declared about itself.
|
|
234
|
+
*
|
|
235
|
+
* The `learnFrom` is here rather than only inside the check because the
|
|
236
|
+
* check is memoized per STUB and this client may be the tenth one built
|
|
237
|
+
* over it. A client that never ran the check itself still learns from the
|
|
238
|
+
* reply the check kept.
|
|
239
|
+
*/
|
|
240
|
+
const ready = async () => {
|
|
241
|
+
learnFrom(await checked());
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* The typed read-door refusal, with the reason it was reached.
|
|
245
|
+
*
|
|
246
|
+
* All three forms name the same fact — this binding cannot write — and
|
|
247
|
+
* differ in how the client learned it, because that is what an operator
|
|
248
|
+
* needs to tell "I bound the read entrypoint" from "the door is older than
|
|
249
|
+
* the declaration" from "the door says it writes and does not".
|
|
250
|
+
*/
|
|
251
|
+
const noWriteVerb = (verb, how, cause) => {
|
|
252
|
+
const why = how === "declared"
|
|
253
|
+
? `its version() reply declares writes: false, so it carries no '${verb}' verb`
|
|
254
|
+
: how === "runtime"
|
|
255
|
+
? `calling '${verb}' on it found no such method, and it declares no writability of its own`
|
|
256
|
+
: `it carries no '${verb}' verb at all`;
|
|
257
|
+
const detail = {
|
|
258
|
+
actor: "(read binding)",
|
|
259
|
+
verb,
|
|
260
|
+
family: "(any)",
|
|
261
|
+
id: "(none)"
|
|
262
|
+
};
|
|
263
|
+
if (cause !== undefined) {
|
|
264
|
+
detail.cause = cause instanceof Error ? cause.message : String(cause);
|
|
265
|
+
}
|
|
266
|
+
return recordDoorError("forbidden", detail, `forbidden: the binding '${binding}' is a read door — ${why}. ` +
|
|
267
|
+
"Which door a Worker holds is deploy configuration, not something code can change.");
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
binding,
|
|
271
|
+
namespace,
|
|
272
|
+
contract: expect,
|
|
273
|
+
get writable() {
|
|
274
|
+
return writable;
|
|
275
|
+
},
|
|
276
|
+
async version() {
|
|
277
|
+
return askVersion();
|
|
278
|
+
},
|
|
279
|
+
async assertContract() {
|
|
280
|
+
await ready();
|
|
281
|
+
},
|
|
282
|
+
async get(id) {
|
|
283
|
+
await ready();
|
|
284
|
+
return rehydrateRecordResult(await stub.get(id));
|
|
285
|
+
},
|
|
286
|
+
async query(q) {
|
|
287
|
+
await ready();
|
|
288
|
+
return rehydrateRecordResult(await stub.query(q));
|
|
289
|
+
},
|
|
290
|
+
async list(q) {
|
|
291
|
+
await ready();
|
|
292
|
+
return rehydrateRecordResult(await stub.list(q));
|
|
293
|
+
},
|
|
294
|
+
async count(args) {
|
|
295
|
+
await ready();
|
|
296
|
+
return rehydrateRecordResult(await stub.count(args));
|
|
297
|
+
},
|
|
298
|
+
async reverseRefs(id, opts) {
|
|
299
|
+
await ready();
|
|
300
|
+
return rehydrateRecordResult(await stub.reverseRefs(id, opts));
|
|
301
|
+
},
|
|
302
|
+
async materialize(prefix) {
|
|
303
|
+
await ready();
|
|
304
|
+
// The wire pages; the caller does not. A page always moves — the
|
|
305
|
+
// door emits an oversized single entry alone rather than stalling —
|
|
306
|
+
// so this walk terminates on any corpus the door will serve.
|
|
307
|
+
//
|
|
308
|
+
// A paged materialize is NOT a snapshot: the cursor is a position in
|
|
309
|
+
// a deterministic order, not a point in time, so a multi-page walk
|
|
310
|
+
// taken while writes land under the same prefix can repeat an entry
|
|
311
|
+
// or miss one. Walk a quiesced namespace when that matters.
|
|
312
|
+
async function* pages() {
|
|
313
|
+
let cursor;
|
|
314
|
+
do {
|
|
315
|
+
const page = rehydrateRecordResult(await stub.materialize(prefix, cursor));
|
|
316
|
+
yield* page.entries;
|
|
317
|
+
cursor = page.cursor;
|
|
318
|
+
} while (cursor !== undefined);
|
|
319
|
+
}
|
|
320
|
+
return pages();
|
|
321
|
+
},
|
|
322
|
+
// The two write verbs share one shape, spelled out at each so neither
|
|
323
|
+
// drifts. Refuse BEFORE calling when writability is known false; call
|
|
324
|
+
// when it is true or unknown; and translate the runtime's method-missing
|
|
325
|
+
// TypeError into the typed refusal only while writability is undeclared
|
|
326
|
+
// AND the error names this verb's own method. A door that declared
|
|
327
|
+
// `writes: true` and still throws is either lying or broken, and any
|
|
328
|
+
// TypeError that is not this verb's method-missing is a real defect —
|
|
329
|
+
// translating either would hide it behind a refusal that reads as
|
|
330
|
+
// configuration (fnd-2026-08-31-mount-write-call-invocation was masked
|
|
331
|
+
// exactly that way for an evening).
|
|
332
|
+
//
|
|
333
|
+
// Each verb is invoked DIRECTLY on the stub, never extracted and
|
|
334
|
+
// re-invoked via Function.prototype.call: workerd's RPC proxy answers
|
|
335
|
+
// the `.call` property access as an RPC method named "call", so the
|
|
336
|
+
// extracted form throws `does not implement the method "call"` on
|
|
337
|
+
// every real binding.
|
|
338
|
+
async append(body) {
|
|
339
|
+
await ready();
|
|
340
|
+
// The second clause is the optional type's, not a second policy: a
|
|
341
|
+
// plain binding missing the verb already read `false` above.
|
|
342
|
+
if (writable === false || stub.append === undefined) {
|
|
343
|
+
throw noWriteVerb("append", isRpcStub ? "declared" : "absent");
|
|
344
|
+
}
|
|
345
|
+
let reply;
|
|
346
|
+
try {
|
|
347
|
+
reply = await stub.append(body);
|
|
348
|
+
}
|
|
349
|
+
catch (ex) {
|
|
350
|
+
if (writable !== true && isVerbMissing(ex, "append")) {
|
|
351
|
+
throw noWriteVerb("append", "runtime", ex);
|
|
352
|
+
}
|
|
353
|
+
throw ex;
|
|
354
|
+
}
|
|
355
|
+
return rehydrateRecordResult(reply);
|
|
356
|
+
},
|
|
357
|
+
async transition(id, expected, next, event) {
|
|
358
|
+
await ready();
|
|
359
|
+
if (writable === false || stub.transition === undefined) {
|
|
360
|
+
throw noWriteVerb("transition", isRpcStub ? "declared" : "absent");
|
|
361
|
+
}
|
|
362
|
+
let reply;
|
|
363
|
+
try {
|
|
364
|
+
reply = await stub.transition(id, expected, next, event);
|
|
365
|
+
}
|
|
366
|
+
catch (ex) {
|
|
367
|
+
if (writable !== true && isVerbMissing(ex, "transition")) {
|
|
368
|
+
throw noWriteVerb("transition", "runtime", ex);
|
|
369
|
+
}
|
|
370
|
+
throw ex;
|
|
371
|
+
}
|
|
372
|
+
return rehydrateRecordResult(reply);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|