@haverstack/core 0.42.0 → 0.44.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 (185) hide show
  1. package/README.md +6 -9
  2. package/dist/access.d.ts +36 -34
  3. package/dist/access.d.ts.map +1 -1
  4. package/dist/access.js +51 -100
  5. package/dist/access.js.map +1 -1
  6. package/dist/adapter/combine.d.ts.map +1 -1
  7. package/dist/adapter/combine.js +14 -7
  8. package/dist/adapter/combine.js.map +1 -1
  9. package/dist/adapter/copy.d.ts +27 -0
  10. package/dist/adapter/copy.d.ts.map +1 -0
  11. package/dist/adapter/copy.js +105 -0
  12. package/dist/adapter/copy.js.map +1 -0
  13. package/dist/adapter/index.d.ts +9 -4
  14. package/dist/adapter/index.d.ts.map +1 -1
  15. package/dist/adapter/index.js +6 -3
  16. package/dist/adapter/index.js.map +1 -1
  17. package/dist/adapter/reach.d.ts +14 -0
  18. package/dist/adapter/reach.d.ts.map +1 -0
  19. package/dist/adapter/reach.js +31 -0
  20. package/dist/adapter/reach.js.map +1 -0
  21. package/dist/adapter/seed.d.ts +13 -0
  22. package/dist/adapter/seed.d.ts.map +1 -0
  23. package/dist/adapter/seed.js +26 -0
  24. package/dist/adapter/seed.js.map +1 -0
  25. package/dist/associations/identity.d.ts +18 -42
  26. package/dist/associations/identity.d.ts.map +1 -1
  27. package/dist/associations/identity.js +60 -84
  28. package/dist/associations/identity.js.map +1 -1
  29. package/dist/associations/validation.d.ts +1 -8
  30. package/dist/associations/validation.d.ts.map +1 -1
  31. package/dist/associations/validation.js +3 -30
  32. package/dist/associations/validation.js.map +1 -1
  33. package/dist/collection.d.ts +7 -11
  34. package/dist/collection.d.ts.map +1 -1
  35. package/dist/collection.js +24 -34
  36. package/dist/collection.js.map +1 -1
  37. package/dist/content-match.d.ts +2 -2
  38. package/dist/content-match.js +2 -2
  39. package/dist/errors.d.ts +41 -12
  40. package/dist/errors.d.ts.map +1 -1
  41. package/dist/errors.js +56 -15
  42. package/dist/errors.js.map +1 -1
  43. package/dist/grants.d.ts +18 -6
  44. package/dist/grants.d.ts.map +1 -1
  45. package/dist/grants.js +48 -37
  46. package/dist/grants.js.map +1 -1
  47. package/dist/hash.d.ts +8 -0
  48. package/dist/hash.d.ts.map +1 -0
  49. package/dist/hash.js +14 -0
  50. package/dist/hash.js.map +1 -0
  51. package/dist/index.d.ts +6 -6
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/install.d.ts +29 -5
  56. package/dist/install.d.ts.map +1 -1
  57. package/dist/install.js +69 -44
  58. package/dist/install.js.map +1 -1
  59. package/dist/lazy.d.ts +13 -0
  60. package/dist/lazy.d.ts.map +1 -0
  61. package/dist/lazy.js +27 -0
  62. package/dist/lazy.js.map +1 -0
  63. package/dist/merge.d.ts +1 -1
  64. package/dist/merge.js +1 -1
  65. package/dist/query-validation.d.ts +41 -28
  66. package/dist/query-validation.d.ts.map +1 -1
  67. package/dist/query-validation.js +85 -71
  68. package/dist/query-validation.js.map +1 -1
  69. package/dist/record-changes.d.ts +12 -36
  70. package/dist/record-changes.d.ts.map +1 -1
  71. package/dist/record-changes.js +9 -98
  72. package/dist/record-changes.js.map +1 -1
  73. package/dist/schema.d.ts.map +1 -1
  74. package/dist/schema.js +2 -5
  75. package/dist/schema.js.map +1 -1
  76. package/dist/scoped-stack/authority-snapshot.d.ts +37 -0
  77. package/dist/scoped-stack/authority-snapshot.d.ts.map +1 -0
  78. package/dist/scoped-stack/authority-snapshot.js +49 -0
  79. package/dist/scoped-stack/authority-snapshot.js.map +1 -0
  80. package/dist/scoped-stack/authority.d.ts +69 -51
  81. package/dist/scoped-stack/authority.d.ts.map +1 -1
  82. package/dist/scoped-stack/authority.js +189 -153
  83. package/dist/scoped-stack/authority.js.map +1 -1
  84. package/dist/scoped-stack/family-policy.d.ts +59 -0
  85. package/dist/scoped-stack/family-policy.d.ts.map +1 -0
  86. package/dist/scoped-stack/family-policy.js +68 -0
  87. package/dist/scoped-stack/family-policy.js.map +1 -0
  88. package/dist/scoped-stack/scoped-stack.d.ts +73 -86
  89. package/dist/scoped-stack/scoped-stack.d.ts.map +1 -1
  90. package/dist/scoped-stack/scoped-stack.js +279 -337
  91. package/dist/scoped-stack/scoped-stack.js.map +1 -1
  92. package/dist/scoped-stack/subscription.d.ts +14 -33
  93. package/dist/scoped-stack/subscription.d.ts.map +1 -1
  94. package/dist/scoped-stack/subscription.js +16 -57
  95. package/dist/scoped-stack/subscription.js.map +1 -1
  96. package/dist/stack/arguments.d.ts +27 -0
  97. package/dist/stack/arguments.d.ts.map +1 -0
  98. package/dist/stack/arguments.js +53 -0
  99. package/dist/stack/arguments.js.map +1 -0
  100. package/dist/stack/attachments.d.ts +1 -1
  101. package/dist/stack/attachments.js +1 -1
  102. package/dist/stack/changes.d.ts +4 -10
  103. package/dist/stack/changes.d.ts.map +1 -1
  104. package/dist/stack/changes.js +8 -8
  105. package/dist/stack/changes.js.map +1 -1
  106. package/dist/stack/client.d.ts +34 -58
  107. package/dist/stack/client.d.ts.map +1 -1
  108. package/dist/stack/field-rules.d.ts +27 -0
  109. package/dist/stack/field-rules.d.ts.map +1 -0
  110. package/dist/stack/field-rules.js +50 -0
  111. package/dist/stack/field-rules.js.map +1 -0
  112. package/dist/stack/install-app.d.ts +2 -2
  113. package/dist/stack/install-app.d.ts.map +1 -1
  114. package/dist/stack/install-app.js +16 -14
  115. package/dist/stack/install-app.js.map +1 -1
  116. package/dist/stack/integrity.d.ts +9 -53
  117. package/dist/stack/integrity.d.ts.map +1 -1
  118. package/dist/stack/integrity.js +47 -107
  119. package/dist/stack/integrity.js.map +1 -1
  120. package/dist/stack/limits.d.ts +1 -1
  121. package/dist/stack/limits.js +1 -1
  122. package/dist/stack/reads.d.ts +9 -5
  123. package/dist/stack/reads.d.ts.map +1 -1
  124. package/dist/stack/reads.js +24 -4
  125. package/dist/stack/reads.js.map +1 -1
  126. package/dist/stack/stack.d.ts +84 -79
  127. package/dist/stack/stack.d.ts.map +1 -1
  128. package/dist/stack/stack.js +236 -306
  129. package/dist/stack/stack.js.map +1 -1
  130. package/dist/stack/system-types.d.ts.map +1 -1
  131. package/dist/stack/system-types.js +1 -3
  132. package/dist/stack/system-types.js.map +1 -1
  133. package/dist/stack/type-grants.d.ts.map +1 -1
  134. package/dist/stack/type-grants.js +9 -6
  135. package/dist/stack/type-grants.js.map +1 -1
  136. package/dist/stack/write-expectation.d.ts +13 -12
  137. package/dist/stack/write-expectation.d.ts.map +1 -1
  138. package/dist/stack/write-expectation.js.map +1 -1
  139. package/dist/stack/write-gate.d.ts +23 -0
  140. package/dist/stack/write-gate.d.ts.map +1 -0
  141. package/dist/stack/write-gate.js +17 -0
  142. package/dist/stack/write-gate.js.map +1 -0
  143. package/dist/testing.d.ts +37 -24
  144. package/dist/testing.d.ts.map +1 -1
  145. package/dist/testing.js +176 -153
  146. package/dist/testing.js.map +1 -1
  147. package/dist/types/adapter.d.ts +115 -43
  148. package/dist/types/adapter.d.ts.map +1 -1
  149. package/dist/types/associations.d.ts +1 -17
  150. package/dist/types/associations.d.ts.map +1 -1
  151. package/dist/types/associations.js +1 -8
  152. package/dist/types/associations.js.map +1 -1
  153. package/dist/types/capabilities.d.ts +8 -8
  154. package/dist/types/capabilities.d.ts.map +1 -1
  155. package/dist/types/events.d.ts +3 -8
  156. package/dist/types/events.d.ts.map +1 -1
  157. package/dist/types/query.d.ts +4 -4
  158. package/dist/types/system.d.ts +2 -8
  159. package/dist/types/system.d.ts.map +1 -1
  160. package/dist/types/system.js.map +1 -1
  161. package/dist/validate.d.ts +2 -0
  162. package/dist/validate.d.ts.map +1 -1
  163. package/dist/validate.js +2 -1
  164. package/dist/validate.js.map +1 -1
  165. package/dist/wire/body.d.ts +32 -9
  166. package/dist/wire/body.d.ts.map +1 -1
  167. package/dist/wire/body.js +69 -8
  168. package/dist/wire/body.js.map +1 -1
  169. package/dist/wire/index.d.ts +4 -4
  170. package/dist/wire/index.d.ts.map +1 -1
  171. package/dist/wire/index.js +3 -3
  172. package/dist/wire/index.js.map +1 -1
  173. package/dist/wire/record.d.ts +1 -8
  174. package/dist/wire/record.d.ts.map +1 -1
  175. package/dist/wire/record.js +0 -48
  176. package/dist/wire/record.js.map +1 -1
  177. package/dist/wire/request.d.ts +18 -18
  178. package/dist/wire/request.d.ts.map +1 -1
  179. package/dist/wire/request.js +41 -36
  180. package/dist/wire/request.js.map +1 -1
  181. package/package.json +1 -1
  182. package/dist/stack/identity-bindings.d.ts +0 -13
  183. package/dist/stack/identity-bindings.d.ts.map +0 -1
  184. package/dist/stack/identity-bindings.js +0 -36
  185. package/dist/stack/identity-bindings.js.map +0 -1
