@haverstack/core 0.28.0 → 0.30.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 (94) hide show
  1. package/README.md +9 -2
  2. package/dist/access.d.ts +20 -0
  3. package/dist/access.d.ts.map +1 -1
  4. package/dist/access.js +26 -0
  5. package/dist/access.js.map +1 -1
  6. package/dist/adapter-entry.d.ts +1 -1
  7. package/dist/adapter-entry.d.ts.map +1 -1
  8. package/dist/adapter-entry.js +1 -1
  9. package/dist/adapter-entry.js.map +1 -1
  10. package/dist/changes.d.ts +15 -2
  11. package/dist/changes.d.ts.map +1 -1
  12. package/dist/changes.js +62 -6
  13. package/dist/changes.js.map +1 -1
  14. package/dist/combine.d.ts.map +1 -1
  15. package/dist/combine.js +1 -4
  16. package/dist/combine.js.map +1 -1
  17. package/dist/errors.d.ts +173 -0
  18. package/dist/errors.d.ts.map +1 -0
  19. package/dist/errors.js +208 -0
  20. package/dist/errors.js.map +1 -0
  21. package/dist/grants.d.ts +84 -0
  22. package/dist/grants.d.ts.map +1 -0
  23. package/dist/grants.js +157 -0
  24. package/dist/grants.js.map +1 -0
  25. package/dist/id.d.ts +7 -5
  26. package/dist/id.d.ts.map +1 -1
  27. package/dist/id.js +17 -15
  28. package/dist/id.js.map +1 -1
  29. package/dist/identity-bindings.d.ts +13 -0
  30. package/dist/identity-bindings.d.ts.map +1 -0
  31. package/dist/identity-bindings.js +42 -0
  32. package/dist/identity-bindings.js.map +1 -0
  33. package/dist/index.d.ts +7 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +5 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/limits.d.ts +23 -0
  38. package/dist/limits.d.ts.map +1 -0
  39. package/dist/limits.js +35 -0
  40. package/dist/limits.js.map +1 -0
  41. package/dist/merge.d.ts +1 -1
  42. package/dist/merge.js +2 -2
  43. package/dist/merge.js.map +1 -1
  44. package/dist/query-validation.d.ts +80 -0
  45. package/dist/query-validation.d.ts.map +1 -0
  46. package/dist/query-validation.js +223 -0
  47. package/dist/query-validation.js.map +1 -0
  48. package/dist/record-changes.d.ts +89 -0
  49. package/dist/record-changes.d.ts.map +1 -0
  50. package/dist/record-changes.js +201 -0
  51. package/dist/record-changes.js.map +1 -0
  52. package/dist/record-id.d.ts +70 -0
  53. package/dist/record-id.d.ts.map +1 -0
  54. package/dist/record-id.js +127 -0
  55. package/dist/record-id.js.map +1 -0
  56. package/dist/schema.d.ts.map +1 -1
  57. package/dist/schema.js +56 -5
  58. package/dist/schema.js.map +1 -1
  59. package/dist/scoped-stack.d.ts +460 -0
  60. package/dist/scoped-stack.d.ts.map +1 -0
  61. package/dist/scoped-stack.js +1163 -0
  62. package/dist/scoped-stack.js.map +1 -0
  63. package/dist/stack-reads.d.ts +38 -0
  64. package/dist/stack-reads.d.ts.map +1 -0
  65. package/dist/stack-reads.js +79 -0
  66. package/dist/stack-reads.js.map +1 -0
  67. package/dist/stack.d.ts +108 -700
  68. package/dist/stack.d.ts.map +1 -1
  69. package/dist/stack.js +235 -2286
  70. package/dist/stack.js.map +1 -1
  71. package/dist/testing.d.ts +3 -14
  72. package/dist/testing.d.ts.map +1 -1
  73. package/dist/testing.js +30 -42
  74. package/dist/testing.js.map +1 -1
  75. package/dist/types.d.ts +107 -30
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/types.js +16 -0
  78. package/dist/types.js.map +1 -1
  79. package/dist/validate.d.ts +31 -0
  80. package/dist/validate.d.ts.map +1 -1
  81. package/dist/validate.js +150 -3
  82. package/dist/validate.js.map +1 -1
  83. package/dist/wire-entry.d.ts +3 -3
  84. package/dist/wire-entry.d.ts.map +1 -1
  85. package/dist/wire-entry.js +3 -3
  86. package/dist/wire-entry.js.map +1 -1
  87. package/dist/wire-record.d.ts +16 -1
  88. package/dist/wire-record.d.ts.map +1 -1
  89. package/dist/wire-record.js +57 -1
  90. package/dist/wire-record.js.map +1 -1
  91. package/dist/wire-request.d.ts.map +1 -1
  92. package/dist/wire-request.js +3 -2
  93. package/dist/wire-request.js.map +1 -1
  94. package/package.json +1 -1
