@substrat-run/kernel 0.126.0 → 0.128.0

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 (44) hide show
  1. package/dist/entity-edges.d.ts +8 -0
  2. package/dist/entity-edges.d.ts.map +1 -1
  3. package/dist/entity-edges.js +26 -12
  4. package/dist/entity-edges.js.map +1 -1
  5. package/dist/index.d.ts +5 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/invocation-log.d.ts +115 -18
  10. package/dist/invocation-log.d.ts.map +1 -1
  11. package/dist/invocation-log.js +183 -66
  12. package/dist/invocation-log.js.map +1 -1
  13. package/dist/lifecycle-flow.d.ts +41 -0
  14. package/dist/lifecycle-flow.d.ts.map +1 -0
  15. package/dist/lifecycle-flow.js +358 -0
  16. package/dist/lifecycle-flow.js.map +1 -0
  17. package/dist/platform-sweep.d.ts +12 -1
  18. package/dist/platform-sweep.d.ts.map +1 -1
  19. package/dist/platform-sweep.js +13 -3
  20. package/dist/platform-sweep.js.map +1 -1
  21. package/dist/routed-node.d.ts +12 -0
  22. package/dist/routed-node.d.ts.map +1 -1
  23. package/dist/routed-node.js +15 -6
  24. package/dist/routed-node.js.map +1 -1
  25. package/dist/scope-host.d.ts +17 -1
  26. package/dist/scope-host.d.ts.map +1 -1
  27. package/dist/scope-host.js.map +1 -1
  28. package/dist/scope-repoint.d.ts +33 -3
  29. package/dist/scope-repoint.d.ts.map +1 -1
  30. package/dist/scope-repoint.js +79 -6
  31. package/dist/scope-repoint.js.map +1 -1
  32. package/dist/scope-tuple-seat.d.ts +6 -2
  33. package/dist/scope-tuple-seat.d.ts.map +1 -1
  34. package/dist/scope-tuple-seat.js +10 -4
  35. package/dist/scope-tuple-seat.js.map +1 -1
  36. package/dist/spine-guard.d.ts +6 -0
  37. package/dist/spine-guard.d.ts.map +1 -1
  38. package/dist/spine-guard.js +6 -2
  39. package/dist/spine-guard.js.map +1 -1
  40. package/dist/spine-restore.d.ts +82 -0
  41. package/dist/spine-restore.d.ts.map +1 -0
  42. package/dist/spine-restore.js +138 -0
  43. package/dist/spine-restore.js.map +1 -0
  44. package/package.json +2 -2
@@ -54,6 +54,9 @@
54
54
  * verification the vertical does for its own routing happens here, with the same secret
55
55
  * and the same dev opt-out, and a failed one writes nothing at all.
56
56
  */
57
+ // The subpath, not the root: this module is bundled into every vertical's upload (the
58
+ // platform entry, #1893), and the root carries every schema in the vocabulary.
59
+ import { encodeInvocationRecord, INVOCATION_RECORD_HEADER, invocationLevelOf, } from '@substrat-run/contracts/invocation-record';
57
60
  import { ulid } from './ulid.js';
58
61
  import { readRoutedNode, RouterAssertionError } from './routed-node.js';
59
62
  /**
@@ -62,27 +65,9 @@ import { readRoutedNode, RouterAssertionError } from './routed-node.js';
62
65
  * package, and a vertical's own routes may fill it in too.
63
66
  */
64
67
  export const INVOCATION_RECORD_KEY = 'substratInvocationRecord';
