@volter/world-core 3.0.38 → 3.0.39

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.
Files changed (179) hide show
  1. package/app-route.cjs +26 -25
  2. package/app-route.d.cts +1 -0
  3. package/dist/app-route.cjs +26 -25
  4. package/dist/app-route.d.cts +1 -0
  5. package/dist/generated/pack-facts.json +409 -352
  6. package/dist/inject.cjs +474 -95
  7. package/dist/network-policy.cjs +108 -13
  8. package/dist/network-policy.d.cts +8 -3
  9. package/dist/src/actions.js +2 -1
  10. package/dist/src/anthropic-wire.d.ts +2 -0
  11. package/dist/src/anthropic-wire.js +5 -0
  12. package/dist/src/attestation.d.ts +6 -0
  13. package/dist/src/attestation.js +27 -0
  14. package/dist/src/bytes.d.ts +3 -0
  15. package/dist/src/bytes.js +17 -0
  16. package/dist/src/changeset.js +2 -1
  17. package/dist/src/clickhouse/sql.js +73 -14
  18. package/dist/src/derived-core.d.ts +210 -32
  19. package/dist/src/derived-core.js +510 -244
  20. package/dist/src/derived-real.d.ts +1 -1
  21. package/dist/src/derived-real.js +704 -93
  22. package/dist/src/derived.d.ts +18 -2
  23. package/dist/src/derived.js +21 -2
  24. package/dist/src/events.d.ts +28 -45
  25. package/dist/src/events.js +40 -58
  26. package/dist/src/exact-json.d.ts +10 -0
  27. package/dist/src/exact-json.js +98 -0
  28. package/dist/src/executor.d.ts +17 -0
  29. package/dist/src/executor.js +91 -30
  30. package/dist/src/file-response.js +1 -1
  31. package/dist/src/form.d.ts +8 -0
  32. package/dist/src/form.js +76 -0
  33. package/dist/src/git/format.d.ts +16 -0
  34. package/dist/src/git/format.js +110 -0
  35. package/dist/src/git/index.d.ts +1 -0
  36. package/dist/src/git/index.js +1 -0
  37. package/dist/src/git/lfs.d.ts +1 -1
  38. package/dist/src/git/lfs.js +4 -3
  39. package/dist/src/graphql-wire.d.ts +6 -0
  40. package/dist/src/graphql-wire.js +6 -3
  41. package/dist/src/grpc-wire.js +2 -1
  42. package/dist/src/head.d.ts +1 -0
  43. package/dist/src/head.js +15 -4
  44. package/dist/src/held-reads.d.ts +6 -0
  45. package/dist/src/held-reads.js +60 -0
  46. package/dist/src/history.js +5 -4
  47. package/dist/src/host-port.d.ts +5 -0
  48. package/dist/src/host-port.js +14 -0
  49. package/dist/src/index.d.ts +11 -3
  50. package/dist/src/index.js +9 -4
  51. package/dist/src/log.js +12 -7
  52. package/dist/src/machines.d.ts +81 -5
  53. package/dist/src/machines.js +125 -9
  54. package/dist/src/managed-database.d.ts +15 -11
  55. package/dist/src/managed-database.js +38 -37
  56. package/dist/src/multipart.d.ts +21 -3
  57. package/dist/src/multipart.js +112 -38
  58. package/dist/src/observe.d.ts +3 -0
  59. package/dist/src/observe.js +6 -2
  60. package/dist/src/openai-wire.d.ts +16 -0
  61. package/dist/src/openai-wire.js +43 -12
  62. package/dist/src/pack-fetch.d.ts +4 -6
  63. package/dist/src/pack-fetch.js +189 -57
  64. package/dist/src/packRegistry.d.ts +7 -1
  65. package/dist/src/packRegistry.js +20 -3
  66. package/dist/src/private-transport.d.ts +1 -0
  67. package/dist/src/private-transport.js +11 -0
  68. package/dist/src/protobuf.js +10 -4
  69. package/dist/src/rateBudget.d.ts +8 -1
  70. package/dist/src/rateBudget.js +1 -0
  71. package/dist/src/redis/engine.d.ts +14 -5
  72. package/dist/src/redis/engine.js +271 -72
  73. package/dist/src/redis/frames.d.ts +17 -0
  74. package/dist/src/redis/frames.js +123 -0
  75. package/dist/src/redis/index.d.ts +6 -0
  76. package/dist/src/redis/index.js +6 -0
  77. package/dist/src/redis/json.d.ts +2 -0
  78. package/dist/src/redis/json.js +14 -0
  79. package/dist/src/redis/lua-libs.d.ts +4 -0
  80. package/dist/src/redis/lua-libs.js +173 -0
  81. package/dist/src/redis/lua.d.ts +1 -0
  82. package/dist/src/redis/lua.js +36 -0
  83. package/dist/src/redis/member-storage.d.ts +5 -0
  84. package/dist/src/redis/member-storage.js +75 -0
  85. package/dist/src/redis/resp2.d.ts +4 -0
  86. package/dist/src/redis/resp2.js +29 -0
  87. package/dist/src/redis/stream.d.ts +14 -0
  88. package/dist/src/redis/stream.js +335 -0
  89. package/dist/src/remote-execute.d.ts +2 -2
  90. package/dist/src/request-body.d.ts +11 -0
  91. package/dist/src/request-body.js +11 -0
  92. package/dist/src/runtime.d.ts +6 -5
  93. package/dist/src/runtime.js +5 -5
  94. package/dist/src/s3/wire.d.ts +4 -4
  95. package/dist/src/s3/wire.js +5 -3
  96. package/dist/src/scenario.d.ts +22 -0
  97. package/dist/src/scenario.js +35 -31
  98. package/dist/src/serve-http.d.ts +2 -1
  99. package/dist/src/serve-http.js +10 -1
  100. package/dist/src/serve.js +7 -4
  101. package/dist/src/signing.d.ts +23 -5
  102. package/dist/src/signing.js +74 -11
  103. package/dist/src/sigv4.d.ts +3 -1
  104. package/dist/src/sigv4.js +4 -2
  105. package/dist/src/smtp.js +2 -1
  106. package/dist/src/sockets.js +8 -3
  107. package/dist/src/state-system.d.ts +3 -0
  108. package/dist/src/storage.js +5 -2
  109. package/dist/src/twin-fetch.d.ts +3 -1
  110. package/dist/src/twin-fetch.js +3 -2
  111. package/dist/src/vendor-call.d.ts +10 -3
  112. package/dist/src/vendor-call.js +58 -14
  113. package/dist/test-fixtures/attestation.SOURCE.md +1 -0
  114. package/dist/test-fixtures/attestation.json +50 -0
  115. package/generated/pack-facts.json +409 -352
  116. package/inject.cjs +474 -95
  117. package/network-policy.cjs +108 -13
  118. package/network-policy.d.cts +8 -3
  119. package/package.json +11 -4
  120. package/src/actions.ts +2 -1
  121. package/src/anthropic-wire.ts +6 -0
  122. package/src/attestation.ts +23 -0
  123. package/src/bytes.ts +21 -0
  124. package/src/changeset.ts +2 -1
  125. package/src/clickhouse/sql.ts +50 -16
  126. package/src/derived-core.ts +573 -249
  127. package/src/derived-real.ts +552 -42
  128. package/src/derived.ts +23 -4
  129. package/src/events.ts +54 -85
  130. package/src/exact-json.ts +71 -0
  131. package/src/executor.ts +86 -27
  132. package/src/file-response.ts +1 -1
  133. package/src/form.ts +66 -0
  134. package/src/git/format.ts +89 -0
  135. package/src/git/index.ts +1 -0
  136. package/src/git/lfs.ts +5 -4
  137. package/src/graphql-wire.ts +8 -3
  138. package/src/grpc-wire.ts +2 -1
  139. package/src/head.ts +15 -5
  140. package/src/held-reads.ts +54 -0
  141. package/src/history.ts +5 -4
  142. package/src/host-port.ts +9 -0
  143. package/src/index.ts +10 -4
  144. package/src/log.ts +11 -7
  145. package/src/machines.ts +135 -13
  146. package/src/managed-database.ts +48 -40
  147. package/src/multipart.ts +110 -35
  148. package/src/observe.ts +6 -3
  149. package/src/openai-wire.ts +45 -14
  150. package/src/pack-fetch.ts +173 -50
  151. package/src/packRegistry.ts +27 -4
  152. package/src/private-transport.ts +8 -0
  153. package/src/protobuf.ts +8 -4
  154. package/src/rateBudget.ts +6 -1
  155. package/src/redis/engine.ts +168 -63
  156. package/src/redis/frames.ts +53 -0
  157. package/src/redis/index.ts +6 -0
  158. package/src/redis/json.ts +13 -0
  159. package/src/redis/lua-libs.ts +62 -0
  160. package/src/redis/lua.ts +20 -0
  161. package/src/redis/member-storage.ts +58 -0
  162. package/src/redis/resp2.ts +25 -0
  163. package/src/redis/stream.ts +102 -0
  164. package/src/remote-execute.ts +2 -2
  165. package/src/request-body.ts +12 -0
  166. package/src/runtime.ts +6 -5
  167. package/src/s3/wire.ts +8 -5
  168. package/src/scenario.ts +34 -13
  169. package/src/serve-http.ts +11 -2
  170. package/src/serve.ts +4 -3
  171. package/src/signing.ts +71 -17
  172. package/src/sigv4.ts +5 -3
  173. package/src/smtp.ts +3 -2
  174. package/src/sockets.ts +8 -3
  175. package/src/state-system.ts +3 -0
  176. package/src/storage.ts +5 -2
  177. package/src/twin-fetch.ts +4 -3
  178. package/src/vendor-call.ts +59 -12
  179. package/test-fixtures/attestation.json +50 -0
@@ -1,3 +1,4 @@
1
+ import { parseExactJson, compareExactNumbers, exactInteger, isExactIntegerValue } from './exact-json.ts';
1
2
  // THE DERIVED CORE — what a derived pack does for an operation it declares no handler for
2
3
  // (docs/contributing/architecture.md, "Protocol 3"). The generated surface says what the
3
4
  // vendor's operations are; the pack's manifest says the vendor facts no spec carries (how ids look,
@@ -15,23 +16,26 @@ import { GitRefs } from './git/refs.ts';
15
16
  import { worldNow } from './world-clock.ts';
16
17
  import { worldEnvValue } from './world-env.ts';
17
18
  import { openSessions, socketWrite, type SocketDecl, type SocketSession } from './sockets.ts';
18
- import { MACHINE_POOL, poolFor, type MachinePool } from './machines.ts';
19
- import { askApplication, deliverEvents, deliverToApplication, type ApplicationAnswer, type ApplicationStandIn, type DeliveryAnswer, type EventRender, type EventScheme, type EventsDecl, type EventValues } from './events.ts';
19
+ import { awaitWrite, awaitCondition, heldReadsWrite } from './held-reads.ts';
20
+ import { MACHINE_POOL, poolFor, guardedMachinePool, type MachinePool, type MachineHttpReply, type LightPoolOptions } from './machines.ts';
21
+ import { askApplication, deliverEvents, type ApplicationAnswer, type ApplicationStandIn, type EventRender, type EventScheme, type EventsDecl, type EventValues } from './events.ts';
20
22
  import type { RateBudgetDeclaration } from './rateBudget.ts';
21
- import { handlerCrypto, hmac, lettersFrom, sha256, uuidFrom, type HandlerCrypto } from './signing.ts';
23
+ import { handlerCrypto, hmac, ksuidFrom, lettersFrom, sha256, uuidFrom, type HandlerCrypto } from './signing.ts';
22
24
  import { hashFieldValue } from './hash.ts';
23
25
  import { readResourceBlob, removeResourceBlob, writeResourceBlob } from './resource-blob.ts';
24
26
  import { managedDatabase, unboundDatabase, type ManagedDatabase } from './managed-database.ts';
25
27
  import { execRedisRun, type RedisDialect, type RunItem } from './redis/engine.ts';
26
28
  import { sendMail, type Mail, type SmtpRoute } from './smtp.ts';
29
+ import { ownField, parseBracketForm } from './form.ts';
30
+ export { ownField, parseBracketForm } from './form.ts';
27
31
  import { multipartBoundary, multipartParts, type MultipartPart } from './multipart.ts';
28
32
  import type { CorsDecl } from './cors.ts';
29
- import { projectOwnerResources, resolveSubjectId, subjectAliases } from './actions.ts';
33
+ import { listActions, projectOwnerResources, resolveSubjectId, subjectAliases, type TwinAction } from './actions.ts';
30
34
  import { vendorFetch } from './vendor-call.ts';
31
35
  import { packReferences } from './references.ts';
32
36
  import type { SubjectFields } from './hash.ts';
33
37
  import { twinPublicBase, withRequestScopes } from './twin-fetch.ts';
34
- import { inVendorMove, isReadOnlyRequest, runAsVendorMove } from './request-scope.ts';
38
+ import { inVendorMove, isReadOnlyRequest, runAsVendorMove, writesRefused } from './request-scope.ts';
35
39
  import type { DerivedCall, DerivedCoreOutcome, DerivedHandler, DerivedOperation } from './derived.ts';
36
40
  import type { PackDescriptor } from './packRegistry.ts';
37
41
  import type { ScenarioDecision, ScenarioHandler } from './scenario.ts';
