@haverstack/core 0.42.0 → 0.43.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 (150) hide show
  1. package/README.md +5 -7
  2. package/dist/access.d.ts +30 -32
  3. package/dist/access.d.ts.map +1 -1
  4. package/dist/access.js +40 -98
  5. package/dist/access.js.map +1 -1
  6. package/dist/adapter/combine.d.ts.map +1 -1
  7. package/dist/adapter/combine.js +11 -3
  8. package/dist/adapter/combine.js.map +1 -1
  9. package/dist/adapter/index.d.ts +6 -4
  10. package/dist/adapter/index.d.ts.map +1 -1
  11. package/dist/adapter/index.js +4 -3
  12. package/dist/adapter/index.js.map +1 -1
  13. package/dist/adapter/reach.d.ts +14 -0
  14. package/dist/adapter/reach.d.ts.map +1 -0
  15. package/dist/adapter/reach.js +31 -0
  16. package/dist/adapter/reach.js.map +1 -0
  17. package/dist/associations/identity.d.ts +18 -42
  18. package/dist/associations/identity.d.ts.map +1 -1
  19. package/dist/associations/identity.js +60 -84
  20. package/dist/associations/identity.js.map +1 -1
  21. package/dist/associations/validation.d.ts +1 -8
  22. package/dist/associations/validation.d.ts.map +1 -1
  23. package/dist/associations/validation.js +3 -30
  24. package/dist/associations/validation.js.map +1 -1
  25. package/dist/collection.d.ts +7 -11
  26. package/dist/collection.d.ts.map +1 -1
  27. package/dist/collection.js +24 -34
  28. package/dist/collection.js.map +1 -1
  29. package/dist/content-match.d.ts +2 -2
  30. package/dist/content-match.js +2 -2
  31. package/dist/errors.d.ts +23 -8
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +28 -9
  34. package/dist/errors.js.map +1 -1
  35. package/dist/grants.d.ts +3 -3
  36. package/dist/grants.d.ts.map +1 -1
  37. package/dist/grants.js +4 -21
  38. package/dist/grants.js.map +1 -1
  39. package/dist/index.d.ts +5 -5
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/lazy.d.ts +13 -0
  44. package/dist/lazy.d.ts.map +1 -0
  45. package/dist/lazy.js +27 -0
  46. package/dist/lazy.js.map +1 -0
  47. package/dist/merge.d.ts +1 -1
  48. package/dist/merge.js +1 -1
  49. package/dist/query-validation.d.ts +41 -28
  50. package/dist/query-validation.d.ts.map +1 -1
  51. package/dist/query-validation.js +85 -71
  52. package/dist/query-validation.js.map +1 -1
  53. package/dist/record-changes.d.ts +12 -36
  54. package/dist/record-changes.d.ts.map +1 -1
  55. package/dist/record-changes.js +9 -98
  56. package/dist/record-changes.js.map +1 -1
  57. package/dist/scoped-stack/authority-snapshot.d.ts +37 -0
  58. package/dist/scoped-stack/authority-snapshot.d.ts.map +1 -0
  59. package/dist/scoped-stack/authority-snapshot.js +49 -0
  60. package/dist/scoped-stack/authority-snapshot.js.map +1 -0
  61. package/dist/scoped-stack/authority.d.ts +51 -38
  62. package/dist/scoped-stack/authority.d.ts.map +1 -1
  63. package/dist/scoped-stack/authority.js +160 -136
  64. package/dist/scoped-stack/authority.js.map +1 -1
  65. package/dist/scoped-stack/family-policy.d.ts +59 -0
  66. package/dist/scoped-stack/family-policy.d.ts.map +1 -0
  67. package/dist/scoped-stack/family-policy.js +68 -0
  68. package/dist/scoped-stack/family-policy.js.map +1 -0
  69. package/dist/scoped-stack/scoped-stack.d.ts +66 -86
  70. package/dist/scoped-stack/scoped-stack.d.ts.map +1 -1
  71. package/dist/scoped-stack/scoped-stack.js +243 -324
  72. package/dist/scoped-stack/scoped-stack.js.map +1 -1
  73. package/dist/scoped-stack/subscription.d.ts +14 -33
  74. package/dist/scoped-stack/subscription.d.ts.map +1 -1
  75. package/dist/scoped-stack/subscription.js +16 -57
  76. package/dist/scoped-stack/subscription.js.map +1 -1
  77. package/dist/stack/arguments.d.ts +27 -0
  78. package/dist/stack/arguments.d.ts.map +1 -0
  79. package/dist/stack/arguments.js +53 -0
  80. package/dist/stack/arguments.js.map +1 -0
  81. package/dist/stack/attachments.d.ts +1 -1
  82. package/dist/stack/attachments.js +1 -1
  83. package/dist/stack/changes.d.ts +4 -10
  84. package/dist/stack/changes.d.ts.map +1 -1
  85. package/dist/stack/changes.js +8 -8
  86. package/dist/stack/changes.js.map +1 -1
  87. package/dist/stack/client.d.ts +34 -58
  88. package/dist/stack/client.d.ts.map +1 -1
  89. package/dist/stack/install-app.d.ts.map +1 -1
  90. package/dist/stack/install-app.js +7 -5
  91. package/dist/stack/install-app.js.map +1 -1
  92. package/dist/stack/integrity.js +2 -2
  93. package/dist/stack/integrity.js.map +1 -1
  94. package/dist/stack/limits.d.ts +1 -1
  95. package/dist/stack/limits.js +1 -1
  96. package/dist/stack/reads.d.ts +9 -5
  97. package/dist/stack/reads.d.ts.map +1 -1
  98. package/dist/stack/reads.js +24 -4
  99. package/dist/stack/reads.js.map +1 -1
  100. package/dist/stack/stack.d.ts +77 -71
  101. package/dist/stack/stack.d.ts.map +1 -1
  102. package/dist/stack/stack.js +229 -256
  103. package/dist/stack/stack.js.map +1 -1
  104. package/dist/stack/type-grants.d.ts.map +1 -1
  105. package/dist/stack/type-grants.js +4 -4
  106. package/dist/stack/type-grants.js.map +1 -1
  107. package/dist/stack/write-expectation.d.ts +13 -12
  108. package/dist/stack/write-expectation.d.ts.map +1 -1
  109. package/dist/stack/write-expectation.js.map +1 -1
  110. package/dist/stack/write-gate.d.ts +23 -0
  111. package/dist/stack/write-gate.d.ts.map +1 -0
  112. package/dist/stack/write-gate.js +17 -0
  113. package/dist/stack/write-gate.js.map +1 -0
  114. package/dist/testing.d.ts +25 -9
  115. package/dist/testing.d.ts.map +1 -1
  116. package/dist/testing.js +147 -123
  117. package/dist/testing.js.map +1 -1
  118. package/dist/types/adapter.d.ts +90 -31
  119. package/dist/types/adapter.d.ts.map +1 -1
  120. package/dist/types/associations.d.ts +1 -17
  121. package/dist/types/associations.d.ts.map +1 -1
  122. package/dist/types/associations.js +1 -8
  123. package/dist/types/associations.js.map +1 -1
  124. package/dist/types/capabilities.d.ts +8 -8
  125. package/dist/types/capabilities.d.ts.map +1 -1
  126. package/dist/types/events.d.ts +3 -8
  127. package/dist/types/events.d.ts.map +1 -1
  128. package/dist/types/query.d.ts +4 -4
  129. package/dist/types/system.d.ts +1 -1
  130. package/dist/validate.d.ts +2 -0
  131. package/dist/validate.d.ts.map +1 -1
  132. package/dist/validate.js +2 -1
  133. package/dist/validate.js.map +1 -1
  134. package/dist/wire/body.d.ts +32 -9
  135. package/dist/wire/body.d.ts.map +1 -1
  136. package/dist/wire/body.js +69 -8
  137. package/dist/wire/body.js.map +1 -1
  138. package/dist/wire/index.d.ts +4 -4
  139. package/dist/wire/index.d.ts.map +1 -1
  140. package/dist/wire/index.js +3 -3
  141. package/dist/wire/index.js.map +1 -1
  142. package/dist/wire/record.d.ts +1 -8
  143. package/dist/wire/record.d.ts.map +1 -1
  144. package/dist/wire/record.js +0 -48
  145. package/dist/wire/record.js.map +1 -1
  146. package/dist/wire/request.d.ts +18 -18
  147. package/dist/wire/request.d.ts.map +1 -1
  148. package/dist/wire/request.js +41 -36
  149. package/dist/wire/request.js.map +1 -1
  150. package/package.json +1 -1
