@haverstack/core 0.29.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 (73) hide show
  1. package/dist/adapter-entry.d.ts +1 -1
  2. package/dist/adapter-entry.d.ts.map +1 -1
  3. package/dist/adapter-entry.js +1 -1
  4. package/dist/adapter-entry.js.map +1 -1
  5. package/dist/changes.d.ts +14 -0
  6. package/dist/changes.d.ts.map +1 -1
  7. package/dist/changes.js +35 -0
  8. package/dist/changes.js.map +1 -1
  9. package/dist/errors.d.ts +173 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/errors.js +208 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/grants.d.ts +84 -0
  14. package/dist/grants.d.ts.map +1 -0
  15. package/dist/grants.js +157 -0
  16. package/dist/grants.js.map +1 -0
  17. package/dist/id.d.ts +7 -5
  18. package/dist/id.d.ts.map +1 -1
  19. package/dist/id.js +17 -15
  20. package/dist/id.js.map +1 -1
  21. package/dist/identity-bindings.d.ts +13 -0
  22. package/dist/identity-bindings.d.ts.map +1 -0
  23. package/dist/identity-bindings.js +42 -0
  24. package/dist/identity-bindings.js.map +1 -0
  25. package/dist/index.d.ts +6 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/limits.d.ts +23 -0
  30. package/dist/limits.d.ts.map +1 -0
  31. package/dist/limits.js +35 -0
  32. package/dist/limits.js.map +1 -0
  33. package/dist/query-validation.d.ts +80 -0
  34. package/dist/query-validation.d.ts.map +1 -0
  35. package/dist/query-validation.js +223 -0
  36. package/dist/query-validation.js.map +1 -0
  37. package/dist/record-changes.d.ts +89 -0
  38. package/dist/record-changes.d.ts.map +1 -0
  39. package/dist/record-changes.js +201 -0
  40. package/dist/record-changes.js.map +1 -0
  41. package/dist/record-id.d.ts +70 -0
  42. package/dist/record-id.d.ts.map +1 -0
  43. package/dist/record-id.js +127 -0
  44. package/dist/record-id.js.map +1 -0
  45. package/dist/scoped-stack.d.ts +460 -0
  46. package/dist/scoped-stack.d.ts.map +1 -0
  47. package/dist/scoped-stack.js +1163 -0
  48. package/dist/scoped-stack.js.map +1 -0
  49. package/dist/stack-reads.d.ts +38 -0
  50. package/dist/stack-reads.d.ts.map +1 -0
  51. package/dist/stack-reads.js +79 -0
  52. package/dist/stack-reads.js.map +1 -0
  53. package/dist/stack.d.ts +37 -643
  54. package/dist/stack.d.ts.map +1 -1
  55. package/dist/stack.js +102 -2254
  56. package/dist/stack.js.map +1 -1
  57. package/dist/testing.d.ts.map +1 -1
  58. package/dist/testing.js +3 -1
  59. package/dist/testing.js.map +1 -1
  60. package/dist/types.d.ts +9 -2
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js +8 -0
  63. package/dist/types.js.map +1 -1
  64. package/dist/wire-entry.d.ts +2 -2
  65. package/dist/wire-entry.d.ts.map +1 -1
  66. package/dist/wire-entry.js +2 -2
  67. package/dist/wire-entry.js.map +1 -1
  68. package/dist/wire-record.js +1 -1
  69. package/dist/wire-record.js.map +1 -1
  70. package/dist/wire-request.d.ts.map +1 -1
  71. package/dist/wire-request.js +3 -2
  72. package/dist/wire-request.js.map +1 -1
  73. package/package.json +1 -1
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"}
package/dist/index.d.ts CHANGED
@@ -14,10 +14,13 @@
14
14
  * Storage adapters are published as separate packages:
15
15
  * @haverstack/adapter-sqlite
16
16
  */
