@substrat-run/contracts 0.83.0 → 0.85.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.
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { primaryKeyOf } from './model.js';
2
3
  // ---------------------------------------------------------------------------
3
4
  // The composer.
4
5
  // ---------------------------------------------------------------------------
@@ -27,7 +28,54 @@ import { z } from 'zod';
27
28
  * ```
28
29
  */
29
30
  export function defineOperations(_entities, _permissions, _engines) {
30
- return (operations) => operations;
31
+ return (operations) => {
32
+ assertListsArePaged(operations);
33
+ return operations;
34
+ };
35
+ }
36
+ /**
37
+ * A read that answers with a whole table must say it is a page (#811).
38
+ *
39
+ * An unbounded list endpoint is a bug with a delay on it: it passes review, it
40
+ * passes tests, and then one tenant's table gets large. So a bare `z.array(...)`
41
+ * output with no `paged` beside it is refused — the operation either pages, or it
42
+ * is not a list.
43
+ *
44
+ * ## Why at module load rather than in a lint tool
45
+ *
46
+ * #811 asked for this as a `lint:model --check` gate. A tool has to FIND the
47
+ * declarations, and the ones it would have missed are exactly the ones that
48
+ * matter: `model-diff` reads entities, not operations, and `api-diff` only sees
49
+ * verticals that opted into `src/api.ts` — which is none of the four engines whose
50
+ * list reads this issue was filed about. Checking here reaches every module that
51
+ * declares operations at all, engine or vertical, with nothing to discover and
52
+ * nothing to opt into.
53
+ *
54
+ * Same reasoning that moved #844's "a state the field cannot hold" check to load
55
+ * time: it runs where it can actually bite. It fires in every build, every test
56
+ * and every dev server, so it cannot be true only of the modules a tool knew about.
57
+ *
58
+ * **A nested array is untouched.** `output: z.object({ ids: z.array(…) })` is an
59
+ * object with a list inside it — a shape whose size the operation controls — not a
60
+ * table read. Only a TOP-LEVEL array output is a list by this rule.
61
+ */
62
+ function assertListsArePaged(operations) {
63
+ for (const [name, op] of Object.entries(operations)) {
64
+ const decl = op;
65
+ if (decl.paged !== undefined)
66
+ continue;
67
+ if (!(decl.output instanceof z.ZodArray))
68
+ continue;
69
+ throw new Error(`model: '${name}' returns a bare array and does not declare \`paged\` — a list read ` +
70
+ 'that answers with the whole table is unbounded by construction.\n' +
71
+ ' Remedy: declare the ENTRY as `output` and add `paged`. Either the kernel ' +
72
+ 'composes the walk —\n' +
73
+ " paged: { over: { entity: 'thing', sortable: ['created_at'], filterable: ['status'] } }\n" +
74
+ ' — or, where it cannot (a kernel table, a correlated subquery, a per-row ' +
75
+ 'permission walk),\n' +
76
+ ' the handler composes its own and names the entry field the cursor walks:\n' +
77
+ " paged: { sortKey: 'article' }");
78
+ }
31
79
  }