65
- /**
66
- * The level of an invocation, from how it ended.
67
- *
68
- * A stamped line is pure JSON, so the log platform sets no level on it (the reader's
69
- * comments in `cf-observability.ts` found this out the hard way). The level histogram
70
- * still needs one per request, so the line carries its own: a 5xx or an escaped throw is
71
- * an error, a 4xx is a warning (the request was refused, which the caller may need to
72
- * hear about, and nothing broke), a success carrying a problem code is a warning too, and
73
- * anything else is info.
74
- */
75
- export function invocationLevelOf(status, threw, problemCode) {
76
- if (threw || status === null || status >= 500)
77
- return 'error';
78
- if (status >= 400)
79
- return 'warn';
80
- // A failure answered IN-BAND — an MCP tool error is a 200 carrying `isError` — is still
81
- // a refused call, and filing it as info would hide it from the one filter that looks.
82
- if (problemCode)
83
- return 'warn';
84
- return 'info';
85
- }
68
+ // The level and its type moved to contracts in #1904 — the router files its datapoint under
69
+ // the same level — and stay exported from here, where every caller already reads them.
70
+ export { invocationLevelOf } from '@substrat-run/contracts/invocation-record';
86
71
  /** Distinct values, first occurrence wins — the order a reader expects to see them in. */
87
72
  function distinct(values) {
88
73
  return [...new Set(values)];
@@ -119,6 +104,86 @@ function pathOf(url) {
119
104
  return '/';
120
105
  }
121
106
  }
107
+ /**
108
+ * The registry of stamps, keyed by the incoming `Request`, on `globalThis` under a GLOBAL
109
+ * symbol. Global rather than module state, because two copies of this code meet in one
110
+ * request: the platform's entry (`withInvocationLog`, prebuilt into the upload) and the
111
+ * kernel the vertical bundled — different module instances, one `Symbol.for`. Weak, so a
112
+ * finished request's stamp goes with its `Request`.
113
+ */
114
+ const STAMPS = Symbol.for('substrat.invocation-stamp');
115
+ function stamps() {
116
+ const g = globalThis;
117
+ return (g[STAMPS] ??= new WeakMap());
118
+ }
119
+ /**
120
+ * The stamp a request already carries, if a layer outside this one started it (#1893).
121
+ * `vertical-host` reads the record through this when the Hono context has none, which is
122
+ * the case when the platform's entry stamped the request and the vertical mounts nothing.
123
+ */
124
+ export function invocationStampOf(request) {
125
+ return stamps().get(request);
126
+ }
127
+ function beginStamp(request) {
128
+ const stamp = { invocationId: ulid(), record: {} };
129
+ stamps().set(request, stamp);
130
+ return stamp;
131
+ }
132
+ /**
133
+ * Write the line for a finished request — the one place it is written, whichever layer
134
+ * started the stamp.
135
+ *
136
+ * No VERIFIED tenant ⇒ no line, and the three ways that happens are all silence here: no
137
+ * assertion at all (an un-routed call), an assertion this worker cannot verify or that is
138
+ * not the router's (`RouterAssertionError`), and — defensively — anything else the read
139
+ * throws. Writing a line for any of them would be filing one caller's request under a
140
+ * tenant of their choosing.
141
+ */
142
+ function writeInvocationLine(stamp, done, options) {
143
+ // Called from a `finally`: a throw here would replace the vertical's answer, or its own
144
+ // error, with a logging failure. The platform wraps every uploaded script in this (#1893),
145
+ // so nothing in it may escape — a line that cannot be written is one missing line.
146
+ try {
147
+ writeLineOrThrow(stamp, done, options);
148
+ }
149
+ catch (e) {
150
+ try {
151
+ console.log(JSON.stringify({ substrat: 'invocation-log-fault', detail: String(e) }));
152
+ }
153
+ catch {
154
+ /* the console itself is gone; there is nowhere left to say so */
155
+ }
156
+ }
157
+ }
158
+ function writeLineOrThrow(stamp, done, options) {
159
+ const node = routedNodeOrNull(done.request.headers, done.env, options);
160
+ if (!node)
161
+ return;
162
+ const { record, invocationId } = stamp;
163
+ const emitted = record.emitted;
164
+ const line = {
165
+ substrat: 'invocation',
166
+ tenantId: node.tenantId,
167
+ scopeId: node.scopeId,
168
+ vertical: node.verticalSlug,
169
+ surface: node.surface,
170
+ method: done.request.method,
171
+ path: pathOf(done.request.url),
172
+ status: done.status,
173
+ threw: done.threw,
174
+ durationMs: Date.now() - done.started,
175
+ invocationId,
176
+ level: invocationLevelOf(done.status, done.threw, record.problemCode),
177
+ operation: record.operation ?? null,
178
+ problemCode: record.problemCode ?? null,
179
+ principalKind: record.principalKind ?? null,
180
+ eventCount: emitted ? emitted.total : null,
181
+ eventTypes: emitted ? distinct(emitted.events.map((e) => e.type)) : [],
182
+ entities: emitted ? distinct(emitted.events.map((e) => e.entity)) : [],
183
+ versionId: versionIdOf(done.env),
184
+ };
185
+ console.log(JSON.stringify(line));
186
+ }
122
187
  /**
123
188
  * Mount as the FIRST middleware on a vertical's app, with the same two answers the
124
189
  * vertical gives `readRoutedNode` in its own `nodeFor`:
@@ -135,26 +200,36 @@ function pathOf(url) {
135
200
  * ```
136
201
  *
137
202
  * First, because Hono composes handlers in registration order and stops at the one that
138
- * returns a response — middleware registered after a route does not wrap that route. A
139
- * vertical that mounts this below its own routes gets a log for some of its surface and
140
- * silence for the rest, which is worse than none, because the silence reads as no
141
- * traffic. `pnpm lint:invocation-log` refuses that arrangement rather than trusting
142
- * anyone to remember it.
203
+ * returns a response — middleware registered after a route does not wrap that route.
204
+ *
205
+ * **Since #1893 the platform stamps every deployed request itself**, by wrapping the
206
+ * uploaded entry with `withInvocationLog`. A vertical that still mounts this middleware
207
+ * then finds the request already stamped and steps aside — it hands the platform's stamp
208
+ * to the handler chain through the context, as it always did, and writes no second line.
209
+ * Mounted where no platform entry runs (a dev server behind a dev router), it stamps as
210
+ * before.
143
211
  *
144
212
  * Nothing here can fail a request: the line is written in a `finally`, and a throw from
145
213
  * the handler is re-thrown untouched for `onError` to map as it always did.
146
214
  */