package/README.md CHANGED
@@ -74,14 +74,12 @@ await notes.patchContent(note.id, { title: 'Updated title' });
74
74
  const same = await notes.get(note.id);
75
75
 
76
76
  // Tag it
77
- await notes.associate(note.id, [{ kind: 'tag', label: 'favourite' }]);
77
+ await notes.editAssociations(note.id, [
78
+ { op: 'add', association: { kind: 'tag', label: 'favourite' } },
79
+ ]);
78
80
 
79
- // Or change several things at once — one version, one atomic write
80
- await notes.mutate(note.id, {
81
- contentPatch: { title: 'Final title' },
82
- permissions: [{ kind: 'anyone', label: 'read' }],
83
- unlisted: false,
84
- });
81
+ // Share it with everyone — each aspect of a record has its own verb
82
+ await notes.setAccess(note.id, [{ kind: 'anyone', label: 'read' }]);
85
83
 
86
84
  // Query the family. stack.query() reads across every type you can see instead.
87
85
  const { records: favourites } = await notes.query({
package/dist/access.d.ts CHANGED
@@ -1,47 +1,52 @@
1
1
  /**
2
2
  * Stack — Permission Resolution
3
3
  * -------------------------------------------------------
4
- * Implements the Permissions model from the spec: a pure predicate over a
5
- * Record's `permissions` field, with no dependency on a transport layer or
6
- * storage backend. Used by ScopedStack (see scoped-stack/scoped-stack.ts) to enforce
7
- * access control; exported standalone for callers that want the raw
8
- * predicate. Also the one home of the `_group` roster rules that predicate
9
- * and the grant path resolve against, and of the owner-acting-alone tier.
4
+ * The record-level permission rules, with no dependency on a transport
5
+ * layer or storage backend: how a grantee reaches an entity, the
6
+ * write-implies-read invariant, and the `_group` roster rules both layers
7
+ * resolve against, plus the owner-acting-alone tier. What a requester may
8
+ * do is a reach per verb, which scoped-stack/authority.ts builds from these.
10
9
  *
11
10
  * A permission set is read as data throughout, never as the type: it can
12
11
  * arrive from an import or a foreign server, where no compiler has seen
13
12
  * it. Every tier is affirmative, so an element read past its own kind —
14
13
  * or carrying no kind at all — confers nothing.
15
14
  */
16
- import type { AuthorityAssociation, DataAssociation, EntityId, GroupRole, RecordId, StackRecord } from './types/index.js';
15
+ import type { AuthorityAssociation, DataAssociation, EntityId, GroupRole, Grantee, RecordId, StackRecord } from './types/index.js';
17
16
  import type { ValidationError } from './validate.js';
18
- export type AccessMode = 'read' | 'write';
17
+ /** Resolves a Record by ID, for walks that need no full adapter. */
18
+ export type RecordResolver = (id: RecordId) => Promise<StackRecord | null>;
19
19
  /**
20
- * Resolves a Record by ID. Used to walk a `_group` Record's associations for
21
- * membership checks without requiring a full StackAdapter.
20
+ * The roles one entity holds on live `_group` rosters, keyed by group id,
21
+ * loaded on first use and at most once per operation, so no resolved role
22
+ * outlives the operation that read it — removal from a group must never go
23
+ * stale. See loadRosterRoles().
22
24
  */
23
- export type RecordResolver = (id: RecordId) => Promise<StackRecord | null>;
25
+ export type RosterRoles = () => Promise<ReadonlyMap<RecordId, GroupRole>>;
26
+ /**
27
+ * The elements beside which a `write` naming `grantee` carries its bit: its
28
+ * own `read`, or an `anyone`, which reaches the grantee already. The one
29
+ * statement of write-implies-read — validation and every reach ask it.
30
+ * See docs/spec/access-control.md § Write implies read.
31
+ */
32
+ export declare function readCompanions(grantee: Grantee): AuthorityAssociation[];
24
33
  /**
25
- * Whether an entity may read or write a Record. The entity is the
26
- * **subject**: record-level permissions are written about who data is for,
27
- * never the software carrying the request, whose authority is asked of the
28
- * principal. See docs/spec/access-control.md § Delegation: principal and subject.
34
+ * Whether a grantee reaches `entityId`, for both layers alike. Read as data,
35
+ * so an arm the write would refuse confers nothing; `member` is the wider
36
+ * role. See docs/spec/access-control.md § Refused at the write, and again at evaluation.
29
37
  */
30
- export declare function checkAccess(record: StackRecord, subjectEntityId: EntityId | null, ownerEntityId: EntityId | null, mode: AccessMode, resolveRecord: RecordResolver): Promise<boolean>;
38
+ export declare function granteeReaches(grantee: unknown, entityId: EntityId, opts: {
39
+ roles: RosterRoles;
40
+ allowGroup?: boolean;
41
+ }): Promise<boolean>;
31
42
  /**
32
- * Rejects a permission set where some grantee holds `write` with no `read`
33
- * beside it — the shape checkAccess() refuses to honor. A cross-element
43
+ * Rejects a permission set where some `write` stands without a
44
+ * readCompanions() element, which no reach would honor. A cross-element
34
45
  * invariant, so it is asked of the set the write would *produce*, the same
35
46
  * way hasGroupAdmin() reads its post-state below.
36
47
  * See docs/spec/access-control.md § Write implies read.
37
48
  */
38
49
  export declare function validatePermissions(permissions: AuthorityAssociation[] | undefined, path?: string): ValidationError[];
39
- /**
40
- * An entity's role on the `_group` roster `groupId` names. `memo`, when
41
- * given, is built per operation, so no resolved role outlives the operation
42
- * that resolved it — removal from a group must never go stale.
43
- */
44
- export declare function resolveGroupRole(groupId: RecordId, entityId: EntityId, resolveRecord: RecordResolver, memo?: Map<string, GroupRole | null>): Promise<GroupRole | null>;
45
50
  /** Whether a held role meets a grantee's: an admin is also a member. */
46
51
  export declare function roleSatisfies(held: GroupRole | null, required: GroupRole): boolean;
47
52
  /**
@@ -49,17 +54,10 @@ export declare function roleSatisfies(held: GroupRole | null, required: GroupRol
49
54
  * whose roster rules apply.
50
55
  */
51
56
  export declare function isGroupRecord(record: StackRecord): boolean;
52
- /**
53
- * Whether a Record is a roster anything may resolve against: a `_group`,
54
- * or an app's own `member` links would turn records into ACLs, and not
55
- * deleted, since deleting a Group withdraws it. Shared with the grant path
56
- * so the two cannot disagree. See docs/spec/identity.md § Group.
57
- */
58
- export declare function carriesRoster(record: StackRecord): boolean;
59
57
  /**
60
58
  * An entity's role on a `_group` roster; a matching `admin` wins regardless
61
59
  * of order. Reads associations alone and cannot tell whose they are, so
62
- * callers pass carriesRoster() first.
60
+ * callers ask it of a live `_group` only — see loadRosterRoles().
63
61
  */
64
62
  export declare function groupRoleFromAssociations(associations: DataAssociation[] | undefined, entityId: EntityId): GroupRole | null;
65
63
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EACf,QAAQ,EAER,SAAS,EAET,QAAQ,EAER,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,QAAQ,GAAG,IAAI,EAChC,aAAa,EAAE,QAAQ,GAAG,IAAI,EAC9B,IAAI,EAAE,UAAU,EAChB,aAAa,EAAE,cAAc,GAC5B,OAAO,CAAC,OAAO,CAAC,CA4BlB;AA0CD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,oBAAoB,EAAE,GAAG,SAAS,EAC/C,IAAI,SAAgB,GACnB,eAAe,EAAE,CAanB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,cAAc,EAC7B,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAS3B;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,EAAE,QAAQ,EAAE,SAAS,GAAG,OAAO,CAElF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,QAAQ,EAAE,QAAQ,GACjB,SAAS,GAAG,IAAI,CASlB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,OAAO,CAElF;AAkBD,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,OAAO,EAAE,QAAQ,GAChB,eAAe,EAAE,CAUnB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE;IAAE,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,EACpE,aAAa,EAAE,QAAQ,GACtB,OAAO,CAGT"}
1
+ {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EACf,QAAQ,EAER,SAAS,EACT,OAAO,EACP,QAAQ,EAER,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,oEAAoE;AACpE,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAE1E;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,oBAAoB,EAAE,CAKvE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD,OAAO,CAAC,OAAO,CAAC,CAelB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,oBAAoB,EAAE,GAAG,SAAS,EAC/C,IAAI,SAAgB,GACnB,eAAe,EAAE,CAcnB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,EAAE,QAAQ,EAAE,SAAS,GAAG,OAAO,CAElF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,QAAQ,EAAE,QAAQ,GACjB,SAAS,GAAG,IAAI,CASlB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,OAAO,CAElF;AAkBD,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,OAAO,EAAE,QAAQ,GAChB,eAAe,EAAE,CAUnB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE;IAAE,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,EACpE,aAAa,EAAE,QAAQ,GACtB,OAAO,CAGT"}
package/dist/access.js CHANGED
@@ -1,12 +1,11 @@
1
1
  /**
2
2
  * Stack — Permission Resolution
3
3
  * -------------------------------------------------------
4
- * Implements the Permissions model from the spec: a pure predicate over a
5
- * Record's `permissions` field, with no dependency on a transport layer or
6
- * storage backend. Used by ScopedStack (see scoped-stack/scoped-stack.ts) to enforce
7
- * access control; exported standalone for callers that want the raw
8
- * predicate. Also the one home of the `_group` roster rules that predicate
9
- * and the grant path resolve against, and of the owner-acting-alone tier.
4
+ * The record-level permission rules, with no dependency on a transport
5
+ * layer or storage backend: how a grantee reaches an entity, the
6
+ * write-implies-read invariant, and the `_group` roster rules both layers
7
+ * resolve against, plus the owner-acting-alone tier. What a requester may
8
+ * do is a reach per verb, which scoped-stack/authority.ts builds from these.
10
9
  *
11
10
  * A permission set is read as data throughout, never as the type: it can
12
11
  * arrive from an import or a foreign server, where no compiler has seen
@@ -14,115 +13,67 @@
14
13
  * or carrying no kind at all — confers nothing.
15
14
  */
16
15
  import { SYSTEM_TYPES } from './types/index.js';
17
- import { granteeEqual } from './associations/identity.js';
16
+ import { associationKey } from './associations/identity.js';
18
17
  import { baseIdOf } from './schema.js';
19
18
  /**
20
- * Whether an entity may read or write a Record. The entity is the
21
- * **subject**: record-level permissions are written about who data is for,
22
- * never the software carrying the request, whose authority is asked of the
23
- * principal. See docs/spec/access-control.md § Delegation: principal and subject.
24
- */
25
- export async function checkAccess(record, subjectEntityId, ownerEntityId, mode, resolveRecord) {
26
- if (subjectEntityId && subjectEntityId === ownerEntityId)
27
- return true;
28
- const perms = record.permissions;
29
- // No permissions = private.
30
- if (!perms || perms.length === 0)
31
- return false;
32
- // Two elements can name one group (a `member` and an `admin` read), and
33
- // the roster answers both the same way within one check.
34
- const roles = new Map();
35
- for (const p of perms) {
36
- if (p?.kind === 'anyone') {
37
- if (mode === 'read' && isWorldRead(p))
38
- return true;
39
- continue;
40
- }
41
- if (p?.kind !== 'permission' || !p.grantee)
42
- continue;
43
- if (p.label !== mode)
44
- continue;
45
- // The write bit is inert without read alongside it: the mutate surface
46
- // hands back the record it wrote and opens its whole history, so a
47
- // write element standing alone would withhold nothing. Refused at the
48
- // write by validatePermissions() and again here.
49
- // See docs/spec/access-control.md § Write implies read.
50
- if (mode === 'write' && !holdsRead(perms, p.grantee))
51
- continue;
52
- if (await granteeCovers(p.grantee, subjectEntityId, resolveRecord, roles))
53
- return true;
54
- }
55
- return false;
56
- }
57
- /**
58
- * Whether the set conveys read to this grantee — a `read` of its own, or
59
- * an `anyone` that already reaches everyone. Demanding a second, redundant
60
- * `read` beside the write would refuse a set that withholds nothing;
61
- * revoking the `anyone` is what the invariant catches instead.
19
+ * The elements beside which a `write` naming `grantee` carries its bit: its
20
+ * own `read`, or an `anyone`, which reaches the grantee already. The one
21
+ * statement of write-implies-read — validation and every reach ask it.
62
22
  * See docs/spec/access-control.md § Write implies read.
63
23
  */
64
- function holdsRead(permissions, grantee) {
65
- return permissions.some((p) => p?.kind === 'anyone'
66
- ? isWorldRead(p)
67
- : p?.kind === 'permission' && p.label === 'read' && granteeEqual(p.grantee, grantee));
68
- }
69
- /**
70
- * Whether an `anyone` element reaches the world. `read` is the only bit it
71
- * can carry, so one labelled otherwise names no reach — refused at the
72
- * write by validateAssociation() and read as nothing here.
73
- */
74
- function isWorldRead(association) {
75
- return association.label === 'read';
24
+ export function readCompanions(grantee) {
25
+ return [
26
+ { kind: 'permission', label: 'read', grantee },
27
+ { kind: 'anyone', label: 'read' },
28
+ ];
76
29
  }
77
30
  /**
78
- * Whether a grantee reaches this subject. `role: 'member'` is the wider
79
- * set — an admin satisfies it — and `role: 'admin'` the narrower.
31
+ * Whether a grantee reaches `entityId`, for both layers alike. Read as data,
32
+ * so an arm the write would refuse confers nothing; `member` is the wider
33
+ * role. See docs/spec/access-control.md § Refused at the write, and again at evaluation.
80
34
  */
81
- async function granteeCovers(grantee, subjectEntityId, resolveRecord, roles) {
82
- if (!subjectEntityId)
35
+ export async function granteeReaches(grantee, entityId, opts) {
36
+ if (!grantee || typeof grantee !== 'object')
83
37
  return false;
84
- if (grantee.kind === 'entity')
85
- return grantee.entityId === subjectEntityId;
86
- const role = await resolveGroupRole(grantee.groupId, subjectEntityId, resolveRecord, roles);
87
- return roleSatisfies(role, grantee.role);
38
+ const g = grantee;
39
+ switch (g.kind) {
40
+ case 'entity':
41
+ return !!g.entityId && g.entityId === entityId;
42
+ case 'group': {
43
+ if (opts.allowGroup === false || !g.groupId)
44
+ return false;
45
+ if (g.role !== 'member' && g.role !== 'admin')
46
+ return false;
47
+ const held = (await opts.roles()).get(g.groupId) ?? null;
48
+ return roleSatisfies(held, g.role);
49
+ }
50
+ default:
51
+ return false;
52
+ }
88
53
  }
89
54
  /**
90
- * Rejects a permission set where some grantee holds `write` with no `read`
91
- * beside it — the shape checkAccess() refuses to honor. A cross-element
55
+ * Rejects a permission set where some `write` stands without a
56
+ * readCompanions() element, which no reach would honor. A cross-element
92
57
  * invariant, so it is asked of the set the write would *produce*, the same
93
58
  * way hasGroupAdmin() reads its post-state below.
94
59
  * See docs/spec/access-control.md § Write implies read.
95
60
  */
96
61
  export function validatePermissions(permissions, path = 'permissions') {
97
62
  const list = permissions ?? [];
63
+ const held = new Set(list.map(associationKey));
98
64
  const errors = [];
99
65
  list.forEach((p, i) => {
100
66
  if (p?.kind !== 'permission' || p.label !== 'write')
101
67
  return;
102
- if (holdsRead(list, p.grantee))
68
+ if (readCompanions(p.grantee).some((c) => held.has(associationKey(c))))
103
69
  return;
104
70
  errors.push({
105
71
  path: `${path}[${i}]`,
106
- message: 'write requires read: a write-holder reaches the record and its history through the mutate surface, so a `write` element with no `read` for the same grantee withholds nothing. Grant `read` and `write` together: grantAccess(id, [read, write])',
72
+ message: 'write requires read: a write-holder reaches the record and its history through the mutate surface, so a `write` element with no `read` for the same grantee withholds nothing. Grant `read` and `write` together in one editAccess() or setAccess() call',
107
73
  });
108
74
  });
109
75
  return errors;
110
76
  }
111
- /**
112
- * An entity's role on the `_group` roster `groupId` names. `memo`, when
113
- * given, is built per operation, so no resolved role outlives the operation
114
- * that resolved it — removal from a group must never go stale.
115
- */
116
- export async function resolveGroupRole(groupId, entityId, resolveRecord, memo) {
117
- const key = `${groupId}:${entityId}`;
118
- const cached = memo?.get(key);
119
- if (cached !== undefined)
120
- return cached;
121
- const group = await resolveRecord(groupId);
122
- const role = group && carriesRoster(group) ? groupRoleFromAssociations(group.associations, entityId) : null;
123
- memo?.set(key, role);
124
- return role;
125
- }
126
77
  /** Whether a held role meets a grantee's: an admin is also a member. */
127
78
  export function roleSatisfies(held, required) {
128
79
  return required === 'admin' ? held === 'admin' : held !== null;
@@ -134,19 +85,10 @@ export function roleSatisfies(held, required) {
134
85
  export function isGroupRecord(record) {
135
86
  return baseIdOf(record.typeId) === SYSTEM_TYPES.GROUP;
136
87
  }
137
- /**
138
- * Whether a Record is a roster anything may resolve against: a `_group`,
139
- * or an app's own `member` links would turn records into ACLs, and not
140
- * deleted, since deleting a Group withdraws it. Shared with the grant path
141
- * so the two cannot disagree. See docs/spec/identity.md § Group.
142
- */
143
- export function carriesRoster(record) {
144
- return isGroupRecord(record) && !record.deletedAt;
145
- }
146
88
  /**
147
89
  * An entity's role on a `_group` roster; a matching `admin` wins regardless
148
90
  * of order. Reads associations alone and cannot tell whose they are, so
149
- * callers pass carriesRoster() first.
91
+ * callers ask it of a live `_group` only — see loadRosterRoles().
150
92
  */
151
93
  export function groupRoleFromAssociations(associations, entityId) {
152
94
  let role = null;
@@ -1 +1 @@
1
- {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAahD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAWvC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAmB,EACnB,eAAgC,EAChC,aAA8B,EAC9B,IAAgB,EAChB,aAA6B;IAE7B,IAAI,eAAe,IAAI,eAAe,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IAEtE,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IAEjC,4BAA4B;IAC5B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAE/C,wEAAwE;IACxE,yDAAyD;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzB,IAAI,IAAI,KAAK,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YACnD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,EAAE,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,OAAO;YAAE,SAAS;QACrD,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;YAAE,SAAS;QAC/B,uEAAuE;QACvE,mEAAmE;QACnE,sEAAsE;QACtE,iDAAiD;QACjD,wDAAwD;QACxD,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;YAAE,SAAS;QAC/D,IAAI,MAAM,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IACzF,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,WAAmC,EAAE,OAAgB;IACtE,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5B,CAAC,EAAE,IAAI,KAAK,QAAQ;QAClB,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CACvF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,WAAiC;IACpD,OAAO,WAAW,CAAC,KAAK,KAAK,MAAM,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,aAAa,CAC1B,OAAgB,EAChB,eAAgC,EAChC,aAA6B,EAC7B,KAAoC;IAEpC,IAAI,CAAC,eAAe;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,QAAQ,KAAK,eAAe,CAAC;IAC3E,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;IAC5F,OAAO,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,WAA+C,EAC/C,IAAI,GAAG,aAAa;IAEpB,MAAM,IAAI,GAAG,WAAW,IAAI,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpB,IAAI,CAAC,EAAE,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;YAAE,OAAO;QAC5D,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;YAAE,OAAO;QACvC,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG;YACrB,OAAO,EACL,kPAAkP;SACrP,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAiB,EACjB,QAAkB,EAClB,aAA6B,EAC7B,IAAoC;IAEpC,MAAM,GAAG,GAAG,GAAG,OAAO,IAAI,QAAQ,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GACR,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjG,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,IAAsB,EAAE,QAAmB;IACvE,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,YAAY,CAAC,KAAK,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,YAA2C,EAC3C,QAAkB;IAElB,IAAI,IAAI,GAAqB,IAAI,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,YAAY,IAAI,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC9F,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;gBAAE,OAAO,OAAO,CAAC;YACxC,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ;gBAAE,IAAI,GAAG,QAAQ,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,YAA2C;IACvE,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,SAAS,uBAAuB,CAC9B,WAAwB;IAExB,OAAO,CACL,WAAW,CAAC,IAAI,KAAK,cAAc;QACnC,WAAW,CAAC,KAAK,KAAK,OAAO;QAC7B,WAAW,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CACrC,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,eAAe,CAC7B,YAA2C,EAC3C,OAAiB;IAEjB,MAAM,IAAI,GAAG,YAAY,IAAI,EAAE,CAAC;IAChC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,OAAO,CACnE,CAAC;IACF,IAAI,YAAY;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO;QACL,GAAG,IAAI;QACP,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;KACxF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAoE,EACpE,aAAuB;IAEvB,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;IAC1F,OAAO,WAAW,KAAK,aAAa,IAAI,KAAK,CAAC,SAAS,KAAK,aAAa,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"combine.d.ts","sourceRoot":"","sources":["../../src/adapter/combine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAEV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,gBAAgB,CAAC;CACxB,GAAG,YAAY,CAsEf"}
1
+ {"version":3,"file":"combine.d.ts","sourceRoot":"","sources":["../../src/adapter/combine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAGV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAGjB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,gBAAgB,CAAC;CACxB,GAAG,YAAY,CA8Ef"}
@@ -25,11 +25,16 @@ export function combineAdapters(parts) {
25
25
  },
26
26
  createRecord: (r, opts) => parts.record.createRecord(r, opts),
27
27
  getRecord: (id) => parts.record.getRecord(id),
28
- mutateRecord: (id, changes, opts) => parts.record.mutateRecord(id, changes, opts),
28
+ patchContent: (id, patch, opts) => parts.record.patchContent(id, patch, opts),
29
29
  deleteRecord: (id, opts) => parts.record.deleteRecord(id, opts),
30
30
  undeleteRecord: (id, opts) => parts.record.undeleteRecord(id, opts),
31
31
  queryRecords: (q) => parts.record.queryRecords(q),
32
- amendAssociations: (id, changes, opts) => parts.record.amendAssociations(id, changes, opts),
32
+ setParent: (id, parentId, opts) => parts.record.setParent(id, parentId, opts),
33
+ setUnlisted: (id, unlisted, opts) => parts.record.setUnlisted(id, unlisted, opts),
34
+ editAssociations: (id, edits, opts) => parts.record.editAssociations(id, edits, opts),
35
+ setAssociations: (id, list, opts) => parts.record.setAssociations(id, list, opts),
36
+ editAccess: (id, edits, opts) => parts.record.editAccess(id, edits, opts),
37
+ setAccess: (id, list, opts) => parts.record.setAccess(id, list, opts),
33
38
  getVersions: (id, query) => parts.record.getVersions(id, query),
34
39
  getVersion: (id, v) => parts.record.getVersion(id, v),
35
40
  saveVersion: (id, v) => parts.record.saveVersion(id, v),
@@ -43,7 +48,10 @@ export function combineAdapters(parts) {
43
48
  // absent on the combined adapter, rather than silently becoming
44
49
  // "supported" via a wrapper that forwards to a missing method.
45
50
  ...(parts.record.deleteUnreferencedAttachmentRecords && {
46
- deleteUnreferencedAttachmentRecords: (fileId, metadataTypeIds) => parts.record.deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds),
51
+ deleteUnreferencedAttachmentRecords: (fileId, metadataBaseId) => parts.record.deleteUnreferencedAttachmentRecords(fileId, metadataBaseId),
52
+ }),
53
+ ...(parts.record.queryReadable && {
54
+ queryReadable: (q, reach) => parts.record.queryReadable(q, reach),
47
55
  }),
48
56
  // Forwarded on the same terms, and it decides more than it looks:
49
57
  // Stack.relaysChanges reads its presence, so dropping it here would
@@ -1 +1 @@
1
- {"version":3,"file":"combine.js","sourceRoot":"","sources":["../../src/adapter/combine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"combine.js","sourceRoot":"","sources":["../../src/adapter/combine.ts"],"names":[],"mappings":"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"}
@@ -7,11 +7,13 @@
7
7
  * adapters). Types an app reads too, such as StackCapabilities, live in
8
8
  * the root entry point instead: every export has exactly one home.
9
9
  */
10
- export type { StackAdapter, StackRecordAdapter, StackBlobAdapter, BlobInfo, BumpVersionOptions, SnapshotOptions, JournalEntryInput, JournalOptions, MutateOptions, } from '../types/index.js';
11
- export { OwnerMismatchError } from '../errors.js';
10
+ export type { AdapterQuery, PlainQuery, AdapterSort, StackAdapter, StackRecordAdapter, StackBlobAdapter, BlobInfo, SnapshotOptions, JournalEntryInput, JournalOptions, MutateOptions, AspectWriteOptions, ElementWriteOptions, Reach, } from '../types/index.js';
11
+ export { sameAssociationSet } from '../associations/identity.js';
12
+ export { ElementSetMovedError, OwnerMismatchError, softDeletedRefusal } from '../errors.js';
12
13
  export { combineAdapters } from './combine.js';
13
- export { assertQueryCapabilities, assertSortCapability, assertValidSort, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '../query-validation.js';
14
+ export { inReach } from './reach.js';
15
+ export type { ReachedRecord } from './reach.js';
16
+ export { assertQueryCapabilities, assertValidSort, resolveQuery, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '../query-validation.js';
14
17
  export { contentSortEntry, contentSortKey, compareSortEntries, constantSortEntry, sortConstant, } from './sort.js';
15
18
  export type { SortEntry } from './sort.js';
16
- export { AUTHORITY_ASSOCIATION_KINDS, partitionAssociations } from '../associations/identity.js';
17
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,GACN,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
@@ -7,9 +7,10 @@
7
7
  * adapters). Types an app reads too, such as StackCapabilities, live in
8
8
  * the root entry point instead: every export has exactly one home.
9
9
  */
10
- export { OwnerMismatchError } from '../errors.js';
10
+ export { sameAssociationSet } from '../associations/identity.js';
11
+ export { ElementSetMovedError, OwnerMismatchError, softDeletedRefusal } from '../errors.js';
11
12
  export { combineAdapters } from './combine.js';
12
- export { assertQueryCapabilities, assertSortCapability, assertValidSort, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '../query-validation.js';
13
+ export { inReach } from './reach.js';
14
+ export { assertQueryCapabilities, assertValidSort, resolveQuery, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '../query-validation.js';
13
15
  export { contentSortEntry, contentSortKey, compareSortEntries, constantSortEntry, sortConstant, } from './sort.js';
14
- export { AUTHORITY_ASSOCIATION_KINDS, partitionAssociations } from '../associations/identity.js';
15
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkBH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Matching a Reach in memory
3
+ * -------------------------------------------------------
4
+ * The one reading of a reach in JavaScript: MemoryAdapter matches a query
5
+ * with it, and ScopedStack asks it of each Record a verb would act on. A
6
+ * SQL adapter states the same conditions in its own query language. See
7
+ * docs/spec/access-control.md § Reach.
8
+ */
9
+ import type { Reach, StackRecord } from '../types/index.js';
10
+ /** What a reach asks of a Record — all a record yet to be created has to show. */
11
+ export type ReachedRecord = Pick<StackRecord, 'typeId' | 'createdBy' | 'permissions'>;
12
+ /** Whether `record` is inside the reach, matched by its family. */
13
+ export declare function inReach(record: ReachedRecord, reach: Reach): boolean;
14
+ //# sourceMappingURL=reach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../../src/adapter/reach.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE5D,kFAAkF;AAClF,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC,CAAC;AAEtF,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAmBpE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Matching a Reach in memory
3
+ * -------------------------------------------------------
4
+ * The one reading of a reach in JavaScript: MemoryAdapter matches a query
5
+ * with it, and ScopedStack asks it of each Record a verb would act on. A
6
+ * SQL adapter states the same conditions in its own query language. See
7
+ * docs/spec/access-control.md § Reach.
8
+ */
9
+ import { associationKey } from '../associations/identity.js';
10
+ import { baseIdOf } from '../schema.js';
11
+ /** Whether `record` is inside the reach, matched by its family. */
12
+ export function inReach(record, reach) {
13
+ const family = baseIdOf(record.typeId);
14
+ if (reach.fence && !reach.fence.includes(family))
15
+ return false;
16
+ const within = reach.within;
17
+ if (!within)
18
+ return true;
19
+ if (within.baseIds.includes(family))
20
+ return true;
21
+ const authored = within.authored;
22
+ if (authored &&
23
+ authored.baseIds.includes(family) &&
24
+ record.createdBy?.subjectId === authored.subjectId) {
25
+ return true;
26
+ }
27
+ const held = new Set((record.permissions ?? []).map(associationKey));
28
+ held.delete(undefined);
29
+ return within.permissions.some((set) => set.length > 0 && set.every((element) => held.has(associationKey(element))));
30
+ }
31
+ //# sourceMappingURL=reach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reach.js","sourceRoot":"","sources":["../../src/adapter/reach.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAMxC,mEAAmE;AACnE,MAAM,UAAU,OAAO,CAAC,MAAqB,EAAE,KAAY;IACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,IACE,QAAQ;QACR,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;QACjC,MAAM,CAAC,SAAS,EAAE,SAAS,KAAK,QAAQ,CAAC,SAAS,EAClD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAC5B,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CACrF,CAAC;AACJ,CAAC"}
@@ -9,19 +9,17 @@
9
9
  */
10
10
  import type { Association, AssociationChange, AssociationEdit, AuthorityAssociation, DataAssociation, Grantee, RelationshipTarget } from '../types/index.js';
11
11
  /**
12
- * Association identity — what dissociate() matches and a repeat associate()
12
+ * Association identity — what a remove edit matches and a repeat add
13
13
  * collides with; the SQLite adapter's primary key. An attachment's
14
14
  * `attachmentRecordId` annotates a reference rather than naming one, so it
15
15
  * is outside; a group grantee's `role` names a different set of people, so
16
16
  * it is inside. See docs/spec/data-model.md § Associations.
17
17
  */
18
+ export declare function associationKey(a: Association): string | undefined;
19
+ /** Whether two associations are the same one — see associationKey(). */
18
20
  export declare function associationEqual(a: Association, b: Association): boolean;
19
- /**
20
- * Structural equality per grantee arm. `role` is required, so there is no
21
- * absent-means-any spelling to normalize.
22
- */
23
21
  export declare function granteeEqual(a: Grantee, b: Grantee): boolean;
24
- /** Structural equality per target arm — what dissociate() matches on. */
22
+ /** Structural equality per target arm — what a remove edit matches on. */
25
23
  export declare function targetEqual(a: RelationshipTarget, b: RelationshipTarget): boolean;
26
24
  /**
27
25
  * Identity plus every annotation outside it, so a write that only re-points
@@ -30,35 +28,26 @@ export declare function targetEqual(a: RelationshipTarget, b: RelationshipTarget
30
28
  * this decides whether it says anything new.
31
29
  */
32
30
  export declare function associationIdentical(a: Association, b: Association): boolean;
33
- /**
34
- * The association kinds that carry authority. Every other kind is data.
35
- * Storage that partitions by kind in a query reads this list rather than
36
- * restating it, so a new authority kind can't fall into the data half.
37
- */
38
- export declare const AUTHORITY_ASSOCIATION_KINDS: readonly AuthorityAssociation['kind'][];
39
31
  /**
40
32
  * Whether an association carries authority rather than data — the partition
41
33
  * `StackRecord.permissions` and `StackRecord.associations` project, and the
42
- * line `associate()`/`dissociate()` refuse to cross.
34
+ * line the association verbs refuse to cross.
43
35
  * See docs/spec/access-control.md § Record-level permissions.
44
36
  */
45
37
  export declare function isAuthorityAssociation(a: Association): a is AuthorityAssociation;
46
38
  /**
47
- * One stored association set split into the two the record presents. An app
48
- * editing tags never sees the authority half, so it cannot drop it.
39
+ * What replacing a record's set with `after` moves, one tagged edit each.
40
+ * An entry matching an existing identity displaced it: a `repoint`,
41
+ * carrying the prior association in full as `previous` — the only place
42
+ * an overwritten `attachmentRecordId` survives.
43
+ * See docs/spec/journal.md § The entry.
49
44
  */
50
- export declare function partitionAssociations(associations: Association[]): {
51
- associations: DataAssociation[];
52
- permissions: AuthorityAssociation[];
53
- };
45
+ export declare function associationDelta(before: readonly Association[], after: readonly Association[]): AssociationChange[];
54
46
  /**
55
- * What a `changes.associations` list moves against a record's current set,
56
- * one tagged edit each. An entry matching an existing identity displaced it:
57
- * a `repoint`, carrying the prior association in full as `previous` — the
58
- * only place an overwritten `attachmentRecordId` survives.
59
- * See docs/spec/journal.md § The entry.
47
+ * Whether two sets hold the same elements, annotations included, in any
48
+ * order — what an element-set write's `expectSet` is compared by.
60
49
  */
61
- export declare function associationDelta(before: Association[], after: Association[]): AssociationChange[];
50
+ export declare function sameAssociationSet(a: readonly Association[], b: readonly Association[]): boolean;
62
51
  /**
63
52
  * The two flat lists a change frame carries, derived from the tagged list.
64
53
  * The feed reports what is true now, so a `repoint` appears only under its
@@ -70,24 +59,11 @@ export declare function feedAssociationDelta(changes: AssociationChange[]): {
70
59
  added: DataAssociation[];
71
60
  removed: DataAssociation[];
72
61
  };
62
+ /** The elements an edit list adds. */
63
+ export declare function addsOf(edits: readonly AssociationEdit[]): Association[];
73
64
  /**
74
- * An association edit list travels one surface: authority and data share
75
- * storage and a delta, and never share a call. Refused here so every
76
- * adapter inherits the same rule.
77
- * See docs/spec/access-control.md § Storage unifies; the API does not.
78
- */
79
- export declare function assertOneSurface(changes: readonly AssociationEdit[]): void;
80
- /**
81
- * The set a list of edits leaves behind: removes first, then adds, an add
82
- * landing on an identity already held in place. Adds naming one identity
83
- * collapse, last wins; an add outlives a remove of the same identity.
84
- * See docs/spec/adapters.md § Amending associations.
65
+ * The set a list of edits leaves behind, an add landing on an identity
66
+ * already held in place. See docs/spec/adapters.md § Editing associations.
85
67
  */
86
68
  export declare function applyAssociationEdits(current: Association[], changes: readonly AssociationEdit[]): Association[];
87
- /**
88
- * The same op over every element of a list — what associate(),
89
- * dissociate(), grantAccess() and revokeAccess() hand to the amend verb
90
- * each is one half of.
91
- */
92
- export declare function editsOf(op: 'add' | 'remove', associations: Association[]): AssociationEdit[];
93
69
  //# sourceMappingURL=identity.d.ts.map