32
80
  /**
33
81
  * The permission keys an operation set actually requires, for the manifest.
@@ -68,6 +116,65 @@ export function eventsEmittedBy(operations) {
68
116
  .sort(([a], [b]) => a.localeCompare(b))
69
117
  .map(([type, schemaVersion]) => ({ type, schemaVersion }));
70
118
  }
119
+ /**
120
+ * The paged lists an operation set declares, for the manifest (#811).
121
+ *
122
+ * Read off every `paged.over` the way `eventsEmittedBy` reads every `emits`, and
123
+ * for the identical reason: the index the kernel provisions and the columns the
124
+ * operation offers are ONE fact, and a manifest restating it by hand is how two
125
+ * descriptions come to disagree. `table` and `idColumn` are resolved here from
126
+ * the same registry the columns are compile-checked against, so nothing states
127
+ * twice where a work order lives.
128
+ *
129
+ * **Two operations may page the same entity** — `workorder/list` and a vertical's
130
+ * portal read over the same table — so their vocabularies are UNIONED rather than
131
+ * refused. The index has to cover both walks either way, and one of them being
132
+ * narrower is not a conflict. (Two *modules* claiming one entity type IS refused;
133
+ * that check belongs to the kernel, which is the only thing that sees them all.)
134
+ */
135
+ export function listsDeclaredBy(operations, entities, engines = []) {
136
+ const byEntity = new Map();
137
+ for (const [name, op] of Object.entries(operations)) {
138
+ const over = op.paged?.over;
139
+ if (!over || typeof over.entity !== 'string')
140
+ continue;
141
+ const acc = byEntity.get(over.entity) ?? { sortable: [], filterable: [] };
142
+ for (const c of (over.sortable ?? [])) {
143
+ if (typeof c === 'string' && !acc.sortable.includes(c))
144
+ acc.sortable.push(c);
145
+ }
146
+ for (const c of (over.filterable ?? [])) {
147
+ if (typeof c === 'string' && !acc.filterable.includes(c))
148
+ acc.filterable.push(c);
149
+ }
150
+ if (!acc.sortable.length) {
151
+ throw new Error(`model: '${name}' declares \`paged.over\` with no sortable column`);
152
+ }
153
+ byEntity.set(over.entity, acc);
154
+ }
155
+ return [...byEntity.entries()]
156
+ .sort(([a], [b]) => a.localeCompare(b))
157
+ .map(([entityType, acc]) => {
158
+ const entity = entities[entityType] ?? engines.map((r) => r[entityType]).find(Boolean);
159
+ if (!entity) {
160
+ throw new Error(`model: a paged list is declared over '${entityType}', which is not a declared entity`);
161
+ }
162
+ const key = primaryKeyOf(entityType, entity);
163
+ if (key.length !== 1) {
164
+ throw new Error(`model: '${entityType}' is keyed by (${key.join(', ')}) and cannot be paged — ` +
165
+ 'a keyset walk needs one column to break ties on, and a composite key has none');
166
+ }
167
+ return {
168
+ entityType,
169
+ // NOT sorted: the first sortable column is the DEFAULT sort, so the order
170
+ // of this array is part of the fact.
171
+ sortable: acc.sortable,
172
+ ...(acc.filterable.length ? { filterable: [...acc.filterable].sort() } : {}),
173
+ table: entity.table,
174
+ idColumn: key[0],
175
+ };
176
+ });
177
+ }
71
178
  /**
72
179
  * The operation half of a module's manifest — derived, not written twice.
73
180
  *
@@ -1 +1 @@
1
- {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,17 @@ import { z } from 'zod';
18
18
  * needs no encoding and survives in a query string. A composite sort key joins
19
19
  * its parts with `|` (first part always `|`-free: a ULID or enum).
20
20
  */
21
+ /**
22
+ * Resolve a requested page size against the platform's default and ceiling.
23
+ *
24
+ * One definition, because there are now three callers that must agree: the HTTP
25
+ * layer (`listPageQuery`), `ctx.page`, and every handler-composed read that
26
+ * builds its own `LIMIT`. An in-process caller — a test, a seed, another
27
+ * operation — legitimately passes nothing, and the answer it gets has to be the
28
+ * same page an HTTP caller would get, or a scenario proves something the wire
29
+ * does not do.
30
+ */
31
+ export declare function listLimitOf(limit?: number): number;
21
32
  /** Default page size on every HTTP list read. */
22
33
  export declare const LIST_PAGE_DEFAULT = 20;
23
34
  /** Hard ceiling on a requested page — same order as a table page (§ introspection). */
@@ -150,4 +161,88 @@ export declare function pageSchema<T extends z.ZodType>(entry: T, withTotal?: bo
150
161
  entries: z.ZodArray<T>;
151
162
  nextCursor: z.ZodNullable<z.ZodString>;
152
163
  }, z.core.$strip>;