147
215
  export function invocationLog(options = {}) {
148
216
  return async (c, next) => {
217
+ // Stamped already by the platform's entry: pass the stamp down, write nothing.
218
+ const existing = invocationStampOf(c.req.raw);
219
+ if (existing) {
220
+ c.set?.('substratInvocationId', existing.invocationId);
221
+ c.set?.(INVOCATION_RECORD_KEY, existing.record);
222
+ await next();
223
+ return;
224
+ }
149
225
  // Host code, so a real clock is correct here — `ctx.now()` is the module-code rule,
150
226
  // and this middleware runs outside any operation's transaction.
151
227
  const started = Date.now();
152
228
  // Minted per request, before anything can emit. A ULID so it sorts by time like
153
229
  // every other id on the spine.
154
- const invocationId = ulid();
155
- c.set?.('substratInvocationId', invocationId);
156
- const record = {};
157
- c.set?.(INVOCATION_RECORD_KEY, record);
230
+ const stamp = beginStamp(c.req.raw);
231
+ c.set?.('substratInvocationId', stamp.invocationId);
232
+ c.set?.(INVOCATION_RECORD_KEY, stamp.record);
158
233
  let threw = false;
159
234
  try {
160
235
  await next();
@@ -166,41 +241,83 @@ export function invocationLog(options = {}) {
166
241
  throw e;
167
242
  }
168
243
  finally {
169
- // No VERIFIED tenant ⇒ no line, and the three ways that happens are all silence
170
- // here: no assertion at all (an un-routed call), an assertion this worker cannot
171
- // verify or that is not the router's (`RouterAssertionError`), and — defensively —
172
- // anything else the read throws. Writing a line for any of them would be filing
173
- // one caller's request under a tenant of their choosing.
174
- const node = routedNodeOrNull(c, options);
175
- if (node) {
176
- const status = threw ? null : (c.res?.status ?? null);
177
- const emitted = record.emitted;
178
- const line = {
179
- substrat: 'invocation',
180
- tenantId: node.tenantId,
181
- scopeId: node.scopeId,
182
- vertical: node.verticalSlug,
183
- surface: node.surface,
184
- method: c.req.method,
185
- path: pathOf(c.req.raw.url),
186
- status,
187
- threw,
188
- durationMs: Date.now() - started,
189
- invocationId,
190
- level: invocationLevelOf(status, threw, record.problemCode),
191
- operation: record.operation ?? null,
192
- problemCode: record.problemCode ?? null,
193
- principalKind: record.principalKind ?? null,
194
- eventCount: emitted ? emitted.total : null,
195
- eventTypes: emitted ? distinct(emitted.events.map((e) => e.type)) : [],
196
- entities: emitted ? distinct(emitted.events.map((e) => e.entity)) : [],
197
- versionId: versionIdOf(c.env),
198
- };
199
- console.log(JSON.stringify(line));
200
- }
244
+ writeInvocationLine(stamp, { request: { method: c.req.method, url: c.req.raw.url, headers: c.req.raw.headers }, env: c.env, status: threw ? null : (c.res?.status ?? null), threw, started }, options);
201
245
  }
202
246
  };
203
247
  }
248
+ /** The handlers a module worker may export besides `fetch`, passed through untouched. */
249
+ const OTHER_HANDLERS = ['scheduled', 'queue', 'email', 'tail', 'trace', 'alarm', 'test'];
250
+ /**
251
+ * The platform's half of the stamp (#1893): wrap a module worker's `fetch` so every request
252
+ * it serves is stamped, whatever framework the worker is written in and whether or not it
253
+ * mounts the middleware.
254
+ *
255
+ * The control plane builds this into the entry it uploads in front of every vertical's
256
+ * bundle (`platform-entry.generated.ts`), which is what makes the stamp the platform's
257
+ * rather than a line each vertical has to remember. A request the vertical's own
258
+ * middleware already stamped — possible only if something wrapped this one — is passed
259
+ * straight through. The other handlers (`scheduled`, `queue`, …) are passed through
260
+ * untouched, bound to the worker, since a stamp is about a routed request.
261
+ *
262
+ * Nothing here can fail a request: the line is written in a `finally`, and a throw is
263
+ * re-thrown untouched.
264
+ */
265
+ export function withInvocationLog(worker, options = {}) {
266
+ // The platform wraps whatever a bundle's default export is (#1893): a class entrypoint or
267
+ // no default at all has no `fetch` to stamp, and is handed back untouched.
268
+ const inner = worker?.fetch;
269
+ if (typeof inner !== 'function')
270
+ return worker;
271
+ const wrapped = {
272
+ async fetch(request, env, ctx) {
273
+ if (invocationStampOf(request))
274
+ return inner.call(worker, request, env, ctx);
275
+ const started = Date.now();
276
+ const stamp = beginStamp(request);
277
+ let status = null;
278
+ let threw = false;
279
+ try {
280
+ const response = await inner.call(worker, request, env, ctx);
281
+ status = response.status;
282
+ handRecordToRouter(response, stamp.record, request, env, options);
283
+ return response;
284
+ }
285
+ catch (e) {
286
+ threw = true;
287
+ throw e;
288
+ }
289
+ finally {
290
+ writeInvocationLine(stamp, { request, env, status, threw, started }, options);
291
+ }
292
+ },
293
+ };
294
+ for (const name of OTHER_HANDLERS) {
295
+ const h = worker[name];
296
+ if (typeof h === 'function')
297
+ wrapped[name] = h.bind(worker);
298
+ }
299
+ return wrapped;
300
+ }
301
+ /**
302
+ * #1904: hand the record back to the router on the response, which meters the request with
303
+ * it and strips the header before the response leaves (`INVOCATION_RECORD_HEADER`).
304
+ *
305
+ * Only on a request the router vouched for: a direct caller gets no header, and the router
306
+ * is the only reader. Never a throw — a response whose headers are immutable (one passed
307
+ * straight through from a `fetch`) reached no operation and has nothing to hand back, and
308
+ * metering must not fail a request either way.
309
+ */
310
+ function handRecordToRouter(response, record, request, env, options) {
311
+ try {
312
+ const value = encodeInvocationRecord(record);
313
+ if (value === null || !routedNodeOrNull(request.headers, env, options))
314
+ return;
315
+ response.headers?.set?.(INVOCATION_RECORD_HEADER, value);
316
+ }
317
+ catch {
318
+ /* immutable headers: see above */
319
+ }
320
+ }
204
321
  /**
205
322
  * The verified node, or `null` — never a throw.
206
323
  *
@@ -210,11 +327,11 @@ export function invocationLog(options = {}) {
210
327
  * would replace the vertical's real answer with a logging failure. So the loudness is
211
328
  * swallowed HERE and only here, and the request is unaffected either way.
212
329
  */
213
- function routedNodeOrNull(c, options) {
330
+ function routedNodeOrNull(headers, env, options) {
214
331
  try {
215
- return readRoutedNode(c.req.raw.headers, {
216
- expectedSecret: options.routerSecret?.(c.env),
217
- allowUnsigned: options.allowUnsigned?.(c.env) ?? false,
332
+ return readRoutedNode(headers, {
333
+ expectedSecret: options.routerSecret?.(env),
334
+ allowUnsigned: options.allowUnsigned?.(env) ?? false,
218
335
  });
219
336
  }
220
337
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"invocation-log.js","sourceRoot":"","sources":["../src/invocation-log.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"invocation-log.js","sourceRoot":"","sources":["../src/invocation-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Replay one entity's declared lifecycle over a scope's outbox (#1744) — the process map's
3
+ * data, with no new write path.
4
+ *
5
+ * A transition is a status write inside an operation body, and the kernel never recorded
6
+ * "entity X moved from A to B". It did record everything a replay needs: every event names
7
+ * its entity, and (#1243) the operation that emitted it — which is exactly what a declared
8
+ * edge is keyed by. So one entity's events, in order, give its state sequence, and the gaps
9
+ * between moves give time-in-state.
10
+ *
11
+ * ## Where a state comes from
12
+ *
13
+ * **The payload first.** A fat event usually carries the row's lifecycle field, and when it
14
+ * does that value is what the row actually held — no inference about what the operation
15
+ * meant. It is read only from events classed `pii_class = 'none'`: the field is an enum, but
16
+ * #1762's rule is per event, and a read that extracts from a classified payload is the kind
17
+ * of exception that later grows.
18
+ *
19
+ * **The declaration otherwise.** An erased, classified or field-less event still names its
20
+ * operation, and the operation column survives a shred. From the state the replay has the
21
+ * entity in, `on[operation]` is where the declaration says it went. Counted apart
22
+ * (`inferred`), because this half is faithful only while the code agrees with the model.
23
+ *
24
+ * The previous state decides what an event means, which is why this is a walk in order and
25
+ * not a GROUP BY: `ticket0/ingest-message` is merely *allowed* in `open` and an *edge* out of
26
+ * `resolved`, so the same operation is a move or not depending on where the entity was.
27
+ *
28
+ * ## Bounded
29
+ *
30
+ * One entity type's events, walked in `(entity_id, id)` order — the `_substrat_outbox_entity`
31
+ * index — in keyset pages, up to `LIFECYCLE_FLOW_EVENT_BUDGET`. Past it the answer says
32
+ * `complete: false`, and every count is a lower bound; it never quietly becomes a smaller
33
+ * answer. History BEFORE the window is replayed too, since the state an entity was in when
34
+ * the window opened is only known from it; events after `until` are not read at all.
35
+ *
36
+ * Same permission posture as every read in `timeline.ts`: the caller checks, this does not.
37
+ */
38
+ import { type LifecycleFlowInput, type LifecycleFlowResult } from '@substrat-run/contracts';
39
+ import type { TimelineReader } from './timeline.js';
40
+ export declare function readLifecycleFlow(ctx: TimelineReader, input: LifecycleFlowInput): LifecycleFlowResult;
41
+ //# sourceMappingURL=lifecycle-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-flow.d.ts","sourceRoot":"","sources":["../src/lifecycle-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA+EpD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CAgRrG"}