@@ -57,7 +61,8 @@ export type Transition = {
57
61
  operation?: string;
58
62
  actor?: Actor;
59
63
  from: string[] | '*';
60
- /** the value it moves to; absent, the value stays (a guard that moves nothing) */
64
+ /** the value it moves to; absent, the value stays (a guard that moves nothing), and absent with a `refusal`, the
65
+ * operation is refused in the `from` states (a move another transition allows from there is taken first) */
61
66
  to?: string;
62
67
  effects?: Record<string, FieldRule>;
63
68
  /** placeholders `{from}`, `{to}`, `{field}`, `{id}` */
@@ -92,7 +97,7 @@ export function stateOf(decl: StateField, value: unknown): string {
92
97
  const w = d.when;
93
98
  if (w.absent !== undefined && (value === undefined || value === null) === w.absent) return d.state;
94
99
  if (w.equals !== undefined && value === w.equals) return d.state;
95
- if (w.gt !== undefined && typeof value === 'number' && value > w.gt) return d.state;
100
+ if (w.gt !== undefined && (typeof value === 'number' || isExactIntegerValue(value)) && compareExactNumbers(value, w.gt) > 0) return d.state;
96
101
  if (w.truthy !== undefined && Boolean(value) === w.truthy) return d.state;
97
102
  }
98
103
  return value === undefined || value === null ? String(decl.initial) : String(value);
@@ -106,8 +111,8 @@ export type ResourceDecl = {
106
111
  * surface names by the schema its GET answers): keyed by the path's parameter, it answers `defaults` under what was
107
112
  * written until first written, and an update merges into it, making it on first write */
108
113
  setting?: { defaults: Record<string, unknown> };
109
- /** this resource's id template, when it is not the manifest's (`ids.template`): `{uuid}`, `{letters:20}` (twenty
110
- * lowercase letters, a Supabase project ref), `{snowflake:<epoch ms>}` (Discord's ids), or a `{prefix}`/`{n}`
114
+ /** this resource's id template, when it is not the manifest's (`ids.template`): `{uuid}`, `{ksuid}` (Clerk's), `{letters:20}` (twenty
115
+ * lowercase letters, a Supabase project ref), `{digits:16}` (fixed-width decimal), `{snowflake:<epoch ms>}` (Discord's ids), or a `{prefix}`/`{n}`
111
116
  * template */
112
117
  ids?: string;
113
118
  /** fields the server assigns on create */
@@ -117,6 +122,20 @@ export type ResourceDecl = {
117
122
  notState?: string[];
118
123
  /** id-holding fields a caller may ask to receive embedded, and the resource each holds */
119
124
  embeds?: Record<string, string>;
125
+ /** `embeds` fields the vendor answers embedded on every answer, asked or not (its schema gives the object with no id
126
+ * alternative: Stripe's issuing card's `cardholder`): still stored as the id, hydrated from the current object */
127
+ alwaysEmbedded?: string[];
128
+ /** the vendor's own fields whose names begin with `_` (GitHub's `_links`, a HAL object's), which the vendor answers:
129
+ * answered like any field, where every other `_` field is the pack's bookkeeping and never answered */
130
+ vendorUnderscored?: string[];
131
+ /** Stored fields omitted from vendor views, without renaming them in retained state. Raw rows and
132
+ * ctx.own retain them for parent scope and semantics; get, rows, render and core answers omit them. */
133
+ viewOmit?: readonly string[];
134
+ /** fields the vendor answers as a list of the current children of another resource (Stripe's subscription `items`:
135
+ * its subscription_items), built on every answer from the child rows whose `by` field holds this id, in the order
136
+ * they were made, each through the child's own view and its embedding; never a stored copy. `list` is the list's own
137
+ * fields around `data`, where `{id}` is this resource's id and `"{count}"` the children's number */
138
+ collections?: Record<string, { resource: string; by: string; list?: Record<string, unknown> }>;
120
139
  /** this resource's deletion answer, when it is not the manifest's (`vector_store.deleted`) */
121
140
  deleted?: unknown;
122
141
  /** a child addressed under its parent: the path parameter naming the parent, the stored field that
@@ -125,7 +144,10 @@ export type ResourceDecl = {
125
144
  * that joins them into what `field` stores (`{owner}/{repo}`). `where` finds the parent by its stored
126
145
  * fields when its stored id is not what the path names (a repository stored as `repo:7` with `owner`
127
146
  * and `name`, addressed as `{owner}/{repo}`): each stored field against a template over the path. */
128
- parent?: { param?: string; params?: string[]; value?: string; field: string; resource: string; allowDeleted?: boolean; where?: Record<string, string> };
147
+ parent?: { param?: string; params?: string[]; value?: string; field: string; resource: string; allowDeleted?: boolean; where?: Record<string, string>;
148
+ /** private fields a child observed under its parent keeps from the parent's parameters (`_jurisdiction:
149
+ * '{jurisdiction}'`), where the child's own read names none and a later read under it needs one ("Refresh") */
150
+ keep?: Record<string, string> };
129
151
  /** a number the resource counts per parent (a repository's issue numbers, a thread's messages):
130
152
  * the stored field, set on create to one past the highest among its siblings */
131
153
  number?: { field: string };
@@ -163,18 +185,33 @@ export type ResourceDecl = {
163
185
  /** fields that name a subject as its id does (an organization by its id or its slug): a path's id and `ctx.find`
164
186
  * match either */
165
187
  alternateKeys?: string[];
188
+ /** fields that pair a subject a deploy settled with no vendor id with the vendor's copy a refresh observes (Slack's
189
+ * `channel_join` message, made by `conversations.join` and answered by no id: its `subtype` and `user`), where the
190
+ * `alternateKeys` do not; a subject missing one is never paired by them ("Refresh", adoption) */
191
+ adoptBy?: string[];
166
192
  /** the list's search parameter (`query`): a row matches on an `exact` field's whole value or a `partial` field's
167
193
  * substring, case-insensitively */
168
194
  search?: { param: string; exact?: string[]; partial?: string[] };
169
195
  /** fields that count this subject's children (an App's `installations_count`): the live subjects of `of` whose `by`
170
- * holds this one's id, only those with `present` set when it is named, plus `plus` (a customer's
196
+ * holds this one's id (or vendor `parentKey`), only those with `present` set when it is named and without
197
+ * any matching `unless` field, plus `plus` (a customer's
171
198
  * `next_invoice_sequence` is one past its numbered invoices). The kernel keeps each whenever a child is written. */
172
- counts?: Record<string, { of: string; by: string; present?: string; plus?: number }>;
199
+ counts?: Record<string, { of: string; by: string; parentKey?: string; present?: string; unless?: Record<string, unknown>; plus?: number }>;
173
200
  /** how the vendor's state of this type is read back into a root (the real-system adapters, "Refresh"): the list that
174
201
  * enumerates it (once per parent, under `parent`; `complete: false` when it is not the whole type; `items`, the answer's
175
202
  * path to them, when the spec does not name the list it answers), the one read of a singleton, or `none` with why the
176
- * vendor offers no read-back. Every stored resource the vendor holds declares one. */
177
- refresh?: { list: string; complete?: false; items?: string } | { get: string } | { none: string };
203
+ * vendor offers no read-back. Every stored resource the vendor holds declares one. `{ get, known: true }`: the vendor
204
+ * lists no whole type, so the World's own subjects of it are each read again by `get` (a 404 is one gone), and what
205
+ * those reads observe anchors its children's lists; completeness is those subjects alone. */
206
+ /** `next`: where an answer names the next page's cursor (AWS's `NextToken`), sent back as `cursor` (the same name
207
+ * unless said) in the query, or in the body of a list asked by POST (an RPC list: AWS JSON's `x-amz-target`); a
208
+ * map of parameter to answer path where a page names several (S3's `key-marker` and `upload-id-marker`); `more`, the
209
+ * answer's flag that another page follows (S3's `IsTruncated`). `headers`: fixed headers of the list, `{name}` filled
210
+ * from the parent's parameters; `variants`: header sets each listed in turn, one type whole across them (R2's
211
+ * jurisdictions). A blob read by an `operation` (a GET of the unit's surface, filled from the subject's fields and
212
+ * its parent's parameters) keeps the bytes in the World's blobs, their key in `field`, and in `headersField` the
213
+ * answer's headers `keepHeaders` names (a name ending `*` a prefix: `x-amz-meta-*`), both fields private. */
214
+ refresh?: ({ list: string; unpaginated?: true; keyed?: true; detail?: string; detailMerge?: string; detailWith?: Record<string, string>; complete?: false; items?: string; shape?: RefreshShape; next?: string | Record<string, string>; nextLink?: { header: string; rel: string; query: string; when?: Record<string, string> }; cursor?: string; more?: string } | { get: string; known?: true }) & { params?: Record<string, string>; headers?: Record<string, string>; variants?: ReadonlyArray<{ headers: Record<string, string> }>; blobs?: ReadonlyArray<{ path: string; key: string; resource?: string; fields?: Record<string, string> } | { operation: string; field: string; headers?: Record<string, string>; headersField?: string; keepHeaders?: string[] }>; query?: Record<string, string>; idAs?: string; graphql?: { query: string; variables?: Record<string, unknown>; items: string; pageInfo?: string; cursor?: string } } | { none: string };
178
215
  /** the other resources one create of this one makes, and where its answer names each's id (a JSON path) */
179
216
  companions?: Record<string, string>;
180
217
  /** the field telling a newer copy of a subject from an older (an `updated_at`): an ingested event older than the
@@ -191,7 +228,18 @@ export type IngestDecl = {
191
228
  object: string;
192
229
  /** event type → the resource it carries, where `events.types` read backwards and `<resource>.created|updated|deleted`
193
230
  * do not say it */
194
- types?: Record<string, { resource: string; deleted?: true }>;
231
+ types?: Record<string, {
232
+ resource: string; deleted?: true;
233
+ /** where this type carries its object, when not at `object` (GitHub's `issue`, `pull_request`, `comment`) */
234
+ object?: string;
235
+ /** the JSON path of the value the resource's `parent.field` holds, when the event names its parent beside the object
236
+ * (GitHub's `repository.full_name`, the `{owner}/{repo}` its children are stored under) */
237
+ parent?: string;
238
+ /** where the object names the subject's id when not as the resource does (Resend's email events: `email_id`) */
239
+ id?: string;
240
+ /** the fields the event's type itself says (Resend's `email.delivered` is the email's `last_event: 'delivered'`) */
241
+ fields?: Record<string, unknown>;
242
+ }>;
195
243
  /** an event answered instead of folded (Slack's url_verification): the body fields it matches, and the answer, a
196
244
  * `$body.<path>` read from the event or a literal */
197
245
  handshake?: { when: Record<string, string>; answer: string };
@@ -267,6 +315,8 @@ export type PerformHookArgs = {
267
315
  export type PerformHook = (args: PerformHookArgs) => Promise<import('./head.ts').PushOutcome>;
268
316
 
269
317
  export type DerivedManifest = {
318
+ /** Vendor data-plane hosts mapped to an already-running customer guest. */
319
+ machineRoutes?: ReadonlyArray<{ host: string; resource: string; where: Record<string, string>; name: string; port: number | string }>;
270
320
  /** The pack's descriptor, as data (docs/contributing/architecture.md, "The descriptor"), every field but `vendor`, which
271
321
  * is the manifest's: the pack registers `packOf(manifest)`, so its index is a fixed file. A lane has none. */
272
322
  descriptor?: Omit<PackDescriptor, 'vendor'>;
@@ -287,6 +337,11 @@ export type DerivedManifest = {
287
337
  /** operations that only read though nothing in the spec says so (a POST that returns data):
288
338
  * a read-only twin answers them */
289
339
  reads?: string[];
340
+ /** operations of the vendor's API that set an account up rather than use it: an operator registering an OAuth client
341
+ * on the vendor's own admin routes, as a person makes settings on the vendor's site. Each is recorded as a door's
342
+ * write is (the World's own), so a deploy never offers it to the vendor and the vendor-backed acceptance holds it as
343
+ * what the account already has; `why` says who performs it and where */
344
+ accountSetup?: Array<{ operation: string; why: string }>;
290
345
  /** how the vendor writes an instant: Unix seconds, Unix milliseconds (Clerk's `created_at`), or ISO 8601 */
291
346
  time: 'unix' | 'unix-ms' | 'iso';
292
347
  /** the vendor's CORS, as its gateway answers a browser (cors.ts): the pack's fetch is served behind `withCors` */
@@ -311,7 +366,8 @@ export type DerivedManifest = {
311
366
  * (`{value}` in its message is the header's value) */
312
367
  version?: { header: string; pattern: string; error: ErrorSpec };
313
368
  /** request headers that carry meaning to the vendor (a tenant header such as `Stripe-Account`): recorded with a write
314
- * so its perform sends them again (the real-system adapters, "What a pack declares"); `version.header` is too */
369
+ * so its perform sends them again (the real-system adapters, "What a pack declares"); `version.header` is too. A name
370
+ * ending `*` is a prefix: every header it begins (S3's `x-amz-meta-*`, an object's own metadata). */
315
371
  headers?: string[];
316
372
  /** the vendor's signed webhooks, folded into a root */
317
373
  ingest?: IngestDecl;
@@ -329,10 +385,11 @@ export type DerivedManifest = {
329
385
  * derived request, which it composes (`send`) ("Protocols a request cannot express", architecture) */
330
386
  performs?: Record<string, PerformHook>;
331
387
  /** `withParam`: the error names the path parameter that held the unknown id */
332
- notFound: { status: number; message: string; code?: string; kind?: string; withParam?: boolean };
388
+ notFound: { status: number; message: string; code?: string | number; kind?: string; withParam?: boolean };
333
389
  /** the list envelope (placeholders `{data}`, `{has_more}`, `{url}`, `{first_id}`, `{last_id}`),
334
- * its page size, its cursors, and where an unknown cursor starts: the first page or past the end */
335
- list: {
390
+ * its page size, its cursors, and where an unknown cursor starts: the first page or past the end.
391
+ * Absent when the unit serves no stored list; the core does not own lists without this declaration. */
392
+ list?: {
336
393
  style: 'envelope';
337
394
  envelope: unknown;
338
395
  limit: { param: string; default: number; max: number };
@@ -341,8 +398,8 @@ export type DerivedManifest = {
341
398
  unknownCursor?: 'start' | 'end';
342
399
  /** an opaque cursor over positions (`{next_cursor}` in the envelope; empty on the last page),
343
400
  * in place of id cursors: `base64-offset` is base64 of `{"o":<offset>}` */
344
- cursor?: { param: string; encoding: 'base64-offset' };
345
- /** numbered pages (1-based `page`, the size in `limit.param`), with the vendor's `Link` header
401
+ cursor?: { param: string; encoding: 'base64-offset' } | { param: string; encoding: 'offset-template'; template: string; link: { previous: string; next: string; results: string; cursor: string } };
402
+ /** numbered pages (1-based, the size in `limit.param`), with the vendor's `Link` header
346
403
  * naming the first, previous, next and last pages (`link`); the envelope may name `{total_count}` and `{max_page}` */
347
404
  page?: { param: string; link: boolean };
348
405
  /** offset paging: `param` names how many items to skip; the page sits under the operation's envelope key beside the
@@ -384,14 +441,19 @@ export type DerivedManifest = {
384
441
  /** operations that check a credential of their own, not the account's (Cloudflare's asset upload reads its upload
385
442
  * session's JWT from the same header): the gate leaves them to their handler */
386
443
  exempt?: string[];
444
+ /** ordered API-skin error wires, selected before scenario matching and ordinary routing */
445
+ wires?: Array<{ paths: string; error: DerivedManifest['error']; missing?: ErrorSpec; invalid?: ErrorSpec }>;
387
446
  /** the vendor checks the key before it routes: a path it does not have is refused a missing or invalid key first */
388
447
  beforeRouting?: boolean };
389
448
  /** the twin door that makes the next request answer the vendor's rate-limit refusal */
390
- /** headers every answer carries (a request id) */
449
+ /** headers every answer carries (a request id); a value's `{hex:N}` is filled per answer, from the World's count of
450
+ * answers (so a second World answers the same ids in the same order), unless the handler set the header itself */
391
451
  answerHeaders?: Record<string, string>;
392
452
  /** an origin the pack writes into URLs it mints, rewritten in every JSON answer to where this
393
453
  * twin is reached for the request (twinPublicBase) */
394
454
  origin?: { placeholder: string };
455
+ /** Disclosed local connection fields, separate from vendor response schemas. */
456
+ worldFields?: Record<string, Record<string, { type: 'array' | 'object' | 'string' | 'number' | 'boolean'; why: string }>>;
395
457
  resources: Record<string, ResourceDecl>;
396
458
  /** Read-only cross-pack credential subjects of the same vendor (architecture A3); no root is exposed to handlers. */
397
459
  ownerReads?: ReadonlyArray<{ owner: string; resource: string }>;
@@ -438,9 +500,19 @@ export type DerivedManifest = {
438
500
  * matched at the path's start: stripped before routing, so the operation it names is the one served. Its named groups
439
501
  * (Jira's `/ex/jira/(?<cloudId>[^/]+)`) are the operation's parameters as well. */
440
502
  pathPrefix?: string;
503
+ /** A prefix the vendor serves as its own base path (Discord answers `/api` and `/api/v9` as `/api/v10`, the spec's), as
504
+ * a regular expression matched at the path's start: put in place of the surface's base path before routing, so an
505
+ * operation compiled under the spec's base answers each. Perform and refresh send the spec's base, as the SDK does. */
506
+ basePathAlias?: string;
441
507
  /** A pattern over the request's vendor host whose named groups are the operation's parameters (E2B's envd answers each
442
508
  * sandbox at `49983-<sandboxID>.e2b.app`: `^\\d+-(?<sandboxID>[a-z0-9]+)\\.`). */
443
509
  hostParams?: string;
510
+ /** The vendor host a lane's calls go to when it carries the call's parameters (R2's `<account>.r2.cloudflarestorage.com`):
511
+ * `{name}` filled from the call's parameters, `values` a parameter's literal in the host (the real-system adapters,
512
+ * "Lanes"). Perform and refresh send to `https://<host><path>`. */
513
+ remoteHost?: { template: string; values?: Record<string, Record<string, string>> };
514
+ /** Named operation parameters from vendor routing headers (shared-host compute services). */
515
+ headerParams?: Record<string, string>;
444
516
  /** The persistent wires the vendor's clients hold open (sockets.ts: Discord's Gateway): each served at its path by
445
517
  * the pack's `semantics/sockets.ts`, which the kernel offers every write after its webhooks. */
446
518
  sockets?: ReadonlyArray<SocketDecl>;
@@ -486,12 +558,12 @@ export function strictFields(m: DerivedManifest, operation: DerivedOperation, bo
486
558
  const { type } = field;
487
559
  const fits = value === null
488
560
  || (type === 'boolean' && typeof value === 'boolean')
489
- || (type === 'integer' && Number.isInteger(value))
490
- || (type === 'number' && typeof value === 'number')
561
+ || (type === 'integer' && isExactIntegerValue(value))
562
+ || (type === 'number' && (typeof value === 'number' || isExactIntegerValue(value)))
491
563
  || (type === 'string' && typeof value === 'string')
492
564
  || !['boolean', 'integer', 'number', 'string'].includes(type);
493
565
  if (!fits) return refuse(strict.type, { name, type: type === 'integer' ? 'an integer' : `a ${type}` });
494
- if (typeof value === 'number' && ((field.minimum !== undefined && value < field.minimum) || (field.maximum !== undefined && value > field.maximum))) {
566
+ if ((typeof value === 'number' || isExactIntegerValue(value)) && ((field.minimum !== undefined && compareExactNumbers(value, field.minimum) < 0) || (field.maximum !== undefined && compareExactNumbers(value, field.maximum) > 0))) {
495
567
  return refuse(strict.range, { name, min: field.minimum ?? null, max: field.maximum ?? null });
496
568
  }
497
569
  fields[name] = value;
@@ -513,9 +585,9 @@ function validateBody(m: DerivedManifest, operation: DerivedOperation, body: unk
513
585
  continue;
514
586
  }
515
587
  const types = field.type.split('|');
516
- const fits = types.some((type) => type === 'null' ? value === null : type === 'integer' ? Number.isInteger(value) : type === 'array' ? Array.isArray(value) : type === 'object' ? value !== null && typeof value === 'object' && !Array.isArray(value) : type === 'string' ? typeof value === 'string' : type === 'number' ? typeof value === 'number' : type === 'boolean' ? typeof value === 'boolean' : true);
588
+ const fits = types.some((type) => type === 'null' ? value === null : type === 'integer' ? isExactIntegerValue(value) : type === 'array' ? Array.isArray(value) : type === 'object' ? value !== null && typeof value === 'object' && !Array.isArray(value) : type === 'string' ? typeof value === 'string' : type === 'number' ? typeof value === 'number' || isExactIntegerValue(value) : type === 'boolean' ? typeof value === 'boolean' : true);
517
589
  if (!fits) return refuse(rule.type, { name: field.name, type: field.type, input: value });
518
- if (typeof value === 'number' && ((field.minimum !== undefined && value < field.minimum) || (field.maximum !== undefined && value > field.maximum))) return refuse(rule.range, { name: field.name, min: field.minimum ?? null, max: field.maximum ?? null, input: value });
590
+ if ((typeof value === 'number' || isExactIntegerValue(value)) && ((field.minimum !== undefined && compareExactNumbers(value, field.minimum) < 0) || (field.maximum !== undefined && compareExactNumbers(value, field.maximum) > 0))) return refuse(rule.range, { name: field.name, min: field.minimum ?? null, max: field.maximum ?? null, input: value });
519
591
  }
520
592
  return undefined;
521
593
  }
@@ -531,64 +603,6 @@ export function flagOf(m: Pick<DerivedManifest, 'booleans'>, value: unknown): bo
531
603
  return undefined;
532
604
  }
533
605
 
534
- /** A form body's bracket notation (`a[b][0][c]=v`, `expand[]=x`) as nested objects and arrays. A form carries only
535
- * text: a field the operation's spec types as a number or a boolean (`scalars`, by bracket path, written by
536
- * world-tooling's formScalarsOf) is read as one when its text is that literal; every other field stays the text sent
537
- * (`metadata[order]=007`, `name=2024`). */
538
- /** A field set as the object's own, whatever its name (`__proto__`, `constructor` and `prototype` are data a caller
539
- * may name: Stripe's metadata keys are the caller's), never through what the object inherits. */
540
- export function ownField(node: Record<string, unknown> | unknown[], key: string, value: unknown): void {
541
- Object.defineProperty(node, key, { value, writable: true, enumerable: true, configurable: true });
542
- }
543
- /** A field the object holds as its own, never one it inherits (`constructor` is every object's). */
544
- const ownValue = (node: Record<string, unknown> | unknown[], key: string): unknown => (Object.hasOwn(node, key) ? (node as Record<string, unknown>)[key] : undefined);
545
-
546
- export function parseBracketForm(text: string, scalars?: Readonly<Record<string, string | undefined>>): Record<string, unknown> {
547
- const out: Record<string, unknown> = {};
548
- for (const [rawKey, raw] of new URLSearchParams(text)) {
549
- const parts = rawKey.replace(/\]/g, '').split('[');
550
- const kind = scalars && scalarAt(scalars, parts);
551
- const value: unknown = kind === 'boolean' ? (raw === 'true' ? true : raw === 'false' ? false : raw)
552
- : kind === 'integer' ? (/^-?\d+$/.test(raw) ? Number(raw) : raw)
553
- : kind === 'number' ? (/^-?\d+(\.\d+)?$/.test(raw) ? Number(raw) : raw)
554
- : raw;
555
- // every field is the node's own, read and written as such: a key naming an object's machinery (`__proto__[x]`,
556
- // `constructor[prototype][x]`) is a field of that name, never a walk into what every object inherits
557
- let node: Record<string, unknown> | unknown[] = out;
558
- for (const [i, key] of parts.entries()) {
559
- // an array holds its items by index (`a[]`, `a[0]`): a named key under one (`a[]=1&a[length]=-1`) has nowhere to
560
- // go, as a key under text has none (`a=1&a[b]=2`): the first shape stands
561
- if (Array.isArray(node) && key !== '' && !/^\d+$/.test(key)) break;
562
- if (i === parts.length - 1) {
563
- if (key === '') {
564
- if (Array.isArray(node)) node.push(value);
565
- } else ownField(node, key, value);
566
- break;
567
- }
568
- const next = parts[i + 1]!;
569
- if (key === '') break;
570
- let child = ownValue(node, key);
571
- if (child === undefined) { child = next === '' || /^\d+$/.test(next) ? [] : {}; ownField(node, key, child); }
572
- if (child === null || typeof child !== 'object') break;
573
- node = child as Record<string, unknown> | unknown[];
574
- }
575
- }
576
- return out;
577
- }
578
-
579
- /** The type `scalars` gives a form key's parts: a segment is a property name, `[]` an array's item (an index or empty),
580
- * `*` a map's key. */
581
- function scalarAt(scalars: Readonly<Record<string, string | undefined>>, parts: string[]): string | undefined {
582
- let paths = [''];
583
- for (const [i, part] of parts.entries()) {
584
- const segs = part === '' || /^\d+$/.test(part) ? ['[]', '*'] : [part, '*'];
585
- paths = paths.flatMap((p) => segs.map((seg) => (i === 0 ? seg : `${p}.${seg}`)));
586
- if (paths.length > 64) paths = paths.filter((p) => Object.keys(scalars).some((k) => k === p || k.startsWith(`${p}.`)));
587
- }
588
- for (const p of paths) if (scalars[p]) return scalars[p];
589
- return undefined;
590
- }
591
-
592
606
  /** A request's parameters, its query's and its body's. With the manifest's `body.form.coerce`, a form's fields the
593
607
  * operation's spec types as numbers or booleans are read as them (parseBracketForm); without an operation (a twin door)
594
608
  * every form value is its text. */
@@ -596,7 +610,7 @@ export async function readParams(manifest: DerivedManifest, request: Request, op
596
610
  const url = new URL(request.url);
597
611
  const scalars = manifest.body.form?.coerce ? (operation?.scalars ?? {}) : undefined;
598
612
  const query = parseBracketForm(url.search.replace(/^\?/, ''), scalars);
599
- if (request.method === 'GET' || request.method === 'HEAD') return query;
613
+ if (request.method === 'GET' || request.method === 'HEAD' || (operation && bytesPayload(operation))) return query;
600
614
  const type = request.headers.get('content-type') ?? '';
601
615
  if (type.includes('multipart/form-data')) {
602
616
  // an upload: text fields as given, each file as its name, media type, size and content (its text), with its raw
@@ -620,7 +634,7 @@ export async function readParams(manifest: DerivedManifest, request: Request, op
620
634
  }
621
635
  // NDJSON (`application/x-ndjson`) is lines of JSON, not a JSON document: the handler reads its text
622
636
  const media = (type.split(';')[0] ?? '').trim().toLowerCase();
623
- const isJson = media.includes('json') && !media.includes('ndjson');
637
+ const isJson = media.includes('json') && !media.includes('ndjson') && !media.includes('connect+');
624
638
  // a form is a body labelled one (application/x-www-form-urlencoded); a body of any other type (an image, a video, an
625
639
  // octet-stream PUT to an upload URL, a text/plain SQL statement, an NDJSON batch) is no form: its parameters are the
626
640
  // query's, and the handler reads its text or bytes itself
@@ -633,7 +647,7 @@ export async function readParams(manifest: DerivedManifest, request: Request, op
633
647
  // as a form only when it is labelled one (a sign-in page's own form, posted to the vendor's page), never a crash
634
648
  let body: unknown;
635
649
  if (asJson) {
636
- try { body = JSON.parse(text); } catch {
650
+ try { body = parseExactJson(text); } catch {
637
651
  if (isJson) throw new SyntaxError('malformed JSON body');
638
652
  if (!isForm) return query;
639
653
  body = parseBracketForm(text, scalars);
@@ -744,27 +758,96 @@ function parentValue(parent: NonNullable<ResourceDecl['parent']>, params: Record
744
758
  return parent.param !== undefined ? params[parent.param] : undefined;
745
759
  }
746
760
 
761
+ function parentField(row: unknown, path: string): unknown {
762
+ if (row && typeof row === 'object' && Object.hasOwn(row, path)) return (row as Record<string, unknown>)[path];
763
+ let value = row;
764
+ for (const key of path.split('.')) value = value && typeof value === 'object' ? (value as Record<string, unknown>)[key] : undefined;
765
+ return value;
766
+ }
767
+
747
768
  function missingParent(m: DerivedManifest, resource: string, call: DerivedCall, root?: string): Response | undefined {
748
769
  const parent = m.resources[resource]?.parent;
749
770
  if (!parent) return undefined;
750
771
  const id = parentValue(parent, call.params);
751
772
  const where = Object.entries(parent.where ?? {}).map(([field, template]) => [field, String(fill(template, call.params))] as const);
752
- const found = (r: TwinResource): boolean => (where.length ? where.every(([field, v]) => String((r as Record<string, unknown>)[field]) === v) : r.id === id);
773
+ const found = (r: TwinResource): boolean => (where.length ? where.every(([field, v]) => String(parentField(r, field)) === v) : r.id === id);
753
774
  return id !== undefined && stored(m, parent.resource, root, { withDeleted: parent.allowDeleted === true }).some(found) ? undefined : notFound(m, parent.resource, String(id), parent.param ?? parent.params?.at(-1));
754
775
  }
755
776
 
756
777
  /** The vendor's view of a stored subject: its own fields, bookkeeping (`_`) left out. */
757
778
  /** A stored subject as the vendor's object: its id first (the subject's, unless the pack kept an id of its own), then
758
779
  * the fields the pack wrote, bookkeeping (`_`) and the kernel's updatedAt left out. */
759
- export function render(r: TwinResource): Record<string, unknown> {
760
- return Object.fromEntries(Object.entries({ id: r.id, ...ownFields(r) }).filter(([k]) => !k.startsWith('_') && k !== 'updatedAt'));
780
+ export function render(r: TwinResource, vendorUnderscored: readonly string[] = []): Record<string, unknown> {
781
+ return renderOwn({ id: r.id, ...ownFields(r) }, vendorUnderscored);
782
+ }
783
+
784
+ /** Filter an own-field view through the same declared vendor fields as a stored row. */
785
+ function renderOwn(own: Record<string, unknown>, vendorUnderscored: readonly string[] = []): Record<string, unknown> {
786
+ return Object.fromEntries(Object.entries(own).filter(([k]) => !k.startsWith('_') || vendorUnderscored.includes(k)));
787
+ }
788
+
789
+ type ViewOmissions = { fields: Map<string, ReadonlySet<string>>; affected: Set<string>; embeds: Map<string, Array<[string, string]>> };
790
+ const viewOmissions = new WeakMap<DerivedManifest, ViewOmissions>();
791
+ // Precompute once at manifest binding: omitted fields, whether any exist, and their transitive containers through embeds.
792
+ function prepareViewOmissions(m: DerivedManifest): ViewOmissions {
793
+ const held = viewOmissions.get(m);
794
+ if (held) return held;
795
+ const fields = new Map<string, ReadonlySet<string>>();
796
+ for (const [resource, decl] of Object.entries(m.resources)) {
797
+ if (decl.viewOmit?.length) fields.set(resource, new Set(decl.viewOmit));
798
+ }
799
+ const affected = new Set(fields.keys());
800
+ const embeds = new Map<string, Array<[string, string]>>();
801
+ if (fields.size) {
802
+ let changed = true;
803
+ while (changed) {
804
+ changed = false;
805
+ for (const [resource, decl] of Object.entries(m.resources)) {
806
+ if (!affected.has(resource) && Object.values(decl.embeds ?? {}).some((target) => affected.has(target))) {
807
+ affected.add(resource);
808
+ changed = true;
809
+ }
810
+ }
811
+ }
812
+ for (const resource of affected) {
813
+ embeds.set(resource, Object.entries(m.resources[resource]?.embeds ?? {}).filter(([, target]) => affected.has(target)));
814
+ }
815
+ }
816
+ const plan = { fields, affected, embeds };
817
+ viewOmissions.set(m, plan);
818
+ return plan;
819
+ }
820
+
821
+ /** The one response-omission mechanism, shared by stored, constructed and expanded resource views. */
822
+ function omitView(m: DerivedManifest, resource: string | undefined, body: Record<string, unknown>, seen?: Map<string, WeakMap<object, Record<string, unknown>>>): Record<string, unknown> {
823
+ const plan = viewOmissions.get(m) ?? prepareViewOmissions(m);
824
+ if (!resource || !plan.affected.has(resource)) return body;
825
+ const held = seen?.get(resource)?.get(body);
826
+ if (held) return held;
827
+ const out = { ...body };
828
+ seen ??= new Map();
829
+ let visited = seen.get(resource);
830
+ if (!visited) { visited = new WeakMap(); seen.set(resource, visited); }
831
+ visited.set(body, out);
832
+ for (const field of plan.fields.get(resource) ?? []) delete out[field];
833
+ for (const [field, target] of plan.embeds.get(resource) ?? []) {
834
+ const present = out[field];
835
+ if (Array.isArray(present)) {
836
+ out[field] = present.map((child) => child && typeof child === 'object' && !Array.isArray(child) ? omitView(m, target, child, seen) : child);
837
+ } else if (present && typeof present === 'object') {
838
+ out[field] = omitView(m, target, present as Record<string, unknown>, seen);
839
+ }
840
+ }
841
+ return out;
761
842
  }
762
843
 
763
844
  /** A stored subject as the vendor answers it: its own fields, its id under the name the vendor gives it. */
764
845
  function view(m: DerivedManifest, resource: string, r: TwinResource): Record<string, unknown> {
765
846
  const idAs = m.resources[resource]?.idAs;
766
- if (!idAs) return render(r);
767
- const { id, ...rest } = render(r);
847
+ const keep = m.resources[resource]?.vendorUnderscored ?? [];
848
+ const shown = omitView(m, resource, render(r, keep));
849
+ if (!idAs) return shown;
850
+ const { id, ...rest } = shown;
768
851
  return { [idAs]: id, ...rest };
769
852
  }
770
853
 
@@ -774,7 +857,12 @@ function view(m: DerivedManifest, resource: string, r: TwinResource): Record<str
774
857
  * vendor's own operation (or the pack's door), made in order against the twin by the runner `volter-world init` writes
775
858
  * beside the copied data. An ordinary path is relative to the twin base. A call naming a browser uses the
776
859
  * vendor's absolute URL and is exported separately as `browserSeed`, after the ordinary `seed` calls. */
777
- export type SeedCall = { browser?: string; method: string; path: string; body?: unknown; headers?: Record<string, string> };
860
+ /** How a refresh list's answer items become subjects' fields where each is not one subject's object (architecture,
861
+ * "Refresh"): `scalar`, a bare item is that field; `value` (with `keyed`), a scalar map value is that field and, with
862
+ * `keyAs`, the map key is that field rather than the id; `spread`, an item expands into the array at `path`, each
863
+ * element an object or a scalar named `as`, beside the item's fields `carry` names (field: path in the item). */
864
+ export type RefreshShape = { scalar?: string; value?: string; keyAs?: string; spread?: { path: string; as?: string; carry?: Record<string, string> } };
865
+ export type SeedCall = { browser?: string; method: string; path: string; body?: unknown; headers?: Record<string, string>; /** the instant the account made it, before the World's time: the call is made with the World clock frozen there */ at?: string };
778
866
 
779
867
  /** Where a call is answered: the World's root, its clock, and the managed Postgres the runtime binds to the pack
780
868
  * (`database`, the descriptor's `managedDatabase`), which a handler reaches as `ctx.engine` — READ ONLY in every
@@ -856,27 +944,47 @@ function worldSigningKey(service: string, root: string | undefined, at: string,
856
944
  return made;
857
945
  }
858
946
 
859
- function mintId(m: DerivedManifest, resource: string, root?: string, at?: string): string {
947
+ function mintId(m: DerivedManifest, resource: string, root?: string, at?: string, snapshot?: readonly TwinResource[]): string {
860
948
  if (!m.resources[resource]) throw new Error(`mint: ${resource} is not a resource ${m.vendor}'s manifest declares`);
861
949
  const prefix = m.resources[resource]!.idPrefix;
862
950
  const template = m.resources[resource]!.ids ?? m.ids.template;
863
- if (template === '{uuid}') return mintUuid(m, resource, root);
951
+ if (template === '{uuid}') return mintUuid(m, resource, root, snapshot);
864
952
  const snowflake = /^\{snowflake:(\d+)\}$/.exec(template);
865
- if (snowflake) return mintSnowflake(m, BigInt(snowflake[1]!), root, at);
866
- // `{letters:N}` and `{hex:N}`, alone or after the resource's prefix (`{prefix}{letters:16}`: Fly's `vol_…`)
867
- const letters = /^(\{prefix\})?\{letters:(\d+)\}$/.exec(template);
868
- if (letters) return mintLetters(m, resource, Number(letters[2]), root, letters[1] ? prefix : '');
869
- const hex = /^(\{prefix\})?\{hex:(\d+)\}$/.exec(template);
870
- if (hex) return mintHex(m, resource, Number(hex[2]), root, hex[1] ? prefix : '');
953
+ if (snowflake) return mintSnowflake(m, BigInt(snowflake[1]!), root, at, snapshot);
954
+ // `{letters:N}` and `{hex:N}`, alone, after the resource's prefix (`{prefix}{letters:16}`: Fly's `vol_…`), or after it
955
+ // and a separator (`{prefix}_{hex:48}`: OpenAI's `resp_67cc…`)
956
+ const letters = /^(?:\{prefix\}([_-]?))?\{letters:(\d+)\}$/.exec(template);
957
+ if (letters) return mintLetters(m, resource, Number(letters[2]), root, letters[1] !== undefined ? `${prefix}${letters[1]}` : '', snapshot);
958
+ const hex = /^(?:\{prefix\}([_-]?))?\{hex:(\d+)\}$/.exec(template);
959
+ if (hex) return mintHex(m, resource, Number(hex[2]), root, hex[1] !== undefined ? `${prefix}${hex[1]}` : '', snapshot);
960
+ const digits = /^\{digits:(\d+)\}$/.exec(template);
961
+ if (digits) {
962
+ const width = Number(digits[1]);
963
+ if (!Number.isInteger(width) || width < 1 || width > 77) throw new Error('mint: decimal width must be between 1 and 77');
964
+ const held = new Set((snapshot ? snapshot.filter((r) => r.type === storedType(m, resource)) : resourcesOfType(m.service, storedType(m, resource), root)).map((r) => r.id));
965
+ const floor = 10n ** BigInt(width - 1);
966
+ for (let n = held.size + 1; ; n++) {
967
+ const id = String(floor + BigInt('0x' + sha256(`${m.service}:${resource}:${n}`)) % (9n * floor));
968
+ if (!held.has(id)) return id;
969
+ if (n - held.size > 9 * Number(floor)) throw new Error('mint: decimal id space exhausted');
970
+ }
971
+ }
972
+ // `{ksuid}` alone, after the prefix, or after it and a separator (`{prefix}_{ksuid}`: Clerk's `user_2…`)
973
+ // `{ksuid:N}` gives the id N characters (a vendor's own length: OpenAI's `proj_` 24, `chatcmpl-` 29), its order kept
974
+ const ksuid = /^(?:\{prefix\}([_-]?))?\{ksuid(?::(\d+))?\}$/.exec(template);
975
+ // narrower than 11 characters a KSUID no longer holds its instant and mint count, so neither order nor uniqueness
976
+ // within a second: such an id is minted otherwise ({letters:N}, {hex:N})
977
+ if (ksuid && ksuid[2] && Number(ksuid[2]) < 11) throw new Error(`${m.service}: ${resource}'s id template ${template} is narrower than a KSUID's 11 ordered characters`);
978
+ if (ksuid) return mintKsuid(m, resource, at, root, ksuid[1] !== undefined ? `${prefix}${ksuid[1]}` : '', snapshot, ksuid[2] ? Number(ksuid[2]) : 27);
871
979
  const [head, tail = ''] = template.split('{n}');
872
980
  const pattern = new RegExp(`^${escapeRe(String(fill(head!, { prefix })))}(\\d+)${escapeRe(String(fill(tail, { prefix })))}$`);
873
- let max = 0;
981
+ let max = 0n;
874
982
  // the one type, from the index that follows writes (a scan of the whole World per create grew with it)
875
- for (const r of resourcesOfType(m.service, storedType(m, resource), root)) {
983
+ for (const r of (snapshot ? snapshot.filter((r) => r.type === storedType(m, resource)) : resourcesOfType(m.service, storedType(m, resource), root))) {
876
984
  const hit = pattern.exec(r.id);
877
- if (hit) max = Math.max(max, Number(hit[1]));
985
+ if (hit && BigInt(hit[1]!) > max) max = BigInt(hit[1]!);
878
986
  }
879
- return String(fill(template, { prefix, n: max + 1 }));
987
+ return String(fill(template, { prefix, n: String(max + 1n) }));
880
988
  }
881
989
 
882
990
  /** The next snowflake (Discord's, X's): the milliseconds since the vendor's epoch shifted left 22 bits, the low bits an
@@ -886,13 +994,13 @@ function mintId(m: DerivedManifest, resource: string, root?: string, at?: string
886
994
  /** The last snowflake minted per vendor and World in this process: two mints before the first is written (a message
887
995
  * and its attachment) must not meet. */
888
996
  const lastSnowflake = new Map<string, bigint>();
889
- function mintSnowflake(m: DerivedManifest, epoch: bigint, root?: string, at?: string): string {
997
+ function mintSnowflake(m: DerivedManifest, epoch: bigint, root?: string, at?: string, snapshot?: readonly TwinResource[]): string {
890
998
  const ms = BigInt(Date.parse(at ?? '') || 0);
891
999
  const base = ms > epoch ? (ms - epoch) << 22n : 0n;
892
1000
  let max = -1n;
893
1001
  for (const [name, decl] of Object.entries(m.resources)) {
894
1002
  if (!/^\{snowflake:\d+\}$/.test(decl.ids ?? m.ids.template)) continue;
895
- for (const r of resourcesOfType(m.service, storedType(m, name), root)) if (/^\d+$/.test(r.id)) { const v = BigInt(r.id); if (v > max) max = v; }
1003
+ for (const r of (snapshot ? snapshot.filter((r) => r.type === storedType(m, name)) : resourcesOfType(m.service, storedType(m, name), root))) if (/^\d+$/.test(r.id)) { const v = BigInt(r.id); if (v > max) max = v; }
896
1004
  }
897
1005
  const key = `${m.service}\0${root ?? ''}`;
898
1006
  const last = lastSnowflake.get(key) ?? -1n;
@@ -902,10 +1010,21 @@ function mintSnowflake(m: DerivedManifest, epoch: bigint, root?: string, at?: st
902
1010
  return String(id);
903
1011
  }
904
1012
 
1013
+ /** The next KSUID for a resource (Clerk's and Svix's ids, `ksuidFrom`): the World's instant, then the type's mint
1014
+ * count, so a frozen clock still lists them as made; past any the World already holds. */
1015
+ function mintKsuid(m: DerivedManifest, resource: string, at: string | undefined, root?: string, prefix = '', snapshot?: readonly TwinResource[], width = 27): string {
1016
+ const held = new Set((snapshot ? snapshot.filter((r) => r.type === storedType(m, resource)) : resourcesOfType(m.service, storedType(m, resource), root)).map((r) => r.id));
1017
+ const seconds = Math.floor((Date.parse(at ?? '') || 0) / 1000);
1018
+ for (let n = held.size + 1; ; n++) {
1019
+ const id = `${prefix}${ksuidFrom(seconds, n, `${m.service}:${resource}:${n}`, width)}`;
1020
+ if (!held.has(id)) return id;
1021
+ }
1022
+ }
1023
+
905
1024
  /** The next id of `count` lowercase letters for a resource, derived from the resource and the count before it, past
906
1025
  * any the World already holds. */
907
- function mintLetters(m: DerivedManifest, resource: string, count: number, root?: string, prefix = ''): string {
908
- const held = new Set(resourcesOfType(m.service, storedType(m, resource), root).map((r) => r.id));
1026
+ function mintLetters(m: DerivedManifest, resource: string, count: number, root?: string, prefix = '', snapshot?: readonly TwinResource[]): string {
1027
+ const held = new Set((snapshot ? snapshot.filter((r) => r.type === storedType(m, resource)) : resourcesOfType(m.service, storedType(m, resource), root)).map((r) => r.id));
909
1028
  for (let n = held.size + 1; ; n++) {
910
1029
  const id = `${prefix}${lettersFrom(`${m.service}:${resource}:${n}`, count)}`;
911
1030
  if (!held.has(id)) return id;
@@ -914,8 +1033,8 @@ function mintLetters(m: DerivedManifest, resource: string, count: number, root?:
914
1033
 
915
1034
  /** The next hex id for a resource: N hex digits derived from the resource and the count before it, past any the World
916
1035
  * already holds (a Cloudflare id, 32). */
917
- function mintHex(m: DerivedManifest, resource: string, count: number, root?: string, prefix = ''): string {
918
- const held = new Set(resourcesOfType(m.service, storedType(m, resource), root).map((r) => r.id));
1036
+ function mintHex(m: DerivedManifest, resource: string, count: number, root?: string, prefix = '', snapshot?: readonly TwinResource[]): string {
1037
+ const held = new Set((snapshot ? snapshot.filter((r) => r.type === storedType(m, resource)) : resourcesOfType(m.service, storedType(m, resource), root)).map((r) => r.id));
919
1038
  for (let n = held.size + 1; ; n++) {
920
1039
  const id = `${prefix}${sha256(`${m.service}:${resource}:${n}`).slice(0, count)}`;
921
1040
  if (!held.has(id)) return id;
@@ -924,28 +1043,43 @@ function mintHex(m: DerivedManifest, resource: string, count: number, root?: str
924
1043
 
925
1044
  /** The next UUID for a resource: a version-4-shaped UUID derived from the resource and the count before it, past any
926
1045
  * the World already holds. */
927
- function mintUuid(m: DerivedManifest, resource: string, root?: string): string {
1046
+ function mintUuid(m: DerivedManifest, resource: string, root?: string, snapshot?: readonly TwinResource[]): string {
928
1047
  const type = storedType(m, resource);
929
- const held = new Set(resourcesOfType(m.service, type, root).map((r) => r.id));
1048
+ const held = new Set((snapshot ? snapshot.filter((r) => r.type === type) : resourcesOfType(m.service, type, root)).map((r) => r.id));
930
1049
  for (let n = held.size + 1; ; n++) {
931
1050
  const id = uuidFrom(`${m.service}:${resource}:${n}`);
932
1051
  if (!held.has(id)) return id;
933
1052
  }
934
1053
  }
935
1054
 
936
- function expand(m: DerivedManifest, resource: string, body: Record<string, unknown>, paths: string[], root?: string): Record<string, unknown> {
1055
+ function expand(m: DerivedManifest, resource: string, body: Record<string, unknown>, paths: string[], root?: string, within: ReadonlySet<string> = new Set()): Record<string, unknown> {
1056
+ body = omitView(m, resource, body);
937
1057
  const embeds = m.resources[resource]?.embeds ?? {};
1058
+ // the fields the vendor always embeds join the caller's paths; a resource already being embedded above is not embedded
1059
+ // again inside itself (a cycle answers the id)
1060
+ const always = within.has(resource) ? [] : (m.resources[resource]?.alwaysEmbedded ?? []);
1061
+ const inner = new Set([...within, resource]);
938
1062
  const out = { ...body };
939
- for (const path of paths) {
1063
+ const idKey = m.resources[resource]?.idAs ?? 'id';
1064
+ const ownId = typeof body[idKey] === 'string' ? body[idKey] as string : undefined;
1065
+ for (const [field, c] of within.has(resource) ? [] : Object.entries(m.resources[resource]?.collections ?? {})) {
1066
+ if (!ownId) continue;
1067
+ const childPaths = paths.filter((p) => p.startsWith(`${field}.data.`)).map((p) => p.slice(field.length + 6));
1068
+ const data = stored(m, c.resource, root).filter((r) => (r as Record<string, unknown>)[c.by] === ownId)
1069
+ .map((r) => expand(m, c.resource, view(m, c.resource, r), childPaths, root, inner));
1070
+ const fill = (v: unknown): unknown => v === '{count}' ? data.length : typeof v === 'string' ? v.replaceAll('{id}', ownId) : v;
1071
+ out[field] = { ...Object.fromEntries(Object.entries(c.list ?? {}).map(([k, v]) => [k, fill(v)])), data };
1072
+ }
1073
+ for (const path of [...new Set([...always, ...paths])]) {
940
1074
  const [head, ...rest] = path.split('.');
941
1075
  const target = embeds[head!];
942
1076
  const id = out[head!];
943
1077
  if (!target) continue;
944
1078
  // an object already embedded (an earlier path's) is walked further, not fetched again
945
- if (id && typeof id === 'object' && !Array.isArray(id)) { if (rest.length) out[head!] = expand(m, target, id as Record<string, unknown>, [rest.join('.')], root); continue; }
1079
+ if (id && typeof id === 'object' && !Array.isArray(id)) { out[head!] = expand(m, target, id as Record<string, unknown>, rest.length ? [rest.join('.')] : [], root, inner); continue; }
946
1080
  if (typeof id !== 'string') continue;
947
1081
  const hit = stored(m, target, root).find((r) => r.id === id);
948
- if (hit) out[head!] = rest.length ? expand(m, target, view(m, target, hit), [rest.join('.')], root) : view(m, target, hit);
1082
+ if (hit) out[head!] = expand(m, target, view(m, target, hit), rest.length ? [rest.join('.')] : [], root, inner);
949
1083
  }
950
1084
  return out;
951
1085
  }
@@ -955,8 +1089,8 @@ function expandPaths(m: DerivedManifest, params: Record<string, unknown>): strin
955
1089
  return Array.isArray(raw) ? raw.map(String) : typeof raw === 'string' ? [raw] : [];
956
1090
  }
957
1091
 
958
- async function write(m: DerivedManifest, call: DerivedCall, resource: string, id: string, fields: Record<string, unknown>, operation: string, params: Record<string, unknown>, root: string | undefined, occurredAt: string): Promise<Record<string, unknown>> {
959
- return (await writeDetailed(m, call, resource, id, fields, operation, params, root, occurredAt)).body;
1092
+ async function write(m: DerivedManifest, call: DerivedCall, resource: string, id: string, fields: Record<string, unknown>, operation: string, params: Record<string, unknown>, root: string | undefined, occurredAt: string, checks?: StateChecks): Promise<Record<string, unknown>> {
1093
+ return (await writeDetailed(m, call, resource, id, fields, operation, params, root, occurredAt, checks)).body;
960
1094
  }
961
1095
 
962
1096
  /** The request a context was opened for, as it came (contextFor): its path parameters, the query names the operation
@@ -969,8 +1103,9 @@ const sentOf = new WeakMap<DerivedCall, SentRequest>();
969
1103
  export const LANE_HEADER = 'x-volter-lane';
970
1104
  function sentRequest(m: DerivedManifest, call: DerivedCall, body: unknown): SentRequest {
971
1105
  const url = new URL(call.request.url);
972
- const named = [...(m.headers ?? []), ...(m.version ? [m.version.header] : [])];
973
- const headers = Object.fromEntries(named.flatMap((h) => { const v = call.request.headers.get(h); return v === null ? [] : [[h.toLowerCase(), v]]; }));
1106
+ const named = [...(m.headers ?? []), ...(m.version ? [m.version.header] : [])].map((h) => h.toLowerCase());
1107
+ const recorded = (h: string): boolean => named.some((n) => (n.endsWith('*') ? h.startsWith(n.slice(0, -1)) : h === n));
1108
+ const headers = Object.fromEntries([...call.request.headers].filter(([h]) => recorded(h.toLowerCase())).map(([h, v]) => [h.toLowerCase(), v]));
974
1109
  const lane = call.request.headers.get(LANE_HEADER) ?? undefined;
975
1110
  const requestId = call.request.headers.get('x-twins-request-id') ?? undefined;
976
1111
  const declared = new Set((call.operation.query ?? []).map((q) => q.name));
@@ -987,6 +1122,9 @@ function sentRequest(m: DerivedManifest, call: DerivedCall, body: unknown): Sent
987
1122
  }
988
1123
  /** What a write records of the call that made it: the surface operation and the request as it came, or which of the
989
1124
  * World's own doors made it (a door or a screen, the gap, the clock), which a perform never sends. */
1125
+ /** An operation whose body is its payload's bytes, one `blob` field: recorded as it came and never read as JSON
1126
+ * ("What the derived core records with a write"). */
1127
+ const bytesPayload = (op: DerivedCall['operation']): boolean => op.body?.length === 1 && op.body[0]!.type === 'blob';
990
1128
  /** A body as it came: `{ $text }` when its bytes are UTF-8, else `{ $base64 }`. */
991
1129
  async function rawBody(request: Request): Promise<{ $text: string } | { $base64: string }> {
992
1130
  const bytes = new Uint8Array(await request.clone().arrayBuffer());
@@ -1017,14 +1155,15 @@ function recordedInput(m: DerivedManifest, call: DerivedCall, merged: Record<str
1017
1155
  }
1018
1156
  if (id === 'clock') return { clock: true };
1019
1157
  if (id === 'unmatched') return { gap: true, ...asSent };
1020
- if ((m.doors ?? []).some((d) => d.id === id) || (m.screens ?? []).some((sc) => sc.id === id) || id === 'machine-pool' || id.startsWith('socket:')) return { door: id, ...asSent };
1158
+ if ((m.doors ?? []).some((d) => d.id === id) || (m.screens ?? []).some((sc) => sc.id === id) || (m.accountSetup ?? []).some((a) => a.operation === id) || id === 'machine-pool' || id.startsWith('socket:')) return { door: id, ...asSent };
1021
1159
  return { operationId: id, ...asSent };
1022
1160
  }
1023
1161
 
1024
1162
  /** The scope each call is served in (serveCore, contextFor), for the contexts a write opens for its hooks. */
1025
1163
  const scopeOfCall = new WeakMap<DerivedCall, CoreScope>();
1026
1164
 
1027
- async function writeDetailed(m: DerivedManifest, call: DerivedCall, resource: string, id: string, fields: Record<string, unknown>, operation: string, params: Record<string, unknown>, root: string | undefined, occurredAt: string): Promise<{ body: Record<string, unknown>; id: string; vendorData?: unknown }> {
1165
+ async function writeDetailed(m: DerivedManifest, call: DerivedCall, resource: string, id: string, fields: Record<string, unknown>, operation: string, params: Record<string, unknown>, root: string | undefined, occurredAt: string, checks?: StateChecks): Promise<{ body: Record<string, unknown>; id: string; vendorData?: unknown }> {
1166
+ const before = stateBeforeWrite(m, resource, id, root);
1028
1167
  const withFiles = async (input: Record<string, unknown>): Promise<Record<string, unknown>> => { const files = input.operationId ? await keptFiles(m, call, root) : undefined; return files ? { ...input, files } : input; };
1029
1168
  const { resource: row, result } = await applyTwinWrite(
1030
1169
  m.service,
@@ -1036,6 +1175,38 @@ async function writeDetailed(m: DerivedManifest, call: DerivedCall, resource: st
1036
1175
  : { operation, subjectType: storedType(m, resource), subjectId: id, fields, input: await withFiles(recordedInput(m, call, params)), occurredAt, actor: { kind: 'agent', ...(m.identity ? { id: m.identity(call.request.headers.get('authorization'), root) } : {}) } },
1037
1176
  root,
1038
1177
  );
1178
+ observeStateWrite(m, result, before, root, checks);
1179
+ return finishWrite(m, call, resource, row, result, operation, params, root, occurredAt);
1180
+ }
1181
+
1182
+ async function atomicWrite(m: DerivedManifest, call: DerivedCall, resource: string,
1183
+ decide: (snapshot: readonly TwinResource[]) => { id: string; fields: Record<string, unknown> } | undefined,
1184
+ operation: string, params: Record<string, unknown>, root: string | undefined, occurredAt: string, unchangedId?: string, checks?: StateChecks,
1185
+ ): Promise<Record<string, unknown> | undefined> {
1186
+ const input = recordedInput(m, call, params);
1187
+ const files = input.operationId ? await keptFiles(m, call, root) : undefined;
1188
+ let before: TwinResource | undefined;
1189
+ const committed = await applyTwinWriteAtomic(m.service, (snapshot) => {
1190
+ const selected = decide(snapshot);
1191
+ if (selected && transitionObserver) before = snapshot.find((r) => r.type === storedType(m, resource) && r.id === selected.id);
1192
+ return selected ? { kind: 'write', value: selected.id, write: {
1193
+ operation, subjectType: storedType(m, resource), subjectId: selected.id, fields: selected.fields,
1194
+ input: files ? { ...input, files } : input, occurredAt, actor: { kind: 'agent' as const, ...(m.identity ? { id: m.identity(call.request.headers.get('authorization'), root) } : {}) },
1195
+ } } : { kind: 'skip', value: undefined };
1196
+ }, root);
1197
+ if (!committed.result || !committed.value) {
1198
+ const row = unchangedId ? stored(m, resource, root).find((r) => r.id === unchangedId) : undefined;
1199
+ return row ? view(m, resource, row) : undefined;
1200
+ }
1201
+ observeStateWrite(m, committed.result, before, root, checks);
1202
+ const row = resourcesOfType(m.service, storedType(m, resource), root).find((r) => r.id === resolveSubjectId(m.service, storedType(m, resource), committed.value!, root))!;
1203
+ return (await finishWrite(m, call, resource, row, committed.result, operation, params, root, occurredAt)).body;
1204
+ }
1205
+
1206
+ async function finishWrite(m: DerivedManifest, call: DerivedCall, resource: string, row: TwinResource,
1207
+ result: { externalId?: string; vendorData?: unknown }, operation: string, params: Record<string, unknown>,
1208
+ root: string | undefined, occurredAt: string,
1209
+ ): Promise<{ body: Record<string, unknown>; id: string; vendorData?: unknown }> {
1039
1210
  await keepCounts(m, call, resource, row as unknown as Record<string, unknown>, params, root, occurredAt);
1040
1211
  const body = view(m, resource, row);
1041
1212
  // the write's own context for its hooks, in the scope the call was served in (a walk's application answers, a tenant,
@@ -1045,9 +1216,11 @@ async function writeDetailed(m: DerivedManifest, call: DerivedCall, resource: st
1045
1216
  if (m.events) {
1046
1217
  const events = m.events;
1047
1218
  const write = { operation, storedType: storedType(m, resource), body, ...(root !== undefined ? { root } : {}), occurredAt, request: call.request };
1048
- await deliverEvents(m.service, events, write, async (type) => (events.render ? events.render(await context(), write, type) : body), undefined, events.values ? async (type) => events.values!(await context(), write, type) : undefined, (answer) => deliveryOutcome(m, answer, root));
1219
+ await deliverEvents(m.service, events, write, async (type) => (events.render ? events.render(await context(), write, type) : body), undefined, events.values ? async (type) => events.values!(await context(), write, type) : undefined);
1049
1220
  }
1050
1221
  if (m.sockets?.length) socketWrite(m.service, root, { operation, storedType: storedType(m, resource), body, ...(root !== undefined ? { root } : {}), occurredAt, request: call.request });
1222
+ // a read held until the World writes (held-reads.ts) hears it
1223
+ heldReadsWrite(m.service, root, storedType(m, resource));
1051
1224
  // the subject's id as it stands after the write: the vendor's, when a live head minted one
1052
1225
  return { body, id: result.externalId ?? row.id, ...(result.vendorData !== undefined ? { vendorData: result.vendorData } : {}) };
1053
1226
  }
@@ -1059,13 +1232,13 @@ async function keepCounts(m: DerivedManifest, call: DerivedCall, resource: strin
1059
1232
  for (const [field, c] of Object.entries(decl.counts ?? {})) {
1060
1233
  if (storedType(m, c.of) !== storedType(m, resource) || row[c.by] === undefined || row[c.by] === null) continue;
1061
1234
  const pid = String(row[c.by]);
1062
- const held = stored(m, parent, root).find((r) => r.id === pid) as Record<string, unknown> | undefined;
1235
+ const held = stored(m, parent, root).find((r) => String(c.parentKey ? ownFields(r)[c.parentKey] : r.id) === pid) as Record<string, unknown> | undefined;
1063
1236
  if (!held) continue;
1064
1237
  const n = stored(m, c.of, root).filter((r) => {
1065
1238
  const x = r as Record<string, unknown>;
1066
- return String(x[c.by]) === pid && (c.present === undefined || (x[c.present] !== undefined && x[c.present] !== null));
1239
+ return String(x[c.by]) === pid && (c.present === undefined || (x[c.present] !== undefined && x[c.present] !== null)) && Object.entries(c.unless ?? {}).every(([key, value]) => ownFields(r)[key] !== value);
1067
1240
  }).length + (c.plus ?? 0);
1068
- if (held[field] !== n) await writeDetailed(m, call, parent, pid, { [field]: n }, `${storedType(m, parent)}.counted`, params, root, occurredAt);
1241
+ if (held[field] !== n) await writeDetailed(m, call, parent, String(held.id), { [field]: n }, `${storedType(m, parent)}.counted`, params, root, occurredAt);
1069
1242
  }
1070
1243
  }
1071
1244
  }
@@ -1079,7 +1252,7 @@ const encodeOffset = (o: number): string => btoa(JSON.stringify({ o }));
1079
1252
  function decodeOffset(cursor: unknown): number {
1080
1253
  if (typeof cursor !== 'string' || !cursor) return 0;
1081
1254
  try {
1082
- const o = (JSON.parse(atob(cursor)) as { o?: unknown }).o;
1255
+ const o = (parseExactJson(atob(cursor)) as { o?: unknown }).o;
1083
1256
  return typeof o === 'number' && o >= 0 ? o : 0;
1084
1257
  } catch {
1085
1258
  return 0;
@@ -1091,58 +1264,65 @@ function decodeOffset(cursor: unknown): number {
1091
1264
  /** What a coverage measurement sees of a pack's declared state logic: each move a request made and each refusal a
1092
1265
  * guard gave, by the transition that decided it. Nothing is observed unless a measurement installs an observer
1093
1266
  * (scripts/life-coverage.ts); serving never depends on it. */
1094
- export type TransitionObserver = (decl: StateField, transition: Transition, outcome: 'moved' | 'refused', from: string) => void;
1267
+ export type TransitionEntry = { action: TwinAction; field: string; actor: Actor };
1268
+ export type TransitionObserver = (decl: StateField, transition: Transition, outcome: 'moved' | 'refused', from: string, entry?: TransitionEntry) => void;
1095
1269
  let transitionObserver: TransitionObserver | undefined;
1096
1270
  export function observeTransitions(observer: TransitionObserver | undefined): void {
1097
1271
  transitionObserver = observer;
1098
1272
  }
1099
1273
 
1274
+ type StateCheck = { transition: Transition; operation: string; from: string; to: string; actor: Actor };
1275
+ type StateChecks = Map<string, StateCheck[]>;
1276
+ const stateCheckKey = (type: string, field: string): string => JSON.stringify([type, field]);
1277
+ const stateValue = (row: Record<string, unknown>, field: string): unknown => field.split('.').reduce<unknown>((value, part) => value && typeof value === 'object' ? (value as Record<string, unknown>)[part] : undefined, row);
1278
+ function stateBeforeWrite(m: DerivedManifest, resource: string, id: string, root?: string): TwinResource | undefined {
1279
+ if (!transitionObserver) return undefined;
1280
+ const type = storedType(m, resource);
1281
+ const key = resolveSubjectId(m.service, type, id, root);
1282
+ return resourcesOfType(m.service, type, root).find((row) => row.id === key);
1283
+ }
1284
+
1285
+ /** Observe the committed entry, not a legality probe's display id. Only a move the machine checks
1286
+ * for this entry's actual states, operation and actor is reported; no serving depends on measurement. */
1287
+ function observeStateWrite(m: DerivedManifest, result: { actionId: string; status: 'performed' | 'replayed' }, before: TwinResource | undefined, root?: string, checks?: StateChecks): void {
1288
+ if (!transitionObserver) return;
1289
+ const action = listActions(m.service, root).find((a) => a.id === result.actionId);
1290
+ if (!action?.fields) return;
1291
+ const prior = before ? ownFields(before) : undefined;
1292
+ const next = { ...prior, ...action.fields };
1293
+ const decisions = new Map<string, StateCheck[]>();
1294
+ for (const [resource, decl] of Object.entries(m.resources)) {
1295
+ if ((decl.storedAs ?? resource) !== action.subject.type) continue;
1296
+ for (const [field, machine] of Object.entries(decl.state ?? {})) {
1297
+ if (!(field.split('.')[0]! in action.fields)) continue;
1298
+ const key = stateCheckKey(action.subject.type, field);
1299
+ // Aliases of one stored field use the same consumed evidence on this one entry.
1300
+ if (!decisions.has(key)) { decisions.set(key, checks?.get(key) ?? []); checks?.delete(key); }
1301
+ if (result.status === 'replayed') continue;
1302
+ const current = prior ? stateValue(prior, field) : undefined, target = stateOf(machine, stateValue(next, field));
1303
+ const from = stateOf(machine, current);
1304
+ const proof = decisions.get(key)!.find((c) => machine.transitions.includes(c.transition) && c.from === from && c.to === target);
1305
+ // Initial-state and same-state moves still need an explicit check and a committed field write.
1306
+ if ((!prior || from === target) && !proof) continue;
1307
+ const actor = proof?.actor ?? (action.actor?.kind === 'system' || action.input?.clock === true ? 'vendor' : 'api');
1308
+ const operation = typeof action.input?.operationId === 'string' ? action.input.operationId : proof?.operation ?? (action.input?.clock === true ? 'clock' : action.operation ?? '');
1309
+ transitionFor(field, machine, operation, current, target, action.subject.id, actor, { action, field, actor });
1310
+ }
1311
+ }
1312
+ }
1313
+
1100
1314
  /** The transition a request asks for on one state field, or the vendor's refusal when none applies. */
1101
1315
  /** What a declared machine says to one move: the transition that allows it, or the refusal it gives (and
1102
1316
  * nothing when it declares neither). The derived core and `legal` ask it; so does an engine that is not
1103
1317
  * HTTP-shaped (a line protocol's session), so one machine rules every wire. */
1104
- /** What a delivery's answer writes on its endpoint (the endpoint kind's `outcome`, architecture "Events and webhooks"):
1105
- * a success resets the failure count and writes its fields; a failure counts, and a run of them makes the declared
1106
- * move, asked of the endpoint's machine as the vendor's (a move the machine does not allow is not made). Read fresh
1107
- * and written in one move, since the answer arrives after the write's own and answers may arrive together. */
1108
- async function deliveryOutcome(m: DerivedManifest, answer: DeliveryAnswer, root?: string): Promise<void> {
1109
- const o = answer.kind.outcome!;
1110
- const type = answer.kind.storedAs;
1111
- const ok = answer.status >= 200 && answer.status < 300;
1112
- // the read and the count are one move under the action lock, so two answers at once count twice
1113
- await runAsVendorMove(() => applyTwinWriteAtomic(m.service, (resources) => {
1114
- const row = (resources as unknown as Array<Record<string, unknown>>).find((r) => r.type === type && String(r.id) === String(answer.row.id));
1115
- if (!row || row.deleted === true) return { kind: 'skip', value: undefined };
1116
- const fields: Record<string, unknown> = {};
1117
- let operation = `${type}.delivered`;
1118
- if (ok) {
1119
- fields[o.failures] = 0;
1120
- const seconds = Math.floor((Date.parse(answer.occurredAt) || 0) / 1000);
1121
- for (const [k, v] of Object.entries(o.success ?? {})) fields[k] = v === '$time.iso' ? answer.occurredAt : v === '$time.s' ? seconds : v;
1122
- } else {
1123
- const failures = (Number(row[o.failures]) || 0) + 1;
1124
- fields[o.failures] = failures;
1125
- operation = `${type}.delivery-failed`;
1126
- const exempt = o.unless !== undefined && row[o.unless.field] === o.unless.equals;
1127
- if (o.move && o.after !== undefined && failures >= o.after && !exempt) {
1128
- const decl = Object.values(m.resources).find((r) => (r.storedAs ?? '') === type) ?? m.resources[type];
1129
- const machine = decl?.state?.[o.move.field];
1130
- const { move } = machine ? transitionFor(o.move.field, machine, o.move.operation, row[o.move.field], o.move.to, String(row.id), 'vendor') : {};
1131
- if (move) { fields[o.move.field] = o.move.to; operation = o.move.operation; }
1132
- }
1133
- }
1134
- return { kind: 'write', value: undefined, write: { operation, subjectType: type, subjectId: String(row.id), fields, occurredAt: answer.occurredAt, actor: { kind: 'system' } } };
1135
- }, root));
1136
- }
1137
-
1138
- export function transitionFor(field: string, decl: StateField, operationId: string, current: unknown, requested: unknown, id?: string, actor: Actor = 'api'): { move?: Transition; refusal?: ErrorSpec } {
1318
+ export function transitionFor(field: string, decl: StateField, operationId: string, current: unknown, requested: unknown, id?: string, actor: Actor = 'api', entry?: TransitionEntry): { move?: Transition; refusal?: ErrorSpec } {
1139
1319
  const now = stateOf(decl, current);
1140
- const candidates = decl.transitions.filter((t) => (t.actor ?? 'api') === actor && (t.operation ? t.operation === operationId : true) && (requested === undefined || (t.to ?? now) === String(requested)));
1141
- const move = candidates.find((t) => t.from === '*' || t.from.includes(now));
1142
- if (move) { transitionObserver?.(decl, move, 'moved', now); return { move }; }
1143
- const refused = candidates.find((t) => t.refusals?.[now]) ?? candidates.find((t) => t.refusal);
1320
+ const candidates = decl.transitions.filter((t) => (t.actor ?? 'api') === actor && (t.operation ? t.operation === operationId : true) && (requested === undefined || (t.to ?? now) === String(requested) || (t.to === undefined && t.refusal !== undefined)));
1321
+ const move = candidates.find((t) => !(t.to === undefined && t.refusal !== undefined) && (t.from === '*' || t.from.includes(now)));
1322
+ if (move) { transitionObserver?.(decl, move, 'moved', now, entry); return { move }; }
1323
+ const refused = candidates.find((t) => t.refusals?.[now]) ?? candidates.find((t) => t.refusal && (t.to !== undefined || t.from === '*' || t.from.includes(now)));
1144
1324
  const r = refused?.refusals?.[now] ?? refused?.refusal;
1145
- if (refused && r) transitionObserver?.(decl, refused, 'refused', now);
1325
+ if (refused && r) transitionObserver?.(decl, refused, 'refused', now, entry);
1146
1326
  if (r) return { refusal: { status: r.status, message: String(fill(r.message, { from: current, to: refused!.to ?? now, field, id: id ?? '' })), ...(r.code ? { code: r.code } : {}) } };
1147
1327
  return {};
1148
1328
  }
@@ -1277,9 +1457,11 @@ function arrangeRows(m: DerivedManifest, resource: string, params: Record<string
1277
1457
  const indexed = rows.map((row, i) => ({ row, i }));
1278
1458
  indexed.sort((a, b) => {
1279
1459
  const x = a.row[order.field]; const y = b.row[order.field];
1280
- const byValue = !byText || typeof x === 'number' || typeof y === 'number' ? Number(x ?? 0) - Number(y ?? 0) : String(x ?? '').localeCompare(String(y ?? ''));
1281
- // ties by mint order (`file-twin-10` after `file-twin-9`), not by the ids' lexical order
1282
- const d = byValue || String(a.row.id).localeCompare(String(b.row.id), undefined, { numeric: true });
1460
+ const byValue = !byText || typeof x === 'number' || typeof y === 'number' ? compareExactNumbers(x ?? 0, y ?? 0) : String(x ?? '').localeCompare(String(y ?? ''));
1461
+ // ties (two made in one instant) by the order they were written: the rows come in the tree's order, which keeps each
1462
+ // subject where it was first written, so this is the creation order whatever the id template (an ordered KSUID or
1463
+ // count, or a hash-derived uuid, hex or letters id whose text says nothing of when it was made)
1464
+ const d = byValue || a.i - b.i;
1283
1465
  return order.direction === 'desc' ? -d : d;
1284
1466
  });
1285
1467
  return indexed.map((x) => x.row);
@@ -1287,70 +1469,98 @@ function arrangeRows(m: DerivedManifest, resource: string, params: Record<string
1287
1469
 
1288
1470
  /** A page of arranged rows, answered in the manifest's list envelope with its paging (cursors, pages, offsets). */
1289
1471
  function pageRows(m: DerivedManifest, op: DerivedOperation, call: DerivedCall, params: Record<string, unknown>, rows: Array<Record<string, unknown>>, resource: string, root: string | undefined, paths: string[]): Response {
1290
- const size = m.list.limits?.[op.id] ?? m.list.limit;
1291
- const asked = params[m.list.limit.param];
1472
+ const list = m.list;
1473
+ if (!list) return vendorError(m, m.gap ?? m.notFound);
1474
+ const size = list.limits?.[op.id] ?? list.limit;
1475
+ const asked = params[list.limit.param];
1292
1476
  const n = asked === undefined || asked === '' ? size.default : Number(asked);
1293
1477
  const limit = Number.isFinite(n) ? Math.min(size.max, Math.max(1, Math.trunc(n))) : size.default;
1294
- const after = m.list.after ? params[m.list.after] : undefined;
1295
- const before = m.list.before ? params[m.list.before] : undefined;
1478
+ const after = list.after ? params[list.after] : undefined;
1479
+ const before = list.before ? params[list.before] : undefined;
1296
1480
  let from = 0;
1297
1481
  let to: number;
1298
1482
  if (typeof before === 'string') {
1299
1483
  const at = rows.findIndex((r) => r.id === before);
1300
- to = at === -1 ? (m.list.unknownCursor === 'end' ? 0 : rows.length) : at;
1484
+ to = at === -1 ? (list.unknownCursor === 'end' ? 0 : rows.length) : at;
1301
1485
  from = Math.max(0, to - limit);
1302
1486
  } else {
1303
1487
  if (typeof after === 'string') {
1304
1488
  const at = rows.findIndex((r) => r.id === after);
1305
- from = at === -1 ? (m.list.unknownCursor === 'end' ? rows.length : 0) : at + 1;
1489
+ from = at === -1 ? (list.unknownCursor === 'end' ? rows.length : 0) : at + 1;
1306
1490
  }
1307
1491
  to = from + limit;
1308
1492
  }
1309
- const page = rows.slice(from, to).map((r) => (paths.length ? expand(m, resource, r, paths.filter((p) => p.startsWith('data.')).map((p) => p.slice(5)), root) : r));
1493
+ const page = rows.slice(from, to).map((r) => expand(m, resource, r, paths.filter((p) => p.startsWith('data.')).map((p) => p.slice(5)), root));
1310
1494
  const hasMore = typeof before === 'string' ? from > 0 : to < rows.length;
1311
1495
  const url = op.path.replace(/\{[^}]+\}/g, (p) => call.params[p.slice(1, -1)] ?? p);
1312
- if (m.list.page) {
1313
- const n = Math.max(1, Math.floor(Number(params[m.list.page.param]) || 1));
1314
- const slice = rows.slice((n - 1) * limit, n * limit);
1496
+ if (list.page) {
1497
+ const n = Math.max(1, Math.floor(Number(params[list.page.param]) || 1));
1498
+ const slice = rows.slice((n - 1) * limit, n * limit).map((r) => expand(m, resource, r, paths.filter((p) => p.startsWith('data.')).map((p) => p.slice(5)), root));
1315
1499
  const last = Math.max(1, Math.ceil(rows.length / limit));
1316
1500
  const headers = new Headers();
1317
- if (m.list.page.link && rows.length > limit) {
1501
+ if (list.page.link && rows.length > limit) {
1318
1502
  const at = (k: number): string => {
1319
1503
  const u = new URL(call.request.url);
1320
- u.searchParams.set(m.list.page!.param, String(k));
1504
+ u.searchParams.set(list.page!.param, String(k));
1321
1505
  return u.toString();
1322
1506
  };
1323
1507
  const rels = [...(n > 1 ? [`<${at(n - 1)}>; rel="prev"`] : []), ...(n < last ? [`<${at(n + 1)}>; rel="next"`, `<${at(last)}>; rel="last"`] : []), ...(n > 1 ? [`<${at(1)}>; rel="first"`] : [])];
1324
1508
  if (rels.length) headers.set('link', rels.join(', '));
1325
1509
  }
1326
1510
  // `{total_count}` the count before paging, `{max_page}` the number of pages (none for an empty list: Polar's pagination)
1327
- return Response.json(fill(m.list.envelope, { data: slice, next_cursor: null, key: op.answers?.key ?? 'data', total_count: rows.length, max_page: Math.ceil(rows.length / limit) }), { headers });
1511
+ return Response.json(fill(list.envelope, { data: slice, next_cursor: null, key: op.answers?.key ?? 'data', total_count: rows.length, max_page: Math.ceil(rows.length / limit) }), { headers });
1328
1512
  }
1329
- if (m.list.offset) {
1330
- const skip = Math.max(0, Math.trunc(Number(params[m.list.offset.param]) || 0));
1513
+ if (list.offset) {
1514
+ const skip = Math.max(0, Math.trunc(Number(params[list.offset.param]) || 0));
1331
1515
  const slice = rows.slice(skip, skip + limit);
1332
1516
  const key = op.answers?.key ?? 'data';
1333
- return Response.json({ ...(m.success ?? {}), [key]: slice, ...(fill(m.list.envelopes?.[op.id] ?? m.list.envelope, { data: slice, total_count: rows.length, key }) as object) });
1517
+ return Response.json({ ...(m.success ?? {}), [key]: slice, ...(fill(list.envelopes?.[op.id] ?? list.envelope, { data: slice, total_count: rows.length, key }) as object) });
1334
1518
  }
1335
- if (m.list.cursor) {
1336
- const start = decodeOffset(params[m.list.cursor.param]);
1519
+ if (list.cursor) {
1520
+ if (list.cursor.encoding === 'offset-template') {
1521
+ const cursor = list.cursor;
1522
+ const names: string[] = [];
1523
+ const pattern = '^' + cursor.template.split(/(\{offset\}|\{reverse\})/).map((part) => {
1524
+ if (part === '{offset}' || part === '{reverse}') { names.push(part.slice(1, -1)); return '(\\d+)'; }
1525
+ return escapeRe(part);
1526
+ }).join('') + '$';
1527
+ if (names.filter((name) => name === 'offset').length !== 1 || names.filter((name) => name === 'reverse').length !== 1)
1528
+ throw new Error('list: offset-template must name offset and reverse once');
1529
+ const value = params[cursor.param];
1530
+ const hit = typeof value === 'string' ? new RegExp(pattern).exec(value) : undefined;
1531
+ if (value !== undefined && !hit) return vendorError(m, { status: 400, message: 'Invalid cursor' });
1532
+ const decoded = Object.fromEntries(names.map((name, i) => [name, Number(hit?.[i + 1] ?? 0)]));
1533
+ if (!Number.isSafeInteger(decoded.offset) || ![0, 1].includes(decoded.reverse!)) return vendorError(m, { status: 400, message: 'Invalid cursor' });
1534
+ const start = decoded.reverse ? Math.max(0, decoded.offset! - limit) : decoded.offset!;
1535
+ const slice = rows.slice(start, start + limit);
1536
+ const link = (offset: number, reverse: number, rel: string, available: boolean): string => {
1537
+ const marker = cursor.template.replace('{offset}', String(offset)).replace('{reverse}', String(reverse));
1538
+ const target = new URL(call.request.url); target.searchParams.set(cursor.param, marker);
1539
+ return `<${target.href}>; rel="${rel}"; ${cursor.link.results}="${available}"; ${cursor.link.cursor}="${marker}"`;
1540
+ };
1541
+ return Response.json(fill(list.envelope, { data: slice, key: op.answers?.key ?? 'data' }), { headers: {
1542
+ link: [link(start, 1, cursor.link.previous, start > 0), link(start + limit, 0, cursor.link.next, start + limit < rows.length)].join(', '),
1543
+ } });
1544
+ }
1545
+ const start = decodeOffset(params[list.cursor.param]);
1337
1546
  const slice = rows.slice(start, start + limit);
1338
1547
  const next = start + limit < rows.length ? encodeOffset(start + limit) : '';
1339
- return Response.json({ ...(m.success ?? {}), ...(fill(m.list.envelope, { data: slice, next_cursor: next, key: op.answers?.key ?? 'data' }) as object), ...(op.answers?.key ? { [op.answers.key]: slice } : {}) });
1548
+ return Response.json({ ...(m.success ?? {}), ...(fill(list.envelope, { data: slice, next_cursor: next, key: op.answers?.key ?? 'data' }) as object), ...(op.answers?.key ? { [op.answers.key]: slice } : {}) });
1340
1549
  }
1341
- return Response.json(fill(m.list.envelope, { data: page, has_more: hasMore, url, first_id: page[0]?.id ?? null, last_id: page.at(-1)?.id ?? null }));
1550
+ return Response.json(fill(list.envelope, { data: page, has_more: hasMore, url, first_id: page[0]?.id ?? null, last_id: page.at(-1)?.id ?? null }));
1342
1551
 
1343
1552
  }
1344
1553
 
1345
1554
  /** Serve one operation from the manifest, or say why the core cannot. */
1346
- /** A setting's fields as the vendor answers them: its own, without the kernel's bookkeeping or the parent it is kept
1347
- * under (a setting answers its values, not an identity). */
1348
- function settingFields(own: Record<string, unknown>, parentField?: string): Record<string, unknown> {
1349
- return Object.fromEntries(Object.entries(own).filter(([k]) => !k.startsWith('_') && k !== 'deleted' && k !== parentField));
1555
+ /** A setting's fields as the vendor answers them: its own, without the kernel's bookkeeping (a `_` field the resource
1556
+ * does not declare the vendor's own, as view() and render() keep it) or the parent it is kept under (a setting answers
1557
+ * its values, not an identity). */
1558
+ function settingFields(own: Record<string, unknown>, parentField?: string, vendorUnderscored: readonly string[] = []): Record<string, unknown> {
1559
+ return Object.fromEntries(Object.entries(own).filter(([k]) => (!k.startsWith('_') || vendorUnderscored.includes(k)) && k !== 'deleted' && k !== parentField));
1350
1560
  }
1351
1561
  function settingView(m: DerivedManifest, resource: string, hit: TwinResource): Record<string, unknown> {
1352
1562
  // ownFields: the kernel's id, type and updatedAt gone, a vendor's own of those names restored
1353
- return settingFields(ownFields(hit), m.resources[resource]?.parent?.field);
1563
+ return settingFields(ownFields(hit), m.resources[resource]?.parent?.field, m.resources[resource]?.vendorUnderscored);
1354
1564
  }
1355
1565
 
1356
1566
  export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: CoreScope = {}): Promise<CoreOutcome> {
@@ -1379,9 +1589,10 @@ export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: Co
1379
1589
  // the vendor's success status from its spec: 201 for most creates, 204 with no body where it answers nothing
1380
1590
  const status = op.successStatus ?? 200;
1381
1591
  const answer = (body: Record<string, unknown>) => ({
1382
- served: status === 204 ? new Response(null, { status }) : Response.json(envelope(m, op, paths.length ? expand(m, resource, body, paths, root) : body), { status }),
1592
+ served: status === 204 ? new Response(null, { status }) : Response.json(envelope(m, op, expand(m, resource,
1593
+ body, paths, root)), { status }),
1383
1594
  });
1384
- const control = new Set([m.expandParam, m.list.limit.param, m.list.after, m.list.before, m.list.offset?.param, m.list.page?.param, m.list.cursor?.param].filter(Boolean) as string[]);
1595
+ const control = new Set([m.expandParam, m.list?.limit.param, m.list?.after, m.list?.before, m.list?.offset?.param, m.list?.page?.param, m.list?.cursor?.param].filter(Boolean) as string[]);
1385
1596
  const data = Object.fromEntries(Object.entries(params).filter(([k]) => !control.has(k) && k !== 'id'));
1386
1597
  // a strict vendor refuses a body it does not declare before the core writes it (body.strict, as ctx.fields)
1387
1598
  if (m.body.strict && (op.class === 'create' || op.class === 'update')) {
@@ -1401,7 +1612,8 @@ export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: Co
1401
1612
  const under: Record<string, unknown> = { ...(parent ? { [parent.field]: parentId } : {}), ...(tenantField ? { [tenantField]: tenant } : {}) };
1402
1613
  if (decl.number) {
1403
1614
  const siblings = stored(m, resource, root, { withDeleted: true }).filter(underParent);
1404
- (under as Record<string, unknown>)[decl.number.field] = 1 + siblings.reduce((n, r) => Math.max(n, Number((r as Record<string, unknown>)[decl.number!.field]) || 0), 0);
1615
+ const next = siblings.reduce((n, r) => { const value = String((r as Record<string, unknown>)[decl.number!.field] ?? '0'); const held = /^-?\d+$/.test(value) ? BigInt(value) : 0n; return held > n ? held : n; }, 0n) + 1n;
1616
+ (under as Record<string, unknown>)[decl.number.field] = exactInteger(String(next));
1405
1617
  }
1406
1618
  const made = { ...assigned, ...initial, ...under, ...data };
1407
1619
  const clash = conflictOf(m, resource, made, undefined, root);
@@ -1415,6 +1627,7 @@ export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: Co
1415
1627
  return hit ? answer(view(m, resource, hit)) : { served: notFound(m, resource, String(idParam), Object.keys(call.params).at(-1)) };
1416
1628
  }
1417
1629
  case 'list': {
1630
+ if (!m.list) return { unmodeled: 'no manifest list configuration' };
1418
1631
  const rows = stored(m, resource, root).filter(underParent).map((r) => view(m, resource, r));
1419
1632
  const arranged = arrangeRows(m, resource, params, rows);
1420
1633
  if (!Array.isArray(arranged)) return { served: vendorError(m, arranged) };
@@ -1439,6 +1652,7 @@ export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: Co
1439
1652
  const current = view(m, resource, hit);
1440
1653
  const changes: Record<string, unknown> = { ...(op.class === 'update' ? data : {}) };
1441
1654
  let moved = false;
1655
+ const stateChecks: StateChecks = new Map();
1442
1656
  for (const [field, sdecl] of Object.entries(decl.state ?? {})) {
1443
1657
  const requested = op.class === 'update' ? data[field] : undefined;
1444
1658
  if (op.class === 'update' && requested === undefined) continue;
@@ -1448,6 +1662,7 @@ export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: Co
1448
1662
  if (op.class === 'update') return { unmodeled: `no declared transition of ${field} to ${String(requested)} by ${op.id}` };
1449
1663
  continue;
1450
1664
  }
1665
+ if (transitionObserver) stateChecks.set(stateCheckKey(storedType(m, resource), field), [{ transition: move, operation: op.id, from: stateOf(sdecl, current[field]), to: move.to ?? stateOf(sdecl, current[field]), actor: 'api' }]);
1451
1666
  const stored = storedState(sdecl, move.to);
1452
1667
  if (stored !== undefined) changes[field] = stored;
1453
1668
  for (const [k, rule] of Object.entries(move.effects ?? {})) changes[k] = applyRule(m, rule, hit.id, { ...current, ...changes }, at);
@@ -1458,7 +1673,7 @@ export async function serveCore(m: DerivedManifest, call: DerivedCall, scope: Co
1458
1673
  for (const [k, v] of Object.entries(changes)) merged[k] = mergeField(decl.update, current[k], v);
1459
1674
  const clash = conflictOf(m, resource, { ...current, ...merged }, hit.id, root);
1460
1675
  if (clash) return { served: vendorError(m, clash) };
1461
- return answer(await write(m, call, resource, hit.id, merged, `${storedType(m, resource)}.${op.class === 'update' ? 'update' : op.id}`, params, root, at));
1676
+ return answer(await write(m, call, resource, hit.id, merged, `${storedType(m, resource)}.${op.class === 'update' ? 'update' : op.id}`, params, root, at, stateChecks));
1462
1677
  }
1463
1678
  case 'delete': {
1464
1679
  const hit = idParam ? stored(m, resource, root).find((r) => namedBy(decl, r, idParam) && underParent(r)) : undefined;
@@ -1512,14 +1727,11 @@ export type SemanticsContext = {
1512
1727
  /** What the World's scenario decided for this turn (a model vendor's answer: the handler matched, or the miss), on an
1513
1728
  * operation the pack's `semantics/scenario.ts` names; undefined elsewhere, or when the World carries no scenario. */
1514
1729
  scenario: ScenarioDecision | undefined;
1515
- /** A message the vendor sends the application's own server that no declared event is (a push notification), through
1516
- * the World's route and egress rule: the receiver's status, or 0 when refused or unreachable. */
1517
- deliver(url: string, init: RequestInit): Promise<number>;
1518
1730
  /** A message the vendor sends the application's server and decides by its answer, waited on for at most `within`
1519
1731
  * milliseconds (a real-time authorization request): its status and body, or why there was none. */
1520
1732
  ask(url: string, init: RequestInit, within: number): Promise<ApplicationAnswer>;
1521
1733
  /** A git repository of the World, by the pack's name for it (the git plane, world-core git/): its objects in the
1522
- * World's content-addressed object store (one per service), its refs in the World's store. Served over smart HTTP
1734
+ * World's repository-scoped content-addressed object store, its refs in the World's store. Served over smart HTTP
1523
1735
  * (`git.serveSmartHttp`), read and written through the library's codecs. */
1524
1736
  git(name: string): { store: GitObjectStore; refs: GitRefs };
1525
1737
  /** A value the World set for its application (world-env.ts): what the app was given and the vendor must agree with (a
@@ -1534,6 +1746,8 @@ export type SemanticsContext = {
1534
1746
  /** who is calling, when the manifest says how to tell */
1535
1747
  actor: string | undefined;
1536
1748
  now(): unknown;
1749
+ /** Render a stored row or vendor view using its resource declaration, retaining the stored subject id fallback. */
1750
+ render(row: Record<string, unknown>, resource?: string): Record<string, unknown>;
1537
1751
  get(resource: string, id: string): Record<string, unknown> | undefined;
1538
1752
  /** One credential subject in a manifest-declared owner's World store. Ambiguous stores refuse the read. */
1539
1753
  ownerRow(owner: string, resource: string, id: string): Record<string, unknown> | undefined;
@@ -1544,6 +1758,11 @@ export type SemanticsContext = {
1544
1758
  issue(resource: string): Promise<string>;
1545
1759
  /** The vendor's open sessions of one of its sockets in this World (a request answered by a session: sockets.ts). */
1546
1760
  sockets(id: string): SocketSession[];
1761
+ /** Hold this read until the World writes (held-reads.ts: HTTP long polling): true when a write of one of the named
1762
+ * resources lands in this World, false when `ms` pass or the client hangs up; at once false in a read-only World. */
1763
+ awaitWrite(resources: ReadonlyArray<string>, ms: number): Promise<boolean>;
1764
+ /** Observe a condition until it holds or the caller's timeout/cancellation, waking on writes and pollMs. */
1765
+ awaitCondition(resources: ReadonlyArray<string>, ms: number, pollMs: number, condition: () => Promise<boolean>): Promise<boolean>;
1547
1766
  /** A secret for `label` (an app's client secret, a store's read token): an HMAC under a seed the World makes once, at
1548
1767
  * random, and keeps as bookkeeping no door answers. The same label gives the same secret for the World's whole life,
1549
1768
  * and no one can compute it from ids the World shows, as they can a value derived from them alone. */
@@ -1558,6 +1777,12 @@ export type SemanticsContext = {
1558
1777
  /** The signing key for `label` when the World has made it (`signingKey`), else undefined: for code that signs where it
1559
1778
  * cannot wait (a cookie's token, read everywhere), after a front made the key before anything is answered. */
1560
1779
  heldSigningKey(label: string, alg?: 'RS256' | 'Ed25519'): { privatePem: string; publicPem: string } | undefined;
1780
+ /** Allocate a stored occurrence and append its fields under one action lock. The optional field factory is pure. */
1781
+ create(resource: string, fields: Record<string, unknown> | ((id: string) => Record<string, unknown>), operation: string): Promise<Record<string, unknown>>;
1782
+ /** Create under declared unique keys and the action lock; a collision returns the existing own-field view without a write. */
1783
+ createUnique(resource: string, fields: Record<string, unknown> | ((id: string) => Record<string, unknown>), operation: string): Promise<{ created: boolean; row: Record<string, unknown> }>;
1784
+ /** Decide an update from the current live stored row under the action lock; undefined leaves it unchanged. */
1785
+ change(resource: string, id: string, decide: (row: Record<string, unknown>) => Record<string, unknown> | undefined, operation: string): Promise<Record<string, unknown> | undefined>;
1561
1786
  write(resource: string, id: string, fields: Record<string, unknown>, operation: string): Promise<Record<string, unknown>>;
1562
1787
  /** Add numeric deltas to an existing bookkeeping row under the kernel's atomic write seam. */
1563
1788
  accumulate(resource: string, id: string, deltas: Record<string, number>, operation: string): Promise<void>;
@@ -1578,8 +1803,8 @@ export type SemanticsContext = {
1578
1803
  tree(): Array<Record<string, unknown>>;
1579
1804
  /** The writes that touched a subject, in order: its history. */
1580
1805
  history(resource: string, id: string): Array<{ operation?: string; fields?: Record<string, unknown>; occurredAt?: string }>;
1581
- /** Write a bookkeeping subject (a `_`-prefixed type the vendor never serves), minting its id when none is given. */
1582
- record(type: string, fields: Record<string, unknown>, id?: string): Promise<string>;
1806
+ /** Write bookkeeping without the incoming request; an explicit operation observes its checked state move. Mint an id when none is given. */
1807
+ record(type: string, fields: Record<string, unknown>, id?: string, operation?: string): Promise<string>;
1583
1808
  /** Decide a write from the tree as it stands and make it, with nothing written between the read
1584
1809
  * and the write (a minted number, a first poll that moves a subject). `decide` returns the write,
1585
1810
  * or nothing to write; the answer is its `value`. */
@@ -1592,6 +1817,8 @@ export type SemanticsContext = {
1592
1817
  /** Validate the request body's top-level derived schema under body.validation, before a write. */
1593
1818
  validate(): Response | undefined;
1594
1819
  refuse(e: ErrorSpec): Response;
1820
+ /** Answer the manifest's declared unserved capability gap. */
1821
+ gap(): Response;
1595
1822
  notFound(resource: string, id: string, param?: string): Response;
1596
1823
  reply(body: unknown, status?: number): Response;
1597
1824
  /** Answer in the vendor's success envelope under this operation's key (`{ ok: true, channel }`). */
@@ -1665,7 +1892,7 @@ export type HandlerContext = Omit<SemanticsContext, 'now' | 'core' | 'atomically
1665
1892
  };
1666
1893
  /** What a write hook reads: the writing call's context without its ways to write, so a hook that renders an event
1667
1894
  * cannot write again (and run itself again). */
1668
- export type WriteHookContext = Omit<HandlerContext, 'write' | 'record' | 'accumulate' | 'at' | 'over'>;
1895
+ export type WriteHookContext = Omit<HandlerContext, 'write' | 'create' | 'createUnique' | 'change' | 'issue' | 'record' | 'accumulate' | 'at' | 'over'>;
1669
1896
  /** A derived pack's handler: one operation, by its operationId, over the contract's context. */
1670
1897
  export type Handler = (ctx: HandlerContext) => Promise<Response>;
1671
1898
 
@@ -1677,13 +1904,15 @@ export async function contextFor(m: DerivedManifest, call: DerivedCall, scope: C
1677
1904
  scopeOfCall.set(call, scope);
1678
1905
  const at = (scope.clock ?? worldNow)();
1679
1906
  // the guard's parse when it made one (taken once), else this context's own
1680
- const parsed = takeParsedBody(call.request);
1681
- const text = parsed ? '' : await bodyTextOf(call.request.clone()).catch(() => '');
1907
+ // a body that is its payload's bytes (S3's PutObject) is never read as JSON, whatever its label
1908
+ const payload = bytesPayload(call.operation);
1909
+ const parsed = payload ? undefined : takeParsedBody(call.request);
1910
+ const text = await bodyTextOf(call.request.clone()).catch(() => '');
1682
1911
  let body: unknown = parsed?.value;
1683
- if (!parsed) { try { body = text ? JSON.parse(text) : undefined; } catch { body = text; } }
1912
+ if (!parsed) { if (payload) body = text || undefined; else { try { body = text ? parseExactJson(text) : undefined; } catch { body = text; } } }
1684
1913
  // the request as it came, before the core's parameter merge and alias adoption: what a write records, so a perform
1685
1914
  // can send it again (the real-system adapters, "What the derived core records with a write")
1686
- const boundary = multipartBoundary(call.request.headers.get('content-type'));
1915
+ const boundary = payload ? null : multipartBoundary(call.request.headers.get('content-type'));
1687
1916
  const parts = boundary === null ? undefined : multipartParts(new Uint8Array(await call.request.clone().arrayBuffer()), boundary);
1688
1917
  // a body that is not JSON is recorded as it came, its type with it: text as text, bytes (a gzip the vendor's client
1689
1918
  // compressed itself, PostHog's `compression=gzip-js`) as base64
@@ -1694,8 +1923,20 @@ export async function contextFor(m: DerivedManifest, call: DerivedCall, scope: C
1694
1923
  // every read of the body is of a copy, so the handler's own `call.request` and the core's answer (`core()`) each
1695
1924
  // still find it unread, whichever of them reads it first
1696
1925
  const pristine = call.request.clone();
1697
- const params = await boundaryParams(m, { ...call, request: call.request.clone() }, root);
1926
+ const params = await boundaryParams(m, { ...call, request: call.request.clone() }, root).catch((error) => {
1927
+ // An around context opens before the credential/JSON guard. Keep non-body parameters available to it without
1928
+ // turning the guard's declared malformed-body refusal into a context-construction exception.
1929
+ if (!(error instanceof SyntaxError) || error.message !== 'malformed JSON body') throw error;
1930
+ return boundaryParams(m, { ...call, request: new Request(call.request.url, { headers: call.request.headers }) }, root);
1931
+ });
1698
1932
  const paths = expandPaths(m, params);
1933
+ const stateChecks: StateChecks = new Map();
1934
+ const rawResources = new WeakMap<Record<string, unknown>, string>();
1935
+ const rawRow = (resource: string, row: TwinResource): Record<string, unknown> => {
1936
+ const name = m.resources[resource] ? resource : Object.keys(m.resources).find((name) => storedType(m, name) === row.type);
1937
+ rawResources.set(row as Record<string, unknown>, name ?? resource);
1938
+ return row as Record<string, unknown>;
1939
+ };
1699
1940
  return {
1700
1941
  call,
1701
1942
  params,
@@ -1705,18 +1946,25 @@ export async function contextFor(m: DerivedManifest, call: DerivedCall, scope: C
1705
1946
  root,
1706
1947
  publicBase: twinPublicBase(call.request),
1707
1948
  scenario: scenarioDecisionOf(call.request),
1708
- deliver: (url, init) => deliverToApplication(url, init),
1709
1949
  ask: (url, init, within) => askApplication(url, init, within, scope.application),
1710
1950
  worldEnv: (name) => worldEnvValue(name),
1711
1951
  asVendor: (fn) => runAsVendorMove(fn),
1712
1952
  git: (name) => {
1713
1953
  if (!/^[A-Za-z0-9_.-]+(\/[A-Za-z0-9_.-]+)*$/.test(name) || name.split('/').some((seg) => seg === '.' || seg === '..')) throw new Error(`semantics: ${name} is not a git repository name`);
1714
1954
  const plane = `${worldPaths(m.service, root).dir}/git`;
1715
- return { store: new GitObjectStore(getActiveBlobStore(), `${plane}/objects`), refs: new GitRefs(getActiveWorldStore(), `${plane}/repos/${name}`) };
1955
+ return { store: new GitObjectStore(getActiveBlobStore(), `${plane}/repos/${name}`), refs: new GitRefs(getActiveWorldStore(), `${plane}/repos/${name}`) };
1716
1956
  },
1717
1957
  mail: (route, mail, headers) => sendMail(route, mail, headers),
1718
1958
  actor: m.identity ? m.identity(call.request.headers.get('authorization'), root) : undefined,
1719
1959
  now: () => now(m, at),
1960
+ render: (row, resource) => {
1961
+ const raw = rawResources.has(row);
1962
+ const name = resource ?? rawResources.get(row);
1963
+ const declared = name && (m.resources[name] ? name : Object.keys(m.resources).find((r) => storedType(m, r) === name));
1964
+ const keep = declared ? m.resources[declared]?.vendorUnderscored ?? [] : [];
1965
+ const shown = raw ? render(row as TwinResource, keep) : renderOwn(row, keep);
1966
+ return omitView(m, declared || undefined, shown);
1967
+ },
1720
1968
  get: (resource, id) => {
1721
1969
  const hit = stored(m, resource, root).find((r) => r.id === id);
1722
1970
  return hit ? view(m, resource, hit) : undefined;
@@ -1729,45 +1977,74 @@ export async function contextFor(m: DerivedManifest, call: DerivedCall, scope: C
1729
1977
  rows: (resource) => stored(m, resource, root).map((r) => view(m, resource, r)),
1730
1978
  mint: (resource) => mintId(m, resource, root, at),
1731
1979
  sockets: (id) => openSessions(m.service, root, id),
1980
+ awaitWrite: (resources, ms) => (scope.readOnly === true || isReadOnlyRequest(call.request) ? Promise.resolve(false) : awaitWrite(m.service, root, resources.map((r) => storedType(m, r)), ms, call.request.signal)),
1981
+ awaitCondition: (resources, ms, pollMs, condition) => awaitCondition(m.service, root, resources.map((r) => storedType(m, r)), scope.readOnly === true || isReadOnlyRequest(call.request) ? 0 : ms, pollMs, condition, call.request.signal),
1732
1982
  issue: async (resource) => {
1733
- const id = mintId(m, resource, root, at);
1734
1983
  if (!resource.startsWith('_')) throw new Error(`semantics: ${resource} is not a bookkeeping type (an issued id's type starts with _)`);
1735
- if (scope.readOnly !== true && !isReadOnlyRequest(call.request)) {
1736
- await applyTwinWrite(m.service, { operation: `${resource.slice(1)}.issue`, subjectType: storedType(m, resource), subjectId: id, fields: { issuedAt: at }, occurredAt: at, actor: { kind: 'system' } }, root);
1737
- }
1738
- return id;
1984
+ if (scope.readOnly === true || isReadOnlyRequest(call.request)) return mintId(m, resource, root, at);
1985
+ const { value } = await applyTwinWriteAtomic(m.service, (snapshot) => {
1986
+ const id = mintId(m, resource, root, at, snapshot);
1987
+ return { kind: 'write', value: id, write: { operation: `${resource.slice(1)}.issue`, subjectType: storedType(m, resource), subjectId: id, fields: { issuedAt: at }, occurredAt: at, actor: { kind: 'system' } } };
1988
+ }, root);
1989
+ return value;
1739
1990
  },
1740
1991
  occurredAt: at,
1741
1992
  secret: async (label) => hmac(await worldSeed(m.service, root, at), label, 'base64url'),
1742
- record: async (type, fields, id) => {
1993
+ record: async (type, fields, id, operation) => {
1743
1994
  if (!type.startsWith('_')) throw new Error(`semantics: ${type} is not a bookkeeping type (bookkeeping types start with _)`);
1744
1995
  if (id !== undefined) {
1745
- await applyTwinWrite(m.service, { operation: `${type.slice(1)}.record`, subjectType: type, subjectId: id, fields, occurredAt: at, actor: { kind: 'system' } }, root);
1996
+ const before = operation ? stateBeforeWrite(m, type, id, root) : undefined;
1997
+ const { result } = await applyTwinWrite(m.service, { operation: operation ?? `${type.slice(1)}.record`, subjectType: type, subjectId: id, fields, occurredAt: at, actor: { kind: 'system' } }, root);
1998
+ if (operation) observeStateWrite(m, result, before, root, stateChecks);
1746
1999
  return id;
1747
2000
  }
1748
- const { value } = await applyTwinWriteAtomic(m.service, (resources) => {
2001
+ const { value, result } = await applyTwinWriteAtomic(m.service, (resources) => {
1749
2002
  // Allocate and append under the same state lock: concurrent sign-ins must never share an issue id.
1750
2003
  const subject = (() => {
1751
2004
  const prefix = `${type.slice(1)}_`;
1752
- let max = 0;
2005
+ let max = 0n;
1753
2006
  for (const r of resources) {
1754
2007
  if (r.type !== type) continue;
1755
- const n = r.id.startsWith(prefix) ? Number(r.id.slice(prefix.length)) : NaN;
1756
- if (Number.isInteger(n) && n > max) max = n;
2008
+ const digits = r.id.startsWith(prefix) ? r.id.slice(prefix.length) : '';
2009
+ if (/^\d+$/.test(digits) && BigInt(digits) > max) max = BigInt(digits);
1757
2010
  }
1758
- return `${prefix}${max + 1}`;
2011
+ return `${prefix}${max + 1n}`;
1759
2012
  })();
1760
- return { kind: 'write', value: subject, write: { operation: `${type.slice(1)}.record`, subjectType: type, subjectId: subject, fields, occurredAt: at, actor: { kind: 'system' } } };
2013
+ return { kind: 'write', value: subject, write: { operation: operation ?? `${type.slice(1)}.record`, subjectType: type, subjectId: subject, fields, occurredAt: at, actor: { kind: 'system' } } };
1761
2014
  }, root, () => resourcesOfType(m.service, type, root));
2015
+ if (operation && result) observeStateWrite(m, result, undefined, root, stateChecks);
1762
2016
  return value;
1763
2017
  },
1764
2018
  get machines() {
1765
- const enrolled = resourcesOfType(m.service, MACHINE_POOL, root).find((r) => r.id === 'pool') as unknown as { kind?: MachinePool['kind'] } | undefined;
1766
- return poolFor(m.service, root, enrolled?.kind ?? 'none');
2019
+ const enrolled = resourcesOfType(m.service, MACHINE_POOL, root).find((r) => r.id === 'pool') as unknown as (LightPoolOptions & { kind?: MachinePool['kind']; httpReplies?: MachineHttpReply[] }) | undefined;
2020
+ return guardedMachinePool(poolFor(m.service, root, enrolled?.kind ?? 'none', enrolled?.httpReplies, { images: enrolled?.images }), m.service, ['retrieve', 'list'].includes(call.operation.class) || (m.reads?.includes(call.operation.id) ?? false));
1767
2021
  },
1768
2022
  signingKey: (label, alg) => worldSigningKey(m.service, root, at, label, alg ?? 'RS256'),
1769
2023
  heldSigningKey: (label, alg) => heldWorldSigningKey(m.service, root, label, alg ?? 'RS256'),
1770
- write: (resource, id, fields, operation) => write(m, call, resource, id, fields, operation, params, root, at),
2024
+ create: (resource, fields, operation) => atomicWrite(m, call, resource, (snapshot) => {
2025
+ const id = mintId(m, resource, root, at, snapshot);
2026
+ return { id, fields: typeof fields === 'function' ? fields(id) : fields };
2027
+ }, operation, params, root, at, undefined, stateChecks).then((row) => row!),
2028
+ createUnique: async (resource, fields, operation) => {
2029
+ const rules = m.resources[resource]?.unique;
2030
+ if (!rules?.length) throw new Error('createUnique requires declared unique keys');
2031
+ let existing: Record<string, unknown> | undefined;
2032
+ const row = await atomicWrite(m, call, resource, (snapshot) => {
2033
+ const id = mintId(m, resource, root, at, snapshot);
2034
+ const selected = typeof fields === 'function' ? fields(id) : fields;
2035
+ const hit = snapshot.find((r) => r.type === storedType(m, resource) && r.deleted !== true && rules.some((rule) => rule.fields.every((f) => selected[f] !== undefined && view(m, resource, r)[f] === selected[f])));
2036
+ if (hit) { existing = view(m, resource, hit); return undefined; }
2037
+ return { id, fields: selected };
2038
+ }, operation, params, root, at, undefined, stateChecks);
2039
+ return existing ? { created: false, row: existing } : { created: true, row: row! };
2040
+ },
2041
+ change: (resource, id, decide, operation) => atomicWrite(m, call, resource, (snapshot) => {
2042
+ const row = snapshot.find((r) => r.type === storedType(m, resource) && r.id === id && r.deleted !== true);
2043
+ if (!row) return undefined;
2044
+ const fields = decide(row as Record<string, unknown>);
2045
+ return fields === undefined ? undefined : { id, fields };
2046
+ }, operation, params, root, at, id, stateChecks),
2047
+ write: (resource, id, fields, operation) => write(m, call, resource, id, fields, operation, params, root, at, stateChecks),
1771
2048
  accumulate: async (resource, id, deltas, operation) => {
1772
2049
  if (!resource.startsWith('_') || !m.resources[resource]) throw new Error('accumulate requires a declared bookkeeping resource');
1773
2050
  await applyTwinWriteAtomic(m.service, (resources) => {
@@ -1782,54 +2059,64 @@ export async function contextFor(m: DerivedManifest, call: DerivedCall, scope: C
1782
2059
  return { kind: 'write', value: undefined, write: { operation, subjectType: storedType(m, resource), subjectId: id, fields, occurredAt: at, actor: { kind: 'agent' } } };
1783
2060
  }, root);
1784
2061
  },
1785
- writeDetailed: (resource, id, fields, operation) => writeDetailed(m, call, resource, id, fields, operation, params, root, at),
2062
+ writeDetailed: (resource, id, fields, operation) => writeDetailed(m, call, resource, id, fields, operation, params, root, at, stateChecks),
1786
2063
  resolve: (resource, id) => resolveSubjectId(m.service, storedType(m, resource), id, root),
1787
2064
  storedType: (resource) => storedType(m, resource),
1788
2065
  ok: (fields) => Response.json({ ...(m.success ?? {}), ...fields }),
1789
- row: (resource, id, opts) => stored(m, resource, root, opts).find((r) => r.id === id) as Record<string, unknown> | undefined,
1790
- rowsRaw: (resource, opts) => stored(m, resource, root, opts) as Array<Record<string, unknown>>,
2066
+ row: (resource, id, opts) => { const row = stored(m, resource, root, opts).find((r) => r.id === id); return row ? rawRow(resource, row) : undefined; },
2067
+ rowsRaw: (resource, opts) => stored(m, resource, root, opts).map((row) => rawRow(resource, row)),
1791
2068
  tree: () => twinResources(m.service, root) as Array<Record<string, unknown>>,
1792
2069
  history: (resource, id) => subjectHistory(m.service, { type: storedType(m, resource), id }, root).map((e) => ({ ...(e.operation ? { operation: e.operation } : {}), ...(e.fields ? { fields: e.fields as Record<string, unknown> } : {}), ...(e.occurredAt ? { occurredAt: e.occurredAt } : {}) })),
1793
2070
  raw: (body, init = {}) => new Response(body, { status: init.status ?? 200, headers: init.headers ?? {} }),
1794
2071
  atomically: async (decide) => {
1795
- const { value } = await applyTwinWriteAtomic(
2072
+ let before: TwinResource | undefined;
2073
+ const { value, result } = await applyTwinWriteAtomic(
1796
2074
  m.service,
1797
2075
  (resources) => {
1798
2076
  const rows = (resource: string): Array<Record<string, unknown>> => resources.filter((r) => r.type === storedType(m, resource) && r.deleted !== true) as Array<Record<string, unknown>>;
1799
2077
  const d = decide(rows);
1800
2078
  if (!d.write) return { kind: 'skip', value: d.value };
1801
2079
  const w = d.write;
2080
+ if (transitionObserver) before = resources.find((r) => r.type === storedType(m, w.resource) && r.id === w.id);
1802
2081
  return { kind: 'write', value: d.value, write: { operation: w.operation, subjectType: storedType(m, w.resource), subjectId: w.id, fields: w.fields, occurredAt: at, actor: { kind: 'agent' } } };
1803
2082
  },
1804
2083
  root,
1805
2084
  );
2085
+ if (result) observeStateWrite(m, result, before, root, stateChecks);
1806
2086
  return value;
1807
2087
  },
1808
2088
  legal: (resource, field, operationId, current, to, id, actor) => {
1809
2089
  const decl = m.resources[resource]?.state?.[field];
1810
2090
  if (!decl) throw new Error(`semantics: ${resource}.${field} is not a declared state field`);
1811
2091
  const { move, refusal } = transitionFor(field, decl, operationId, current, to, id ?? Object.values(call.params).at(-1), actor ?? 'api');
1812
- if (move) return undefined;
2092
+ if (move) {
2093
+ if (transitionObserver) {
2094
+ const key = stateCheckKey(storedType(m, resource), field);
2095
+ stateChecks.set(key, [...(stateChecks.get(key) ?? []), { transition: move, operation: operationId, from: stateOf(decl, current), to: move.to ?? stateOf(decl, current), actor: actor ?? 'api' }]);
2096
+ }
2097
+ return undefined;
2098
+ }
1813
2099
  if (refusal) return refusal;
1814
2100
  throw new Error(`semantics: ${operationId} moves ${resource}.${field} from ${String(current)}${to ? ` to ${to}` : ''}, which the machine does not declare`);
1815
2101
  },
1816
2102
  refuse: (e) => vendorError(m, e),
2103
+ gap: () => vendorError(m, m.gap ?? m.notFound),
1817
2104
  notFound: (resource, id, param) => notFound(m, resource, id, param),
1818
2105
  validate: () => validateBody(m, call.operation, body),
1819
2106
  reply: (body, status = 200) => Response.json(body, { status }),
1820
2107
  wrap: (body, extra = {}) => Response.json({ ...envelope(m, call.operation, body), ...extra }),
1821
2108
  sse: (events) => sse(m, events, call.operation.id),
1822
- expand: (resource, body, given) => ((given ?? paths).length ? expand(m, resource, body, given ?? paths, root) : body),
2109
+ expand: (resource, body, given) => expand(m, resource, body, given ?? paths, root),
1823
2110
  at: (when) => contextFor(m, { ...call, request: new Request(call.request.url) }, { ...scope, clock: () => when }),
1824
2111
  crypto: handlerCrypto,
1825
2112
  flag: (value) => flagOf(m, value),
1826
2113
  fields: (body) => strictFields(m, call.operation, body),
1827
- vendorFetch: (url, init) => vendorFetch(url, init),
2114
+ vendorFetch: (url, init) => vendorFetch(url, init, scope.application),
1828
2115
  parts: async () => {
1829
2116
  const boundary = multipartBoundary(call.request.headers.get('content-type'));
1830
2117
  return boundary === null ? [] : multipartParts(new Uint8Array(await call.request.clone().arrayBuffer()), boundary);
1831
2118
  },
1832
- engine: scope.database === undefined ? unboundDatabase(m.service) : managedDatabase(scope.database, { readOnly: scope.readOnly === true || isReadOnlyRequest(call.request), clock: at }),
2119
+ engine: scope.database === undefined ? unboundDatabase(m.service) : managedDatabase(scope.database, { readOnly: scope.readOnly === true || isReadOnlyRequest(call.request) }),
1833
2120
  redis: (commands, options) => execRedisRun(commands, {
1834
2121
  root, occurredAt: at, service: m.service, readOnly: scope.readOnly === true || isReadOnlyRequest(call.request), dialect: options.dialect,
1835
2122
  ...(options.database === undefined ? {} : { database: options.database }),
@@ -1862,15 +2149,17 @@ export async function contextFor(m: DerivedManifest, call: DerivedCall, scope: C
1862
2149
 
1863
2150
  /** A pack's semantics handlers as the dispatch's handlers. */
1864
2151
  export function bindSemantics(m: DerivedManifest, handlers: Record<string, Semantics>, scope: CoreScope = {}): Record<string, DerivedHandler> {
2152
+ prepareViewOmissions(m);
1865
2153
  return Object.fromEntries(Object.entries(handlers).map(([id, h]) => [id, async (call: DerivedCall) => h(await contextFor(m, call, scope))]));
1866
2154
  }
1867
2155
 
1868
2156
  /** The derived core as the dispatch's core: it owns every operation on a resource the manifest declares. */
1869
2157
  export function coreFor(m: DerivedManifest, scope: CoreScope = {}): { owns: (o: DerivedOperation) => boolean; serve: (call: DerivedCall) => Promise<DerivedCoreOutcome> } {
2158
+ prepareViewOmissions(m);
1870
2159
  const crud = new Set(['create', 'retrieve', 'list', 'update', 'delete']);
1871
2160
  const owns = (o: DerivedOperation): boolean => {
1872
2161
  const decl = o.resource !== undefined ? m.resources[o.resource] : undefined;
1873
- if (!decl || m.unmodeled?.includes(o.id)) return false;
2162
+ if (!decl || m.unmodeled?.includes(o.id) || (o.class === 'list' && !m.list)) return false;
1874
2163
  if (crud.has(o.class)) return true;
1875
2164
  return o.class === 'action' && Object.values(decl.state ?? {}).some((f) => f.transitions.some((t) => t.operation === o.id));
1876
2165
  };
@@ -1892,13 +2181,17 @@ export const SCENARIO_DECISION_HEADER = 'x-volter-scenario-decision';
1892
2181
  function scenarioDecisionOf(request: Request): ScenarioDecision | undefined {
1893
2182
  const raw = request.headers.get(SCENARIO_DECISION_HEADER);
1894
2183
  if (!raw) return undefined;
1895
- try { return JSON.parse(raw) as ScenarioDecision; } catch { return undefined; }
2184
+ try { return parseExactJson(raw) as ScenarioDecision; } catch { return undefined; }
1896
2185
  }
1897
2186
 
1898
2187
  export function authRefusal(m: DerivedManifest, request: Request, root?: string): Response | undefined {
1899
2188
  if (!m.auth) return undefined;
1900
2189
  const url = new URL(request.url);
1901
2190
  if (m.auth.paths !== undefined && !new RegExp(m.auth.paths).test(url.pathname)) return undefined;
2191
+ const wire = m.auth.wires?.find((w) => new RegExp(w.paths).test(url.pathname));
2192
+ const errorManifest = wire ? { ...m, error: wire.error } : m;
2193
+ const invalid = wire?.invalid ?? m.auth.invalid;
2194
+ const missing = wire?.missing ?? m.auth.missing;
1902
2195
  const sources = [{ header: m.auth.header, scheme: m.auth.scheme }, ...(m.auth.also ?? [])];
1903
2196
  const given = sources.map((s) => (s.query !== undefined ? url.searchParams.get(s.query) : s.header ? request.headers.get(s.header) : null)).some((v) => v !== null);
1904
2197
  if (!given && !m.auth.gateWhenAbsent) return undefined;
@@ -1912,17 +2205,41 @@ export function authRefusal(m: DerivedManifest, request: Request, root?: string)
1912
2205
  };
1913
2206
  const key = sources.map(keyOf).find((k) => k !== '') ?? '';
1914
2207
  // no credential sent is `missing`; one sent in a form the vendor does not read is a key it does not know
1915
- if (!key) return vendorError(m, given ? m.auth.invalid : m.auth.missing);
1916
- if (m.auth.invalidKeys.includes(key) || (m.auth.keyFormat && !new RegExp(m.auth.keyFormat).test(key))) return vendorError(m, m.auth.invalid);
2208
+ if (!key) return vendorError(errorManifest, given ? invalid : missing);
2209
+ if (m.auth.invalidKeys.includes(key) || (m.auth.keyFormat && !new RegExp(m.auth.keyFormat).test(key))) return vendorError(errorManifest, invalid);
1917
2210
  const held = m.auth.held;
1918
2211
  if (held && !held.standing?.includes(key)) {
1919
2212
  const hash = sha256(key);
1920
2213
  const holds = resourcesOfType(m.service, held.storedAs, root).some((r) => r.deleted !== true && (r as Record<string, unknown>)[held.hashField] === hash && (held.unless === undefined || (r as Record<string, unknown>)[held.unless] !== true));
1921
- if (!holds) return vendorError(m, m.auth.invalid);
2214
+ if (!holds) return vendorError(errorManifest, invalid);
1922
2215
  }
1923
2216
  return undefined;
1924
2217
  }
1925
2218
 
2219
+ /** The number of answers a World has given that carried a filled answer header (`answerHeaders`'s `{hex:N}`), kept in
2220
+ * the World's own directory beside its rate-budget ledger (bookkeeping, never a log entry): a fresh World counts from 1,
2221
+ * so two Worlds made alike answer the same values in the same order, and a resumed World counts on, never answering a
2222
+ * value the count holds again. A World with no root (an in-memory walk) counts in this process. */
2223
+ const answeredInMemory = new Map<string, number>();
2224
+ function nextAnswer(service: string, root: string | undefined): number {
2225
+ const key = `${service}:${root ?? ''}`;
2226
+ if (root === undefined) { const n = (answeredInMemory.get(key) ?? 0) + 1; answeredInMemory.set(key, n); return n; }
2227
+ const store = getActiveWorldStore();
2228
+ const path = `${worldPaths(service, root).dir}/answer-count`;
2229
+ // a read-only request writes nothing, bookkeeping and its lock included, and reads the count without the lock: it
2230
+ // counts on in this process past the kept count, so after a restart, or from another process serving the same World,
2231
+ // the answers given to read-only requests since the last write may be given again
2232
+ if (writesRefused()) { const kept = Number(store.read(path) ?? '0') || 0; const extra = (answeredInMemory.get(key) ?? 0) + 1; answeredInMemory.set(key, extra); return kept + extra; }
2233
+ // read and written under the count's lock, as the rate-budget ledger is: two writes from processes serving one World never
2234
+ // take one number
2235
+ return store.withLock(`${path}.lock`, () => {
2236
+ const n = (Number(store.read(path) ?? '0') || 0) + (answeredInMemory.get(key) ?? 0) + 1;
2237
+ answeredInMemory.delete(key);
2238
+ store.writeAtomic(path, String(n));
2239
+ return n;
2240
+ });
2241
+ }
2242
+
1926
2243
  export function crossCutting(m: DerivedManifest, opts: CoreScope & { readOnly?: boolean } = {}): (call: DerivedCall, next: () => Promise<Response>) => Promise<Response> {
1927
2244
  const finish = async (r: Response, request: Request): Promise<Response> => {
1928
2245
  if (!m.origin || !(r.headers.get('content-type') ?? '').includes('json')) return withHeaders(r);
@@ -1932,7 +2249,14 @@ export function crossCutting(m: DerivedManifest, opts: CoreScope & { readOnly?:
1932
2249
  const withHeaders = (r: Response): Response => {
1933
2250
  if (!m.answerHeaders) return r;
1934
2251
  const headers = new Headers(r.headers);
1935
- for (const [k, v] of Object.entries(m.answerHeaders)) if (!headers.has(k)) headers.set(k, v);
2252
+ const world = `${m.service}:${opts.root ?? ''}`;
2253
+ let seed: string | undefined;
2254
+ const filled = (v: string): string => v.replace(/\{hex:(\d+)\}/g, (_, w: string) => {
2255
+ seed ??= `${world}:answer:${nextAnswer(m.service, opts.root)}`;
2256
+ let hex = ''; for (let i = 0; hex.length < Number(w); i++) hex += sha256(`${seed}:${i}`);
2257
+ return hex.slice(0, Number(w));
2258
+ });
2259
+ for (const [k, v] of Object.entries(m.answerHeaders)) if (!headers.has(k)) headers.set(k, filled(v));
1936
2260
  return new Response(r.body, { status: r.status, statusText: r.statusText, headers });
1937
2261
  };
1938
2262
  return async (call, next) => finish(await guarded(call, next), call.request);
@@ -1950,7 +2274,7 @@ export function crossCutting(m: DerivedManifest, opts: CoreScope & { readOnly?:
1950
2274
  // multipart or form: Cloudflare's asset and script uploads)
1951
2275
  const declared = call.operation.bodyEncoding;
1952
2276
  const labelled = request.headers.get('content-type') ?? '';
1953
- if ((labelled.includes('json') && !labelled.includes('ndjson')) || (m.body.json === 'always' && declared !== 'multipart' && declared !== 'form')) {
2277
+ if (!bytesPayload(call.operation) && ((labelled.includes('json') && !labelled.includes('ndjson') && !labelled.includes('connect+')) || (m.body.json === 'always' && declared !== 'multipart' && declared !== 'form'))) {
1954
2278
  const ok = request.method === 'GET' || request.method === 'HEAD' ? true : await checkJsonBody(request.clone()).catch(() => true);
1955
2279
  if (!ok) return vendorError(m, m.malformedBody ?? { status: 400, message: 'The request body could not be parsed as JSON.' });
1956
2280
  }
@@ -1986,8 +2310,8 @@ export function crossCutting(m: DerivedManifest, opts: CoreScope & { readOnly?:
1986
2310
  * coming (another wire's mutation, a door, a screen) — and answered with the vendor's own read-only
1987
2311
  * error; `GET /twin` advertises `requestScopes`. The pack's catch-up runs under `runAsVendorMove`
1988
2312
  * (request-scope.ts), so time's moves still land when a read-only request is what arrives. */
1989
- export function derivedRequestScopes<F extends (request: Request) => Promise<Response>>(m: DerivedManifest, fetch: F): F {
1990
- return withRequestScopes(fetch, { refuse: () => vendorError(m, m.readOnly) });
2313
+ export function derivedRequestScopes<F extends (request: Request) => Promise<Response>>(m: DerivedManifest, fetch: F, readOnly = false): F {
2314
+ return withRequestScopes(fetch, { readOnly, refuse: () => vendorError(m, m.readOnly) });
1991
2315
  }
1992
2316
 
1993
2317
  /** A semantics context for a request no surface operation names: another wire's (GraphQL) resolvers