164
+ /**
165
+ * Re-shape a page's entries without touching its walk (#811).
166
+ *
167
+ * The kernel-composed half of a paged read (`paged.over`) returns a page of
168
+ * ROWS, because the kernel knows the table and nothing else: `toWorkOrder`,
169
+ * `facilities: […]`, a per-row aggregate are all the handler's business. So the
170
+ * handler maps, and the cursor and total have to survive that untouched.
171
+ *
172
+ * Written as a helper rather than left to a spread because the spread is wrong
173
+ * in a way that type-checks: `{ ...page, entries: page.entries.map(f) }` keeps
174
+ * `total` only when the source had one, which is exactly right — and
175
+ * `{ entries: …, nextCursor: page.nextCursor }` silently drops it, which is the
176
+ * mistake somebody makes once per adopter.
177
+ */
178
+ /**
179
+ * Overloaded rather than conditional on a `P extends Page<A>` parameter: that
180
+ * form cannot infer `A` from the argument, so every call site had to name its
181
+ * entry type or fall back to `unknown`. The counted overload comes first so a
182
+ * `CountedPage` keeps its total in the RESULT type, not merely at runtime.
183
+ */
184
+ export declare function mapPage<A, B>(page: CountedPage<A>, fn: (entry: A, index: number) => B): CountedPage<B>;
185
+ export declare function mapPage<A, B>(page: Page<A>, fn: (entry: A, index: number) => B): Page<B>;
186
+ /**
187
+ * The query parameter naming which declared sort a caller wants (#811).
188
+ *
189
+ * `sort` rather than `order_by`/`sortBy`: it is one word, it is what Stripe and
190
+ * GitHub both spell, and `order` — already taken, already shipped — is the
191
+ * DIRECTION. Two adjacent parameters whose names could be confused for each
192
+ * other is worth avoiding at the point where they are named, not documented
193
+ * around afterwards.
194
+ */
195
+ export declare const LIST_SORT_PARAM = "sort";
196
+ /**
197
+ * A cursor is only valid for the sort that ISSUED it.
198
+ *
199
+ * Keyset paging compares against the sort column, so a cursor taken from a walk
200
+ * ordered by `number` and replayed against one ordered by `created_at` compares
201
+ * a number to a timestamp: the page is not wrong-looking, it is silently
202
+ * arbitrary. Nothing in the cursor says which sort produced it — `pagination.ts`
203
+ * pins it as "the last entry's sort key verbatim … it needs no encoding", and
204
+ * that stays true.
205
+ *
206
+ * **Following the `Link` header is always safe**, which is the whole reason the
207
+ * walk is handed over as a URL rather than a bare cursor: `nextPageLink` rebuilds
208
+ * it from the request, so `sort` and every filter ride along unchanged and a
209
+ * client that follows links cannot construct this mistake.
210
+ *
211
+ * A client that hand-assembles `?cursor=…&sort=other` can, and this is the
212
+ * documented reason not to. Encoding the sort into the cursor would let the
213
+ * server refuse it outright; that was weighed and declined here, because it
214
+ * breaks the "no encoding, survives in a query string" property that makes a
215
+ * cursor debuggable by eye — see K-041.
216
+ */
217
+ export declare const CURSOR_BELONGS_TO_ITS_SORT = "a cursor is only valid for the sort it was issued under \u2014 follow the Link header";
218
+ /**
219
+ * A page of the entries a caller may actually SEE (#811).
220
+ *
221
+ * The shape a portal read needs, and the one a naive page gets wrong. Callout and
222
+ * Handlebar both list work orders and then check `read` per row, because the
223
+ * proof walk (workorder → facility → customer) is what decides visibility. Twenty
224
+ * rows fetched can leave three after that walk, so the fetch size and the page
225
+ * size are not the same number and cannot be made the same number.
226
+ *
227
+ * **This page may come back short, and a short page does NOT end the walk.** That
228
+ * is a deliberate divergence from `pageOf`, whose whole cursor rule is "full page
229
+ * ⇒ there may be more, short page ⇒ done". Under a per-row filter that rule is
230
+ * unavailable: the only honest signal left is the underlying cursor, so the walk
231
+ * ends where `nextCursor` is null and nowhere else. A client following the `Link`
232
+ * header does the right thing without knowing any of this; one that stops at the
233
+ * first short page will truncate its own results, which is why it is stated here
234
+ * rather than left to be discovered.
235
+ *
236
+ * The cursor returned is the underlying walk's — the last row EXAMINED, not the
237
+ * last row returned. Advancing by the last row returned would re-examine every
238
+ * row the filter rejected on the next request, and a page whose rows are all
239
+ * rejected would never advance at all.
240
+ */
241
+ export declare function pageVisible<T>(fetch: (params: {
242
+ limit: number;
243
+ cursor?: string;
244
+ }) => Page<T>, params: {
245
+ limit?: number;
246
+ cursor?: string;
247
+ } | undefined, allow: (entry: T) => Promise<boolean> | boolean): Promise<Page<T>>;
153
248
  //# sourceMappingURL=pagination.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;GAkBG;AAEH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,uFAAuF;AACvF,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;iBAIxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB;AAED,uDAAuD;AACvD,MAAM,WAAW,IAAI,CAAC,CAAC;IACrB,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,CAAC,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAGzF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,OAAO,EAAE,CAAC,EAAE,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,EACzB,KAAK,EAAE,MAAM,GACZ,WAAW,CAAC,CAAC,CAAC,CAEhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAEvC,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,oCAAiD,CAAC;AAEnF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAKzF;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,CAO7D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,UAAQ;;;kBAY1E"}
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,21 @@ import { z } from 'zod';
18
18
  * needs no encoding and survives in a query string. A composite sort key joins