package/README.md CHANGED
@@ -34,7 +34,6 @@ const keyPath = './my-stack.key.json'; // see "Key custody" under Identity for w
34
34
  const adapter = await LocalAdapter.open({
35
35
  path: dbPath,
36
36
  create: 'ifMissing',
37
- timezone: 'America/New_York',
38
37
  ownerEntityId: async () => {
39
38
  const { did, privateKey } = await generateDidKeypair();
40
39
  await writeFile(keyPath, JSON.stringify(await exportDidPrivateKeyJwk(privateKey)));
@@ -74,14 +73,12 @@ await notes.patchContent(note.id, { title: 'Updated title' });
74
73
  const same = await notes.get(note.id);
75
74
 
76
75
  // Tag it
77
- await notes.associate(note.id, [{ kind: 'tag', label: 'favourite' }]);
76
+ await notes.editAssociations(note.id, [
77
+ { op: 'add', association: { kind: 'tag', label: 'favourite' } },
78
+ ]);
78
79
 
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
- });
80
+ // Share it with everyone — each aspect of a record has its own verb
81
+ await notes.setAccess(note.id, [{ kind: 'anyone', label: 'read' }]);
85
82
 
86
83
  // Query the family. stack.query() reads across every type you can see instead.
87
84
  const { records: favourites } = await notes.query({
@@ -206,7 +203,7 @@ export const manifest: AppManifest = {
206
203
 
207
204
  **When the owner runs the app**, it opens with `Stack.open(adapter, { migrations })`, calls `install(stack)`, then `new Library(stack)`. A server hands each requester `new Library(stack.asActor(session))`.
208
205
 
209
- **When the app runs elsewhere** — holding its own key and reaching the stack through `APIAdapter` — it can't define types, so it asks: `adapter.requestInstall(manifest)` resolves to `pending` until the owner approves. The owner reviews `stack.planInstall(manifest, { did })` and applies it with `stack.installApp(plan)`, which defines the types, registers the app's key and grants it the requests. The app still opens with its `migrations`, then builds `new Library(stack)` over its own connection. See [App installs](https://github.com/haverstack/core/blob/main/docs/spec/apps.md).
206
+ **When the app runs elsewhere** — holding its own key and reaching the stack through `APIAdapter` — it can't define types, so it asks: `adapter.requestInstall(manifest)` resolves to `pending` until the owner approves. The owner reviews `stack.planInstall(manifest, { did })` and applies it with `stack.installApp(manifest, { did, fingerprint: plan.fingerprint })`, which defines the types, registers the app's key and grants it the requests. The app still opens with its `migrations`, then builds `new Library(stack)` over its own connection. See [App installs](https://github.com/haverstack/core/blob/main/docs/spec/apps.md).
210
207
 
211
208
  **Name content with an interface**, as above. `ContentOf` flattens its result, so a `type` alias shows the expanded object in editor hovers, while an interface keeps its name. typescript-eslint's recommended `no-empty-object-type` rule rejects an empty interface; its `allowInterfaces: 'with-single-extends'` option admits this one.
212
209
 
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 { Actor, AuthorityAssociation, DataAssociation, EntityId, GroupRole, Grantee, RecordId, StackRecord, TypeId } 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
  /**
@@ -69,8 +67,12 @@ export declare function groupRoleFromAssociations(associations: DataAssociation[
69
67
  * the last. Reads associations alone, like groupRoleFromAssociations().
70
68
  */
71
69
  export declare function hasGroupAdmin(associations: DataAssociation[] | undefined): boolean;
72
- /** Bootstraps a `_group` record's first admin at create time. */
73
- export declare function stampGroupAdmin(associations: DataAssociation[] | undefined, creator: EntityId): DataAssociation[];
70
+ /**
71
+ * The associations a create stores: as given, plus a `_group`'s creator —
72
+ * the owner when none is named — stamped as its first admin.
73
+ * See docs/spec/identity.md § Group.
74
+ */
75
+ export declare function createAssociations(typeId: TypeId, associations: DataAssociation[] | undefined, createdBy: Actor | undefined, ownerEntityId: EntityId): DataAssociation[] | undefined;
74
76
  /**
75
77
  * Whether a request is the stack owner acting alone — the tier behind every
76
78
  * unconditional owner authority. Being the owner is never sufficient on its
@@ -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,EACV,KAAK,EAEL,oBAAoB,EACpB,eAAe,EACf,QAAQ,EAER,SAAS,EACT,OAAO,EACP,QAAQ,EAER,WAAW,EACX,MAAM,EACP,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;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,SAAS,EAAE,KAAK,GAAG,SAAS,EAC5B,aAAa,EAAE,QAAQ,GACtB,eAAe,EAAE,GAAG,SAAS,CAG/B;AAiBD;;;;;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;
@@ -180,8 +122,17 @@ function isGroupAdminAssociation(association) {
180
122
  association.label === 'admin' &&
181
123
  association.target.kind === 'entity');
182
124
  }
183
- /** Bootstraps a `_group` record's first admin at create time. */
184
- export function stampGroupAdmin(associations, creator) {
125
+ /**
126
+ * The associations a create stores: as given, plus a `_group`'s creator —
127
+ * the owner when none is named — stamped as its first admin.
128
+ * See docs/spec/identity.md § Group.
129
+ */
130
+ export function createAssociations(typeId, associations, createdBy, ownerEntityId) {
131
+ if (baseIdOf(typeId) !== SYSTEM_TYPES.GROUP)
132
+ return associations;
133
+ return stampGroupAdmin(associations, createdBy?.subjectId ?? ownerEntityId);
134
+ }
135
+ function stampGroupAdmin(associations, creator) {
185
136
  const list = associations ?? [];
186
137
  const alreadyAdmin = list.some((a) => isGroupAdminAssociation(a) && a.target.entityId === creator);
187
138
  if (alreadyAdmin)
@@ -1 +1 @@
1
- {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
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,EAKV,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"}
@@ -20,19 +20,20 @@ export function combineAdapters(parts) {
20
20
  get ownerEntityId() {
21
21
  return parts.record.ownerEntityId;
22
22
  },
23
- get timezone() {
24
- return parts.record.timezone;
25
- },
26
23
  createRecord: (r, opts) => parts.record.createRecord(r, opts),
27
24
  getRecord: (id) => parts.record.getRecord(id),
28
- mutateRecord: (id, changes, opts) => parts.record.mutateRecord(id, changes, opts),
25
+ patchContent: (id, patch, opts) => parts.record.patchContent(id, patch, opts),
29
26
  deleteRecord: (id, opts) => parts.record.deleteRecord(id, opts),
30
27
  undeleteRecord: (id, opts) => parts.record.undeleteRecord(id, opts),
31
28
  queryRecords: (q) => parts.record.queryRecords(q),
32
- amendAssociations: (id, changes, opts) => parts.record.amendAssociations(id, changes, opts),
29
+ setParent: (id, parentId, opts) => parts.record.setParent(id, parentId, opts),
30
+ setUnlisted: (id, unlisted, opts) => parts.record.setUnlisted(id, unlisted, opts),
31
+ editAssociations: (id, edits, opts) => parts.record.editAssociations(id, edits, opts),
32
+ setAssociations: (id, list, opts) => parts.record.setAssociations(id, list, opts),
33
+ editAccess: (id, edits, opts) => parts.record.editAccess(id, edits, opts),
34
+ setAccess: (id, list, opts) => parts.record.setAccess(id, list, opts),
33
35
  getVersions: (id, query) => parts.record.getVersions(id, query),
34
36
  getVersion: (id, v) => parts.record.getVersion(id, v),
35
- saveVersion: (id, v) => parts.record.saveVersion(id, v),
36
37
  getJournal: (id, query) => parts.record.getJournal(id, query),
37
38
  restoreVersion: (id, v, opts) => parts.record.restoreVersion(id, v, opts),
38
39
  commitMigration: (id, toTypeId, content, opts) => parts.record.commitMigration(id, toTypeId, content, opts),
@@ -43,7 +44,13 @@ export function combineAdapters(parts) {
43
44
  // absent on the combined adapter, rather than silently becoming
44
45
  // "supported" via a wrapper that forwards to a missing method.
45
46
  ...(parts.record.deleteUnreferencedAttachmentRecords && {
46
- deleteUnreferencedAttachmentRecords: (fileId, metadataTypeIds) => parts.record.deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds),
47
+ deleteUnreferencedAttachmentRecords: (fileId, metadataBaseId) => parts.record.deleteUnreferencedAttachmentRecords(fileId, metadataBaseId),
48
+ }),
49
+ ...(parts.record.importRecord && {
50
+ importRecord: (r, history) => parts.record.importRecord(r, history),
51
+ }),
52
+ ...(parts.record.queryReadable && {
53
+ queryReadable: (q, reach) => parts.record.queryReadable(q, reach),
47
54
  }),
48
55
  // Forwarded on the same terms, and it decides more than it looks:
49
56
  // 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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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copying a stack
3
+ * -------------------------------------------------------
4
+ * One stack, read in full from one adapter and written in full to a new
5
+ * store: every Record (soft-deleted and unlisted included) with its version
6
+ * snapshots and journal, `_config` among them as the seed the store is
7
+ * created from, every Type, and every blob. Each is read as the store
8
+ * enumerates it, never by what a Record says, so nothing is filtered on the
9
+ * way. Export, import and moving
10
+ * between storage engines are all this one call, with a different source
11
+ * and a different kind of store to create. Full trust, like any direct
12
+ * adapter access: authorship and history land as recorded, which no write
13
+ * through Stack may do. See docs/spec/portability.md.
14
+ */
15
+ import type { StackAdapter, StoreSeed } from '../types/index.js';
16
+ /** Creates a new store from `seed`: `LocalAdapter.open({ path, seed })`, say. */
17
+ export type CreateStore = (seed: StoreSeed) => Promise<StackAdapter> | StackAdapter;
18
+ /**
19
+ * Copy everything `from` holds into the store `create` makes from its
20
+ * `_config` record and that record's history, and return that store, open.
21
+ * Not atomic: a failure closes the store part-written, and the caller, whose
22
+ * store it is, removes it. The source must not be written meanwhile; nothing
23
+ * here can check that.
24
+ * See docs/spec/portability.md § Copying a stack.
25
+ */
26
+ export declare function copyStack(from: StackAdapter, create: CreateStore): Promise<StackAdapter>;
27
+ //# sourceMappingURL=copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../../src/adapter/copy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAGV,YAAY,EAEZ,SAAS,EACV,MAAM,mBAAmB,CAAC;AAG3B,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;AAYpF;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAgC9F"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Copying a stack
3
+ * -------------------------------------------------------
4
+ * One stack, read in full from one adapter and written in full to a new
5
+ * store: every Record (soft-deleted and unlisted included) with its version
6
+ * snapshots and journal, `_config` among them as the seed the store is
7
+ * created from, every Type, and every blob. Each is read as the store
8
+ * enumerates it, never by what a Record says, so nothing is filtered on the
9
+ * way. Export, import and moving
10
+ * between storage engines are all this one call, with a different source
11
+ * and a different kind of store to create. Full trust, like any direct
12
+ * adapter access: authorship and history land as recorded, which no write
13
+ * through Stack may do. See docs/spec/portability.md.
14
+ */
15
+ import { InvalidAdapterError, OwnerMismatchError } from '../errors.js';
16
+ import { resolveQuery } from '../query-validation.js';
17
+ import { SYSTEM_TYPES } from '../types/index.js';
18
+ /** Records read per page; any size works, as each page is written before the next is read. */
19
+ const PAGE_SIZE = 100;
20
+ /** Every Record a store holds but `_config`, which no query reaches: a store is created from it. */
21
+ const EVERYTHING = {
22
+ filter: { includeDeleted: true, includeUnlisted: true },
23
+ sort: { field: 'createdAt', direction: 'asc' },
24
+ limit: PAGE_SIZE,
25
+ };
26
+ /**
27
+ * Copy everything `from` holds into the store `create` makes from its
28
+ * `_config` record and that record's history, and return that store, open.
29
+ * Not atomic: a failure closes the store part-written, and the caller, whose
30
+ * store it is, removes it. The source must not be written meanwhile; nothing
31
+ * here can check that.
32
+ * See docs/spec/portability.md § Copying a stack.
33
+ */
34
+ export async function copyStack(from, create) {
35
+ const listBlobs = from.listBlobs?.bind(from);
36
+ if (!listBlobs) {
37
+ throw new InvalidAdapterError('Cannot copy from this adapter: it has no listBlobs(), so its blobs cannot be enumerated.');
38
+ }
39
+ const config = await from.getRecord(SYSTEM_TYPES.CONFIG);
40
+ if (!config) {
41
+ throw new InvalidAdapterError('Cannot copy from this adapter: it holds no _config record.');
42
+ }
43
+ const [versions, journal] = await Promise.all([
44
+ from.getVersions(config.id),
45
+ from.getJournal(config.id),
46
+ ]);
47
+ const to = await create({ config, history: { versions, journal } });
48
+ try {
49
+ const importRecord = to.importRecord?.bind(to);
50
+ if (!importRecord)
51
+ refuseTarget('it has no importRecord(), so it cannot write history');
52
+ const listHeld = to.listBlobs?.bind(to);
53
+ if (!listHeld)
54
+ refuseTarget('it has no listBlobs(), so it could not be copied from in turn');
55
+ await assertNewStore(to, config.content.ownerEntityId, listHeld);
56
+ for (const type of await from.listTypes())
57
+ await to.saveType(type);
58
+ // Bytes before the Records that name them, as every upload writes them.
59
+ for (const { fileId } of await listBlobs())
60
+ await to.putBlob(await from.getBlob(fileId));
61
+ await copyRecords(from, importRecord);
62
+ return to;
63
+ }
64
+ catch (err) {
65
+ // The failure that stopped the copy is the one to report.
66
+ await to.close?.().catch(() => { });
67
+ throw err;
68
+ }
69
+ }
70
+ function refuseTarget(why) {
71
+ throw new InvalidAdapterError(`Cannot copy into the store create() returned: ${why}.`);
72
+ }
73
+ /**
74
+ * A target is a store as its adapter creates one: the source's owner,
75
+ * checked as open() checks it, and nothing held yet — no Record, no Type, no
76
+ * blob. Every end of a copy lists all it holds, so a target is itself a
77
+ * store a later copy can read in full.
78
+ */
79
+ async function assertNewStore(to, ownerEntityId, listHeld) {
80
+ if (to.ownerEntityId !== ownerEntityId) {
81
+ throw new OwnerMismatchError(ownerEntityId, to.ownerEntityId, 'the store create() returned');
82
+ }
83
+ if ((await to.queryRecords(resolveQuery({ ...EVERYTHING, limit: 1 }))).records.length) {
84
+ refuseTarget('it already holds Records');
85
+ }
86
+ if ((await to.listTypes()).length)
87
+ refuseTarget('it already holds Types');
88
+ if ((await listHeld()).length)
89
+ refuseTarget('it already holds blobs');
90
+ }
91
+ async function copyRecords(from, importRecord) {
92
+ let cursor;
93
+ do {
94
+ const page = await from.queryRecords(resolveQuery({ ...EVERYTHING, cursor }));
95
+ for (const record of page.records) {
96
+ const [versions, journal] = await Promise.all([
97
+ from.getVersions(record.id),
98
+ from.getJournal(record.id),
99
+ ]);
100
+ await importRecord(record, { versions, journal });
101
+ }
102
+ cursor = page.cursor ?? undefined;
103
+ } while (cursor);
104
+ }
105
+ //# sourceMappingURL=copy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.js","sourceRoot":"","sources":["../../src/adapter/copy.ts"],"names":[],"mappings":"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"}