@raybase/record-mount 0.0.2 → 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.
@@ -0,0 +1,149 @@
1
+ /**
2
+ * A contract-conformant, in-memory record door — the dev-time half.
3
+ *
4
+ * It exists so that this package's own suite drives the real client
5
+ * against something that answers the wire contract's shapes, and so that a
6
+ * consuming app's tests can exercise its mount without a deployed Worker,
7
+ * a service binding, or a network. It is exported for exactly that.
8
+ *
9
+ * **What it is honest about.** It implements the contract's *shapes* — the
10
+ * result envelope, the closed taxonomy, the Row projection, `version()`,
11
+ * keyset paging, append idempotency by content hash, the ordering rule
12
+ * that `forbidden` outranks `not_found`. It is generic: no family, no id
13
+ * prefix and no lifecycle vocabulary is baked in, and every one of those
14
+ * is a hook with a documented generic default.
15
+ *
16
+ * **What it is not.** It is not the door. Bytes that this double accepts
17
+ * are not thereby proven acceptable to a deployed door — its frontmatter
18
+ * reader is this package's own, written to this package's own dialect, so
19
+ * a suite green against it is a builder-authored oracle for wire
20
+ * acceptance and nothing more. The real-door cross-check is a consumer-side
21
+ * act against the running door itself, and this double does not stand in
22
+ * for it. Its materialize path layout is its own invention (`family/id`),
23
+ * because the real one belongs to the consumer's substrate.
24
+ *
25
+ * ## Seam fidelity: why the default is a Proxy
26
+ *
27
+ * The double used to hand back a plain object that simply OMITTED
28
+ * `append`/`transition` for a read door. That is not how the production seam
29
+ * behaves, and the difference cost a real defect: a Cloudflare RPC
30
+ * service-binding stub answers EVERY property access with a callable, so
31
+ * every property-presence check the client made read `true` against a
32
+ * deployed read door while reading `false` against this fixture. The suite
33
+ * was green on a shape that does not exist in production — a self-authored
34
+ * oracle in its purest form.
35
+ *
36
+ * So by default the double presents as an RPC-faithful stub: a Proxy whose
37
+ * every property is a function, where calling one the underlying door does
38
+ * not carry rejects with a `TypeError` imitating the runtime's method-missing
39
+ * shape. Pass `rpc: false` for the plain object, which is the honest fixture
40
+ * for an in-process mount that really does hold a plain implementation.
41
+ *
42
+ * The imitation is an imitation, but its SHAPE is load-bearing: the client
43
+ * translates a `TypeError` only when its message carries the observed
44
+ * method-missing form — `does not implement` naming the verb — so the double
45
+ * reproduces that form. Do not pin the full string in a test as if it were
46
+ * workerd's contract; the wording is workerd's to change, and when it changes
47
+ * the client deliberately stops translating rather than guessing.
48
+ */
49
+ import type { RecordDoorStub, RecordEnvelope, RecordErrorCode, RecordId } from "./contract.js";
50
+ export type RecordDoorDoubleOptions = {
51
+ /** What `version()` serves. Set it to something else to drive the
52
+ * client's fail-closed contract pin. */
53
+ contract?: string;
54
+ deployment?: string;
55
+ /** A read door has no `append`/`transition` at all. Default true. */
56
+ writable?: boolean;
57
+ /**
58
+ * Present as a Cloudflare RPC service-binding stub: a Proxy answering
59
+ * EVERY property access with a callable, where a verb this door does not
60
+ * carry rejects with a method-missing `TypeError` rather than being
61
+ * absent. **Default true** — this is what the production seam does, and a
62
+ * fixture that omits methods tests a shape no deployment has.
63
+ *
64
+ * Pass `false` for the plain object: the honest fixture for an in-process
65
+ * mount over a real implementation, where absence really is absence.
66
+ */
67
+ rpc?: boolean;
68
+ /**
69
+ * What `version()` declares in its `writes` field.
70
+ *
71
+ * Defaults to whatever `writable` says, which is what a door built after
72
+ * the field existed does. Pass `"omit"` to simulate an OLDER door that
73
+ * predates the declaration — the case where the client must stay
74
+ * `"unknown"` and translate whatever the runtime says instead.
75
+ */
76
+ declaredWrites?: boolean | "omit";
77
+ /**
78
+ * Answer every verb but `version()` with `invalid_binding`, as a door
79
+ * whose caller's props name no usable actor or namespace does.
80
+ * `version()` is the deliberate carve-out: the client's pin calls it
81
+ * before the contract is known, so it must answer on a props-less
82
+ * binding.
83
+ */
84
+ invalidBinding?: {
85
+ missing?: string[];
86
+ actor?: string;
87
+ served?: string[];
88
+ };
89
+ /** A handle built without a serializer refuses `transition` structurally. */
90
+ serializer?: boolean;
91
+ /**
92
+ * Family from an id — the real door's prefix-to-family map.
93
+ *
94
+ * Supply it and the double enforces that a body's `type` field equals the
95
+ * family its id's prefix names, exactly as the door does. Omit it and the
96
+ * double takes the family from the body's own `type`, falling back to the
97
+ * id's prefix.
98
+ *
99
+ * The default is deliberately NOT "prefix equals family": in a real
100
+ * corpus a prefix and a family name are different strings (`dec` names
101
+ * `decision`), so a default that demanded equality would refuse every
102
+ * realistic body and teach consumers to work around their own fixture.
103
+ */
104
+ familyOfId?: (id: string) => string | null;
105
+ /** The id shape. Default: `<prefix>-<yyyy-mm-dd>-<slug>`. */
106
+ idPattern?: RegExp;
107
+ /** The lifecycle frontmatter key per family, or null for a family that
108
+ * is final at filing. Default: `"status"` for every family. */
109
+ lifecycleField?: (family: string) => string | null;
110
+ /** Whether an edge is legal. Default: every edge between non-empty
111
+ * values is legal. A note (`expected === next`) never asks. */
112
+ isLegalTransition?: (family: string, from: string, to: string) => boolean;
113
+ /** The write grant. Default: every write is granted. */
114
+ canWrite?: (input: {
115
+ verb: "append" | "transition";
116
+ family: string;
117
+ id: RecordId;
118
+ }) => boolean;
119
+ /** How a Row's headline derives. Default: the body's first `# ` H1,
120
+ * else its first non-blank prose line, else the id's slug. */
121
+ headline?: (input: {
122
+ envelope: RecordEnvelope;
123
+ body: string;
124
+ }) => string;
125
+ /** The refs edge. Default: the `refs` list field plus a scalar `ref`. */
126
+ refsOf?: (fields: Record<string, unknown>) => string[];
127
+ /** Entries per `materialize` page. Default 500, as the contract's. */
128
+ materializePageEntries?: number;
129
+ };
130
+ export type RecordDoorDouble = RecordDoorStub & {
131
+ /**
132
+ * Make the next call of one verb refuse with a code the double cannot
133
+ * reach on its own — `unavailable` when storage fails, `corrupt` when
134
+ * an integrity violation is discovered. One shot, consumed on use.
135
+ */
136
+ failNext(verb: keyof RecordDoorStub, error: {
137
+ code: RecordErrorCode;
138
+ detail?: Record<string, unknown>;
139
+ message?: string;
140
+ }): void;
141
+ /** Every id the double holds, in insertion order. */
142
+ ids(): RecordId[];
143
+ };
144
+ /** Build the door double. */
145
+ export declare function createRecordDoorDouble(options?: RecordDoorDoubleOptions): RecordDoorDouble;
146
+ /** An `env`-shaped object carrying the double under a binding name — what
147
+ * `openRecordDoor({ env, binding })` reads. */
148
+ export declare function recordDoorEnv<Name extends string>(binding: Name, door: RecordDoorStub): Record<Name, RecordDoorStub>;
149
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AASH,OAAO,KAAK,EAGV,cAAc,EACd,cAAc,EACd,eAAe,EAEf,QAAQ,EAMT,MAAM,eAAe,CAAC;AAYvB,MAAM,MAAM,uBAAuB,GAAG;IACpC;6CACyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC3E,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC3C,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;oEACgE;IAChE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACnD;oEACgE;IAChE,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1E,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QACjB,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC;QACf,EAAE,EAAE,QAAQ,CAAC;KACd,KAAK,OAAO,CAAC;IACd;mEAC+D;IAC/D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IACzE,yEAAyE;IACzE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;IACvD,sEAAsE;IACtE,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,cAAc,EAAE,KAAK,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjI,qDAAqD;IACrD,GAAG,IAAI,QAAQ,EAAE,CAAC;CACnB,CAAC;AAQF,6BAA6B;AAC7B,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,uBAA4B,GACpC,gBAAgB,CAgXlB;AAuED;gDACgD;AAChD,wBAAgB,aAAa,CAAC,IAAI,SAAS,MAAM,EAC/C,OAAO,EAAE,IAAI,EACb,IAAI,EAAE,cAAc,GACnB,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAE9B"}