@haverstack/core 0.40.0 → 0.42.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 (287) hide show
  1. package/README.md +121 -59
  2. package/dist/access.d.ts +34 -36
  3. package/dist/access.d.ts.map +1 -1
  4. package/dist/access.js +65 -42
  5. package/dist/access.js.map +1 -1
  6. package/dist/{combine.d.ts → adapter/combine.d.ts} +8 -1
  7. package/dist/adapter/combine.d.ts.map +1 -0
  8. package/dist/{combine.js → adapter/combine.js} +7 -0
  9. package/dist/adapter/combine.js.map +1 -0
  10. package/dist/{adapter-entry.d.ts → adapter/index.d.ts} +6 -5
  11. package/dist/adapter/index.d.ts.map +1 -0
  12. package/dist/{adapter-entry.js → adapter/index.js} +5 -4
  13. package/dist/adapter/index.js.map +1 -0
  14. package/dist/{sort.d.ts → adapter/sort.d.ts} +20 -20
  15. package/dist/adapter/sort.d.ts.map +1 -0
  16. package/dist/{sort.js → adapter/sort.js} +37 -27
  17. package/dist/adapter/sort.js.map +1 -0
  18. package/dist/associations/identity.d.ts +93 -0
  19. package/dist/associations/identity.d.ts.map +1 -0
  20. package/dist/associations/identity.js +198 -0
  21. package/dist/associations/identity.js.map +1 -0
  22. package/dist/associations/validation.d.ts +90 -0
  23. package/dist/associations/validation.d.ts.map +1 -0
  24. package/dist/associations/validation.js +339 -0
  25. package/dist/associations/validation.js.map +1 -0
  26. package/dist/attachment-records.d.ts +48 -0
  27. package/dist/attachment-records.d.ts.map +1 -0
  28. package/dist/attachment-records.js +52 -0
  29. package/dist/attachment-records.js.map +1 -0
  30. package/dist/collection.d.ts +182 -0
  31. package/dist/collection.d.ts.map +1 -0
  32. package/dist/collection.js +407 -0
  33. package/dist/collection.js.map +1 -0
  34. package/dist/content-match.d.ts +25 -0
  35. package/dist/content-match.d.ts.map +1 -0
  36. package/dist/content-match.js +51 -0
  37. package/dist/content-match.js.map +1 -0
  38. package/dist/content-translation.d.ts +42 -0
  39. package/dist/content-translation.d.ts.map +1 -0
  40. package/dist/content-translation.js +108 -0
  41. package/dist/content-translation.js.map +1 -0
  42. package/dist/did/did.d.ts.map +1 -0
  43. package/dist/{did.js → did/did.js} +3 -4
  44. package/dist/did/did.js.map +1 -0
  45. package/dist/{did-entry.d.ts → did/index.d.ts} +1 -1
  46. package/dist/did/index.d.ts.map +1 -0
  47. package/dist/{did-entry.js → did/index.js} +3 -3
  48. package/dist/did/index.js.map +1 -0
  49. package/dist/errors.d.ts +36 -63
  50. package/dist/errors.d.ts.map +1 -1
  51. package/dist/errors.js +35 -58
  52. package/dist/errors.js.map +1 -1
  53. package/dist/grants.d.ts +20 -43
  54. package/dist/grants.d.ts.map +1 -1
  55. package/dist/grants.js +71 -134
  56. package/dist/grants.js.map +1 -1
  57. package/dist/id.d.ts +13 -31
  58. package/dist/id.d.ts.map +1 -1
  59. package/dist/id.js +13 -31
  60. package/dist/id.js.map +1 -1
  61. package/dist/index.d.ts +14 -8
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +9 -5
  64. package/dist/index.js.map +1 -1
  65. package/dist/install.d.ts +141 -0
  66. package/dist/install.d.ts.map +1 -0
  67. package/dist/install.js +219 -0
  68. package/dist/install.js.map +1 -0
  69. package/dist/merge.d.ts.map +1 -1
  70. package/dist/merge.js +5 -11
  71. package/dist/merge.js.map +1 -1
  72. package/dist/migration-steps.d.ts +102 -0
  73. package/dist/migration-steps.d.ts.map +1 -0
  74. package/dist/migration-steps.js +293 -0
  75. package/dist/migration-steps.js.map +1 -0
  76. package/dist/query-validation.d.ts +28 -94
  77. package/dist/query-validation.d.ts.map +1 -1
  78. package/dist/query-validation.js +156 -336
  79. package/dist/query-validation.js.map +1 -1
  80. package/dist/record-changes.d.ts +2 -117
  81. package/dist/record-changes.d.ts.map +1 -1
  82. package/dist/record-changes.js +10 -212
  83. package/dist/record-changes.js.map +1 -1
  84. package/dist/schema.d.ts +18 -7
  85. package/dist/schema.d.ts.map +1 -1
  86. package/dist/schema.js +59 -30
  87. package/dist/schema.js.map +1 -1
  88. package/dist/scoped-stack/authority.d.ts +156 -0
  89. package/dist/scoped-stack/authority.d.ts.map +1 -0
  90. package/dist/scoped-stack/authority.js +384 -0
  91. package/dist/scoped-stack/authority.js.map +1 -0
  92. package/dist/scoped-stack/scoped-stack.d.ts +338 -0
  93. package/dist/scoped-stack/scoped-stack.d.ts.map +1 -0
  94. package/dist/scoped-stack/scoped-stack.js +776 -0
  95. package/dist/scoped-stack/scoped-stack.js.map +1 -0
  96. package/dist/scoped-stack/subscription.d.ts +55 -0
  97. package/dist/scoped-stack/subscription.d.ts.map +1 -0
  98. package/dist/scoped-stack/subscription.js +110 -0
  99. package/dist/scoped-stack/subscription.js.map +1 -0
  100. package/dist/stack/attachments.d.ts +24 -0
  101. package/dist/stack/attachments.d.ts.map +1 -0
  102. package/dist/stack/attachments.js +113 -0
  103. package/dist/stack/attachments.js.map +1 -0
  104. package/dist/{changes.d.ts → stack/changes.d.ts} +29 -65
  105. package/dist/stack/changes.d.ts.map +1 -0
  106. package/dist/{changes.js → stack/changes.js} +32 -66
  107. package/dist/stack/changes.js.map +1 -0
  108. package/dist/stack/client.d.ts +271 -0
  109. package/dist/stack/client.d.ts.map +1 -0
  110. package/dist/stack/client.js +9 -0
  111. package/dist/stack/client.js.map +1 -0
  112. package/dist/stack/identity-bindings.d.ts.map +1 -0
  113. package/dist/{identity-bindings.js → stack/identity-bindings.js} +8 -14
  114. package/dist/stack/identity-bindings.js.map +1 -0
  115. package/dist/stack/install-app.d.ts +22 -0
  116. package/dist/stack/install-app.d.ts.map +1 -0
  117. package/dist/stack/install-app.js +260 -0
  118. package/dist/stack/install-app.js.map +1 -0
  119. package/dist/stack/integrity.d.ts +94 -0
  120. package/dist/stack/integrity.d.ts.map +1 -0
  121. package/dist/stack/integrity.js +268 -0
  122. package/dist/stack/integrity.js.map +1 -0
  123. package/dist/{limits.d.ts → stack/limits.d.ts} +2 -5
  124. package/dist/stack/limits.d.ts.map +1 -0
  125. package/dist/{limits.js → stack/limits.js} +3 -6
  126. package/dist/stack/limits.js.map +1 -0
  127. package/dist/stack/migrations.d.ts +59 -0
  128. package/dist/stack/migrations.d.ts.map +1 -0
  129. package/dist/stack/migrations.js +141 -0
  130. package/dist/stack/migrations.js.map +1 -0
  131. package/dist/{stack-reads.d.ts → stack/reads.d.ts} +33 -6
  132. package/dist/stack/reads.d.ts.map +1 -0
  133. package/dist/{stack-reads.js → stack/reads.js} +49 -7
  134. package/dist/stack/reads.js.map +1 -0
  135. package/dist/stack/record-id.d.ts +35 -0
  136. package/dist/stack/record-id.d.ts.map +1 -0
  137. package/dist/stack/record-id.js +82 -0
  138. package/dist/stack/record-id.js.map +1 -0
  139. package/dist/stack/stack.d.ts +438 -0
  140. package/dist/stack/stack.d.ts.map +1 -0
  141. package/dist/stack/stack.js +1366 -0
  142. package/dist/stack/stack.js.map +1 -0
  143. package/dist/stack/system-types.d.ts +16 -0
  144. package/dist/stack/system-types.d.ts.map +1 -0
  145. package/dist/stack/system-types.js +111 -0
  146. package/dist/stack/system-types.js.map +1 -0
  147. package/dist/stack/type-grants.d.ts +20 -0
  148. package/dist/stack/type-grants.d.ts.map +1 -0
  149. package/dist/stack/type-grants.js +69 -0
  150. package/dist/stack/type-grants.js.map +1 -0
  151. package/dist/stack/write-expectation.d.ts +78 -0
  152. package/dist/stack/write-expectation.d.ts.map +1 -0
  153. package/dist/stack/write-expectation.js +71 -0
  154. package/dist/stack/write-expectation.js.map +1 -0
  155. package/dist/testing.d.ts +21 -26
  156. package/dist/testing.d.ts.map +1 -1
  157. package/dist/testing.js +50 -70
  158. package/dist/testing.js.map +1 -1
  159. package/dist/type-handle.d.ts +67 -78
  160. package/dist/type-handle.d.ts.map +1 -1
  161. package/dist/type-handle.js +44 -111
  162. package/dist/type-handle.js.map +1 -1
  163. package/dist/types/adapter.d.ts +223 -0
  164. package/dist/types/adapter.d.ts.map +1 -0
  165. package/dist/types/adapter.js +5 -0
  166. package/dist/types/adapter.js.map +1 -0
  167. package/dist/types/associations.d.ts +156 -0
  168. package/dist/types/associations.d.ts.map +1 -0
  169. package/dist/types/associations.js +13 -0
  170. package/dist/types/associations.js.map +1 -0
  171. package/dist/types/capabilities.d.ts +89 -0
  172. package/dist/types/capabilities.d.ts.map +1 -0
  173. package/dist/types/capabilities.js +5 -0
  174. package/dist/types/capabilities.js.map +1 -0
  175. package/dist/types/events.d.ts +155 -0
  176. package/dist/types/events.d.ts.map +1 -0
  177. package/dist/types/events.js +5 -0
  178. package/dist/types/events.js.map +1 -0
  179. package/dist/types/ids.d.ts +43 -0
  180. package/dist/types/ids.d.ts.map +1 -0
  181. package/dist/types/ids.js +5 -0
  182. package/dist/types/ids.js.map +1 -0
  183. package/dist/types/index.d.ts +19 -0
  184. package/dist/types/index.d.ts.map +1 -0
  185. package/dist/types/index.js +19 -0
  186. package/dist/types/index.js.map +1 -0
  187. package/dist/types/journal.d.ts +47 -0
  188. package/dist/types/journal.d.ts.map +1 -0
  189. package/dist/types/journal.js +5 -0
  190. package/dist/types/journal.js.map +1 -0
  191. package/dist/types/query.d.ts +185 -0
  192. package/dist/types/query.d.ts.map +1 -0
  193. package/dist/types/query.js +10 -0
  194. package/dist/types/query.js.map +1 -0
  195. package/dist/types/records.d.ts +76 -0
  196. package/dist/types/records.d.ts.map +1 -0
  197. package/dist/types/records.js +5 -0
  198. package/dist/types/records.js.map +1 -0
  199. package/dist/types/schema.d.ts +74 -0
  200. package/dist/types/schema.d.ts.map +1 -0
  201. package/dist/types/schema.js +5 -0
  202. package/dist/types/schema.js.map +1 -0
  203. package/dist/types/system.d.ts +161 -0
  204. package/dist/types/system.d.ts.map +1 -0
  205. package/dist/types/system.js +32 -0
  206. package/dist/types/system.js.map +1 -0
  207. package/dist/types/tokens.d.ts +39 -0
  208. package/dist/types/tokens.d.ts.map +1 -0
  209. package/dist/types/tokens.js +5 -0
  210. package/dist/types/tokens.js.map +1 -0
  211. package/dist/validate.d.ts +29 -70
  212. package/dist/validate.d.ts.map +1 -1
  213. package/dist/validate.js +65 -97
  214. package/dist/validate.js.map +1 -1
  215. package/dist/{attachment-download.d.ts → wire/attachment-download.d.ts} +0 -57
  216. package/dist/wire/attachment-download.d.ts.map +1 -0
  217. package/dist/{attachment-download.js → wire/attachment-download.js} +0 -61
  218. package/dist/wire/attachment-download.js.map +1 -0
  219. package/dist/{auth.d.ts → wire/auth.d.ts} +5 -8
  220. package/dist/wire/auth.d.ts.map +1 -0
  221. package/dist/{auth.js → wire/auth.js} +5 -8
  222. package/dist/wire/auth.js.map +1 -0
  223. package/dist/{wire-body.d.ts → wire/body.d.ts} +13 -5
  224. package/dist/wire/body.d.ts.map +1 -0
  225. package/dist/{wire-body.js → wire/body.js} +65 -7
  226. package/dist/wire/body.js.map +1 -0
  227. package/dist/{wire-entry.d.ts → wire/index.d.ts} +12 -12
  228. package/dist/wire/index.d.ts.map +1 -0
  229. package/dist/{wire-entry.js → wire/index.js} +13 -17
  230. package/dist/wire/index.js.map +1 -0
  231. package/dist/{wire-record.d.ts → wire/record.d.ts} +10 -22
  232. package/dist/wire/record.d.ts.map +1 -0
  233. package/dist/{wire-record.js → wire/record.js} +12 -24
  234. package/dist/wire/record.js.map +1 -0
  235. package/dist/{wire-request.d.ts → wire/request.d.ts} +20 -37
  236. package/dist/wire/request.d.ts.map +1 -0
  237. package/dist/{wire-request.js → wire/request.js} +79 -54
  238. package/dist/wire/request.js.map +1 -0
  239. package/package.json +7 -7
  240. package/dist/adapter-entry.d.ts.map +0 -1
  241. package/dist/adapter-entry.js.map +0 -1
  242. package/dist/attachment-download.d.ts.map +0 -1
  243. package/dist/attachment-download.js.map +0 -1
  244. package/dist/auth.d.ts.map +0 -1
  245. package/dist/auth.js.map +0 -1
  246. package/dist/changes.d.ts.map +0 -1
  247. package/dist/changes.js.map +0 -1
  248. package/dist/combine.d.ts.map +0 -1
  249. package/dist/combine.js.map +0 -1
  250. package/dist/did-entry.d.ts.map +0 -1
  251. package/dist/did-entry.js.map +0 -1
  252. package/dist/did.d.ts.map +0 -1
  253. package/dist/did.js.map +0 -1
  254. package/dist/identity-bindings.d.ts.map +0 -1
  255. package/dist/identity-bindings.js.map +0 -1
  256. package/dist/limits.d.ts.map +0 -1
  257. package/dist/limits.js.map +0 -1
  258. package/dist/record-id.d.ts +0 -70
  259. package/dist/record-id.d.ts.map +0 -1
  260. package/dist/record-id.js +0 -127
  261. package/dist/record-id.js.map +0 -1
  262. package/dist/scoped-stack.d.ts +0 -532
  263. package/dist/scoped-stack.d.ts.map +0 -1
  264. package/dist/scoped-stack.js +0 -1219
  265. package/dist/scoped-stack.js.map +0 -1
  266. package/dist/sort.d.ts.map +0 -1
  267. package/dist/sort.js.map +0 -1
  268. package/dist/stack-reads.d.ts.map +0 -1
  269. package/dist/stack-reads.js.map +0 -1
  270. package/dist/stack.d.ts +0 -971
  271. package/dist/stack.d.ts.map +0 -1
  272. package/dist/stack.js +0 -2012
  273. package/dist/stack.js.map +0 -1
  274. package/dist/types.d.ts +0 -1250
  275. package/dist/types.d.ts.map +0 -1
  276. package/dist/types.js +0 -49
  277. package/dist/types.js.map +0 -1
  278. package/dist/wire-body.d.ts.map +0 -1
  279. package/dist/wire-body.js.map +0 -1
  280. package/dist/wire-entry.d.ts.map +0 -1
  281. package/dist/wire-entry.js.map +0 -1
  282. package/dist/wire-record.d.ts.map +0 -1
  283. package/dist/wire-record.js.map +0 -1
  284. package/dist/wire-request.d.ts.map +0 -1
  285. package/dist/wire-request.js.map +0 -1
  286. /package/dist/{did.d.ts → did/did.d.ts} +0 -0
  287. /package/dist/{identity-bindings.d.ts → stack/identity-bindings.d.ts} +0 -0
