@substrat-run/contracts 0.86.0 → 0.88.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.
- package/dist/concurrency.d.ts +82 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +94 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/denial.d.ts +121 -0
- package/dist/denial.d.ts.map +1 -0
- package/dist/denial.js +117 -0
- package/dist/denial.js.map +1 -0
- package/dist/errors.d.ts +6 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +82 -13
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/openapi.d.ts +14 -0
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +61 -1
- package/dist/openapi.js.map +1 -1
- package/dist/operations.d.ts +263 -11
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +319 -2
- package/dist/operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimistic concurrency on the operation surface (#129).
|
|
3
|
+
*
|
|
4
|
+
* Two people open the same record, both save, and the second write silently
|
|
5
|
+
* destroys the first. Nobody notices until the data is gone — there is no error,
|
|
6
|
+
* no log line, and no way to reconstruct what was lost. This is the vocabulary
|
|
7
|
+
* that lets an operation refuse the second write instead.
|
|
8
|
+
*
|
|
9
|
+
* ## What a version is, and why it is not in here
|
|
10
|
+
*
|
|
11
|
+
* The version is the ULID of the last event about the entity (`entityVersionOf`
|
|
12
|
+
* in the kernel, #901). This module holds only the WIRE half — the header names
|
|
13
|
+
* and the comparison — because contracts sits below the spine: it can say what an
|
|
14
|
+
* `ETag` is called and what makes one match, and must not know which table
|
|
15
|
+
* answers the question.
|
|
16
|
+
*
|
|
17
|
+
* ## Why an opaque token rather than a number
|
|
18
|
+
*
|
|
19
|
+
* A caller compares; it never reads. That is what lets the version be a ULID
|
|
20
|
+
* today and something else later without a client noticing, and it is also what
|
|
21
|
+
* stops a client doing arithmetic on it — `If-Match: <version + 1>` is not a
|
|
22
|
+
* thing anyone can write.
|
|
23
|
+
*
|
|
24
|
+
* ## The direction this fails
|
|
25
|
+
*
|
|
26
|
+
* ANY event about the entity moves the version, including one that changed
|
|
27
|
+
* nothing the caller read. So a precondition built on it is CONSERVATIVE: it can
|
|
28
|
+
* refuse a write that would in fact have been safe, and it cannot admit one that
|
|
29
|
+
* would not. Documented here as well as in the kernel because this is the half a
|
|
30
|
+
* client author reads when they are debugging a 412 they believe is spurious.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* The response header carrying an entity's current version.
|
|
34
|
+
*
|
|
35
|
+
* Strong, never weak (`W/"…"`): the comparison is exact, and a weak validator
|
|
36
|
+
* means "semantically equivalent", which is precisely the judgement no generic
|
|
37
|
+
* layer is entitled to make about a domain entity.
|
|
38
|
+
*/
|
|
39
|
+
export declare const ETAG_HEADER = "ETag";
|
|
40
|
+
/** The request header carrying the version the caller believes it is writing over. */
|
|
41
|
+
export declare const IF_MATCH_HEADER = "If-Match";
|
|
42
|
+
/**
|
|
43
|
+
* The headers a cross-origin browser client cannot read unless the server says it
|
|
44
|
+
* may — the same trap already documented for `Link` and `X-Total-Count` in
|
|
45
|
+
* `PAGE_EXPOSED_HEADERS`, and worse here.
|
|
46
|
+
*
|
|
47
|
+
* A `Link` a browser cannot read looks like "there is only one page". An `ETag` a
|
|
48
|
+
* browser cannot read looks like nothing at all: `response.headers.get('ETag')` is
|
|
49
|
+
* `null`, the client sends no `If-Match`, the server has nothing to compare, and
|
|
50
|
+
* every write succeeds. The protection silently switches itself off, in the one
|
|
51
|
+
* deployment shape (an SPA on another origin) where two editors are most likely.
|
|
52
|
+
*
|
|
53
|
+
* So a vertical exposing its API cross-origin must expose these, and this constant
|
|
54
|
+
* is what it lists rather than a string it retypes.
|
|
55
|
+
*/
|
|
56
|
+
export declare const CONCURRENCY_EXPOSED_HEADERS: readonly ["ETag"];
|
|
57
|
+
/**
|
|
58
|
+
* Format a version as an `ETag` value.
|
|
59
|
+
*
|
|
60
|
+
* Quoted, per RFC 9110 §8.8.3 — an entity-tag is a quoted string and a bare token
|
|
61
|
+
* is malformed. Intermediaries do parse this: an unquoted value is the kind of
|
|
62
|
+
* thing that works against a dev server and is stripped by a proxy in production.
|
|
63
|
+
*/
|
|
64
|
+
export declare function etagOf(version: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Does the caller's `If-Match` admit this version?
|
|
67
|
+
*
|
|
68
|
+
* Handles the three spellings RFC 9110 §13.1.1 allows a client to send: the quoted
|
|
69
|
+
* tag, a comma-separated list of them, and `*` (meaning "any current version" —
|
|
70
|
+
* i.e. the entity must merely EXIST, which for us means it has a version at all).
|
|
71
|
+
*
|
|
72
|
+
* A weak tag (`W/"…"`) never matches. §13.1.1 requires the strong comparison
|
|
73
|
+
* function for `If-Match`, and honouring a weak one here would admit exactly the
|
|
74
|
+
* write this exists to refuse.
|
|
75
|
+
*
|
|
76
|
+
* `version` is null when the entity has no events yet. Nothing matches that except
|
|
77
|
+
* an absent header, which is not this function's call to make — a caller that sent
|
|
78
|
+
* `If-Match` against an entity with no version is asking to write over something
|
|
79
|
+
* that was never written, and gets a refusal.
|
|
80
|
+
*/
|
|
81
|
+
export declare function ifMatchAdmits(ifMatch: string, version: string | null): boolean;
|
|
82
|
+
//# sourceMappingURL=concurrency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,SAAS,CAAC;AAElC,sFAAsF;AACtF,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,2BAA2B,mBAAyB,CAAC;AAElE;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAQ9E"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimistic concurrency on the operation surface (#129).
|
|
3
|
+
*
|
|
4
|
+
* Two people open the same record, both save, and the second write silently
|
|
5
|
+
* destroys the first. Nobody notices until the data is gone — there is no error,
|
|
6
|
+
* no log line, and no way to reconstruct what was lost. This is the vocabulary
|
|
7
|
+
* that lets an operation refuse the second write instead.
|
|
8
|
+
*
|
|
9
|
+
* ## What a version is, and why it is not in here
|
|
10
|
+
*
|
|
11
|
+
* The version is the ULID of the last event about the entity (`entityVersionOf`
|
|
12
|
+
* in the kernel, #901). This module holds only the WIRE half — the header names
|
|
13
|
+
* and the comparison — because contracts sits below the spine: it can say what an
|
|
14
|
+
* `ETag` is called and what makes one match, and must not know which table
|
|
15
|
+
* answers the question.
|
|
16
|
+
*
|
|
17
|
+
* ## Why an opaque token rather than a number
|
|
18
|
+
*
|
|
19
|
+
* A caller compares; it never reads. That is what lets the version be a ULID
|
|
20
|
+
* today and something else later without a client noticing, and it is also what
|
|
21
|
+
* stops a client doing arithmetic on it — `If-Match: <version + 1>` is not a
|
|
22
|
+
* thing anyone can write.
|
|
23
|
+
*
|
|
24
|
+
* ## The direction this fails
|
|
25
|
+
*
|
|
26
|
+
* ANY event about the entity moves the version, including one that changed
|
|
27
|
+
* nothing the caller read. So a precondition built on it is CONSERVATIVE: it can
|
|
28
|
+
* refuse a write that would in fact have been safe, and it cannot admit one that
|
|
29
|
+
* would not. Documented here as well as in the kernel because this is the half a
|
|
30
|
+
* client author reads when they are debugging a 412 they believe is spurious.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* The response header carrying an entity's current version.
|
|
34
|
+
*
|
|
35
|
+
* Strong, never weak (`W/"…"`): the comparison is exact, and a weak validator
|
|
36
|
+
* means "semantically equivalent", which is precisely the judgement no generic
|
|
37
|
+
* layer is entitled to make about a domain entity.
|
|
38
|
+
*/
|
|
39
|
+
export const ETAG_HEADER = 'ETag';
|
|
40
|
+
/** The request header carrying the version the caller believes it is writing over. */
|
|
41
|
+
export const IF_MATCH_HEADER = 'If-Match';
|
|
42
|
+
/**
|
|
43
|
+
* The headers a cross-origin browser client cannot read unless the server says it
|
|
44
|
+
* may — the same trap already documented for `Link` and `X-Total-Count` in
|
|
45
|
+
* `PAGE_EXPOSED_HEADERS`, and worse here.
|
|
46
|
+
*
|
|
47
|
+
* A `Link` a browser cannot read looks like "there is only one page". An `ETag` a
|
|
48
|
+
* browser cannot read looks like nothing at all: `response.headers.get('ETag')` is
|
|
49
|
+
* `null`, the client sends no `If-Match`, the server has nothing to compare, and
|
|
50
|
+
* every write succeeds. The protection silently switches itself off, in the one
|
|
51
|
+
* deployment shape (an SPA on another origin) where two editors are most likely.
|
|
52
|
+
*
|
|
53
|
+
* So a vertical exposing its API cross-origin must expose these, and this constant
|
|
54
|
+
* is what it lists rather than a string it retypes.
|
|
55
|
+
*/
|
|
56
|
+
export const CONCURRENCY_EXPOSED_HEADERS = [ETAG_HEADER];
|
|
57
|
+
/**
|
|
58
|
+
* Format a version as an `ETag` value.
|
|
59
|
+
*
|
|
60
|
+
* Quoted, per RFC 9110 §8.8.3 — an entity-tag is a quoted string and a bare token
|
|
61
|
+
* is malformed. Intermediaries do parse this: an unquoted value is the kind of
|
|
62
|
+
* thing that works against a dev server and is stripped by a proxy in production.
|
|
63
|
+
*/
|
|
64
|
+
export function etagOf(version) {
|
|
65
|
+
return `"${version}"`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Does the caller's `If-Match` admit this version?
|
|
69
|
+
*
|
|
70
|
+
* Handles the three spellings RFC 9110 §13.1.1 allows a client to send: the quoted
|
|
71
|
+
* tag, a comma-separated list of them, and `*` (meaning "any current version" —
|
|
72
|
+
* i.e. the entity must merely EXIST, which for us means it has a version at all).
|
|
73
|
+
*
|
|
74
|
+
* A weak tag (`W/"…"`) never matches. §13.1.1 requires the strong comparison
|
|
75
|
+
* function for `If-Match`, and honouring a weak one here would admit exactly the
|
|
76
|
+
* write this exists to refuse.
|
|
77
|
+
*
|
|
78
|
+
* `version` is null when the entity has no events yet. Nothing matches that except
|
|
79
|
+
* an absent header, which is not this function's call to make — a caller that sent
|
|
80
|
+
* `If-Match` against an entity with no version is asking to write over something
|
|
81
|
+
* that was never written, and gets a refusal.
|
|
82
|
+
*/
|
|
83
|
+
export function ifMatchAdmits(ifMatch, version) {
|
|
84
|
+
const candidates = ifMatch
|
|
85
|
+
.split(',')
|
|
86
|
+
.map((raw) => raw.trim())
|
|
87
|
+
.filter((raw) => raw.length > 0);
|
|
88
|
+
if (candidates.includes('*'))
|
|
89
|
+
return version !== null;
|
|
90
|
+
if (version === null)
|
|
91
|
+
return false;
|
|
92
|
+
return candidates.includes(etagOf(version));
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAElC,sFAAsF;AACtF,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,WAAW,CAAU,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,OAAe;IACpC,OAAO,IAAI,OAAO,GAAG,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,OAAsB;IACnE,MAAM,UAAU,GAAG,OAAO;SACvB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;SACxB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACnC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,KAAK,IAAI,CAAC;IACtD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/denial.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The read side of the K-35 denial log — a scope-local record of every ENFORCED
|
|
4
|
+
* permission refusal (`assertAllowed`), written on the deny path as a fresh
|
|
5
|
+
* autocommit AFTER the rollback it is evidence of.
|
|
6
|
+
*
|
|
7
|
+
* This is the *other* kind of evidence from a conformance receipt. A receipt says
|
|
8
|
+
* "we attempted the attack in CI at commit X"; these rows say "on your data, in
|
|
9
|
+
* production, here is every refusal, by whom, against which key". K-35 made the
|
|
10
|
+
* case for the row itself: a denial is the one event where an actor's intent and
|
|
11
|
+
* the permission model visibly disagree.
|
|
12
|
+
*
|
|
13
|
+
* Two properties of the log shape everything below, and both come straight from
|
|
14
|
+
* K-35's own reasoning about why denials are NOT admin-log entries:
|
|
15
|
+
*
|
|
16
|
+
* 1. **The volume is attacker-influenceable.** A probing client mints unlimited
|
|
17
|
+
* rows, so a newest-first page of raw rows is the wrong default view — 200 rows
|
|
18
|
+
* from one prober hide everyone else. That is why `denialSummary` exists beside
|
|
19
|
+
* the row list, and why K-35 called rate-bucketing sanctionable up front.
|
|
20
|
+
* 2. **The window is a storage bound, not a retention policy.** Rows `drain` rather
|
|
21
|
+
* than expire (K-24's split). Until a Tier-2 sink exists, what is here is simply
|
|
22
|
+
* what has not been pruned — so the summary reports the window's own floor
|
|
23
|
+
* (`windowOldestAt`) rather than letting a caller read absence as "never happened".
|
|
24
|
+
*/
|
|
25
|
+
/** How many denial rows an unbounded read returns — a screenful, newest-first. */
|
|
26
|
+
export declare const DEFAULT_DENIAL_LIMIT = 50;
|
|
27
|
+
/** The hard ceiling on one page of denial rows, and on one page of buckets. */
|
|
28
|
+
export declare const DENIAL_LIMIT_MAX = 200;
|
|
29
|
+
/**
|
|
30
|
+
* One recorded refusal. `scopeId` is null for a tenant-node check (one that named no
|
|
31
|
+
* scope); `operation` is null when the denial unwound something that was not an
|
|
32
|
+
* operation invocation. `drainedAt` marks a row already shipped to a Tier-2 sink and
|
|
33
|
+
* therefore eligible to be pruned — bookkeeping, not a judgement about the denial.
|
|
34
|
+
*/
|
|
35
|
+
export declare const permissionDenial: z.ZodObject<{
|
|
36
|
+
id: z.ZodString;
|
|
37
|
+
actor: z.ZodUnion<readonly [z.core.$ZodBranded<z.ZodString, "PrincipalId", "out">, z.ZodObject<{
|
|
38
|
+
system: z.core.$ZodBranded<z.ZodString, "ModuleId", "out">;
|
|
39
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
40
|
+
connection: z.ZodString;
|
|
41
|
+
}, z.core.$strip>]>;
|
|
42
|
+
permission: z.core.$ZodBranded<z.ZodString, "PermissionKey", "out">;
|
|
43
|
+
tenantId: z.core.$ZodBranded<z.ZodString, "TenantId", "out">;
|
|
44
|
+
scopeId: z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ScopeId", "out">>;
|
|
45
|
+
operation: z.ZodNullable<z.ZodString>;
|
|
46
|
+
at: z.ZodString;
|
|
47
|
+
drainedAt: z.ZodNullable<z.ZodString>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
export type PermissionDenial = z.infer<typeof permissionDenial>;
|
|
50
|
+
/**
|
|
51
|
+
* What narrows a denial read. Every field is an exact match except the `since`/`until`
|
|
52
|
+
* bounds on `at` (inclusive lower, exclusive upper) — enough to answer "who probed
|
|
53
|
+
* this key", "what did this actor try", and "what happened during the incident window"
|
|
54
|
+
* without a SQL console.
|
|
55
|
+
*
|
|
56
|
+
* `actor` takes the LOGICAL actor — a bare principal ULID, or the object form for a
|
|
57
|
+
* system/connection actor (`{"system":"invoicing"}`). The writer persists
|
|
58
|
+
* `JSON.stringify(actor)`, so a principal is stored with its quotes; normalizing to that
|
|
59
|
+
* encoding is the reader's job (`storedActor` in the kernel's query builder), not every
|
|
60
|
+
* caller's.
|
|
61
|
+
*/
|
|
62
|
+
export declare const denialFilter: z.ZodObject<{
|
|
63
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
64
|
+
permission: z.ZodOptional<z.ZodString>;
|
|
65
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
66
|
+
since: z.ZodOptional<z.ZodString>;
|
|
67
|
+
until: z.ZodOptional<z.ZodString>;
|
|
68
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
69
|
+
}, z.core.$strip>;
|
|
70
|
+
export type DenialFilter = z.infer<typeof denialFilter>;
|
|
71
|
+
/**
|
|
72
|
+
* One (actor, permission) bucket — K-35's "first occurrence + count per actor/key/
|
|
73
|
+
* window", which is the shape that survives a flood. `operations` is the number of
|
|
74
|
+
* DISTINCT operations the actor was refused this key on, and it is the discriminator
|
|
75
|
+
* worth the extra aggregate: one operation refused four hundred times is a broken
|
|
76
|
+
* screen or a misconfigured role, while the same count spread across a dozen
|
|
77
|
+
* operations is someone walking the surface.
|
|
78
|
+
*/
|
|
79
|
+
export declare const denialBucket: z.ZodObject<{
|
|
80
|
+
actor: z.ZodUnion<readonly [z.core.$ZodBranded<z.ZodString, "PrincipalId", "out">, z.ZodObject<{
|
|
81
|
+
system: z.core.$ZodBranded<z.ZodString, "ModuleId", "out">;
|
|
82
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
83
|
+
connection: z.ZodString;
|
|
84
|
+
}, z.core.$strip>]>;
|
|
85
|
+
permission: z.core.$ZodBranded<z.ZodString, "PermissionKey", "out">;
|
|
86
|
+
count: z.ZodNumber;
|
|
87
|
+
operations: z.ZodNumber;
|
|
88
|
+
firstAt: z.ZodString;
|
|
89
|
+
lastAt: z.ZodString;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
export type DenialBucket = z.infer<typeof denialBucket>;
|
|
92
|
+
/**
|
|
93
|
+
* The bucketed view of a scope's denial log, plus the facts that keep it honest.
|
|
94
|
+
*
|
|
95
|
+
* `total` counts every row matching the filter, so a caller can tell a capped bucket
|
|
96
|
+
* list from a complete one. `windowOldestAt` is deliberately computed WITHOUT the
|
|
97
|
+
* filter: it is a statement about the log, not about the query — the earliest moment
|
|
98
|
+
* this scope can still speak to. An empty result older than it means "no denials";
|
|
99
|
+
* an empty result at it means "we no longer hold that far back".
|
|
100
|
+
*/
|
|
101
|
+
export declare const denialSummary: z.ZodObject<{
|
|
102
|
+
buckets: z.ZodArray<z.ZodObject<{
|
|
103
|
+
actor: z.ZodUnion<readonly [z.core.$ZodBranded<z.ZodString, "PrincipalId", "out">, z.ZodObject<{
|
|
104
|
+
system: z.core.$ZodBranded<z.ZodString, "ModuleId", "out">;
|
|
105
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
106
|
+
connection: z.ZodString;
|
|
107
|
+
}, z.core.$strip>]>;
|
|
108
|
+
permission: z.core.$ZodBranded<z.ZodString, "PermissionKey", "out">;
|
|
109
|
+
count: z.ZodNumber;
|
|
110
|
+
operations: z.ZodNumber;
|
|
111
|
+
firstAt: z.ZodString;
|
|
112
|
+
lastAt: z.ZodString;
|
|
113
|
+
}, z.core.$strip>>;
|
|
114
|
+
total: z.ZodNumber;
|
|
115
|
+
actors: z.ZodNumber;
|
|
116
|
+
windowOldestAt: z.ZodNullable<z.ZodString>;
|
|
117
|
+
windowNewestAt: z.ZodNullable<z.ZodString>;
|
|
118
|
+
drained: z.ZodNumber;
|
|
119
|
+
}, z.core.$strip>;
|
|
120
|
+
export type DenialSummary = z.infer<typeof denialSummary>;
|
|
121
|
+
//# sourceMappingURL=denial.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"denial.d.ts","sourceRoot":"","sources":["../src/denial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;iBAc3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY;;;;;;;iBASvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;iBAQvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;iBAgBxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC"}
|
package/dist/denial.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { actor } from './events.js';
|
|
3
|
+
import { permissionKey, scopeId, tenantId } from './ids.js';
|
|
4
|
+
/**
|
|
5
|
+
* The read side of the K-35 denial log — a scope-local record of every ENFORCED
|
|
6
|
+
* permission refusal (`assertAllowed`), written on the deny path as a fresh
|
|
7
|
+
* autocommit AFTER the rollback it is evidence of.
|
|
8
|
+
*
|
|
9
|
+
* This is the *other* kind of evidence from a conformance receipt. A receipt says
|
|
10
|
+
* "we attempted the attack in CI at commit X"; these rows say "on your data, in
|
|
11
|
+
* production, here is every refusal, by whom, against which key". K-35 made the
|
|
12
|
+
* case for the row itself: a denial is the one event where an actor's intent and
|
|
13
|
+
* the permission model visibly disagree.
|
|
14
|
+
*
|
|
15
|
+
* Two properties of the log shape everything below, and both come straight from
|
|
16
|
+
* K-35's own reasoning about why denials are NOT admin-log entries:
|
|
17
|
+
*
|
|
18
|
+
* 1. **The volume is attacker-influenceable.** A probing client mints unlimited
|
|
19
|
+
* rows, so a newest-first page of raw rows is the wrong default view — 200 rows
|
|
20
|
+
* from one prober hide everyone else. That is why `denialSummary` exists beside
|
|
21
|
+
* the row list, and why K-35 called rate-bucketing sanctionable up front.
|
|
22
|
+
* 2. **The window is a storage bound, not a retention policy.** Rows `drain` rather
|
|
23
|
+
* than expire (K-24's split). Until a Tier-2 sink exists, what is here is simply
|
|
24
|
+
* what has not been pruned — so the summary reports the window's own floor
|
|
25
|
+
* (`windowOldestAt`) rather than letting a caller read absence as "never happened".
|
|
26
|
+
*/
|
|
27
|
+
/** How many denial rows an unbounded read returns — a screenful, newest-first. */
|
|
28
|
+
export const DEFAULT_DENIAL_LIMIT = 50;
|
|
29
|
+
/** The hard ceiling on one page of denial rows, and on one page of buckets. */
|
|
30
|
+
export const DENIAL_LIMIT_MAX = 200;
|
|
31
|
+
/**
|
|
32
|
+
* One recorded refusal. `scopeId` is null for a tenant-node check (one that named no
|
|
33
|
+
* scope); `operation` is null when the denial unwound something that was not an
|
|
34
|
+
* operation invocation. `drainedAt` marks a row already shipped to a Tier-2 sink and
|
|
35
|
+
* therefore eligible to be pruned — bookkeeping, not a judgement about the denial.
|
|
36
|
+
*/
|
|
37
|
+
export const permissionDenial = z.object({
|
|
38
|
+
/** ULID — chronological, so it is also the sort key. */
|
|
39
|
+
id: z.string().min(1),
|
|
40
|
+
/** WHO was refused: a principal, a `{ system }` module, or a `{ connection }`. */
|
|
41
|
+
actor,
|
|
42
|
+
/** The key `assertAllowed` checked and refused. */
|
|
43
|
+
permission: permissionKey,
|
|
44
|
+
tenantId,
|
|
45
|
+
scopeId: scopeId.nullable(),
|
|
46
|
+
/** The operation the denial rolled back, e.g. `workorder/complete`. */
|
|
47
|
+
operation: z.string().nullable(),
|
|
48
|
+
/** ISO 8601. */
|
|
49
|
+
at: z.string().min(1),
|
|
50
|
+
drainedAt: z.string().nullable(),
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* What narrows a denial read. Every field is an exact match except the `since`/`until`
|
|
54
|
+
* bounds on `at` (inclusive lower, exclusive upper) — enough to answer "who probed
|
|
55
|
+
* this key", "what did this actor try", and "what happened during the incident window"
|
|
56
|
+
* without a SQL console.
|
|
57
|
+
*
|
|
58
|
+
* `actor` takes the LOGICAL actor — a bare principal ULID, or the object form for a
|
|
59
|
+
* system/connection actor (`{"system":"invoicing"}`). The writer persists
|
|
60
|
+
* `JSON.stringify(actor)`, so a principal is stored with its quotes; normalizing to that
|
|
61
|
+
* encoding is the reader's job (`storedActor` in the kernel's query builder), not every
|
|
62
|
+
* caller's.
|
|
63
|
+
*/
|
|
64
|
+
export const denialFilter = z.object({
|
|
65
|
+
actor: z.string().min(1).optional(),
|
|
66
|
+
permission: z.string().min(1).optional(),
|
|
67
|
+
operation: z.string().min(1).optional(),
|
|
68
|
+
/** ISO 8601, inclusive. */
|
|
69
|
+
since: z.string().min(1).optional(),
|
|
70
|
+
/** ISO 8601, exclusive. */
|
|
71
|
+
until: z.string().min(1).optional(),
|
|
72
|
+
limit: z.number().int().min(1).max(DENIAL_LIMIT_MAX).optional(),
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* One (actor, permission) bucket — K-35's "first occurrence + count per actor/key/
|
|
76
|
+
* window", which is the shape that survives a flood. `operations` is the number of
|
|
77
|
+
* DISTINCT operations the actor was refused this key on, and it is the discriminator
|
|
78
|
+
* worth the extra aggregate: one operation refused four hundred times is a broken
|
|
79
|
+
* screen or a misconfigured role, while the same count spread across a dozen
|
|
80
|
+
* operations is someone walking the surface.
|
|
81
|
+
*/
|
|
82
|
+
export const denialBucket = z.object({
|
|
83
|
+
actor,
|
|
84
|
+
permission: permissionKey,
|
|
85
|
+
count: z.number().int().positive(),
|
|
86
|
+
operations: z.number().int().nonnegative(),
|
|
87
|
+
/** ISO 8601 — the first occurrence still in the window (see `windowOldestAt`). */
|
|
88
|
+
firstAt: z.string().min(1),
|
|
89
|
+
lastAt: z.string().min(1),
|
|
90
|
+
});
|
|
91
|
+
/**
|
|
92
|
+
* The bucketed view of a scope's denial log, plus the facts that keep it honest.
|
|
93
|
+
*
|
|
94
|
+
* `total` counts every row matching the filter, so a caller can tell a capped bucket
|
|
95
|
+
* list from a complete one. `windowOldestAt` is deliberately computed WITHOUT the
|
|
96
|
+
* filter: it is a statement about the log, not about the query — the earliest moment
|
|
97
|
+
* this scope can still speak to. An empty result older than it means "no denials";
|
|
98
|
+
* an empty result at it means "we no longer hold that far back".
|
|
99
|
+
*/
|
|
100
|
+
export const denialSummary = z.object({
|
|
101
|
+
buckets: z.array(denialBucket),
|
|
102
|
+
/** Rows matching the filter. Bucket counts sum to this when `buckets` is uncapped. */
|
|
103
|
+
total: z.number().int().nonnegative(),
|
|
104
|
+
/** Distinct actors among the matching rows. */
|
|
105
|
+
actors: z.number().int().nonnegative(),
|
|
106
|
+
/**
|
|
107
|
+
* The oldest row STILL HELD, filter ignored — the window's floor, null when the log
|
|
108
|
+
* is empty. The window is a storage bound and not a retention policy (K-35): what is
|
|
109
|
+
* absent before this instant was not necessarily never recorded.
|
|
110
|
+
*/
|
|
111
|
+
windowOldestAt: z.string().nullable(),
|
|
112
|
+
/** The newest row held, filter ignored. */
|
|
113
|
+
windowNewestAt: z.string().nullable(),
|
|
114
|
+
/** Rows already shipped to a Tier-2 sink, filter ignored — prunable, not pruned. */
|
|
115
|
+
drained: z.number().int().nonnegative(),
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=denial.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"denial.js","sourceRoot":"","sources":["../src/denial.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -140,7 +140,12 @@ export declare const PROBLEM_EXTENSIONS: {
|
|
|
140
140
|
message: z.ZodString;
|
|
141
141
|
}, z.core.$strip>>>;
|
|
142
142
|
}, z.core.$strip>;
|
|
143
|
-
readonly precondition_failed: z.ZodObject<{
|
|
143
|
+
readonly precondition_failed: z.ZodObject<{
|
|
144
|
+
entity: z.ZodOptional<z.ZodObject<{
|
|
145
|
+
entityType: z.ZodString;
|
|
146
|
+
entityId: z.ZodString;
|
|
147
|
+
}, z.core.$strip>>;
|
|
148
|
+
}, z.core.$strip>;
|
|
144
149
|
readonly rate_limited: z.ZodObject<{
|
|
145
150
|
retryAfter: z.ZodOptional<z.ZodNumber>;
|
|
146
151
|
}, z.core.$strip>;
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,gCAAgC,CAAC;AAE/D;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;EAWpB,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAElD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe;;iBACP,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,iBAAiB;;;iBACnC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,mBAAmB;;;iBAC/C,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,WAAW;;;iBAC/B,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,WAAW;;;iBAChC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,UAAU;;;iBACrB,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,mBAAmB;;;iBACrC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,qBAAqB;;;iBAChD,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,cAAc;;;iBACnC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,qBAAqB;;;iBAC5C,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,gBAAgB;;CACuB,CAAC;AAE1E,0DAA0D;AAC1D,eAAO,MAAM,eAAe;;;iBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,gCAAgC,CAAC;AAE/D;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;EAWpB,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAElD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe;;iBACP,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,iBAAiB;;;iBACnC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,mBAAmB;;;iBAC/C,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,WAAW;;;iBAC/B,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,WAAW;;;iBAChC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,UAAU;;;iBACrB,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,mBAAmB;;;iBACrC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,qBAAqB;;;iBAChD,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,cAAc;;;iBACnC,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,qBAAqB;;;iBAC5C,MAAM,EAAE,GAAG;iBAAE,KAAK,EAAE,gBAAgB;;CACuB,CAAC;AAE1E,0DAA0D;AAC1D,eAAO,MAAM,eAAe;;;iBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BkB,CAAC;AAElD,kFAAkF;AAClF,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BlB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAoB7C;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEvD,YAAY,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAMrF;CACF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAgB/D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAC/C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAC5B,aAAa,CAGf;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,aAAa,CAElE;AAED,qDAAqD;AACrD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,eAAe,EAAE,CAEzE;AA+BD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CA+BlE;AAoBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,SAAS,EAStD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;iBAOtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAUvD;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,KAAK,CAO3D"}
|
package/dist/errors.js
CHANGED
|
@@ -102,7 +102,19 @@ export const PROBLEM_EXTENSIONS = {
|
|
|
102
102
|
not_found: z.strictObject({}),
|
|
103
103
|
conflict: z.object({ reason: z.string().min(1).optional() }),
|
|
104
104
|
validation_failed: z.object({ errors: z.array(validationIssue).optional() }),
|
|
105
|
-
precondition_failed: z.
|
|
105
|
+
precondition_failed: z.object({
|
|
106
|
+
/**
|
|
107
|
+
* The entity whose version moved under the caller (#129).
|
|
108
|
+
*
|
|
109
|
+
* **The current version is deliberately NOT carried.** Handing it back turns
|
|
110
|
+
* the obvious client fix into a blind retry with the new tag, which writes
|
|
111
|
+
* over the change that caused the refusal — the exact lost update the
|
|
112
|
+
* precondition exists to prevent, now with a 412 in the log claiming it was
|
|
113
|
+
* prevented. A client that wants to proceed re-reads, and re-reading is what
|
|
114
|
+
* gives its user something to merge.
|
|
115
|
+
*/
|
|
116
|
+
entity: entityRef.optional(),
|
|
117
|
+
}),
|
|
106
118
|
rate_limited: z.object({ retryAfter: z.number().int().nonnegative().optional() }),
|
|
107
119
|
unavailable: z.strictObject({}),
|
|
108
120
|
internal: z.strictObject({}),
|
|
@@ -162,9 +174,11 @@ export const ERROR_NAME_PREFIX = 'Substrat.';
|
|
|
162
174
|
* Keeping `PermissionDenied` named `PermissionDenied` rather than renaming it to the
|
|
163
175
|
* generic form is deliberate: `vertical-host`'s classifier and several verticals match
|
|
164
176
|
* on that exact string today, and a rename would be a silent behaviour change bundled
|
|
165
|
-
* into a refactor. `ZodError` earns its row because a parse failure
|
|
166
|
-
*
|
|
167
|
-
* without fields still beats
|
|
177
|
+
* into a refactor. `ZodError` earns its row because a parse failure can arrive with its
|
|
178
|
+
* prototype gone and only its `name` left — a duplicate copy of zod, a structured clone,
|
|
179
|
+
* the legacy pre-envelope RPC path. `validation_failed` without fields still beats
|
|
180
|
+
* `internal`. On the envelope path the fields are no longer lost: `toWireFailure` carries
|
|
181
|
+
* them as `extensions.errors` (#831).
|
|
168
182
|
*/
|
|
169
183
|
const CODE_BY_ERROR_NAME = {
|
|
170
184
|
PermissionDenied: 'permission_denied',
|
|
@@ -236,11 +250,29 @@ export function isSubstratError(err) {
|
|
|
236
250
|
}
|
|
237
251
|
/** Zod's issue list, flattened to the wire shape. */
|
|
238
252
|
export function validationIssuesFrom(error) {
|
|
239
|
-
return error.issues
|
|
240
|
-
|
|
241
|
-
|
|
253
|
+
return flattenIssues(error.issues);
|
|
254
|
+
}
|
|
255
|
+
function flattenIssues(issues) {
|
|
256
|
+
return issues.map((issue) => ({
|
|
257
|
+
path: (issue.path ?? []).map(String).join('.'),
|
|
258
|
+
message: typeof issue.message === 'string' ? issue.message : String(issue.message),
|
|
242
259
|
}));
|
|
243
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* A parse failure's field issues, read BY SHAPE rather than by `instanceof`.
|
|
263
|
+
*
|
|
264
|
+
* Same doctrine as `errorCodeOf` one screen up, for the same two reasons: two copies
|
|
265
|
+
* of zod in one build make `instanceof` a coin toss, and `vertical-host`'s classifier
|
|
266
|
+
* already reads a parse failure this way (`isParseFailure`). `issues` is zod's own
|
|
267
|
+
* array and nothing else on these paths carries one.
|
|
268
|
+
*
|
|
269
|
+
* Returns `undefined` — not `[]` — for a throw that is not a parse failure, so a
|
|
270
|
+
* caller can tell "no issues to report" from "not that kind of error at all".
|
|
271
|
+
*/
|
|
272
|
+
function parseIssuesOf(err) {
|
|
273
|
+
const issues = err?.issues;
|
|
274
|
+
return Array.isArray(issues) ? flattenIssues(issues) : undefined;
|
|
275
|
+
}
|
|
244
276
|
/**
|
|
245
277
|
* Map any throw onto a problem body and its status — the one function replacing every
|
|
246
278
|
* hand-rolled `onError` and the control plane's regex table.
|
|
@@ -251,12 +283,27 @@ export function validationIssuesFrom(error) {
|
|
|
251
283
|
* quietly widening it in the name of better errors.
|
|
252
284
|
*/
|
|
253
285
|
export function toProblem(err, instance) {
|
|
254
|
-
if (err instanceof z.ZodError) {
|
|
255
|
-
return build('validation_failed', 'the input did not parse', instance, {
|
|
256
|
-
errors: validationIssuesFrom(err),
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
286
|
const code = errorCodeOf(err);
|
|
287
|
+
if (code === 'validation_failed') {
|
|
288
|
+
// #831. The issues are the whole value of a parse failure, and they reach here two
|
|
289
|
+
// ways: live on the throw (in-process), or in `extensions.errors` once
|
|
290
|
+
// `toWireFailure` carried them across the ScopeDO hop.
|
|
291
|
+
//
|
|
292
|
+
// Only a PARSE failure takes this branch, and the `errors` list is what identifies
|
|
293
|
+
// one. `validation_failed` is also thrown SEMANTICALLY — `endDate precedes
|
|
294
|
+
// startDate`, `invalid interval`, `at most one party may sign as primary` — where
|
|
295
|
+
// the sentence IS the information and no field list exists. Those fall through to
|
|
296
|
+
// the general branch below and keep their own message, exactly as before.
|
|
297
|
+
const carried = err?.extensions?.errors;
|
|
298
|
+
const errors = parseIssuesOf(err) ?? (Array.isArray(carried) ? carried : undefined);
|
|
299
|
+
if (errors !== undefined) {
|
|
300
|
+
// The detail is the canonical sentence rather than the throw's message: a raw
|
|
301
|
+
// `ZodError` stringifies its whole issue list into `message` as JSON, and echoing
|
|
302
|
+
// that beside the parsed `errors` array publishes the same thing twice — in the
|
|
303
|
+
// shape this change exists to stop clients re-parsing.
|
|
304
|
+
return build('validation_failed', 'the input did not parse', instance, { errors });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
260
307
|
if (code !== undefined && err instanceof Error) {
|
|
261
308
|
// `internal` is still generic even when a throw asked for it by name: the rule is
|
|
262
309
|
// about what reaches a client, not about who chose the code.
|
|
@@ -329,9 +376,31 @@ export function toWireFailure(err) {
|
|
|
329
376
|
name: err.name,
|
|
330
377
|
message: err.message,
|
|
331
378
|
code,
|
|
332
|
-
extensions:
|
|
379
|
+
extensions: extensionsFor(code, err),
|
|
333
380
|
};
|
|
334
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* The extensions a throw carries onto the wire.
|
|
384
|
+
*
|
|
385
|
+
* A `SubstratError` already holds its own, declared and parsed at the throw site. A
|
|
386
|
+
* `ZodError` holds none — it holds `issues`, which is the same information in zod's
|
|
387
|
+
* shape rather than ours, and #831's whole complaint was that this function dropped it:
|
|
388
|
+
* the field list survived only as JSON inside `message`, leaving every vertical to
|
|
389
|
+
* re-parse a string for what `validationIssue` already models.
|
|
390
|
+
*
|
|
391
|
+
* That mattered more after #893 than before it. The host now parses a declared
|
|
392
|
+
* operation input at the scope door, so on the hosted path the refusal is raised
|
|
393
|
+
* INSIDE the ScopeDO and this is the only seam it crosses — while the same operation
|
|
394
|
+
* under `adapter-sqlite` throws in-process with `issues` intact. Structured in a
|
|
395
|
+
* scenario test, bare in production, is the worst of the two available failures.
|
|
396
|
+
*/
|
|
397
|
+
function extensionsFor(code, err) {
|
|
398
|
+
const declared = { ...(err.extensions ?? {}) };
|
|
399
|
+
if (code !== 'validation_failed' || declared.errors !== undefined)
|
|
400
|
+
return declared;
|
|
401
|
+
const issues = parseIssuesOf(err);
|
|
402
|
+
return issues === undefined ? declared : { ...declared, errors: issues };
|
|
403
|
+
}
|
|
335
404
|
/**
|
|
336
405
|
* Rebuild a throw from the wire.
|
|
337
406
|
*
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|