package/dist/errors.js ADDED
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Stack errors — the error taxonomy
3
+ * -------------------------------------------------------
4
+ * Every error a Stack operation can raise, in one place. The hierarchy has
5
+ * a single root so a server's error middleware can ask one question —
6
+ * `instanceof StackError` — before serializing a wire body, and each
7
+ * subclass carries its wire discriminator as an instance `code`.
8
+ *
9
+ * Three errors sit deliberately outside that root (StackClosedError,
10
+ * StackRelayScopeError, and — in their own modules — IdGenerationError and
11
+ * InvalidDidError): they report a local programming error or an assembled
12
+ * topology, not a state a request can be in, so no server ever responds
13
+ * with one.
14
+ */
15
+ import { baseIdOf, parseTypeId } from './schema.js';
16
+ /**
17
+ * Root of the Stack error taxonomy. A single `instanceof StackError` answers
18
+ * "is this a Stack-domain error or a bug?" — the question a server's error
19
+ * middleware asks before serializing a wire body, and one a nine-arm
20
+ * instanceof ladder answers only by exhaustion. Every subclass carries its
21
+ * discriminator as an instance `code`, so serialization is a lookup rather
22
+ * than a chain of class tests.
23
+ *
24
+ * Membership implies a wire mapping: every code has an entry in
25
+ * WIRE_ERROR_STATUS. Errors with no wire representation (IdGenerationError,
26
+ * InvalidDidError) deliberately stay outside this hierarchy.
27
+ *
28
+ * Subclassing adds no hierarchy beyond this root — notably
29
+ * StackVersionConflictError is a sibling of StackConflictError, not a
30
+ * subtype. See docs/spec/wire-format.md § Error responses.
31
+ */
32
+ export class StackError extends Error {
33
+ }
34
+ export class StackValidationError extends StackError {
35
+ errors;
36
+ static code = 'validation';
37
+ code = StackValidationError.code;
38
+ constructor(errors) {
39
+ super(`Content validation failed:\n` + errors.map((e) => ` ${e.path}: ${e.message}`).join('\n'));
40
+ this.errors = errors;
41
+ this.name = 'StackValidationError';
42
+ }
43
+ }
44
+ export class StackMigrationError extends StackError {
45
+ static code = 'migration';
46
+ code = StackMigrationError.code;
47
+ constructor(message) {
48
+ super(message);
49
+ this.name = 'StackMigrationError';
50
+ }
51
+ }
52
+ /** Thrown by ScopedStack when a requester lacks permission for the operation. */
53
+ export class StackPermissionError extends StackError {
54
+ static code = 'permission';
55
+ code = StackPermissionError.code;
56
+ constructor(message = 'Permission denied') {
57
+ super(message);
58
+ this.name = 'StackPermissionError';
59
+ }
60
+ }
61
+ /** Thrown when a record (or specific version) does not exist. */
62
+ export class StackNotFoundError extends StackError {
63
+ static code = 'not_found';
64
+ code = StackNotFoundError.code;
65
+ constructor(message) {
66
+ super(message);
67
+ this.name = 'StackNotFoundError';
68
+ }
69
+ }
70
+ /** Thrown when an operation cannot proceed due to a constraint violation (e.g. deleting an attachment that is still referenced). */
71
+ export class StackConflictError extends StackError {
72
+ static code = 'conflict';
73
+ code = StackConflictError.code;
74
+ constructor(message) {
75
+ super(message);
76
+ this.name = 'StackConflictError';
77
+ }
78
+ }
79
+ /**
80
+ * Thrown when an `ifVersion` precondition doesn't match a record's current
81
+ * version. Deliberately not a StackConflictError subtype — the two have
82
+ * different recovery stories and HTTP statuses (409 vs. 412). See
83
+ * docs/spec/versioning.md § Optimistic concurrency (`ifVersion`).
84
+ */
85
+ export class StackVersionConflictError extends StackError {
86
+ recordId;
87
+ expectedVersion;
88
+ actualVersion;
89
+ static code = 'version_conflict';
90
+ code = StackVersionConflictError.code;
91
+ constructor(message, recordId, expectedVersion, actualVersion) {
92
+ super(message);
93
+ this.recordId = recordId;
94
+ this.expectedVersion = expectedVersion;
95
+ this.actualVersion = actualVersion;
96
+ this.name = 'StackVersionConflictError';
97
+ }
98
+ }
99
+ /**
100
+ * Thrown when a request is structurally malformed — not a content-validation
101
+ * failure, but input the adapter/server can't even interpret (e.g. an
102
+ * undecodable pagination cursor, a malformed TypeId, search text the engine
103
+ * cannot parse, or a typeId no definition exists for). Distinct from
104
+ * StackValidationError, which means the request was well-formed but content
105
+ * failed schema validation.
106
+ *
107
+ * Naming something absent belongs here rather than under `not_found`: a
108
+ * request whose *type* is undefined never addressed a record, so answering
109
+ * 404 would say a record was missing when none was asked for.
110
+ */
111
+ export class StackQueryError extends StackError {
112
+ static code = 'bad_request';
113
+ code = StackQueryError.code;
114
+ constructor(message) {
115
+ super(message);
116
+ this.name = 'StackQueryError';
117
+ }
118
+ }
119
+ /**
120
+ * Thrown when an attachment upload exceeds the adapter's declared
121
+ * `limits.attachmentBytes` ceiling — checked client-side before any bytes
122
+ * are sent; a server still enforces 413 authoritatively regardless. See
123
+ * docs/spec/wire-format.md § Attachments.
124
+ */
125
+ export class StackPayloadTooLargeError extends StackError {
126
+ static code = 'payload_too_large';
127
+ code = StackPayloadTooLargeError.code;
128
+ constructor(message) {
129
+ super(message);
130
+ this.name = 'StackPayloadTooLargeError';
131
+ }
132
+ }
133
+ /**
134
+ * Thrown when a server abandons an operation for taking too long — in
135
+ * practice a full-text search, the one query whose cost the sanitizers
136
+ * bound the *grammar* of but not the execution of (see
137
+ * docs/spec/data-model.md § Capability-gated filters).
138
+ *
139
+ * Never produced in-process: both SQLite engines run synchronously, so
140
+ * there is nothing to interrupt from inside the call. It exists so a
141
+ * server that bounds query time has a class to serialize, and so the app
142
+ * catching it can tell "too expensive, narrow it and retry" from
143
+ * StackQueryError's "malformed, don't bother retrying" — the distinction
144
+ * that would be lost if a timeout reused `bad_request`.
145
+ */
146
+ export class StackTimeoutError extends StackError {
147
+ static code = 'timeout';
148
+ code = StackTimeoutError.code;
149
+ constructor(message) {
150
+ super(message);
151
+ this.name = 'StackTimeoutError';
152
+ }
153
+ }
154
+ /**
155
+ * Thrown by defineType() when redefining an existing typeId with a schema
156
+ * change beyond additive evolution. The remedy is a new version, never an
157
+ * in-place redefinition. See docs/spec/data-model.md § Schema drift
158
+ * detection.
159
+ */
160
+ export class StackSchemaDriftError extends StackError {
161
+ typeId;
162
+ violations;
163
+ static code = 'schema_drift';
164
+ code = StackSchemaDriftError.code;
165
+ constructor(typeId, violations) {
166
+ super(`Schema drift detected for type "${typeId}": the stored schema and the new definition ` +
167
+ `differ beyond additive evolution (new optional fields only). Bump the version instead ` +
168
+ `of redefining "${typeId}" in place — e.g. defineType(\`${baseIdOf(typeId)}@${(parseTypeId(typeId)?.version ?? 0) + 1}\`, ...) plus registerMigration().\n` +
169
+ violations.map((v) => ` ${v.path || '(root)'}: ${v.message}`).join('\n'));
170
+ this.typeId = typeId;
171
+ this.violations = violations;
172
+ this.name = 'StackSchemaDriftError';
173
+ }
174
+ }
175
+ /**
176
+ * Thrown when a Stack or ScopedStack is used after close(). Deliberately
177
+ * outside the StackError taxonomy, alongside IdGenerationError and
178
+ * InvalidDidError: a caller holding a closed client is a local programming
179
+ * error with no wire representation — no server ever responds with it.
180
+ * See docs/spec/adapters.md § Lifecycle.
181
+ */
182
+ export class StackClosedError extends Error {
183
+ constructor(message = 'This Stack has been closed.') {
184
+ super(message);
185
+ this.name = 'StackClosedError';
186
+ }
187
+ }
188
+ /**
189
+ * Thrown when a scoped view is asked to observe a stack whose adapter
190
+ * relays a remote feed. Outside the StackError taxonomy for the same
191
+ * reason StackClosedError is: it reports a topology the caller assembled,
192
+ * not a state a request can be in.
193
+ *
194
+ * A relayed frame is scoped by the authority that opened the feed, and a
195
+ * narrower scope cannot re-derive that decision — a purge leaves no record
196
+ * to check `canRead` against. Delivering anyway would break the promise
197
+ * that a subscriber never sees what it may not read; delivering only local
198
+ * writes would silently drop every change made elsewhere, which is the
199
+ * failure that looks fine in testing. So it refuses.
200
+ * See docs/spec/events.md § Permission scoping.
201
+ */
202
+ export class StackRelayScopeError extends Error {
203
+ constructor(message) {
204
+ super(message);
205
+ this.name = 'StackRelayScopeError';
206
+ }
207
+ }
208
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Grant vocabulary and coverage
3
+ * -------------------------------------------------------
4
+ * What a `_grant` Record says, and who it says it about. The coverage
5
+ * question — does this grant reach this grantee — is answered here rather
6
+ * than at each call site so that the access checks and listGrants() cannot
7
+ * drift apart about it.
8
+ *
9
+ * See docs/spec/access-control.md § Type-level grants.
10
+ */
11
+ import type { GroupRole } from './access.js';
12
+ import type { EntityId, GrantAction, GrantContent, QueryResult, RecordId, StackQuery, StackRecord } from './types.js';
13
+ /**
14
+ * Valid GrantAction values, for runtime validation in Stack.grant().
15
+ * Built from GRANT_ACTIONS (types.ts), the source of truth GrantAction is
16
+ * itself derived from — so this can't drift from the type.
17
+ */
18
+ export declare const GRANT_ACTION_SET: ReadonlySet<GrantAction>;
19
+ /**
20
+ * The read actions that make a mutate action coherent, scope for scope: a
21
+ * `-any` verb needs read over the same reach it can mutate, a `-own` verb
22
+ * needs read over its author's own. A grant carrying neither conveys the
23
+ * verb to nobody. `create` has no companion — writing a Record you then
24
+ * cannot read is the drop-box, and it discloses nothing.
25
+ * See docs/spec/access-control.md § Write implies read.
26
+ */
27
+ export declare const READ_COMPANIONS: ReadonlyMap<GrantAction, readonly GrantAction[]>;
28
+ /**
29
+ * Whether one grant's action list conveys `action`. The companion has to
30
+ * sit in the same `_grant` Record, not merely somewhere in the grantee's
31
+ * set: a grant is revoked whole, so a rule satisfied across two records
32
+ * would let revoking the read one leave a mutate-without-read grant
33
+ * standing — the configuration this rule exists to refuse, arrived at
34
+ * without anyone writing it.
35
+ */
36
+ export declare function grantConveys(actions: readonly string[], action: GrantAction): boolean;
37
+ /**
38
+ * Who a grant() / revoke() / listGrants() call targets: a specific entity
39
+ * (DID), a `_group` Record's roster (by ID), or `null` for the default
40
+ * grant / default-only listing. See docs/spec/access-control.md § Type-level
41
+ * grants.
42
+ */
43
+ export type GrantTarget = EntityId | {
44
+ groupId: RecordId;
45
+ } | null;
46
+ /** Direct (non-roster) match between a stored _grant's content and a GrantTarget. */
47
+ export declare function matchesGrantTarget(content: GrantContent, target: GrantTarget): boolean;
48
+ /**
49
+ * Reject a grant target that names nobody. An empty groupId or entityId is
50
+ * falsy, so a stored record carrying one reads as a *default* grant — every
51
+ * authenticated entity — instead of the target the caller meant. `null` is
52
+ * the only way to say "default".
53
+ */
54
+ export declare function validateGrantTarget(target: GrantTarget): void;
55
+ /**
56
+ * Whether a stored _grant covers `grantee`: a direct DID match, roster
57
+ * membership when `allowGroup`, or a default when `allowDefault`. Presence
58
+ * decides the tier, never truthiness — an empty grantee field names nobody.
59
+ * Module-level so the access checks and listGrants() cannot drift apart.
60
+ * See docs/spec/access-control.md § Type-level grants.
61
+ */
62
+ export declare function grantCoversGrantee(c: GrantContent, grantee: EntityId, opts: {
63
+ allowDefault: boolean;
64
+ allowGroup: boolean;
65
+ groupRoles: Map<string, GroupRole | null>;
66
+ resolveRecord: (id: RecordId) => Promise<StackRecord | null>;
67
+ }): Promise<boolean>;
68
+ /**
69
+ * System type families grant() refuses to target: a grant on any of them
70
+ * would let the grantee mint their own grants, touch stack config, or
71
+ * register an app card claiming a DID that isn't theirs — the last of
72
+ * which is what verified app attribution rests on. See
73
+ * docs/spec/access-control.md § Type-level grants.
74
+ */
75
+ export declare const UNGRANTABLE_SYSTEM_TYPES: ReadonlySet<string>;
76
+ /**
77
+ * Every `_grant` Record, cursor-walked. Read through an unscoped query at
78
+ * every call site: a grant is what decides who may read, so it can never
79
+ * itself sit behind a read check. No content prefilter — a stored grant's
80
+ * typeId may be a bare baseId or versioned, so exact matching would wrongly
81
+ * exclude family versions.
82
+ */
83
+ export declare function loadGrantRecords(query: (q: StackQuery) => Promise<QueryResult>): Promise<StackRecord[]>;
84
+ //# sourceMappingURL=grants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,WAAW,CAA0B,CAAC;AAEjF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,WAAW,EAAE,SAAS,WAAW,EAAE,CAK3E,CAAC;AAEH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAIrF;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,GAAG,IAAI,CAAC;AAElE,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAYtF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAgB7D;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,CAAC,EAAE,YAAY,EACf,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE;IACJ,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;IAC1C,aAAa,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC9D,GACA,OAAO,CAAC,OAAO,CAAC,CAiBlB;AA+BD;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAIvD,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,WAAW,CAAC,GAC7C,OAAO,CAAC,WAAW,EAAE,CAAC,CAExB"}
package/dist/grants.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Grant vocabulary and coverage
3
+ * -------------------------------------------------------
4
+ * What a `_grant` Record says, and who it says it about. The coverage
5
+ * question — does this grant reach this grantee — is answered here rather
6
+ * than at each call site so that the access checks and listGrants() cannot
7
+ * drift apart about it.
8
+ *
9
+ * See docs/spec/access-control.md § Type-level grants.
10
+ */
11
+ import { baseIdOf } from './schema.js';
12
+ import { StackQueryError } from './errors.js';
13
+ import { SYSTEM_TYPES, GRANT_ACTIONS } from './types.js';
14
+ import { groupRoleFromAssociations } from './access.js';
15
+ import { queryAllPages } from './stack-reads.js';
16
+ /**
17
+ * Valid GrantAction values, for runtime validation in Stack.grant().
18
+ * Built from GRANT_ACTIONS (types.ts), the source of truth GrantAction is
19
+ * itself derived from — so this can't drift from the type.
20
+ */
21
+ export const GRANT_ACTION_SET = new Set(GRANT_ACTIONS);
22
+ /**
23
+ * The read actions that make a mutate action coherent, scope for scope: a
24
+ * `-any` verb needs read over the same reach it can mutate, a `-own` verb
25
+ * needs read over its author's own. A grant carrying neither conveys the
26
+ * verb to nobody. `create` has no companion — writing a Record you then
27
+ * cannot read is the drop-box, and it discloses nothing.
28
+ * See docs/spec/access-control.md § Write implies read.
29
+ */
30
+ export const READ_COMPANIONS = new Map([
31
+ ['update-own', ['read-own', 'read-any']],
32
+ ['delete-own', ['read-own', 'read-any']],
33
+ ['update-any', ['read-any']],
34
+ ['delete-any', ['read-any']],
35
+ ]);
36
+ /**
37
+ * Whether one grant's action list conveys `action`. The companion has to
38
+ * sit in the same `_grant` Record, not merely somewhere in the grantee's
39
+ * set: a grant is revoked whole, so a rule satisfied across two records
40
+ * would let revoking the read one leave a mutate-without-read grant
41
+ * standing — the configuration this rule exists to refuse, arrived at
42
+ * without anyone writing it.
43
+ */
44
+ export function grantConveys(actions, action) {
45
+ if (!actions.includes(action))
46
+ return false;
47
+ const companions = READ_COMPANIONS.get(action);
48
+ return !companions || companions.some((c) => actions.includes(c));
49
+ }
50
+ /** Direct (non-roster) match between a stored _grant's content and a GrantTarget. */
51
+ export function matchesGrantTarget(content, target) {
52
+ if (target !== null && typeof target === 'object') {
53
+ // Guarded so an absent groupId can't match the absent granteeGroupId on
54
+ // every entity-targeted and default grant — `undefined === undefined`
55
+ // would otherwise sweep them all into a revoke aimed at one group.
56
+ if (!target.groupId)
57
+ return false;
58
+ return content.granteeGroupId === target.groupId;
59
+ }
60
+ if (target === null) {
61
+ return !content.granteeEntityId && !content.granteeGroupId;
62
+ }
63
+ return content.granteeEntityId === target;
64
+ }
65
+ /**
66
+ * Reject a grant target that names nobody. An empty groupId or entityId is
67
+ * falsy, so a stored record carrying one reads as a *default* grant — every
68
+ * authenticated entity — instead of the target the caller meant. `null` is
69
+ * the only way to say "default".
70
+ */
71
+ export function validateGrantTarget(target) {
72
+ if (target === null)
73
+ return;
74
+ if (typeof target === 'string') {
75
+ if (target.length === 0) {
76
+ throw new StackQueryError('A grant target entityId cannot be empty. Pass null for a default grant.');
77
+ }
78
+ return;
79
+ }
80
+ if (typeof target.groupId !== 'string' || target.groupId.length === 0) {
81
+ throw new StackQueryError('A group grant target requires a non-empty groupId.');
82
+ }
83
+ // Not a format check: granteeGroupId is a reference to an existing
84
+ // Record, like parentId or an association's recordId, and none of those
85
+ // are parsed either. One that resolves to nothing simply denies.
86
+ }
87
+ /**
88
+ * Whether a stored _grant covers `grantee`: a direct DID match, roster
89
+ * membership when `allowGroup`, or a default when `allowDefault`. Presence
90
+ * decides the tier, never truthiness — an empty grantee field names nobody.
91
+ * Module-level so the access checks and listGrants() cannot drift apart.
92
+ * See docs/spec/access-control.md § Type-level grants.
93
+ */
94
+ export async function grantCoversGrantee(c, grantee, opts) {
95
+ const namesEntity = c.granteeEntityId !== undefined;
96
+ const namesGroup = c.granteeGroupId !== undefined;
97
+ if (!namesEntity && !namesGroup)
98
+ return opts.allowDefault;
99
+ if (namesEntity && c.granteeEntityId !== grantee)
100
+ return false;
101
+ if (namesGroup) {
102
+ if (!opts.allowGroup)
103
+ return false;
104
+ if (!c.granteeGroupId)
105
+ return false;
106
+ const role = await resolveGroupRoleMemoized(c.granteeGroupId, grantee, opts.groupRoles, opts.resolveRecord);
107
+ if (role === null)
108
+ return false;
109
+ }
110
+ return true;
111
+ }
112
+ /**
113
+ * An entity's role on a `_group` roster, memoized in the caller's
114
+ * `groupRoles` map. That map is built per operation and threaded alongside
115
+ * `prefetchedGrants`, so no resolved role outlives the operation that
116
+ * resolved it — removal from a group must never go stale.
117
+ * See docs/spec/access-control.md § Type-level grants.
118
+ */
119
+ async function resolveGroupRoleMemoized(groupId, entityId, groupRoles, resolveRecord) {
120
+ const key = `${groupId}:${entityId}`;
121
+ const cached = groupRoles.get(key);
122
+ if (cached !== undefined)
123
+ return cached;
124
+ const group = await resolveRecord(groupId);
125
+ // Only a real `_group` Record carries a roster. Without the family check
126
+ // any Record's relationship associations would serve as one, and a group
127
+ // migrated out of the family would keep resolving after it had stopped
128
+ // being a group.
129
+ const role = group && baseIdOf(group.typeId) === SYSTEM_TYPES.GROUP
130
+ ? groupRoleFromAssociations(group.associations, entityId)
131
+ : null;
132
+ groupRoles.set(key, role);
133
+ return role;
134
+ }
135
+ /**
136
+ * System type families grant() refuses to target: a grant on any of them
137
+ * would let the grantee mint their own grants, touch stack config, or
138
+ * register an app card claiming a DID that isn't theirs — the last of
139
+ * which is what verified app attribution rests on. See
140
+ * docs/spec/access-control.md § Type-level grants.
141
+ */
142
+ export const UNGRANTABLE_SYSTEM_TYPES = new Set([
143
+ SYSTEM_TYPES.GRANT,
144
+ SYSTEM_TYPES.CONFIG,
145
+ SYSTEM_TYPES.APP,
146
+ ]);
147
+ /**
148
+ * Every `_grant` Record, cursor-walked. Read through an unscoped query at
149
+ * every call site: a grant is what decides who may read, so it can never
150
+ * itself sit behind a read check. No content prefilter — a stored grant's
151
+ * typeId may be a bare baseId or versioned, so exact matching would wrongly
152
+ * exclude family versions.
153
+ */
154
+ export function loadGrantRecords(query) {
155
+ return queryAllPages(query, { filter: { typeId: `${SYSTEM_TYPES.GRANT}@1` } });
156
+ }
157
+ //# sourceMappingURL=grants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"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"}
package/dist/id.d.ts CHANGED
@@ -15,9 +15,6 @@ export declare const RAND_SUFFIX_LENGTH = 3;
15
15
  export declare class IdGenerationError extends Error {
16
16
  constructor(message?: string);
17
17
  }