19
19
  * its parts with `|` (first part always `|`-free: a ULID or enum).
20
20
  */
21
+ /**
22
+ * Resolve a requested page size against the platform's default and ceiling.
23
+ *
24
+ * One definition, because there are now three callers that must agree: the HTTP
25
+ * layer (`listPageQuery`), `ctx.page`, and every handler-composed read that
26
+ * builds its own `LIMIT`. An in-process caller — a test, a seed, another
27
+ * operation — legitimately passes nothing, and the answer it gets has to be the
28
+ * same page an HTTP caller would get, or a scenario proves something the wire
29
+ * does not do.
30
+ */
31
+ export function listLimitOf(limit) {
32
+ if (limit === undefined || !Number.isFinite(limit) || limit < 1)
33
+ return LIST_PAGE_DEFAULT;
34
+ return Math.min(Math.floor(limit), LIST_PAGE_MAX);
35
+ }
21
36
  /** Default page size on every HTTP list read. */
22
37
  export const LIST_PAGE_DEFAULT = 20;
23
38
  /** Hard ceiling on a requested page — same order as a table page (§ introspection). */
@@ -135,4 +150,74 @@ export function pageSchema(entry, withTotal = false) {
135
150
  })
136
151
  : base;
137
152
  }
153
+ export function mapPage(page, fn) {
154
+ return { ...page, entries: page.entries.map(fn) };
155
+ }
156
+ /**
157
+ * The query parameter naming which declared sort a caller wants (#811).
158
+ *
159
+ * `sort` rather than `order_by`/`sortBy`: it is one word, it is what Stripe and
160
+ * GitHub both spell, and `order` — already taken, already shipped — is the
161
+ * DIRECTION. Two adjacent parameters whose names could be confused for each
162
+ * other is worth avoiding at the point where they are named, not documented
163
+ * around afterwards.
164
+ */
165
+ export const LIST_SORT_PARAM = 'sort';
166
+ /**
167
+ * A cursor is only valid for the sort that ISSUED it.
168
+ *
169
+ * Keyset paging compares against the sort column, so a cursor taken from a walk
170
+ * ordered by `number` and replayed against one ordered by `created_at` compares
171
+ * a number to a timestamp: the page is not wrong-looking, it is silently
172
+ * arbitrary. Nothing in the cursor says which sort produced it — `pagination.ts`
173
+ * pins it as "the last entry's sort key verbatim … it needs no encoding", and
174
+ * that stays true.
175
+ *
176
+ * **Following the `Link` header is always safe**, which is the whole reason the
177
+ * walk is handed over as a URL rather than a bare cursor: `nextPageLink` rebuilds
178
+ * it from the request, so `sort` and every filter ride along unchanged and a
179
+ * client that follows links cannot construct this mistake.
180
+ *
181
+ * A client that hand-assembles `?cursor=…&sort=other` can, and this is the
182
+ * documented reason not to. Encoding the sort into the cursor would let the
183
+ * server refuse it outright; that was weighed and declined here, because it
184
+ * breaks the "no encoding, survives in a query string" property that makes a
185
+ * cursor debuggable by eye — see K-041.
186
+ */
187
+ export const CURSOR_BELONGS_TO_ITS_SORT = 'a cursor is only valid for the sort it was issued under — follow the Link header';
188
+ /**
189
+ * A page of the entries a caller may actually SEE (#811).
190
+ *
191
+ * The shape a portal read needs, and the one a naive page gets wrong. Callout and
192
+ * Handlebar both list work orders and then check `read` per row, because the
193
+ * proof walk (workorder → facility → customer) is what decides visibility. Twenty
194
+ * rows fetched can leave three after that walk, so the fetch size and the page
195
+ * size are not the same number and cannot be made the same number.
196
+ *
197
+ * **This page may come back short, and a short page does NOT end the walk.** That
198
+ * is a deliberate divergence from `pageOf`, whose whole cursor rule is "full page
199
+ * ⇒ there may be more, short page ⇒ done". Under a per-row filter that rule is
200
+ * unavailable: the only honest signal left is the underlying cursor, so the walk
201
+ * ends where `nextCursor` is null and nowhere else. A client following the `Link`
202
+ * header does the right thing without knowing any of this; one that stops at the
203
+ * first short page will truncate its own results, which is why it is stated here
204
+ * rather than left to be discovered.
205
+ *
206
+ * The cursor returned is the underlying walk's — the last row EXAMINED, not the
207
+ * last row returned. Advancing by the last row returned would re-examine every
208
+ * row the filter rejected on the next request, and a page whose rows are all
209
+ * rejected would never advance at all.
210
+ */
211
+ export async function pageVisible(fetch, params, allow) {
212
+ // `params` may be absent entirely: an operation with no declared input, invoked
213
+ // in process by a test or a seed, is handed `undefined` rather than an empty
214
+ // object. `listLimitOf` then supplies the same page an HTTP caller would get.
215
+ const batch = fetch({ limit: listLimitOf(params?.limit), cursor: params?.cursor });
216
+ const entries = [];
217
+ for (const entry of batch.entries) {
218
+ if (await allow(entry))
219
+ entries.push(entry);
220
+ }
221
+ return { entries, nextCursor: batch.nextCursor };
222
+ }
138
223
  //# sourceMappingURL=pagination.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,64 @@ export declare const platformRequestInput: z.ZodObject<{