17
- export { Stack, ScopedStack, StackError, StackValidationError, StackMigrationError, StackPermissionError, StackNotFoundError, StackConflictError, StackVersionConflictError, StackQueryError, StackSchemaDriftError, StackPayloadTooLargeError, StackTimeoutError, StackClosedError, StackRelayScopeError, } from './stack.js';
18
- export type { StackErrorCode, StackClient, CreateRecordOptions, BackdatableCreateRecordOptions, StackOptions, GetRecordOptions, DeleteRecordOptions, IfVersionOptions, CollectAttachmentGarbageOptions, CollectAttachmentGarbageResult, } from './stack.js';
17
+ export { Stack } from './stack.js';
18
+ export { ScopedStack } from './scoped-stack.js';
19
+ export type { StackClient, CreateRecordOptions, BackdatableCreateRecordOptions, StackOptions, GetRecordOptions, DeleteRecordOptions, IfVersionOptions, CollectAttachmentGarbageOptions, CollectAttachmentGarbageResult, } from './stack.js';
20
+ export { StackError, StackValidationError, StackMigrationError, StackPermissionError, StackNotFoundError, StackConflictError, StackVersionConflictError, StackQueryError, StackSchemaDriftError, StackPayloadTooLargeError, StackTimeoutError, StackClosedError, StackRelayScopeError, } from './errors.js';
21
+ export type { StackErrorCode } from './errors.js';
19
22
  export type { RecordId, TypeId, FileId, EntityId, AttachmentContent, StackRecord, RecordChanges, RecordVersion, ActorOptions, ChangeKind, ChangeOp, ChangeActor, RecordChange, ChangeFilter, SubscribeOptions, Unsubscribe, ExpectedVersionOptions, SnapshotOptions, StackType, TypeSchema, FieldDef, ScalarFieldDef, ArrayFieldDef, ObjectFieldDef, ScalarFieldKind, Association, TagAssociation, AttachmentAssociation, RelationshipAssociation, RelationshipTarget, RecordTarget, EntityTarget, ExternalTarget, Permission, StackQuery, RecordFilter, RelatedToFilter, RelationshipTargetPattern, QuerySort, NativeSortField, ContentFilterReach, QueryResult, DateRange, Migration, MigrationFn, StackAdapter, StackFeatures, TokenSession, EntityContent, AppContent, GroupContent, GrantAction, GrantContent, ConfigContent, } from './types.js';
20
- export { SYSTEM_TYPES } from './types.js';
23
+ export { SYSTEM_TYPES, NATIVE_SORT_FIELDS } from './types.js';
21
24
  export { generateId } from './id.js';
22
25
  export { hashSchema, isCompatible } from './schema.js';
23
26
  export type { SchemaDriftViolation } from './schema.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EACL,KAAK,EACL,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,8BAA8B,EAC9B,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,EACZ,eAAe,EACf,yBAAyB,EACzB,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,8BAA8B,EAC9B,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,YAAY,EACV,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,EACZ,eAAe,EACf,yBAAyB,EACzB,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAM9D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -15,8 +15,11 @@
15
15
  * @haverstack/adapter-sqlite
16
16
  */
17
17
  // Core class and client interface
18
- export { Stack, ScopedStack, StackError, StackValidationError, StackMigrationError, StackPermissionError, StackNotFoundError, StackConflictError, StackVersionConflictError, StackQueryError, StackSchemaDriftError, StackPayloadTooLargeError, StackTimeoutError, StackClosedError, StackRelayScopeError, } from './stack.js';
19
- export { SYSTEM_TYPES } from './types.js';
18
+ export { Stack } from './stack.js';
19
+ export { ScopedStack } from './scoped-stack.js';
20
+ // The error taxonomy
21
+ export { StackError, StackValidationError, StackMigrationError, StackPermissionError, StackNotFoundError, StackConflictError, StackVersionConflictError, StackQueryError, StackSchemaDriftError, StackPayloadTooLargeError, StackTimeoutError, StackClosedError, StackRelayScopeError, } from './errors.js';
22
+ export { SYSTEM_TYPES, NATIVE_SORT_FIELDS } from './types.js';
20
23
  // Utilities
21
24
  // No in-repo caller: client-minted IDs are the default per spec, and an app
22
25
  // needing the ID before the write round-trips has to mint one itself.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,kCAAkC;AAClC,OAAO,EACL,KAAK,EACL,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAwEpB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,YAAY;AAEZ,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,kCAAkC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAahD,qBAAqB;AACrB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AA6DrB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9D,YAAY;AAEZ,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Declared-ceiling pre-checks