18
- export declare class IdGenerationOverflowError extends IdGenerationError {
19
- constructor(message?: string);
20
- }
21
18
  export declare const crockford32Encode: (n: number) => string;
22
19
  export declare const crockford32Decode: (s: string) => number;
23
20
  export declare const _setLastNowId: (chars: string) => void;
@@ -39,8 +36,13 @@ export declare const MAX_ID_TIMESTAMP: number;
39
36
  /**
40
37
  * Generate a new Stack record ID. Time-sortable: lexicographic order
41
38
  * matches creation order, with same-millisecond IDs monotonically
42
- * incremented. Throws IdGenerationOverflowError past 32^3 IDs in one
43
- * millisecond.
39
+ * incremented.
40
+ *
41
+ * A millisecond whose suffix space is spent carries into the next one
42
+ * rather than failing, so how many IDs a millisecond holds never depends
43
+ * on where its random suffix happened to start — which is what keeps the
44
+ * order total and the call free of a capacity a caller cannot see.
45
+ * See docs/spec/data-model.md § Record IDs.
44
46
  *
45
47
  * @param timestamp - Override the timestamp (ms since epoch). Defaults to Date.now().
46
48
  */
package/dist/id.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,eAAO,MAAM,IAAI,QAAoB,CAAC;AAGtC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAcpC,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,SAAK;CAIzB;AAED,qBAAa,yBAA0B,SAAQ,iBAAiB;gBAClD,OAAO,SAAK;CAIzB;AAMD,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAiB7C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAe7C,CAAC;AAoCF,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,IAK7C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,IAKjD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,KAAG,IAErD,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,aAAa,QAAO,IAIhC,CAAC;AAMF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,QAA2C,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,YAAW,MAAmB,KAAG,MAU3D,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAAI,WAAW,MAAM,KAAG,MAS1D,CAAC;AASF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,IAAI,MAAM,KAAG,OAA6B,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,MAAM,KAAG,MACa,CAAC"}
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,eAAO,MAAM,IAAI,QAAoB,CAAC;AAGtC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAcpC,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,SAAK;CAIzB;AAMD,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAiB7C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAe7C,CAAC;AAkCF,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,IAK7C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,IAKjD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,KAAG,IAErD,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,aAAa,QAAO,IAIhC,CAAC;AAMF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,QAA2C,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GAAI,YAAW,MAAmB,KAAG,MAgB3D,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAAI,WAAW,MAAM,KAAG,MAS1D,CAAC;AASF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,IAAI,MAAM,KAAG,OAA6B,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,MAAM,KAAG,MACa,CAAC"}
package/dist/id.js CHANGED
@@ -30,12 +30,6 @@ export class IdGenerationError extends Error {
30
30
  this.name = 'IdGenerationError';
31
31
  }