23
23
  payload: z.ZodUnknown;
24
24
  }, z.core.$strip>;
25
25
  export type PlatformRequestInput = z.infer<typeof platformRequestInput>;
26
+ /**
27
+ * WHO refused a settled intent — the fact the drawer's caption was guessing at (#841).
28
+ *
29
+ * A `connector:<provider>` delivery passes through two authorities before it is over: our
30
+ * own permission check on the way to the bytes, and the provider's answer once they are
31
+ * sent. Both surfaced as a `lastError` string and nothing distinguished them, so the
32
+ * dashboard captioned every failure as the provider's — and a `permission denied:
33
+ * protocol:read` raised on OUR side of egress was rendered as *"what Scrive said, in
34
+ * full"*. An operator reading that goes to look at their Scrive account, presses **Test
35
+ * connection** (which passes, because the credential is fine), and concludes the platform
36
+ * is broken. That is the failure this record exists to end.
37
+ *
38
+ * - `platform` — one of ours, raised before anything reached the provider. The refusal
39
+ * carries a taxonomy `code`, so it can be said precisely rather than quoted.
40
+ * - `provider` — the provider ANSWERED and refused. Only this one may be captioned as
41
+ * their words.
42
+ * - `unknown` — classified and could not be told apart: a bug, a socket that never
43
+ * opened, a give-up at the attempt ceiling. Distinct from a NULL `failure`, which means
44
+ * nobody classified at all (a row settled before this field existed, or one that never
45
+ * failed) — honestly unrecorded rather than honestly unknown.
46
+ */
47
+ export declare const platformRequestFailureOrigin: z.ZodEnum<{
48
+ platform: "platform";
49
+ provider: "provider";
50
+ unknown: "unknown";
51
+ }>;
52
+ export type PlatformRequestFailureOrigin = z.infer<typeof platformRequestFailureOrigin>;
53
+ /**
54
+ * How a settled intent's last failure was attributed. Written by the drain from the throw
55
+ * itself, never re-derived downstream from the message — a reader that has to regex prose
56
+ * to know who refused is the bug, not the fix.
57
+ *
58
+ * `permission` is the one extension lifted out of the error and given a column of its own,
59
+ * because it is what makes the join mechanical: a refused key that is absent from the
60
+ * connection's grants is the whole diagnosis, and both halves are already rendered inches
61
+ * apart in the integration drawer. Every other extension stays in the message.
62
+ */
63
+ export declare const platformRequestFailure: z.ZodObject<{
64
+ origin: z.ZodEnum<{
65
+ platform: "platform";
66
+ provider: "provider";
67
+ unknown: "unknown";
68
+ }>;
69
+ code: z.ZodNullable<z.ZodEnum<{
70
+ conflict: "conflict";
71
+ forbidden: "forbidden";
72
+ internal: "internal";
73
+ not_found: "not_found";
74
+ permission_denied: "permission_denied";
75
+ precondition_failed: "precondition_failed";
76
+ rate_limited: "rate_limited";
77
+ unauthenticated: "unauthenticated";
78
+ unavailable: "unavailable";
79
+ validation_failed: "validation_failed";
80
+ }>>;
81
+ permission: z.ZodNullable<z.ZodString>;
82
+ }, z.core.$strip>;
83
+ export type PlatformRequestFailure = z.infer<typeof platformRequestFailure>;
26
84
  /** The full kernel-stamped intent record as it lives in the spine and is read by the drain. */