3
+ * -------------------------------------------------------
4
+ * Client-side guards against an adapter's declared `limits`. Local
5
+ * adapters declare `null` and skip them entirely; only a server names a
6
+ * ceiling, and its own request-size limit stays authoritative — these just
7
+ * spare an app the round trip and give it a typed failure instead of a 413
8
+ * it has to interpret.
9
+ *
10
+ * See docs/spec/wire-format.md § Request size limits.
11
+ */
12
+ /** Shared by Stack.putAttachment() and ScopedStack.putAttachment(). */
13
+ export declare function assertAttachmentSize(byteLength: number, attachmentBytes: number | null): void;
14
+ /**
15
+ * The content half of the same pre-check, on Stack.create() and
16
+ * Stack.mutate(). Local adapters declare `limits.contentBytes: null` and skip
17
+ * the serialization entirely; only a server declares a ceiling, and its
18
+ * own request-size limit stays authoritative — this just spares an app the
19
+ * round trip and gives it a typed failure instead of a 413 it has to
20
+ * interpret. See docs/spec/wire-format.md § Request size limits.
21
+ */
22
+ export declare function assertContentSize(content: Record<string, unknown>, contentBytes: number | null, what: 'Content' | 'Patch'): void;
23
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAM7F;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,IAAI,EAAE,SAAS,GAAG,OAAO,GACxB,IAAI,CAQN"}
package/dist/limits.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Declared-ceiling pre-checks
3
+ * -------------------------------------------------------
4
+ * Client-side guards against an adapter's declared `limits`. Local
5
+ * adapters declare `null` and skip them entirely; only a server names a
6
+ * ceiling, and its own request-size limit stays authoritative — these just
7
+ * spare an app the round trip and give it a typed failure instead of a 413
8
+ * it has to interpret.
9
+ *
10
+ * See docs/spec/wire-format.md § Request size limits.
11
+ */
12
+ import { StackPayloadTooLargeError } from './errors.js';
13
+ /** Shared by Stack.putAttachment() and ScopedStack.putAttachment(). */
14
+ export function assertAttachmentSize(byteLength, attachmentBytes) {
15
+ if (attachmentBytes !== null && byteLength > attachmentBytes) {
16
+ throw new StackPayloadTooLargeError(`Attachment (${byteLength} bytes) exceeds the ${attachmentBytes}-byte limit.`);
17
+ }
18
+ }
19
+ /**
20
+ * The content half of the same pre-check, on Stack.create() and
21
+ * Stack.mutate(). Local adapters declare `limits.contentBytes: null` and skip
22
+ * the serialization entirely; only a server declares a ceiling, and its
23
+ * own request-size limit stays authoritative — this just spares an app the
24
+ * round trip and gives it a typed failure instead of a 413 it has to
25
+ * interpret. See docs/spec/wire-format.md § Request size limits.
26
+ */
27
+ export function assertContentSize(content, contentBytes, what) {
28
+ if (contentBytes === null)
29
+ return;
30
+ const byteLength = new TextEncoder().encode(JSON.stringify(content)).length;
31
+ if (byteLength > contentBytes) {
32
+ throw new StackPayloadTooLargeError(`${what} (${byteLength} bytes) exceeds the ${contentBytes}-byte limit.`);
33
+ }
34
+ }
35
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExD,uEAAuE;AACvE,MAAM,UAAU,oBAAoB,CAAC,UAAkB,EAAE,eAA8B;IACrF,IAAI,eAAe,KAAK,IAAI,IAAI,UAAU,GAAG,eAAe,EAAE,CAAC;QAC7D,MAAM,IAAI,yBAAyB,CACjC,eAAe,UAAU,uBAAuB,eAAe,cAAc,CAC9E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAgC,EAChC,YAA2B,EAC3B,IAAyB;IAEzB,IAAI,YAAY,KAAK,IAAI;QAAE,OAAO;IAClC,MAAM,UAAU,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5E,IAAI,UAAU,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,IAAI,yBAAyB,CACjC,GAAG,IAAI,KAAK,UAAU,uBAAuB,YAAY,cAAc,CACxE,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Query and association validation
3
+ * -------------------------------------------------------
4
+ * The sanitizers a query passes through before any adapter sees it, plus
5
+ * the relationship-target rules an association is held to. Both sides of
6
+ * the same job: a discriminated union and a `QuerySort` field are compile-
7
+ * time promises, and a server mapping a request body onto either supplies
8
+ * raw JSON that no compiler has seen.
9
+ *
10
+ * These live in the invariant layer for the reason validation and
11
+ * `_config` protection do — so no adapter can forget one. `assertQueryCapabilities`
12
+ * and its neighbours are shared verbatim with `adapter-api`, which asks the
13
+ * same questions of a request arriving over the wire.
14
+ * See docs/spec/data-model.md § Capability-gated filters.
15
+ */
16
+ import type { ValidationError } from './validate.js';
17
+ import type { Association, QuerySort, RecordFilter, StackFeatures } from './types.js';
18
+ /**
19
+ * Fail loud rather than silently widen: a filter the adapter can't honor
20
+ * would otherwise be dropped, returning an unfiltered superset presented
21
+ * as the filtered result. Shared by Stack.query() and
22
+ * APIAdapter.queryRecords(). See docs/spec/data-model.md
23
+ * § Capability-gated filters.
24
+ */
25
+ export declare function assertQueryCapabilities(filter: RecordFilter | undefined, capabilities: Pick<StackFeatures, 'filter'>): void;
26
+ /**
27
+ * Whether a single-segment content filter can be pushed down to the
28
+ * adapter. Callers that read a content field pair this with an in-memory
29
+ * predicate over the wider result, so the query stays correct against an
30
+ * adapter that reaches no content at all — the rung comparison lives here
31
+ * rather than at each of them.
32
+ */
33
+ export declare function filtersContent(features: Pick<StackFeatures, 'filter'>): boolean;
34
+ /**
35
+ * Split a content filter key into path segments. Write-time validation
36
+ * keeps stored field names free of the path metacharacters, but a filter
37
+ * arrives from a request body and has made no such promise, so the same
38
+ * rule is enforced here. See docs/spec/data-model.md § Content field names.
39
+ */
40
+ export declare function parseContentFilterKey(key: string): string[];
41
+ /**
42
+ * Reject a sort whose field or direction is outside the closed set the
43
+ * types promise. `QuerySort` is typed `'asc' | 'desc'`, but a type is not a
44
+ * runtime guard: a server mapping `?direction=` onto a query, or a
45
+ * delegated app calling query(), supplies a raw string. A SQLite record
46
+ * adapter interpolates the direction straight into `ORDER BY`, so an
47
+ * unvalidated value there is a SQL-injection sink reachable from every
48
+ * untrusted caller. Validating in the invariant layer — the same reason
49
+ * emission and _config protection live here — means no adapter can forget
50
+ * it. See docs/spec/data-model.md § Sorting and pagination.
51
+ */
52
+ export declare function assertValidSort(sort: QuerySort | undefined): void;
53
+ /**
54
+ * Fail loud rather than silently reorder: an adapter that can't honor the
55
+ * requested sort would otherwise answer in some other order, which a
56
+ * caller paging a bounded window has no way to notice. The companion to
57
+ * assertQueryCapabilities(), split from it because a sort is not a filter
58
+ * — see docs/spec/data-model.md § Capability-gated filters.
59
+ */
60
+ export declare function assertSortCapability(sort: QuerySort | undefined, capabilities: Pick<StackFeatures, 'sort'>): void;
61
+ /**
62
+ * Reject a relationship target outside the closed set the types promise.
63
+ * A discriminated union is not a runtime guard — a server mapping a
64
+ * request body onto an association supplies raw JSON — and an
65
+ * unrecognized scope would otherwise be stored under the one arm that
66
+ * names a Record in this stack. See docs/spec/data-model.md
67
+ * § Relationship targets.
68
+ */
69
+ export declare function validateAssociation(association: Association, path?: string): ValidationError[];
70
+ /** validateAssociation() over a create's `associations` array. */
71
+ export declare function validateAssociations(associations: Association[] | undefined, path?: string): ValidationError[];
72
+ /**
73
+ * Reject a relationship filter that names neither a label nor a target,
74
+ * or whose target is malformed. `RelatedToFilter` promises one half is
75
+ * always present; without the runtime check a filter decoded from a
76
+ * request could arrive empty and match every record carrying any
77
+ * relationship. See docs/spec/data-model.md § Filter.
78
+ */
79
+ export declare function assertValidRelatedTo(relatedTo: RecordFilter['relatedTo']): void;
80
+ //# sourceMappingURL=query-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-validation.d.ts","sourceRoot":"","sources":["../src/query-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,YAAY,EAGZ,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,GAC1C,IAAI,CA6BN;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG,OAAO,CAE/E;AAKD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAwB3D;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CA2BjE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GACxC,IAAI,CAiBN;AA0CD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,IAAI,SAAgB,GACnB,eAAe,EAAE,CAGnB;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,WAAW,EAAE,GAAG,SAAS,EACvC,IAAI,SAAiB,GACpB,eAAe,EAAE,CAEnB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,GAAG,IAAI,CAY/E"}