package/dist/stack.d.ts DELETED
@@ -1,971 +0,0 @@
1
- /**
2
- * Stack — Core Stack Class
3
- * -------------------------------------------------------
4
- * The Stack class is the primary interface for apps. It sits
5
- * on top of a StackAdapter and adds:
6
- *
7
- * - ID generation
8
- * - Type definition and schema hashing
9
- * - Content validation on write
10
- * - Migration registry and explicit, owner-driven migrateAll()
11
- * - Version snapshotting on update
12
- * - Soft delete and purge
13
- *
14
- * Apps should never talk to a StackAdapter directly.
15
- */
16
- import type { StackRecord, StackType, TypeSchema, TypeId, BaseId, StackAdapter, StackQuery, QueryResult, AssociationEdit, AuthorityAssociation, DataAssociation, Migration, RecordVersion, StackCapabilities, IfVersionOptions, GrantContent, TypeGrant, PutAttachmentOptions, AttachmentContent, FileId, EntityId, AppId, RecordId, Actor, ActorOptions, RecordChange, RecordChangeSet, SubscribeOptions, Unsubscribe, JournalQuery, VersionsQuery, RecordJournalEntry } from './types.js';
17
- import type { GrantQuery } from './grants.js';
18
- import { ScopedStack } from './scoped-stack.js';
19
- import type { ContentOf, PatchOf, ReadonlyTypeSchema, TypedChange, TypedChangeSet, TypedQuery, TypedSubscribeOptions, TypedRecord, TypeHandle } from './type-handle.js';
20
- /**
21
- * An Actor in the one form `Stack` stores: a `principalId` equal to the
22
- * subject is dropped, so "acted as itself" has a single spelling, and an
23
- * empty id is refused rather than stored as a name for nobody.
24
- * See docs/spec/data-model.md § Actor.
25
- */
26
- export declare function normalizeActor(actor: Actor | undefined): Actor | undefined;
27
- export type CreateRecordOptions = {
28
- /**
29
- * Client-minted record ID. Must be 12 lowercase Crockford base-32
30
- * characters and may not use the reserved `_` prefix. Omit to let the
31
- * library generate one. See Stack.create() and ScopedStack.create()
32
- * for the validation each applies.
33
- */
34
- id?: RecordId;
35
- parentId?: RecordId;
36
- /**
37
- * The author. ScopedStack sets this from its own identities; callers of
38
- * plain Stack supply it only when reconstructing an attributed write.
39
- * See StackRecord.createdBy.
40
- */
41
- createdBy?: Actor;
42
- /** Reverse-DNS identifier of the writing software — see AppId. */
43
- appId?: AppId;
44
- permissions?: AuthorityAssociation[];
45
- associations?: DataAssociation[];
46
- /**
47
- * Create the record already unlisted, so the create event itself is
48
- * withheld from the feed — there is no window where the record exists
49
- * and is listed before a mutation catches up. See
50
- * docs/spec/unlisted.md.
51
- */
52
- unlisted?: boolean;
53
- };
54
- /**
55
- * CreateRecordOptions extended with createdAt/updatedAt, for backdating a
56
- * record's clock fields on import. Unscoped Stack.create() accepts them
57
- * unconditionally; ScopedStack.create() only from the stack owner acting
58
- * alone, since a grantee or a delegated app could otherwise forge a sort
59
- * position the same way a raw `id` could.
60
- * See docs/spec/data-model.md § Record IDs.
61
- */
62
- export type BackdatableCreateRecordOptions = CreateRecordOptions & {
63
- /**
64
- * The record's creation time. When `id` is also supplied, its embedded
65
- * timestamp must agree with this within `idTimestampSkewMs` (default 24h;
66
- * see StackOptions.idTimestampSkewMs) or the create throws
67
- * StackValidationError. Omit `id` to have it derived from this timestamp
68
- * instead. Defaults to now.
69
- */
70
- createdAt?: Date;
71
- /**
72
- * The record's last-modified time. Defaults to `createdAt` (or now, if
73
- * `createdAt` is omitted too) — never to the actual current time — so a
74
- * plain import doesn't fabricate a fake edit. Must not precede
75
- * `createdAt`.
76
- */
77
- updatedAt?: Date;
78
- };
79
- export type StackOptions = {
80
- /**
81
- * Ensures the owner's own `_entity` profile record exists, creating it on
82
- * first run. Idempotent — safe to pass on every open. See
83
- * docs/spec/identity.md § Entity.
84
- */
85
- ownerProfile?: {
86
- name: string;
87
- handle?: string;
88
- };
89
- /**
90
- * Clock-skew tolerance (ms) for two timestamp-prefix checks: the one
91
- * ScopedStack.create() runs on a non-backdated create's client-supplied
92
- * `id` against the current time, and the one Stack.create() runs between
93
- * an explicit `id` and an explicit `createdAt` when both are supplied —
94
- * reached directly when unscoped, or via ScopedStack.create() when the
95
- * requester is the owner acting alone. Default: 24 hours; null disables
96
- * both. See docs/spec/data-model.md § Record IDs.
97
- */
98
- idTimestampSkewMs?: number | null;
99
- };
100
- export type CollectAttachmentGarbageOptions = {
101
- /**
102
- * How recent an unreferenced file must be to survive collection, covering
103
- * the upload-then-associate window. Default: 24 hours. Pass 0 to collect
104
- * anything unreferenced right now.
105
- */
106
- graceMs?: number;
107
- /** Compute what would be deleted without deleting anything. Default: false. */
108
- dryRun?: boolean;
109
- };
110
- export type CollectAttachmentGarbageResult = {
111
- /** The files collected — or, on a dry run, the ones that would be. */
112
- deletedFileIds: FileId[];
113
- reclaimedBytes: number;
114
- };
115
- export type GetRecordOptions = {
116
- /**
117
- * Records are returned exactly as stored by default. Pass "latest" to
118
- * apply the registered migration chain in memory — never written back.
119
- * See docs/spec/data-model.md § Type migrations.
120
- */
121
- presentAt?: 'stored' | 'latest';
122
- /**
123
- * Soft-deleted records are hidden by default, as query() hides them: a
124
- * tombstone reads as `null`. Pass true to read it back.
125
- * See docs/spec/versioning.md § Deletion.
126
- */
127
- includeDeleted?: boolean;
128
- };
129
- export type DeleteRecordOptions = IfVersionOptions & {
130
- /** If true, permanently remove the record and all its history. Default: false */
131
- purge?: boolean;
132
- };
133
- /**
134
- * What a delete reports back. Information, not action: a purge never
135
- * touches the blob store, and nothing here is deleted by having been
136
- * named. See docs/spec/attachments.md § A purge strands the bytes it
137
- * referenced.
138
- */
139
- export type DeleteResult = {
140
- /**
141
- * The files the purged record referenced — its attachment associations
142
- * and its `file-ref` content fields, deduplicated. The purge removes the
143
- * only rows naming them, so this is the caller's one chance to hold the
144
- * argument `deleteAttachment()` takes. Empty on a soft delete, which
145
- * strands nothing: a tombstone's references stand.
146
- */
147
- referencedFileIds: FileId[];
148
- };
149
- /**
150
- * What deleteAndReturn() reports back — DeleteResult plus the record itself,
151
- * captured inside the same atomic operation as the destroy rather than a
152
- * read beforehand. See Stack.deleteAndReturn().
153
- */
154
- export type DeleteAndReturnResult = DeleteResult & {
155
- /**
156
- * The record as it stood at the moment of deletion: immediately before
157
- * destruction for a purge, or the resulting tombstone for a soft
158
- * delete. Null only for the purge no-op — there was nothing to
159
- * delete, and nothing to report; every other outcome throws instead of
160
- * returning null (see Stack.delete()'s own no-op cases).
161
- */
162
- record: StackRecord | null;
163
- };
164
- /** The argument to Stack.defineType(). */
165
- export type DefineTypeOptions = {
166
- id: TypeId;
167
- name: string;
168
- /** A handle's `schema` is accepted as written, readonly members and all. */
169
- schema: ReadonlyTypeSchema;
170
- migratesFrom?: TypeId;
171
- };
172
- /**
173
- * The app-facing record API, implemented by both Stack and ScopedStack.
174
- * Accept this type in plugin or extension code to remain adapter-agnostic
175
- * and work equally well with a full Stack or a permission-scoped view.
176
- */
177
- export interface StackClient {
178
- readonly capabilities: StackCapabilities;
179
- create<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, content: ContentOf<S>, opts?: CreateRecordOptions): Promise<TypedRecord<S>>;
180
- create<T extends Record<string, unknown> = Record<string, unknown>>(typeId: TypeId, content: T, opts?: CreateRecordOptions): Promise<StackRecord & {
181
- content: T;
182
- }>;
183
- /**
184
- * Typed read: the record at `presentAt: 'latest'`, checked to be exactly
185
- * the handle's Type with every enum field holding a value the handle
186
- * lists, else it throws. Live records only — a tombstone reads as `null`,
187
- * and there is no `includeDeleted`.
188
- * See docs/spec/data-model.md § Type handles.
189
- */
190
- get<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, id: RecordId): Promise<TypedRecord<S> | null>;
191
- get(id: RecordId, opts?: GetRecordOptions): Promise<StackRecord | null>;
192
- /** Typed read of the handle's whole family — see the typed `get()`. */
193
- query<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, query?: TypedQuery): Promise<{
194
- records: TypedRecord<S>[];
195
- cursor: string | null;
196
- }>;
197
- query(query?: StackQuery): Promise<QueryResult>;
198
- /**
199
- * Resolve a DID to its `_entity` card — family-wide and soft-deleted
200
- * inclusive, per docs/spec/identity.md § DID bindings. No caching.
201
- *
202
- * Under `ScopedStack`, `null` covers missing, unreadable, and (unless the
203
- * owner is acting alone) unlisted — never evidence the card is absent.
204
- */
205
- getEntityByDid(did: EntityId): Promise<StackRecord | null>;
206
- /** getEntityByDid() for the one DID every stack reserves: its own owner. */
207
- getOwnerEntity(): Promise<StackRecord | null>;
208
- /**
209
- * Apply a change set: any combination of content patch, `parentId`,
210
- * `permissions`, `associations` and `unlisted`, in one atomic write —
211
- * producing one version where it names `contentPatch`, and none where it
212
- * doesn't. Keys are read for presence, so `unlisted: false`
213
- * and `parentId: null` are changes; a change set naming no key at all is
214
- * a StackBadRequestError. Under ScopedStack each key carries its own gate and
215
- * one refused key refuses the call.
216
- * See docs/spec/data-model.md § Mutations.
217
- */
218
- mutate<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, id: RecordId, changes: TypedChangeSet<S>, opts?: IfVersionOptions): Promise<TypedRecord<S>>;
219
- mutate(id: RecordId, changes: RecordChangeSet, opts?: IfVersionOptions): Promise<StackRecord>;
220
- /** mutate() with `contentPatch` alone — the common case, named for it. */
221
- patchContent<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, id: RecordId, patch: PatchOf<S>, opts?: IfVersionOptions): Promise<TypedRecord<S>>;
222
- patchContent(id: RecordId, patch: Record<string, unknown | null>, opts?: IfVersionOptions): Promise<StackRecord>;
223
- /**
224
- * Add associations in one atomic write and one journal entry. Never bumps
225
- * `version`/`updatedAt` and takes no `ifVersion` — a set-add composes
226
- * regardless of write order. See docs/spec/data-model.md § Mutations and
227
- * docs/spec/versioning.md § Version history.
228
- */
229
- associate(id: RecordId, associations: DataAssociation[]): Promise<StackRecord>;
230
- /** Remove associations, matched by identity — see associate(). */
231
- dissociate(id: RecordId, associations: DataAssociation[]): Promise<StackRecord>;
232
- /**
233
- * Apply a list of adds and removes to a record's data associations as one
234
- * atomic write — the cover swap. associate() and dissociate() are this with
235
- * one half each; `mutate({ associations })` is the only whole-set write.
236
- * `repoint` is not a request: an add naming an attachment the record
237
- * already holds re-points it. See docs/spec/data-model.md § Mutations.
238
- */
239
- amendAssociations(id: RecordId, changes: AssociationEdit[]): Promise<StackRecord>;
240
- /**
241
- * Extend who reaches a record — the record-level mirror of the type-level
242
- * `grantType()`, and the amending spelling of the `permissions` key, which
243
- * replaces the whole set. `write` never implies `read`: grant both,
244
- * `grantAccess(id, [read, write])`. No-bump, like associate().
245
- * See docs/spec/access-control.md § Write implies read.
246
- */
247
- grantAccess(id: RecordId, permissions: AuthorityAssociation[]): Promise<StackRecord>;
248
- /** Withdraw elements of who reaches a record — see grantAccess(). */
249
- revokeAccess(id: RecordId, permissions: AuthorityAssociation[]): Promise<StackRecord>;
250
- /**
251
- * amendAssociations() for permissions: adds and removes in one atomic
252
- * write, so a downgrade (remove `write`, keep `read`) is one change.
253
- * See docs/spec/access-control.md § Record-level permissions.
254
- */
255
- amendAccess(id: RecordId, changes: AssociationEdit[]): Promise<StackRecord>;
256
- delete(id: RecordId, opts?: DeleteRecordOptions): Promise<DeleteResult>;
257
- /**
258
- * delete(), plus the record it acted on — read and destroyed as one
259
- * atomic operation, so a caller that needs the record for its own
260
- * response (e.g. a server building a purge's body) never opens a
261
- * gap between reading it and destroying it. See Stack.deleteAndReturn().
262
- */
263
- deleteAndReturn(id: RecordId, opts?: DeleteRecordOptions): Promise<DeleteAndReturnResult>;
264
- undelete(id: RecordId, opts?: IfVersionOptions): Promise<StackRecord>;
265
- getVersions(id: RecordId, query?: VersionsQuery): Promise<RecordVersion[]>;
266
- getVersion(id: RecordId, version: number): Promise<RecordVersion | null>;
267
- restoreVersion(id: RecordId, version: number, opts?: IfVersionOptions): Promise<StackRecord>;
268
- /**
269
- * A record's change journal, oldest first. On the mutate surface, on the
270
- * same footing as getVersions() — a plain reader is refused.
271
- *
272
- * Required of every adapter rather than optional, which is why it sits
273
- * here beside the rest: an empty log has to mean "nothing changed"
274
- * unconditionally, so an adapter with nothing to read refuses and names
275
- * why instead. See docs/spec/journal.md § Reading it.
276
- */
277
- getJournal(id: RecordId, query?: JournalQuery): Promise<RecordJournalEntry[]>;
278
- /**
279
- * Commit a per-record migration: change `typeId` and `content` together,
280
- * validated against `toTypeId`'s schema. The only way a record's typeId
281
- * changes after creation — see docs/spec/wire-format.md § Migration
282
- * commit. Takes `ifVersion` like every other mutation that bumps a
283
- * record's version (see docs/spec/versioning.md § Optimistic
284
- * concurrency); over the wire that is `If-Match`.
285
- */
286
- commitMigration(id: RecordId, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions): Promise<StackRecord>;
287
- getAttachment(fileId: FileId): Promise<Uint8Array>;
288
- putAttachment(data: Uint8Array, opts: PutAttachmentOptions): Promise<StackRecord & {
289
- content: AttachmentContent;
290
- }>;
291
- deleteAttachment(fileId: FileId): Promise<void>;
292
- collectAttachmentGarbage(opts?: CollectAttachmentGarbageOptions): Promise<CollectAttachmentGarbageResult>;
293
- /**
294
- * Typed subscription: delivers only changes to records of exactly the
295
- * handle's Type, each `record` typed as its content. See
296
- * docs/spec/data-model.md § Type handles.
297
- */
298
- subscribe<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, handler: (change: TypedChange<S>) => void, opts?: TypedSubscribeOptions): Promise<Unsubscribe>;
299
- subscribe(handler: (change: RecordChange) => void, opts?: SubscribeOptions): Promise<Unsubscribe>;
300
- }
301
- export declare class Stack implements StackClient {
302
- private readonly adapter;
303
- private readonly idTimestampSkewMsValue;
304
- private readonly migrations;
305
- /**
306
- * Highest version this instance has defineType()'d, per baseId — what
307
- * this app process understands, as distinct from what exists in shared
308
- * storage. Used to detect the stale-writer case in presentAtLatest().
309
- */
310
- private readonly maxDefinedVersion;
311
- /**
312
- * Types this instance has fetched or defined, keyed by versioned id. A
313
- * Type's schema is immutable once defined, so entries are never
314
- * invalidated, only added; listTypes() refreshes wholesale. See
315
- * docs/spec/data-model.md § Type cache.
316
- */
317
- private readonly typeCache;
318
- /** Set by close(). See docs/spec/adapters.md § Lifecycle. */
319
- private closed;
320
- /**
321
- * Every change made through this Stack. `ScopedStack` filters this same
322
- * stream rather than opening its own, so no scoped view can observe a
323
- * change the stack did not emit. See docs/spec/events.md.
324
- */
325
- private readonly changes;
326
- private constructor();
327
- /**
328
- * Announce a mutation that has already been persisted. Called after the
329
- * adapter write resolves and before the mutating method settles, so
330
- * `await stack.mutate(...)` guarantees subscribers have been notified —
331
- * and nothing about work they deferred. A handler cannot fail the write:
332
- * there is nothing left to fail. See docs/spec/events.md § Handlers.
333
- */
334
- private announce;
335
- /**
336
- * The actor for a `create` journal entry, read off the record as just
337
- * built. Create is the one journaled write that stamps the requester
338
- * onto the row itself, so the record in hand is authoritative — and it
339
- * carries `appId`, which ActorOptions has no field for.
340
- */
341
- private static createActor;
342
- private getTypeCached;
343
- /**
344
- * Open a Stack over an adapter. Reads ownerEntityId and timezone from the adapter.
345
- */
346
- static open(adapter: StackAdapter, opts?: StackOptions): Promise<Stack>;
347
- /**
348
- * Idempotent bootstrap for StackOptions.ownerProfile. Filters by family
349
- * only (content filtering is capability-gated) and matches `content.did`
350
- * in memory, cursor-walking so an owner card past page one isn't missed
351
- * and duplicated. The probe must be blind to nothing the binding rules
352
- * see, or it mints a card they then refuse and the stack won't open with
353
- * `ownerProfile`: soft-deleted cards still reserve their `did`, and a card
354
- * migrated to a later version still holds one.
355
- * See docs/spec/identity.md § DID bindings.
356
- */
357
- private ensureOwnerEntity;
358
- get ownerEntityId(): EntityId;
359
- getEntityByDid(did: EntityId): Promise<StackRecord | null>;
360
- getOwnerEntity(): Promise<StackRecord | null>;
361
- get timezone(): string | undefined;
362
- get capabilities(): StackCapabilities;
363
- /**
364
- * Get a permission-scoped view of this Stack, as if requests came from
365
- * the given entity acting as itself (null = anonymous). Plain Stack
366
- * methods are unscoped; use asEntity() when one Stack serves multiple,
367
- * possibly untrusted, entities. See
368
- * docs/spec/access-control.md § Enforcement: Stack.asEntity().
369
- */
370
- asEntity(entityId: EntityId | null): ScopedStack;
371
- /**
372
- * Scope to an Actor — a delegated app acting for its user, or a
373
- * TokenSession exactly as StackTokenStore.lookupToken() returns it, which
374
- * is what a server should reach for at its request boundary. Authority is
375
- * the intersection of both parties' grants, while authorship and `-own`
376
- * resolve against the subject. Taking the pair whole leaves no order to
377
- * swap. See docs/spec/access-control.md § Delegation: principal and subject.
378
- */
379
- asActor(actor: Actor): ScopedStack;
380
- private scope;
381
- /**
382
- * Define and persist a Type; call at app startup before creating records
383
- * of the type. Redefining an existing typeId is checked against the
384
- * stored schema: identical is a no-op, a name-only change persists, and
385
- * anything beyond additive evolution throws StackSchemaDriftError. See
386
- * docs/spec/data-model.md § Schema drift detection.
387
- */
388
- defineType({ id, name, schema: declared, migratesFrom, }: DefineTypeOptions): Promise<StackType>;
389
- getType(id: TypeId): Promise<StackType | null>;
390
- /** Refreshes typeCache wholesale — the explicit way to see a rename made by another writer. */
391
- listTypes(): Promise<StackType[]>;
392
- /**
393
- * Check whether a record's type is compatible with a required schema.
394
- * Useful for duck-typed consumption across types.
395
- */
396
- typeIsCompatible(typeId: TypeId, requiredSchema: TypeSchema): Promise<boolean>;
397
- /**
398
- * Register a migration function between two adjacent Type versions; call
399
- * at app startup after defineType(). Runs in-memory — nothing is written
400
- * until migrateAll(). Adjacent migrations are composed into chains
401
- * automatically.
402
- */
403
- registerMigration(migration: Migration): void;
404
- /**
405
- * Find and compose a migration path from one TypeId to another.
406
- * Returns null if no path exists.
407
- */
408
- private resolveMigrationPath;
409
- /**
410
- * Find the latest registered version of a type family.
411
- * Follows the migration chain from the given typeId to the end.
412
- */
413
- private latestTypeId;
414
- /**
415
- * Eagerly migrate all records of a type family to the latest version —
416
- * the only way disk state changes version. Sweeps soft-deleted records
417
- * too, validates each result before writing, and aborts on the first
418
- * validation failure. See docs/spec/data-model.md § Type migrations.
419
- */
420
- migrateAll(baseId: BaseId): Promise<{
421
- migrated: number;
422
- }>;
423
- /**
424
- * Create a new record. Validates content against the type's schema.
425
- * `_group` records get their author stamped as the first `admin` roster
426
- * association here — the single stamping site for both Stack.create()
427
- * and ScopedStack.create(). `opts.createdAt`/`updatedAt` let a caller
428
- * backdate an imported record's clock fields — unconditionally here;
429
- * ScopedStack.create() forwards to this same method, but only reaches
430
- * this far with them when the requester is the stack owner acting alone
431
- * — see BackdatableCreateRecordOptions and docs/spec/data-model.md §
432
- * Record IDs.
433
- */
434
- create<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, content: ContentOf<S>, opts?: BackdatableCreateRecordOptions): Promise<TypedRecord<S>>;
435
- create<T extends Record<string, unknown> = Record<string, unknown>>(typeId: TypeId, content: T, opts?: BackdatableCreateRecordOptions): Promise<StackRecord & {
436
- content: T;
437
- }>;
438
- /**
439
- * Apply the registered migration chain in memory, for presentAt:
440
- * 'latest'. Never writes back. Throws StackMigrationError when the
441
- * record's version can't be reconciled with what this instance has
442
- * registered — the stale-writer case. See docs/spec/data-model.md
443
- * § Type migrations.
444
- */
445
- private presentAtLatest;
446
- /**
447
- * Get a record by ID, exactly as stored — no implicit migration. Pass
448
- * { presentAt: 'latest' } to migrate in memory; only migrateAll()
449
- * commits migrations to disk. A soft-deleted record answers `null` unless
450
- * { includeDeleted: true } is passed.
451
- */
452
- get<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, id: RecordId): Promise<TypedRecord<S> | null>;
453
- get(id: RecordId, opts?: GetRecordOptions): Promise<StackRecord | null>;
454
- /**
455
- * Apply a change set as one atomic write — see the StackClient
456
- * declaration above for what it accepts. Atomicity is what it is for: a
457
- * publish is one act, so nothing it names may half-land.
458
- *
459
- * Every key present is checked against the record as it stands, and a set
460
- * already satisfied in all of them writes nothing. Content is validated
461
- * against the record's *current* stored type; `typeId` never changes here.
462
- * See docs/spec/data-model.md § Mutations.
463
- */
464
- mutate<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, id: RecordId, changes: TypedChangeSet<S>, opts?: IfVersionOptions & ActorOptions): Promise<TypedRecord<S>>;
465
- mutate(id: RecordId, changes: RecordChangeSet, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
466
- /**
467
- * mutate() with `contentPatch` alone. The common case by a wide margin,
468
- * and named for what it does rather than for a symmetry with create()
469
- * that a patch does not have.
470
- */
471
- patchContent<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, id: RecordId, patch: PatchOf<S>, opts?: IfVersionOptions & ActorOptions): Promise<TypedRecord<S>>;
472
- patchContent(id: RecordId, patch: Record<string, unknown | null>, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
473
- /**
474
- * Every check a change set owes before anything is written, in one pass:
475
- * one refused key refuses the call, so none of them may run after a
476
- * partial write. Returns the merged content when the set carries a
477
- * content patch, since the caller needs it to decide whether content
478
- * actually moved.
479
- *
480
- * Parent existence and acyclicity are asked only when the destination
481
- * differs from where the record already sits — a move to where it
482
- * already is names no new edge, and would otherwise cost a read apiece.
483
- */
484
- private validateChangeSet;
485
- /**
486
- * Refuse a write that would leave a `_group` Record with no `admin` on its
487
- * roster. Asked of the roster the write would *produce*, which is the
488
- * whole of the self-removal question: an admin removing themselves passes
489
- * while another remains and is refused when they are the last, without
490
- * either case naming who is going.
491
- *
492
- * An integrity constraint on the Record, not a permission question, so it
493
- * lives here rather than in `ScopedStack` and binds every requester — the
494
- * stack owner included. See docs/spec/identity.md § Group.
495
- */
496
- private assertGroupAdminRemains;
497
- /**
498
- * Add associations to a record — no-bump and no snapshot, per the
499
- * StackClient declaration above.
500
- *
501
- * An association the record already holds, saying the same thing, is a
502
- * no-op; one matching an existing association's identity but naming a
503
- * different `attachmentRecordId` — or none, which clears the one stored —
504
- * re-points it in place rather than adding a second reference to the same
505
- * file. What a re-point overwrote is kept only by the journal, on that
506
- * entry's `repoint`. See docs/spec/journal.md § The entry.
507
- */
508
- associate(id: RecordId, associations: DataAssociation[], opts?: ActorOptions): Promise<StackRecord>;
509
- /**
510
- * Remove associations from a record — see associate(). Matched by kind,
511
- * label and payload; an element not found is a no-op. The emitted event
512
- * names each association by identity only, since an attachment's
513
- * `attachmentRecordId` describes nothing current once it is gone; the
514
- * journal is where it survives.
515
- */
516
- dissociate(id: RecordId, associations: DataAssociation[], opts?: ActorOptions): Promise<StackRecord>;
517
- /**
518
- * The atomic form of associate() and dissociate(): one list of adds and
519
- * removes, one adapter write, one journal entry. The journal records each
520
- * element in full — a remove with its annotation, a re-point with what it
521
- * overwrote — so the entry is as undoable as a single-element one.
522
- * See docs/spec/data-model.md § Mutations.
523
- */
524
- amendAssociations(id: RecordId, changes: AssociationEdit[], opts?: ActorOptions, surface?: string): Promise<StackRecord>;
525
- /**
526
- * Extend who reaches a record. The spelling that survives two admins
527
- * sharing a record at once: a `permissions` key write replaces the whole
528
- * set, so the later of two concurrent ones drops what the earlier
529
- * granted. No-bump, like associate(); an element the record already
530
- * carries is a no-op. `write` never implies `read` — name both.
531
- */
532
- grantAccess(id: RecordId, permissions: AuthorityAssociation[], opts?: ActorOptions): Promise<StackRecord>;
533
- /**
534
- * Withdraw elements of who reaches a record — see grantAccess() for why
535
- * this is a verb rather than a key write. An element the record does not
536
- * carry is a no-op. Returns the record as it now stands.
537
- */
538
- revokeAccess(id: RecordId, permissions: AuthorityAssociation[], opts?: ActorOptions): Promise<StackRecord>;
539
- /**
540
- * The atomic form of grantAccess() and revokeAccess(). The
541
- * write-implies-read invariant is asked once, of the set the whole list
542
- * produces. See docs/spec/access-control.md § Write implies read.
543
- */
544
- amendAccess(id: RecordId, changes: AssociationEdit[], opts?: ActorOptions, surface?: string): Promise<StackRecord>;
545
- /**
546
- * Hold a permission set to the cross-element invariant — see
547
- * validatePermissions() in access.ts for why it reads the post-state.
548
- */
549
- private assertPermissionSet;
550
- /**
551
- * A soft-deleted record has no current state to edit, so the verbs that
552
- * edit one refuse it; undelete() and commitMigration() do not call this.
553
- * Asked after the record is found and, under ScopedStack, after the
554
- * authority decision. See docs/spec/versioning.md § Mutations are refused,
555
- * not applied to a tombstone.
556
- */
557
- private refuseIfDeleted;
558
- /** The record, or the not-found refusal every mutating verb owes. */
559
- private requireRecord;
560
- /**
561
- * The checks a *caller-named* destination owes, before the cycle walk:
562
- * `parentId` is a real, well-formed record id. Format first, so a
563
- * malformed one is a 400 naming the problem rather than a read that
564
- * cannot match. Existence closes the gap between the owner path and a
565
- * non-owner's, where canReadReferent() already refuses a parent that
566
- * isn't there.
567
- *
568
- * restoreVersion() deliberately does not call this — see its own comment.
569
- * See docs/spec/data-model.md § Reparenting.
570
- */
571
- private assertParentExists;
572
- /**
573
- * Refuse an edge that would make a record its own ancestor — asked at
574
- * every site that adds one. Nothing downstream (a generator deriving a
575
- * page path, a folder view) is written to survive a cycle.
576
- *
577
- * Walks with the unscoped adapter: a walk that skipped the links a
578
- * requester cannot read would let a cycle be assembled through them and
579
- * break the invariant for every reader.
580
- *
581
- * Read-then-write, so two moves racing on opposite ends of one chain can
582
- * both pass — the same deferral as checkBindingUnique() above, since
583
- * closing it would put a graph constraint in the storage contract.
584
- * Consumers walking `parentId` should carry a visited set rather than
585
- * trust this alone. See docs/spec/data-model.md § Reparenting.
586
- */
587
- private assertNoParentCycle;
588
- /**
589
- * Soft-delete a record (default) or purge it permanently, removing
590
- * the record and its version history. Soft delete snapshots and bumps
591
- * version; a no-op if already deleted. `_config` is never deletable
592
- * (docs/spec.md § The `_config` record). See docs/spec/versioning.md
593
- * § Deletion.
594
- *
595
- * A purge reports the files the record referenced. It deletes none of
596
- * them — byte lifetime is not the purged record's to decide — but it
597
- * destroys the only rows naming them, so a caller who means to erase the
598
- * bytes too has nowhere else to read the argument from afterwards. See
599
- * docs/spec/attachments.md § A purge strands the bytes it referenced.
600
- *
601
- * deleteAndReturn() is this plus the record itself; reach for that
602
- * instead when the caller needs the record too — building a response
603
- * body from a separate read beforehand is exactly the race and the extra
604
- * round trip it exists to remove.
605
- */
606
- delete(id: RecordId, opts?: DeleteRecordOptions & ActorOptions): Promise<DeleteResult>;
607
- /**
608
- * delete(), reporting the record it acted on alongside the files it
609
- * stranded: as it stood immediately before destruction for a
610
- * purge, or as the resulting tombstone for a soft delete. Both are
611
- * captured inside the same write that destroys or tombstones the record,
612
- * never a read beforehand — a read-then-delete would leave a window
613
- * where a concurrent write can land, get destroyed or overwritten by
614
- * this call, and never appear in the record this returns.
615
- */
616
- deleteAndReturn(id: RecordId, opts?: DeleteRecordOptions & ActorOptions): Promise<DeleteAndReturnResult>;
617
- /**
618
- * The files a record references, by the same definition
619
- * deleteAttachment() and the garbage sweep use: attachment associations
620
- * and top-level `file-ref` content fields. An `_attachment` record's own
621
- * `fileId` is a plain string by design and is not one, which is why
622
- * purging a metadata record reports nothing — see initSystemTypes().
623
- */
624
- private referencedFileIds;
625
- /**
626
- * Reverse a soft delete. Idempotent — undeleting a record that isn't
627
- * deleted returns it unchanged. Purged records are gone, so this
628
- * throws StackNotFoundError for them just like any other missing record.
629
- * Snapshots and bumps version, same as delete().
630
- */
631
- undelete(id: RecordId, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
632
- /**
633
- * Query records. filter.baseId matches every version of a type family,
634
- * resolved against registered Types. Results come back exactly as
635
- * stored; pass presentAt: 'latest' to migrate in memory. See
636
- * docs/spec/data-model.md § Queries.
637
- */
638
- query<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, query?: TypedQuery): Promise<{
639
- records: TypedRecord<S>[];
640
- cursor: string | null;
641
- }>;
642
- query(query?: StackQuery): Promise<QueryResult>;
643
- /**
644
- * Resolve filter.baseId into a concrete typeId set (intersected with
645
- * filter.typeId when both are given), so adapters never need their own
646
- * baseId concept. Returns EMPTY_FAMILY when the resolved set is empty so
647
- * the caller can short-circuit without an adapter round trip.
648
- */
649
- private resolveBaseIdFilter;
650
- getVersions(id: RecordId, query?: VersionsQuery): Promise<RecordVersion[]>;
651
- /**
652
- * A record's change journal, oldest first — what moved, who moved it, and
653
- * the association deltas nothing else retains. `ScopedStack` applies the
654
- * mutate-surface gate; this layer is unscoped and trusted by definition.
655
- *
656
- * A record that isn't there is StackNotFoundError, a purged one included:
657
- * a destroyed log and an empty one are not the same answer.
658
- * See docs/spec/journal.md § Reading it.
659
- */
660
- getJournal(id: RecordId, query?: JournalQuery): Promise<RecordJournalEntry[]>;
661
- getVersion(id: RecordId, version: number): Promise<RecordVersion | null>;
662
- /**
663
- * Restore a record to a previous version by creating a new version —
664
- * never rewrites history. The snapshot is validated against its own
665
- * stored typeId, and puts back `content` and `typeId` alone: a snapshot
666
- * carries nothing else. See docs/spec/versioning.md § Restore semantics.
667
- */
668
- restoreVersion(id: RecordId, version: number, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
669
- /**
670
- * Commit a per-record migration — the single-record counterpart to
671
- * migrateAll(), with `content` supplied by the caller rather than by a
672
- * registered Migration function. See docs/spec/wire-format.md § Migration
673
- * commit.
674
- *
675
- * Because `content` is a full replacement written under a new `typeId`,
676
- * this is create-shaped at the destination *and* update-shaped over the
677
- * record as it stands, so it owes both sets of integrity checks. Missing
678
- * either half would make migrate a second, unguarded write path to a
679
- * state create()/mutate() refuse to reach.
680
- */
681
- commitMigration(id: RecordId, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
682
- /**
683
- * The checked migration write, shared by commitMigration() and
684
- * migrateAll(). Takes the record already in hand rather than an id: a
685
- * batch pass holds each record from its own query page, and re-fetching
686
- * per record would cost a read apiece for nothing.
687
- *
688
- * Both callers owe the same checks. migrateAll()'s content comes from a
689
- * registered Migration function rather than a request body, but "app
690
- * code" is not a trust boundary here — the app calling commitMigration()
691
- * is the same app that registered the function, and neither may move a
692
- * DID binding or repoint an attachment. registerMigration() also places
693
- * no constraint on `from` and `to` sharing a baseId, so a migration path
694
- * can cross type families; family-crossing is exactly what the checks
695
- * below care about.
696
- */
697
- private commitMigrationChecked;
698
- /** Uniqueness for every unique binding field a newly created card claims. */
699
- private checkBindingsOnCreate;
700
- /**
701
- * Immutability for every binding field a patch touches, then uniqueness
702
- * for the subset that carries it. Fields absent from the patch carry no
703
- * new claim — a content patch is a merge, so an untouched binding is the one the
704
- * card already holds.
705
- */
706
- private checkBindingsOnUpdate;
707
- /**
708
- * Bindings across a migration. `content` is a full replacement, so every
709
- * binding field either keeps its value, moves to a new one, or is shed by
710
- * omission — and immutability refuses the last two. Asked across the
711
- * union of both families' binding fields, so a card can neither shed its
712
- * DID by migrating out of `_entity`/`_app` nor pick one up on the way in.
713
- *
714
- * Uniqueness is asked only of the destination family, excluding the
715
- * record itself. See docs/spec/identity.md § DID bindings.
716
- */
717
- private checkBindingsOnMigrate;
718
- /**
719
- * A unique binding field is what a lookup resolves *by* — an Actor's
720
- * `principalId` by `_app.did`, its `subjectId` by `_entity.did`. Two cards
721
- * claiming one value would leave that lookup without a single answer, and
722
- * ambiguity is all an impersonating card needs. Enforced here rather than
723
- * by schema, since uniqueness is a property of the set, not of the value.
724
- *
725
- * Read-then-write, so two creates racing on one value can both pass:
726
- * closing that means a unique index over a JSON field, which is a
727
- * decision about where uniqueness lives rather than a local fix.
728
- * See docs/spec/identity.md § DID bindings.
729
- */
730
- private checkBindingUnique;
731
- /**
732
- * A binding is permanent once made: uniqueness stops a second card
733
- * claiming a value, but only immutability stops an existing card being
734
- * moved onto one, which reaches the same impersonation by another route.
735
- * Adopting a value is therefore a one-way step, and a subject whose key
736
- * changes gets a new card — matching identity.md's deferral of key
737
- * rotation, where a new key is a new identity rather than the same one
738
- * relabelled. See docs/spec/identity.md § DID bindings.
739
- */
740
- private checkBindingImmutable;
741
- /**
742
- * mimeType is a property of the fileId, not the uploader's perspective:
743
- * the first metadata record for a fileId fixes it, and a conflicting
744
- * later upload is rejected. See docs/spec/attachments.md § The
745
- * `_attachment` record type.
746
- *
747
- * Best-effort by construction — check-then-create with no storage-level
748
- * uniqueness behind it, so two racing first uploads can both land on a
749
- * concurrent server. What survives that race is the *resolution*:
750
- * getAttachmentRecords() returns the candidates in the same total order
751
- * a server serving Content-Type applies, so both sides name the same
752
- * winner however many records exist.
753
- */
754
- private checkAttachmentMimeTypeOnCreate;
755
- /**
756
- * An attachment association's `attachmentRecordId` names an `_attachment`
757
- * record for the same `fileId` — checked here so a reference can't be
758
- * annotated with an unrelated record's filename.
759
- *
760
- * Best-effort, like the mimeType check above: the named record can be
761
- * deleted afterwards, so every reader of the field falls back rather than
762
- * trusting it. `stored` is what the record already holds, asked so a
763
- * change set restating an association is never refused for a pointer the
764
- * record has carried since before the named record was deleted.
765
- * See docs/spec/attachments.md § Naming the upload a reference came from.
766
- */
767
- private checkAttachmentAssociationPointers;
768
- /**
769
- * filename is the only mutable field on an `_attachment@1` record; fileId,
770
- * size and mimeType are immutable, and the correction flow is delete +
771
- * re-upload. `violates` decides what counts as touching one, because the
772
- * two write shapes disagree: a patch names only what it changes, while a
773
- * migration replaces content wholesale and necessarily re-sends all three.
774
- *
775
- * Repointing `fileId` is the one that matters most: an `_attachment@1`
776
- * record naming a fileId is what canAccessFile()'s uploader clause reads,
777
- * so moving an existing record onto another file's hash is a route to
778
- * bytes the record's author never uploaded.
779
- * See docs/spec/attachments.md § The `_attachment` record type.
780
- */
781
- private assertAttachmentImmutable;
782
- /**
783
- * `_config.entityId` defines stack ownership; neither patchContent() nor
784
- * restoreVersion() may change it. A conflict with stack integrity, not a
785
- * schema violation — hence StackConflictError. See docs/spec.md § The
786
- * `_config` record.
787
- */
788
- private checkConfigEntityIdUnchanged;
789
- /**
790
- * Store bytes and create an _attachment@1 metadata record (owner-
791
- * attributed, no createdBy), returning that record — `content.fileId`
792
- * addresses the bytes, `id` addresses the metadata. Delegates to the
793
- * adapter's atomic putAttachmentWithMetadata() when implemented, trusting
794
- * the returned record as backend-authoritative; otherwise falls back to
795
- * bytes-then-create(). See docs/spec/wire-format.md § Attachments.
796
- */
797
- putAttachment(data: Uint8Array, opts: PutAttachmentOptions): Promise<StackRecord & {
798
- content: AttachmentContent;
799
- }>;
800
- getAttachment(fileId: FileId): Promise<Uint8Array>;
801
- /**
802
- * Every `_attachment` record describing `fileId`, earliest-recorded
803
- * first — so `records[0]` is the one that establishes the file's
804
- * mimeType, and firstRecordedAttachment() is only needed by a caller
805
- * narrowing the set further. See docs/spec/attachments.md § Finding a
806
- * `fileId`'s metadata records.
807
- *
808
- * On `Stack` and not `StackClient`: the lookup answers a presentation
809
- * question about an access decision already made, so a scoped version
810
- * would impose a second, different permission check rather than a
811
- * narrower correct answer.
812
- */
813
- getAttachmentRecords(fileId: FileId): Promise<(StackRecord & {
814
- content: AttachmentContent;
815
- })[]>;
816
- /**
817
- * The `_attachment` family as concrete typeIds. Resolved here rather
818
- * than in the adapter, which has no baseId concept of its own — see
819
- * resolveBaseIdFilter().
820
- */
821
- private attachmentTypeIds;
822
- /**
823
- * Delete an attachment's bytes and every _attachment metadata record for
824
- * it, family-wide. Throws StackConflictError if any record in the stack
825
- * still references the file, StackNotFoundError if neither metadata
826
- * records nor bytes exist.
827
- */
828
- deleteAttachment(fileId: FileId, opts?: ActorOptions): Promise<void>;
829
- /**
830
- * Non-atomic fallback for adapters that don't implement
831
- * deleteUnreferencedAttachmentRecords(): a concurrent associate() can
832
- * race between the reference check below and the deletes it guards.
833
- */
834
- private deleteUnreferencedAttachmentRecordsFallback;
835
- /**
836
- * Sweep for attachment bytes unreachable from any record — live or
837
- * soft-deleted — and delete bytes + metadata. Deletion goes through
838
- * deleteAttachment(), so a file re-referenced by sweep time is skipped,
839
- * not a failure. See docs/spec/attachments.md § Garbage collection.
840
- */
841
- collectAttachmentGarbage(opts?: CollectAttachmentGarbageOptions & ActorOptions): Promise<CollectAttachmentGarbageResult>;
842
- /**
843
- * Observe every change made through this Stack. Unscoped, so no
844
- * permission filter applies — a caller holding a `Stack` already reaches
845
- * every record by other means; `ScopedStack.subscribe()` is the filtered
846
- * view. See docs/spec/events.md.
847
- *
848
- * Async so that a remote stack can resolve once its feed is live, which
849
- * makes subscribe-then-query the gap-free startup order everywhere. A
850
- * local stack is live immediately.
851
- */
852
- subscribe<S extends ReadonlyTypeSchema>(handle: TypeHandle<S>, handler: (change: TypedChange<S>) => void, opts?: TypedSubscribeOptions): Promise<Unsubscribe>;
853
- subscribe(handler: (change: RecordChange) => void, opts?: SubscribeOptions): Promise<Unsubscribe>;
854
- /**
855
- * Ask the adapter to relay changes that originated elsewhere. Absent on
856
- * every local adapter, where one process owns the storage and there is
857
- * no third party whose writes could have been missed.
858
- *
859
- * The subscription's own filter goes to the relay rather than being
860
- * applied on the way back: the emitter at the far end holds the record,
861
- * so it can answer `createdBy` and `parentId`, which the envelope
862
- * deliberately does not carry. That is also why a relay is opened per
863
- * subscription rather than shared.
864
- */
865
- private openRelay;
866
- /** Whether changes reach this stack from elsewhere. */
867
- private get relaysChanges();
868
- /**
869
- * Flush pending writes to the underlying storage. A no-op for adapters
870
- * that commit on every call (SQLite, the API adapter); meaningful for
871
- * ones that buffer, and for checkpointing a stack that stays open —
872
- * close() covers the teardown case on its own.
873
- */
874
- flush(): Promise<void>;
875
- /**
876
- * Flush, then release any resources the adapter holds (connections, file
877
- * handles, lock files). A failed flush still releases them before it
878
- * propagates: an unwritable stack must not also leak a lock file.
879
- * See docs/spec/adapters.md § Lifecycle.
880
- */
881
- close(): Promise<void>;
882
- /** Throws once close() has run. */
883
- private assertOpen;
884
- /**
885
- * Create a _grant record authorizing `grantee` to take `actions` on
886
- * records of `typeId`: `{ kind: 'entity' }` for one DID, `{ kind: 'group' }`
887
- * for a `_group` Record's roster at a role, `{ kind: 'authenticated' }` for
888
- * any authenticated entity. The type-level mirror of grantAccess() —
889
- * subject first, grantee inside the element.
890
- *
891
- * Granting an **app** a `-own` action does not contain it the way the
892
- * suffix suggests: when that app acts for someone, `-own` is read as the
893
- * bare verb and the subject decides which records are in reach, so in a
894
- * personal stack — where nearly every record is owner-authored — a
895
- * delegated `read-own` is close to `read-any`. Grant an app the types it
896
- * needs, not the suffix that looks narrowest. See
897
- * docs/spec/access-control.md § Delegation: principal and subject.
898
- *
899
- * `baseId` names the whole family and is refused when it carries an
900
- * `@version` suffix. See docs/spec/access-control.md § Type-level grants.
901
- */
902
- grantType(baseId: BaseId, grant: TypeGrant): Promise<StackRecord & {
903
- content: GrantContent;
904
- }>;
905
- /**
906
- * List _grant records. Omit `query` for all grants;
907
- * `{ kind: 'authenticated' }` for only default grants;
908
- * `{ kind: 'group' }` for grants naming that exact group and role, or
909
- * `role: 'any'` for every grant naming the group;
910
- * `{ kind: 'entity' }` for the grants that currently apply to that entity
911
- * (ones naming them, ones naming a group they belong to at a role they
912
- * hold, plus every default grant) — the same resolution hasGrant() uses.
913
- *
914
- * Every arm but `entity` answers identity — what `grantType()` would have
915
- * written with the same argument. The `entity` arm answers coverage, so
916
- * its result is not a preview of what `revokeType()` would withdraw.
917
- * See docs/spec/access-control.md § Type-level grants.
918
- */
919
- listTypeGrants(query?: GrantQuery): Promise<(StackRecord & {
920
- content: GrantContent;
921
- })[]>;
922
- /**
923
- * The inverse of grantType(): soft-deletes the _grant records on
924
- * `baseId`'s family matching `grant`, at the same granularity
925
- * grantType() writes — the grantee is matched whole, role included, and
926
- * the actions exactly. A soft delete like any other — the owner can
927
- * undelete a revocation.
928
- *
929
- * Returns the grants it withdrew, as they stood: an array, where
930
- * revokeAccess() returns one Record, because one target can match several
931
- * _grant records. An empty result is not an error — re-running a
932
- * revocation has to stay safe. See
933
- * docs/spec/access-control.md § Listing and revoking.
934
- */
935
- revokeType(baseId: BaseId, grant: TypeGrant): Promise<(StackRecord & {
936
- content: GrantContent;
937
- })[]>;
938
- /**
939
- * Actions must be known GrantAction values; the target is held to the
940
- * same rule every `_grant` write meets — see validateGrantBaseId().
941
- */
942
- private checkGrantValid;
943
- private seedSystemTypes;
944
- /**
945
- * Fast-fail for the ifVersion precondition using the already-fetched
946
- * record. The adapter re-checks atomically at write time (the source of
947
- * truth for concurrent writers) — this just skips validation and
948
- * snapshotting work when the mismatch is already visible.
949
- */
950
- private checkIfVersion;
951
- /**
952
- * The options every version-bumping adapter write carries: the `ifVersion`
953
- * precondition, the prior-state snapshot that has to land in the same
954
- * atomic write, and who to attribute the change to. Taken together so a
955
- * new mutating verb cannot quietly omit one — the actor's `principalId`
956
- * most of all, whose absence reads as an undelegated write.
957
- * See docs/spec/versioning.md § Version history.
958
- */
959
- private writeOptions;
960
- /**
961
- * Snapshot of a record's prior state, passed with the mutating adapter
962
- * call so snapshot and mutation land in one atomic write. Carries what
963
- * only a snapshot preserves: `content` and the `typeId` it is read
964
- * under. Containment, listing and associations — the authority ones
965
- * among them — are all kept by the journal instead, so no version ever
966
- * snapshots them and there is nothing for a restore to roll them back
967
- * to. See docs/spec/versioning.md § Version history.
968
- */
969
- private buildVersionSnapshot;
970
- }
971
- //# sourceMappingURL=stack.d.ts.map