32
32
  }
33
- export class IdGenerationOverflowError extends IdGenerationError {
34
- constructor(message = '') {
35
- super(message || 'Too many IDs have been generated in the same millisecond.');
36
- this.name = 'IdGenerationOverflowError';
37
- }
38
- }
39
33
  // -------------------------------------------------------
40
34
  // Encoding / decoding
41
35
  // -------------------------------------------------------
@@ -88,12 +82,10 @@ const generateRandChars = () => {
88
82
  } while (value > Math.floor(0xffffffff / modulus) * modulus);
89
83
  return pad(crockford32Encode(value % modulus), RAND_SUFFIX_LENGTH);
90
84
  };
85
+ /** The next suffix after `randChars`, or null once the space is spent. */
91
86
  const incrementRandChars = (randChars) => {
92
87
  const next = crockford32Encode(crockford32Decode(randChars) + 1);
93
- if (next.length > RAND_SUFFIX_LENGTH) {
94
- throw new IdGenerationOverflowError();
95
- }
96
- return pad(next, RAND_SUFFIX_LENGTH);
88
+ return next.length > RAND_SUFFIX_LENGTH ? null : pad(next, RAND_SUFFIX_LENGTH);
97
89
  };
98
90
  // -------------------------------------------------------
99
91
  // Test hooks (package-private)
@@ -136,15 +128,25 @@ export const MAX_ID_TIMESTAMP = Math.pow(BASE, MIN_TIMESTAMP_LENGTH) - 1;
136
128
  /**
137
129
  * Generate a new Stack record ID. Time-sortable: lexicographic order
138
130
  * matches creation order, with same-millisecond IDs monotonically
139
- * incremented. Throws IdGenerationOverflowError past 32^3 IDs in one
140
- * millisecond.
131
+ * incremented.
132
+ *
133
+ * A millisecond whose suffix space is spent carries into the next one
134
+ * rather than failing, so how many IDs a millisecond holds never depends
135
+ * on where its random suffix happened to start — which is what keeps the
136
+ * order total and the call free of a capacity a caller cannot see.
137
+ * See docs/spec/data-model.md § Record IDs.
141
138
  *
142
139
  * @param timestamp - Override the timestamp (ms since epoch). Defaults to Date.now().
143
140
  */
144
141
  export const generateId = (timestamp = Date.now()) => {
145
- const effectiveTimestamp = Math.max(timestamp, lastTimestamp);
146
- const nowId = pad(crockford32Encode(effectiveTimestamp), MIN_TIMESTAMP_LENGTH);
147
- const randChars = nowId !== lastNowId ? generateRandChars() : incrementRandChars(lastRandChars);
142
+ let effectiveTimestamp = Math.max(timestamp, lastTimestamp);
143
+ let nowId = pad(crockford32Encode(effectiveTimestamp), MIN_TIMESTAMP_LENGTH);
144
+ let randChars = nowId === lastNowId ? incrementRandChars(lastRandChars) : generateRandChars();
145
+ if (randChars === null) {
146
+ effectiveTimestamp += 1;
147
+ nowId = pad(crockford32Encode(effectiveTimestamp), MIN_TIMESTAMP_LENGTH);
148
+ randChars = generateRandChars();
149
+ }
148
150
  lastTimestamp = effectiveTimestamp;
149
151
  lastNowId = nowId;
150
152
  lastRandChars = randChars;
package/dist/id.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * DID bindings
3
+ * -------------------------------------------------------
4
+ * Which content fields are lookup keys rather than display values, and
5
+ * which of those a stack resolves *by*. Every one is immutable once set;
6
+ * the unique subset is additionally one-per-stack, because a lookup with
7
+ * two answers is all an impersonating card needs.
8
+ *
9
+ * See docs/spec/identity.md § DID bindings.
10
+ */
11
+ export declare const bindingFieldsOf: (family: string) => readonly ("did" | "appId")[];
12
+ export declare const uniqueBindingFieldsOf: (family: string) => readonly ("did" | "appId")[];
13
+ //# sourceMappingURL=identity-bindings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-bindings.d.ts","sourceRoot":"","sources":["../src/identity-bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkCH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,EACzC,CAAC;AAEnC,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,KAAG,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,EACxC,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DID bindings
3
+ * -------------------------------------------------------
4
+ * Which content fields are lookup keys rather than display values, and
5
+ * which of those a stack resolves *by*. Every one is immutable once set;
6
+ * the unique subset is additionally one-per-stack, because a lookup with
7
+ * two answers is all an impersonating card needs.
8
+ *
9
+ * See docs/spec/identity.md § DID bindings.
10
+ */
11
+ import { SYSTEM_TYPES } from './types.js';
12
+ /**
13
+ * Content fields that are lookup keys rather than display values: a card
14
+ * claims one, and something later resolves through it. Every one of them is
15
+ * immutable once set. See docs/spec/identity.md § DID bindings.
16
+ */
17
+ const BINDING_FIELDS = new Map([
18
+ [SYSTEM_TYPES.APP, ['did', 'appId']],
19
+ [SYSTEM_TYPES.ENTITY, ['did']],
20
+ ]);
21
+ /**
22
+ * The subset that is additionally unique per stack: the fields something
23
+ * resolves *by*. A Record's `principalId` finds its card by `_app.did` and
24
+ * its `entityId` by `_entity.did`, so a second card claiming either leaves
25
+ * that lookup without a single answer — and ambiguity is all an
26
+ * impersonating card needs.
27
+ *
28
+ * `_app.appId` is deliberately absent. Nothing resolves a card by it — the
29
+ * cross-check reaches the card by `did` and only compares `appId` — so
30
+ * uniqueness would buy no disambiguation, while forbidding the second card
31
+ * key rotation is supposed to produce: `appId` is required, so a
32
+ * replacement card for the same software necessarily repeats it. Moving one
33
+ * card onto another's `appId` is what immutability already refuses.
34
+ * See docs/spec/identity.md § DID bindings.
35
+ */
36
+ const UNIQUE_BINDING_FIELDS = new Map([
37
+ [SYSTEM_TYPES.APP, ['did']],
38
+ [SYSTEM_TYPES.ENTITY, ['did']],
39
+ ]);
40
+ export const bindingFieldsOf = (family) => BINDING_FIELDS.get(family) ?? [];
41
+ export const uniqueBindingFieldsOf = (family) => UNIQUE_BINDING_FIELDS.get(family) ?? [];
42
+ //# sourceMappingURL=identity-bindings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-bindings.js","sourceRoot":"","sources":["../src/identity-bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;GAIG;AACH,MAAM,cAAc,GAAsD,IAAI,GAAG,CAAC;IAChF,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,CAAU,CAAC;IAC7C,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,KAAK,CAAU,CAAC;CACxC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,qBAAqB,GAAsD,IAAI,GAAG,CAAC;IACvF,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,KAAK,CAAU,CAAC;IACpC,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,KAAK,CAAU,CAAC;CACxC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAc,EAAgC,EAAE,CAC9E,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAEnC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAc,EAAgC,EAAE,CACpF,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC"}