27
85
  export declare const platformRequest: z.ZodObject<{
28
86
  id: z.core.$ZodBranded<z.ZodString, "PlatformRequestId", "out">;
@@ -40,6 +98,26 @@ export declare const platformRequest: z.ZodObject<{
40
98
  }>;
41
99
  attempts: z.ZodNumber;
42
100
  lastError: z.ZodNullable<z.ZodString>;
101
+ failure: z.ZodNullable<z.ZodObject<{
102
+ origin: z.ZodEnum<{
103
+ platform: "platform";
104
+ provider: "provider";
105
+ unknown: "unknown";
106
+ }>;
107
+ code: z.ZodNullable<z.ZodEnum<{
108
+ conflict: "conflict";
109
+ forbidden: "forbidden";
110
+ internal: "internal";
111
+ not_found: "not_found";
112
+ permission_denied: "permission_denied";
113
+ precondition_failed: "precondition_failed";
114
+ rate_limited: "rate_limited";
115
+ unauthenticated: "unauthenticated";
116
+ unavailable: "unavailable";
117
+ validation_failed: "validation_failed";
118
+ }>>;
119
+ permission: z.ZodNullable<z.ZodString>;
120
+ }, z.core.$strip>>;
43
121
  result: z.ZodNullable<z.ZodUnknown>;
44
122
  requestedAt: z.core.$ZodBranded<z.ZodString, "Instant", "out">;
45
123
  settledAt: z.ZodNullable<z.core.$ZodBranded<z.ZodString, "Instant", "out">>;
@@ -1 +1 @@
1
- {"version":3,"file":"platform-request.d.ts","sourceRoot":"","sources":["../src/platform-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;GAMG;AAEH,eAAO,MAAM,qBAAqB;;;;EAAwC,CAAC;AAC3E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;iBAG/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,+FAA+F;AAC/F,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;iBAW1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E,2FAA2F;AAC3F,eAAO,MAAM,sCAAsC,KAAK,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB;;;;iBAIlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E,uEAAuE;AACvE,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;iBAAwB,CAAC;AACzD,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;iBAG/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;iBAkBjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;iBAKjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,eAAe,CAAC;AAE3D,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,aAAc,MAAM,KAAG,MACT,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGnC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
1
+ {"version":3,"file":"platform-request.d.ts","sourceRoot":"","sources":["../src/platform-request.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { instant, platformRequestId, scopeId } from './ids.js';
3
3
  import { actor, domainEvent } from './events.js';
4
+ import { errorCode } from './errors.js';
4
5
  /**
5
6
  * Platform intents (docs/architecture/platform-intents.md) — how a sandbox-clean vertical asks the
6
7
  * platform to perform a privileged action (provision a sibling scope, request quota, …) without
@@ -19,6 +20,45 @@ export const platformRequestInput = z.object({
19
20
  kind: z.string().min(1),
20
21
  payload: z.unknown(),
21
22
  });
23
+ /**
24
+ * WHO refused a settled intent — the fact the drawer's caption was guessing at (#841).
25
+ *
26
+ * A `connector:<provider>` delivery passes through two authorities before it is over: our
27
+ * own permission check on the way to the bytes, and the provider's answer once they are
28
+ * sent. Both surfaced as a `lastError` string and nothing distinguished them, so the
29
+ * dashboard captioned every failure as the provider's — and a `permission denied:
30
+ * protocol:read` raised on OUR side of egress was rendered as *"what Scrive said, in
31
+ * full"*. An operator reading that goes to look at their Scrive account, presses **Test
32
+ * connection** (which passes, because the credential is fine), and concludes the platform
33
+ * is broken. That is the failure this record exists to end.
34
+ *
35
+ * - `platform` — one of ours, raised before anything reached the provider. The refusal
36
+ * carries a taxonomy `code`, so it can be said precisely rather than quoted.
37
+ * - `provider` — the provider ANSWERED and refused. Only this one may be captioned as
38
+ * their words.
39
+ * - `unknown` — classified and could not be told apart: a bug, a socket that never
40
+ * opened, a give-up at the attempt ceiling. Distinct from a NULL `failure`, which means
41
+ * nobody classified at all (a row settled before this field existed, or one that never
42
+ * failed) — honestly unrecorded rather than honestly unknown.
43
+ */
44
+ export const platformRequestFailureOrigin = z.enum(['platform', 'provider', 'unknown']);
45
+ /**
46
+ * How a settled intent's last failure was attributed. Written by the drain from the throw
47
+ * itself, never re-derived downstream from the message — a reader that has to regex prose
48
+ * to know who refused is the bug, not the fix.
49
+ *
50
+ * `permission` is the one extension lifted out of the error and given a column of its own,
51
+ * because it is what makes the join mechanical: a refused key that is absent from the
52
+ * connection's grants is the whole diagnosis, and both halves are already rendered inches
53
+ * apart in the integration drawer. Every other extension stays in the message.
54
+ */
55
+ export const platformRequestFailure = z.object({
56
+ origin: platformRequestFailureOrigin,
57
+ /** The taxonomy code, when the refusal was one of ours. `null` for a provider's answer. */
58
+ code: errorCode.nullable(),
59
+ /** The permission key a `permission_denied` names, when it named one. */
60
+ permission: z.string().min(1).nullable(),
61
+ });
22
62
  /** The full kernel-stamped intent record as it lives in the spine and is read by the drain. */
23
63
  export const platformRequest = z.object({
24
64
  id: platformRequestId,
@@ -28,6 +68,8 @@ export const platformRequest = z.object({
28
68
  status: platformRequestStatus,
29
69
  attempts: z.number().int().nonnegative(),
30
70
  lastError: z.string().nullable(),
71
+ /** How `lastError` was attributed (#841). `null` = unrecorded, never "nobody's fault". */
72
+ failure: platformRequestFailure.nullable(),
31
73
  result: z.unknown().nullable(),
32
74
  requestedAt: instant, // stamped kernel-side
33
75
  settledAt: instant.nullable(),
@@ -1 +1 @@
1
- {"version":3,"file":"platform-request.js","sourceRoot":"","sources":["../src/platform-request.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-request.js","sourceRoot":"","sources":["../src/platform-request.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@substrat-run/contracts",
3
- "version": "0.83.0",
3
+ "version": "0.85.0",
4
4
  "description": "Substrat kernel contract schemas — Zod is the source of truth (master plan D-22); OAS/JSON Schema are emitted artifacts",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {