@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/types.d.ts DELETED
@@ -1,1250 +0,0 @@
1
- /**
2
- * Stack — Core Type Definitions
3
- * -------------------------------------------------------
4
- * This file is the source of truth for all types in the
5
- * Stack library. No logic lives here — just shapes.
6
- */
7
- /**
8
- * Crockford base-32 encoded ID — time-sortable, unique within a stack.
9
- * Format: 9-char timestamp prefix + 3-char random suffix = 12 chars total.
10
- * Human-readable and URL-safe. Uniqueness is within-stack only;
11
- * cross-stack references must include a stackUrl to disambiguate.
12
- */
13
- export type RecordId = string;
14
- /** Namespaced, versioned type identifier e.g. "com.example.myapp/note@2" */
15
- export type TypeId = string;
16
- /** A type family: a TypeId without its version, e.g. "com.example.myapp/note" */
17
- export type BaseId = string;
18
- /** Opaque file identifier returned by putBlob() */
19
- export type FileId = string;
20
- /**
21
- * Reverse-DNS identifier for the software that wrote a Record, e.g.
22
- * "com.example.myapp" — not a RecordId. Self-reported and never a
23
- * permission input; `StackRecord.createdBy.principalId` is the verified
24
- * counterpart.
25
- * See docs/spec/identity.md § App.
26
- */
27
- export type AppId = string;
28
- /**
29
- * Identifies a "who" — a DID string, e.g. "did:key:z6Mk...". A
30
- * self-certifying identifier that means the same thing in every stack,
31
- * unlike a RecordId. did:key is the mandatory floor method (see did.ts).
32
- * See docs/spec/identity.md.
33
- */
34
- export type EntityId = string;
35
- /**
36
- * Who did something, and through which principal. `subjectId` is who the
37
- * act is attributed to; `principalId` is who authenticated, present only
38
- * when it differs — a delegated app acting for its user. `Stack` stores
39
- * a `principalId` equal to `subjectId` as absent, so the two spellings of
40
- * "acted as itself" never diverge. See docs/spec/data-model.md § Actor.
41
- */
42
- export type Actor = {
43
- subjectId: EntityId;
44
- principalId?: EntityId;
45
- };
46
- export type TagAssociation = {
47
- kind: 'tag';
48
- label: string;
49
- };
50
- /**
51
- * `attachmentRecordId` names the `_attachment` record whose upload
52
- * established this reference — annotation, not identity, since `fileId`
53
- * alone is what GC, the `referencesFileId` filter and file access ask
54
- * about. See docs/spec/attachments.md § Naming the upload a reference came from.
55
- */
56
- export type AttachmentAssociation = {
57
- kind: 'attachment';
58
- label: string;
59
- fileId: FileId;
60
- attachmentRecordId?: RecordId;
61
- };
62
- /**
63
- * A Record in this stack, or in another one. `stackUrl` is what makes a
64
- * RecordId meaningful outside the stack that minted it — absent means this
65
- * stack. See docs/spec/data-model.md § Associations.
66
- */
67
- export type RecordTarget = {
68
- kind: 'record';
69
- recordId: RecordId;
70
- stackUrl?: string;
71
- };
72
- /**
73
- * A "who" — a DID, which means the same thing in every stack. Group
74
- * rosters are the canonical use: membership names identities, not records.
75
- * See docs/spec/identity.md § Group.
76
- */
77
- export type EntityTarget = {
78
- kind: 'entity';
79
- entityId: EntityId;
80
- };
81
- /**
82
- * Something outside the stack entirely. `ns` names the scheme that
83
- * interprets `id` — e.g. "atproto", "activitypub", "email", "url" — so an
84
- * app selects on it rather than sniffing the identifier.
85
- * See docs/spec/data-model.md § Relationship targets.
86
- */
87
- export type ExternalTarget = {
88
- kind: 'external';
89
- ns: string;
90
- id: string;
91
- };
92
- /** What a relationship association points at. */
93
- export type RelationshipTarget = RecordTarget | EntityTarget | ExternalTarget;
94
- export type RelationshipAssociation = {
95
- kind: 'relationship';
96
- label: string;
97
- target: RelationshipTarget;
98
- };
99
- /**
100
- * A grant of access to the Record carrying it. The bit is the label, so
101
- * granting and revoking are a plain add and remove and the element's
102
- * identity carries its whole meaning. Its grantee shape is its own rather
103
- * than a `RelationshipTarget`: a target names no role and has no group
104
- * arm. See docs/spec/access-control.md § Record-level permissions.
105
- */
106
- export type PermissionAssociation = {
107
- kind: 'permission';
108
- label: 'read' | 'write';
109
- grantee: Grantee;
110
- };
111
- /** An entity's standing within a `_group` Record's roster. `admin` implies `member` for ACL purposes. */
112
- export type GroupRole = 'member' | 'admin';
113
- /**
114
- * The grantee arms both permission layers share, spelled identically so a
115
- * value built for one is valid for the other. `role` is required — `member`
116
- * is the wider set, `admin` the narrower, matching the roster labels where
117
- * admin implies member. See docs/spec/access-control.md § Who a grant reaches.
118
- */
119
- export type Grantee = {
120
- kind: 'entity';
121
- entityId: EntityId;
122
- } | {
123
- kind: 'group';
124
- groupId: RecordId;
125
- role: GroupRole;
126
- };
127
- /**
128
- * Reach to the world, spelled affirmatively: it names no grantee and
129
- * carries no bit beyond `read`, so no dropped field on a
130
- * `PermissionAssociation` can produce it.
131
- * See docs/spec/access-control.md § Record-level permissions.
132
- */
133
- export type AnyoneAssociation = {
134
- kind: 'anyone';
135
- label: 'read';
136
- };
137
- /**
138
- * The authority half of the association table — what the `permissions`
139
- * field projects. See docs/spec/access-control.md § Record-level
140
- * permissions.
141
- */
142
- export type AuthorityAssociation = PermissionAssociation | AnyoneAssociation;
143
- /**
144
- * The data half — what the `associations` field projects, and the only
145
- * kinds `associate()`/`dissociate()` accept.
146
- * See docs/spec/data-model.md § Associations.
147
- */
148
- export type DataAssociation = TagAssociation | AttachmentAssociation | RelationshipAssociation;
149
- /**
150
- * Every edge a Record carries, authority and data alike. One shape, one
151
- * delta and one durability tier; the two halves stay separate call
152
- * surfaces because they carry different authority.
153
- * See docs/spec/access-control.md § Record-level permissions.
154
- */
155
- export type Association = DataAssociation | AuthorityAssociation;
156
- /**
157
- * One association a write moved, and what it moved from. Every inverse is
158
- * local to one element — there is no join back to a sibling list, so there
159
- * is no join key to get wrong. `previous` is carried in full, annotation
160
- * included, which is what makes a `repoint` undoable; a `remove` names the
161
- * element it took away in `association`, in full.
162
- * See docs/spec/journal.md § The entry.
163
- */
164
- export type AssociationChange = {
165
- op: 'add';
166
- association: Association;
167
- } | {
168
- op: 'repoint';
169
- association: Association;
170
- previous: Association;
171
- } | {
172
- op: 'remove';
173
- association: Association;
174
- };
175
- /**
176
- * What a caller sends: the journal's shape without `repoint`, which the
177
- * journal records and no caller requests. See docs/spec/journal.md § The entry.
178
- */
179
- export type AssociationEdit = Exclude<AssociationChange, {
180
- op: 'repoint';
181
- }>;
182
- /**
183
- * The aspects of an existing record one mutate() call may move, in any
184
- * combination. Every key replaces the aspect it names except
185
- * `contentPatch`, which merges at the top level — omitted keeps, `null`
186
- * removes. Keys are read for presence, so `unlisted: false` and
187
- * `parentId: null` are changes; an omitted key is untouched.
188
- *
189
- * `associations` replaces the whole set. associate()/dissociate() amend it
190
- * instead, which is the spelling that survives two writers touching one
191
- * record, so they stay their own verbs rather than folding in here.
192
- * See docs/spec/data-model.md § Mutations.
193
- */
194
- export type RecordChangeSet = {
195
- contentPatch?: Record<string, unknown | null>;
196
- parentId?: RecordId | null;
197
- permissions?: AuthorityAssociation[];
198
- associations?: DataAssociation[];
199
- unlisted?: boolean;
200
- };
201
- /** The keys of a change set, for presence checks that must not miss one. */
202
- export declare const RECORD_CHANGE_SET_KEYS: readonly ["contentPatch", "parentId", "permissions", "associations", "unlisted"];
203
- export type StackRecord = {
204
- id: RecordId;
205
- typeId: TypeId;
206
- createdAt: Date;
207
- updatedAt: Date;
208
- content: Record<string, unknown>;
209
- /**
210
- * Ordinal of the snapshot history, not a count of changes: associations,
211
- * permissions, parent and listing moves leave it alone. See
212
- * docs/spec/versioning.md § Version history.
213
- */
214
- version: number;
215
- parentId?: RecordId;
216
- appId?: AppId;
217
- /**
218
- * The record's author, stamped once by the create. A `principalId` here
219
- * is what lets `appId` be checked against the `_app` record naming that
220
- * DID; without one, `appId` is an unverifiable self-report.
221
- * See docs/spec/data-model.md § Authorship and attribution.
222
- */
223
- createdBy?: Actor;
224
- /**
225
- * Who performed the mutation this version records — unlike `createdBy`,
226
- * it moves with every write. Absent means an unscoped `Stack` wrote it,
227
- * which names no requester.
228
- * See docs/spec/data-model.md § Authorship and attribution.
229
- */
230
- updatedBy?: Actor;
231
- deletedAt?: Date;
232
- /**
233
- * Present when the record is withheld from enumeration — absent from
234
- * `query()` and the change feed by default, but still reachable by
235
- * `get()` for anyone who may read it. Orthogonal to `permissions`: it
236
- * says nothing about who may read the record, only whether its
237
- * existence is discoverable without already holding its ID. See
238
- * docs/spec/unlisted.md.
239
- */
240
- unlistedAt?: Date;
241
- /**
242
- * Who reaches this Record, projected from the association table's
243
- * authority kinds. Absent or empty means private — readable only by the
244
- * stack owner. Declarative intent; enforcement is `ScopedStack`'s.
245
- * See docs/spec/access-control.md § Record-level permissions.
246
- */
247
- permissions?: AuthorityAssociation[];
248
- associations?: DataAssociation[];
249
- };
250
- export type RecordVersion = {
251
- version: number;
252
- /** The record's typeId at the moment this version was snapshotted. */
253
- typeId: TypeId;
254
- content: Record<string, unknown>;
255
- updatedAt: Date;
256
- /**
257
- * The record's author, carried through from `record.createdBy` — not the
258
- * actor of the change this version records.
259
- * See docs/spec/versioning.md § Version history.
260
- */
261
- createdBy?: Actor;
262
- /** Who performed the mutation that produced this version. */
263
- updatedBy?: Actor;
264
- };
265
- export type ScalarFieldKind = 'string' | 'number' | 'boolean' | 'date' | 'text' | 'record-ref' | 'file-ref';
266
- /**
267
- * `enum` is a non-empty, duplicate-free list of the strings the field may
268
- * hold. See docs/spec/data-model.md § Types.
269
- */
270
- export type StringFieldDef = {
271
- kind: 'string';
272
- enum?: string[];
273
- required?: boolean;
274
- };
275
- export type ScalarFieldDef = StringFieldDef | {
276
- kind: Exclude<ScalarFieldKind, 'string'>;
277
- required?: boolean;
278
- };
279
- /**
280
- * A list. `open: true` leaves its elements unvalidated,
281
- * which is how a heterogeneous or null-bearing list is spelled. Opacity is
282
- * declared rather than inferred from a missing `items`, so a schema that
283
- * forgets to describe its elements is a type error rather than a silently
284
- * unchecked field. See docs/spec/data-model.md § Undeclared content fields.
285
- */
286
- export type ArrayFieldDef = {
287
- kind: 'array';
288
- items: FieldDef;
289
- open?: false;
290
- required?: boolean;
291
- } | {
292
- kind: 'array';
293
- open: true;
294
- items?: undefined;
295
- required?: boolean;
296
- };
297
- /**
298
- * An object. `open: true` leaves its keys unvalidated,
299
- * which is the one way to store a shape a schema does not describe. Same
300
- * reason as ArrayFieldDef for making it a declaration rather than an
301
- * omission. See docs/spec/data-model.md § Undeclared content fields.
302
- */
303
- export type ObjectFieldDef = {
304
- kind: 'object';
305
- properties: TypeSchema;
306
- open?: false;
307
- required?: boolean;
308
- } | {
309
- kind: 'object';
310
- open: true;
311
- properties?: undefined;
312
- required?: boolean;
313
- };
314
- /**
315
- * A field definition in a Type schema. Supports scalars, arrays, and nested
316
- * objects. Arrays and nested objects are schema-validated on write but are
317
- * opaque to the query engine in v1 — only top-level scalar fields support
318
- * exact-match content filtering in queries.
319
- */
320
- export type FieldDef = ScalarFieldDef | ArrayFieldDef | ObjectFieldDef;
321
- export type TypeSchema = {
322
- [fieldName: string]: FieldDef;
323
- };
324
- export type StackType = {
325
- id: TypeId;
326
- baseId: BaseId;
327
- version: number;
328
- name: string;
329
- schema: TypeSchema;
330
- schemaHash: string;
331
- migratesFrom?: TypeId;
332
- createdAt: Date;
333
- };
334
- /**
335
- * Content for _entity records: a stack-local profile card *about* a DID,
336
- * not the identity itself. `name`/`handle` are this owner's local labels
337
- * for that DID (the petname pattern), so two stacks holding different
338
- * names for the same DID is correct. See docs/spec/identity.md § Entity.
339
- */
340
- export type EntityContent = {
341
- /**
342
- * The identity this profile is about. e.g. "did:key:z6Mk...". A binding,
343
- * not a value — an Actor's `subjectId` resolves through it to the name
344
- * this card carries, so it is unique per stack and immutable once set.
345
- * See docs/spec/identity.md § DID bindings.
346
- */
347
- did: string;
348
- /** Display name — human-friendly, not necessarily unique. May contain spaces and punctuation. e.g. "Jane Smith" */
349
- name: string;
350
- /**
351
- * Short, conventionally URL-safe label. e.g. "janesmith". A label, not a
352
- * key: duplicates are legitimate and `did` is what identifies this
353
- * profile. See docs/spec/identity.md § Entity.
354
- */
355
- handle?: string;
356
- };
357
- /** Content for _app records */
358
- export type AppContent = {
359
- /**
360
- * The software this card is about, in reverse-DNS form — the value a
361
- * Record's `appId` is checked against. Distinct from the card Record's
362
- * own `appId`, which names whatever wrote the card (an admin console
363
- * registering a third-party app is the ordinary case, and the two differ
364
- * there). See docs/spec/identity.md § App.
365
- */
366
- appId: AppId;
367
- /** Display name of the app e.g. "My Notes App" */
368
- name: string;
369
- /** Semver string e.g. "1.0.0". `appId` is the machine-readable identity, so no handle is needed here. */
370
- version?: string;
371
- /**
372
- * The DID this app authenticates with, when it holds a key of its own.
373
- * Lets an attribution UI resolve an Actor's `principalId` to this card,
374
- * and a server check a self-reported `appId` against the principal that
375
- * wrote it. Absent for apps that ride their user's identity. Unique per
376
- * stack and immutable once set, like `appId` above — see
377
- * docs/spec/identity.md § DID bindings.
378
- */
379
- did?: string;
380
- };
381
- /** Content for _group records */
382
- export type GroupContent = {
383
- /** Display name — human-friendly, not necessarily unique. May contain spaces and punctuation. e.g. "Jane's Book Club" */
384
- name: string;
385
- /**
386
- * Short, conventionally URL-safe label. e.g. "janes-book-club". A label,
387
- * not a key: duplicates are legitimate, and a group is addressed by
388
- * `stackUrl` when it has one and by its record id otherwise. See
389
- * docs/spec/identity.md § Group.
390
- */
391
- handle?: string;
392
- /** If present, this group owns a shared collaborative stack at this URL. Absent = permission-only group. */
393
- stackUrl?: string;
394
- };
395
- /**
396
- * Actions that can be granted via a _grant record. The array is the source
397
- * of truth — GrantAction is derived from it so runtime validation (see
398
- * Stack.grantType()) can't drift from the type.
399
- */
400
- export declare const GRANT_ACTIONS: readonly ["create", "read-own", "read-any", "update-own", "update-any", "delete-own", "delete-any"];
401
- export type GrantAction = (typeof GRANT_ACTIONS)[number];
402
- /**
403
- * Who a Grant reaches, spelled affirmatively. Each tier names itself, so
404
- * no dropped field turns one into another: a `_grant` Record that lost its
405
- * grantee names nobody rather than everybody.
406
- *
407
- * `{ kind: 'authenticated' }` reaches any authenticated entity — narrower
408
- * than a Record permission's `{ kind: 'anyone' }`, which reaches anonymous
409
- * requesters too. The two tiers deliberately share no word.
410
- *
411
- * `role` is required on the group arm — `member` is the wider set, `admin`
412
- * the narrower, matching the roster labels where admin implies member (see
413
- * docs/spec/identity.md § Group). A group grantee never satisfies the
414
- * principal half of a delegated request.
415
- * See docs/spec/access-control.md § Type-level grants.
416
- */
417
- export type GrantGrantee = Grantee | {
418
- kind: 'authenticated';
419
- };
420
- /**
421
- * What `Stack.grantType()` and `Stack.revokeType()` take beside the type they act
422
- * on — the type-level counterpart of the `AuthorityAssociation`
423
- * `grantAccess()` takes beside a record id.
424
- */
425
- export type TypeGrant = {
426
- actions: GrantAction[];
427
- grantee: GrantGrantee;
428
- };
429
- /** Content for _grant records */
430
- export type GrantContent = {
431
- /** The type family the grant applies to: a bare baseId, never a versioned TypeId. */
432
- baseId: BaseId;
433
- /** Which actions are permitted. */
434
- actions: GrantAction[];
435
- /** Who the grant reaches. Required — see GrantGrantee. */
436
- grantee: GrantGrantee;
437
- };
438
- /** The metadata an upload's `_attachment@1` record carries beside its bytes. */
439
- export type PutAttachmentOptions = {
440
- mimeType: string;
441
- filename?: string;
442
- /** Attribution for the metadata record, as create()'s `appId`. */
443
- appId?: AppId;
444
- };
445
- /** Content for _attachment records — one per upload, tracks file metadata. */
446
- export type AttachmentContent = {
447
- /** Content-addressed file identifier (SHA-256 hex). */
448
- fileId: FileId;
449
- /** MIME type of the file. */
450
- mimeType: string;
451
- /** File size in bytes. */
452
- size: number;
453
- /** Original filename, if provided at upload time. */
454
- filename?: string;
455
- };
456
- /** Content for _config records — one singleton per stack, created on initialization. */
457
- export type ConfigContent = {
458
- /**
459
- * DID of the stack owner. Immutable — see docs/spec.md § The `_config`
460
- * record for why ownership transfer isn't a field write.
461
- */
462
- entityId: EntityId;
463
- /**
464
- * IANA timezone string e.g. "America/New_York". Optional passthrough app
465
- * metadata — nothing in core reads it for behavior, and there is no
466
- * default. See docs/spec.md § The `_config` record.
467
- */
468
- timezone?: string;
469
- };
470
- /** Reserved system type IDs */
471
- export declare const SYSTEM_TYPES: {
472
- readonly ENTITY: "_entity";
473
- readonly APP: "_app";
474
- readonly GROUP: "_group";
475
- /** Creation-permission grants. See GrantContent. */
476
- readonly GRANT: "_grant";
477
- /** Attachment metadata records. See AttachmentContent. */
478
- readonly ATTACHMENT: "_attachment";
479
- /** Stack-level configuration singleton. See ConfigContent. */
480
- readonly CONFIG: "_config";
481
- };
482
- /**
483
- * A target pattern in `RecordFilter.relatedTo` — the association shape with
484
- * the parts a query may leave open: an `external` target without `id`
485
- * matches its whole namespace.
486
- */
487
- export type RelationshipTargetPattern = {
488
- kind: 'record';
489
- recordId: RecordId;
490
- stackUrl?: string;
491
- } | {
492
- kind: 'entity';
493
- entityId: EntityId;
494
- } | {
495
- kind: 'external';
496
- ns: string;
497
- id?: string;
498
- };
499
- /**
500
- * A relationship query names a label, a target, or both — never neither.
501
- * "Carries any relationship at all" is deliberately not expressible, in
502
- * line with `tags` and `attachment`, which likewise have no match-any
503
- * form. See docs/spec/data-model.md § Filter.
504
- */
505
- export type RelatedToFilter = {
506
- label: string;
507
- target?: RelationshipTargetPattern;
508
- } | {
509
- label?: string;
510
- target: RelationshipTargetPattern;
511
- };
512
- /**
513
- * An attachment query names a label, a file, or both — never neither —
514
- * and both halves together match one association. See
515
- * docs/spec/data-model.md § Filter.
516
- */
517
- export type AttachmentFilter = {
518
- label: string;
519
- fileId?: FileId;
520
- } | {
521
- label?: string;
522
- fileId: FileId;
523
- };
524
- export type DateRange = {
525
- before?: Date;
526
- after?: Date;
527
- };
528
- export type RecordFilter = {
529
- typeId?: TypeId | TypeId[];
530
- /**
531
- * Match every version of a type family — e.g. baseId: "com.example/note"
532
- * matches both "com.example/note@1" and "com.example/note@2" records.
533
- * Resolved against registered Types, not parsed from typeId strings, so
534
- * it works regardless of which versions happen to exist. Combined with
535
- * typeId (if both given) as an intersection. A value carrying an
536
- * `@version` suffix is refused with StackValidationError.
537
- */
538
- baseId?: BaseId | BaseId[];
539
- parentId?: RecordId | null;
540
- appId?: AppId | AppId[];
541
- /** Matches the record's author — never the latest actor, which isn't filterable. */
542
- createdBy?: {
543
- subjectId?: EntityId | EntityId[];
544
- principalId?: EntityId | EntityId[];
545
- };
546
- createdAt?: DateRange;
547
- updatedAt?: DateRange;
548
- tags?: string[];
549
- /** Records carrying a matching attachment association. See docs/spec/data-model.md § Filter. */
550
- attachment?: AttachmentFilter;
551
- /**
552
- * Records carrying a matching relationship association. Either half may
553
- * be given alone and each is a pattern: a bare `label` matches every
554
- * target under it, and an `external` target with no `id` matches the
555
- * whole namespace. An absent `stackUrl` on a `record` target matches
556
- * only local targets. See docs/spec/data-model.md § Filter.
557
- */
558
- relatedTo?: RelatedToFilter;
559
- /**
560
- * Records that reference this file, through an attachment association
561
- * or a top-level `file-ref` content field — the question GC and
562
- * `deleteAttachment()` ask. See docs/spec/attachments.md § Deleting attachments.
563
- */
564
- referencesFileId?: FileId;
565
- /**
566
- * Exact match on content fields, keyed by a dot-separated path
567
- * (`'emails.value'`). An array anywhere along the path is matched
568
- * element-wise, so the motivating question — which contact holds this
569
- * email address — is one filter. A multi-segment key needs the
570
- * `filter.content: 'path'` capability. POST /records/query only.
571
- * See docs/spec/data-model.md § Filter.
572
- */
573
- content?: Record<string, unknown>;
574
- /**
575
- * Paths that must hold a value: the question `content` cannot ask,
576
- * since a filter value matches what is there rather than whether
577
- * anything is. Element-wise like `content`, so a path holds a value
578
- * when at least one non-null value is reachable at it. Needs the
579
- * filter.contentPresent capability. POST /records/query only.
580
- * See docs/spec/data-model.md § Filter.
581
- */
582
- contentPresent?: string[];
583
- search?: string;
584
- includeDeleted?: boolean;
585
- /**
586
- * Unlisted records are excluded by default, like soft-deleted ones.
587
- * Owner-only under `ScopedStack` — enumeration standing rests on nothing
588
- * but ownership, so a grant or delegation never carries it. See
589
- * docs/spec/unlisted.md.
590
- */
591
- includeUnlisted?: boolean;
592
- };
593
- /**
594
- * The Record columns every adapter stores natively, and can order by. The
595
- * array is the source of truth — NativeSortField is derived from it so the
596
- * runtime checks that narrow a wire value or a cursor to this set (see
597
- * parseQuery(), normalizeCapabilities(), and the SQLite adapters' cursor
598
- * decoding) can't drift from the type.
599
- */
600
- export declare const NATIVE_SORT_FIELDS: readonly ["createdAt", "updatedAt", "version"];
601
- export type NativeSortField = (typeof NATIVE_SORT_FIELDS)[number];
602
- /**
603
- * Order by a native column or by a top-level content field — two members
604
- * rather than one widened `field`, because a content field named
605
- * `version` would otherwise be indistinguishable from the native column.
606
- * A `'content.'` prefix can't carry the distinction either: `.` is the
607
- * path separator `parseContentFilterKey()` splits on.
608
- * `direction` defaults to `asc` for any named sort, native or content, as
609
- * `ORDER BY` does; a query with no sort is `createdAt`, newest first.
610
- * See docs/spec/data-model.md § Sorting by a content field and
611
- * § Sorting and pagination.
612
- */
613
- export type QuerySort = {
614
- field: NativeSortField;
615
- contentField?: never;
616
- direction?: 'asc' | 'desc';
617
- } | {
618
- field?: never;
619
- contentField: string;
620
- direction?: 'asc' | 'desc';
621
- };
622
- export type StackQuery = {
623
- filter?: RecordFilter;
624
- sort?: QuerySort;
625
- limit?: number;
626
- cursor?: string;
627
- /**
628
- * Records are returned exactly as stored by default ("stored"). Pass
629
- * "latest" to apply the registered migration chain in memory before
630
- * returning — never written back. Throws StackMigrationError if any
631
- * matched record has no registered path to the latest version.
632
- */
633
- presentAt?: 'stored' | 'latest';
634
- };
635
- /**
636
- * A page of results and where the next one resumes. There is no count of
637
- * the whole match: see docs/spec/data-model.md § Sorting and pagination.
638
- */
639
- export type QueryResult = {
640
- records: StackRecord[];
641
- cursor: string | null;
642
- };
643
- export type MigrationFn = (content: Record<string, unknown>) => Record<string, unknown>;
644
- export type Migration = {
645
- from: TypeId;
646
- to: TypeId;
647
- migrate: MigrationFn;
648
- };
649
- /**
650
- * How far into `content` a filter key may reach. The rungs nest —
651
- * `'path'` is `'field'` plus traversal — so they are one ordered value
652
- * rather than a pair of booleans that can spell a state no adapter can be
653
- * in. A value a client does not recognize reads as `'none'`: refusing a
654
- * query is recoverable, answering it with an unfiltered superset presented
655
- * as a filtered result is not.
656
- * See docs/spec/adapters.md § Adapter capabilities.
657
- */
658
- export type ContentFilterReach =
659
- /** No `filter.content` and no `filter.contentPresent` at all. */
660
- 'none'
661
- /** Single-segment field names only (`'did'`). */
662
- | 'field'
663
- /** Multi-segment paths too (`'emails.value'`). */
664
- | 'path';
665
- /**
666
- * What a Stack — and the adapter under it — honors, grouped by the query surface each entry gates:
667
- * every flag under `filter`/`sort` is named for the query key it answers
668
- * for, so the capability a query needs is derivable from the query rather
669
- * than memorized. `limits` sits apart because a byte ceiling is not a
670
- * feature to gate on but a number to pre-check against.
671
- * See docs/spec/adapters.md § Adapter capabilities.
672
- */
673
- export type StackCapabilities = {
674
- filter: {
675
- /**
676
- * Required `'path'` for local/in-process adapters; a wire adapter may
677
- * report a shallower rung, driven by the server's discovery response.
678
- * Stack.query() throws StackBadRequestError rather than silently widening.
679
- */
680
- content: ContentFilterReach;
681
- /**
682
- * Whether `filter.contentPresent` is honored. Beside the reach ladder
683
- * rather than a rung on it: a server promising to match a content
684
- * value has not thereby promised to answer whether one is there at
685
- * all, and reading it as such hands a client the unfiltered superset
686
- * that ignoring the filter produces. Meaningful only where `content`
687
- * is not `'none'` — presence travels in the `POST /records/query`
688
- * body, which a server reaching no content does not expose.
689
- */
690
- contentPresent: boolean;
691
- /**
692
- * Whether `filter.search` is honored. Not a rung on the ladder: a
693
- * full-text index is a different mechanism from field matching, and
694
- * neither implies the other. Local adapters may decline it.
695
- */
696
- search: boolean;
697
- };
698
- sort: {
699
- /** Which native columns this adapter can order by; see NativeSortField. */
700
- fields: NativeSortField[];
701
- /**
702
- * Whether `sort.contentField` is honored. A boolean rather than names
703
- * in `fields`: content fields are app-defined and unbounded, and an
704
- * adapter that indexes content for sorting indexes every top-level
705
- * scalar, so there is nothing per-field left to declare. Independent
706
- * of `filter.content` — a server may order by a content field without
707
- * offering to filter on one, and filtering is a scan where ordering
708
- * wants an index.
709
- */
710
- contentField: boolean;
711
- };
712
- /**
713
- * Ceilings a client can check before spending a request, never a
714
- * substitute for the server's own limit, which stays authoritative.
715
- * `null` means this client cannot pre-check, not that nothing is
716
- * enforced. Local adapters declare `null` for both: nothing at the
717
- * storage layer imposes a ceiling, and a caller with in-process access
718
- * to the database can spend its own memory however it likes.
719
- * See docs/spec/adapters.md § Adapter capabilities.
720
- */
721
- limits: {
722
- /** Maximum attachment upload size in bytes. */
723
- attachmentBytes: number | null;
724
- /**
725
- * Maximum serialized size in bytes of a Record's content — a create
726
- * body or a merge patch. Stack.create()/Stack.mutate() pre-check
727
- * against it and throw StackPayloadTooLargeError before sending.
728
- */
729
- contentBytes: number | null;
730
- };
731
- };
732
- /**
733
- * A capability a query can be refused for, as its path into
734
- * StackCapabilities — the same name the spec and a discovery response
735
- * use, so an error says which key to look at. `limits` never appears: a
736
- * ceiling is not something a query can lack.
737
- */
738
- export type MissingCapability = 'filter.content' | 'filter.contentPresent' | 'filter.search' | 'sort.fields' | 'sort.contentField';
739
- /**
740
- * Opt-in optimistic-concurrency precondition, accepted by every mutation
741
- * that bumps a record's version. On mismatch the mutation throws
742
- * StackVersionConflictError and changes nothing; omit to keep
743
- * last-writer-wins. An adapter checks it atomically inside its write, never
744
- * as a read-then-write. See docs/spec/versioning.md § Optimistic
745
- * concurrency (`ifVersion`).
746
- */
747
- export type IfVersionOptions = {
748
- ifVersion?: number;
749
- };
750
- /**
751
- * Accepted by every mutating StackRecordAdapter method. The adapter
752
- * persists this prior-state snapshot as part of the SAME atomic write as
753
- * the mutation, so a crash between the two can't leave an orphan versions
754
- * row. Server-backed adapters (already atomic per-request) can ignore it.
755
- */
756
- export type SnapshotOptions = {
757
- snapshot?: RecordVersion;
758
- };
759
- /**
760
- * Whether a mutateRecord() call advances `version`/`updatedAt` at all.
761
- * `Stack` computes this from which aspects a change set actually moves — a
762
- * change set touching only `associations`, `parentId` and/or `unlisted`
763
- * doesn't bump, the same rule StackRecordAdapter.amendAssociations()
764
- * follows unconditionally. Absent means `true`; every other mutating method
765
- * bumps every time, so only mutateRecord() takes this. See
766
- * docs/spec/versioning.md § Version history.
767
- */
768
- export type BumpVersionOptions = {
769
- bumpsVersion?: boolean;
770
- };
771
- /**
772
- * Who is performing a mutation, stamped onto the record in the same write.
773
- * `ScopedStack` supplies it from the request's identities; a caller of
774
- * plain `Stack` supplies it only when reconstructing an attributed write.
775
- * See docs/spec/data-model.md § Authorship and attribution.
776
- */
777
- export type ActorOptions = {
778
- actor?: Actor;
779
- };
780
- /**
781
- * The part of a change that the record cannot report once the write has
782
- * landed: which aspects moved, who moved them, and what an association
783
- * mutation added, removed or overwrote. Everything else a journal entry
784
- * carries — `seq`, `at`, `version`, `typeId`, `parentId` — the adapter
785
- * stamps from the row it just wrote, so the two can never drift.
786
- *
787
- * That split is the whole argument for the tier: content's prior state is
788
- * recoverable from a snapshot, and nothing else's is recoverable from
789
- * anything but this. See docs/spec/journal.md.
790
- */
791
- export type JournalEntryInput = {
792
- ops: ChangeOp[];
793
- kind: ChangeKind;
794
- actor?: ChangeActor;
795
- /** The container a move took the record out of, `null` for the root. */
796
- previousParentId?: RecordId | null;
797
- /**
798
- * What an association mutation moved, one tagged edit per association.
799
- * The journal's own shape, not the feed's two flat lists: a log whose
800
- * whole argument is prior state carries `previous` beside the thing that
801
- * displaced it, rather than leaving a consumer to re-derive which entry
802
- * of one list overwrote which entry of another. See
803
- * docs/spec/journal.md § The entry.
804
- */
805
- associations?: AssociationChange[];
806
- };
807
- /**
808
- * One durable entry in a record's change journal.
809
- *
810
- * Envelope-level by design — `content` lives on a RecordVersion, and
811
- * duplicating it here would make the journal the larger of the two stores
812
- * for no recovery anyone asked for.
813
- * See docs/spec/journal.md § Ordering for why `seq` is the only ordering.
814
- */
815
- export type RecordJournalEntry = JournalEntryInput & {
816
- seq: number;
817
- /** When the entry was appended — not the record's `updatedAt`, which an association change leaves alone. */
818
- at: Date;
819
- /**
820
- * The version this change produced; unchanged from before on
821
- * associate/dissociate, reparent, unlist and list.
822
- */
823
- version: number;
824
- typeId: TypeId;
825
- /** Where the record sat after the change, absent for the root. */
826
- parentId?: RecordId;
827
- };
828
- /**
829
- * Accepted by every mutating StackRecordAdapter method, and by
830
- * amendAssociations(), which takes no other options. The adapter
831
- * appends the entry inside the SAME write as the mutation, so a crash
832
- * between the two cannot leave a change unjournaled.
833
- *
834
- * Unlike SnapshotOptions there is no collision to heal: `seq` is
835
- * allocated by the adapter inside that write rather than computed by
836
- * `Stack` from a value it read earlier.
837
- */
838
- export type JournalOptions = {
839
- journal?: JournalEntryInput;
840
- };
841
- /** Window into a record's journal. Omitting both reads the whole log, oldest first. */
842
- export type JournalQuery = {
843
- /** Entries after this seq, exclusive. */
844
- afterSeq?: number;
845
- limit?: number;
846
- };
847
- /**
848
- * Window into a record's version history. Newest first, the reverse of
849
- * JournalQuery; omitting both reads every version.
850
- * See docs/spec/versioning.md § Version history.
851
- */
852
- export type VersionsQuery = {
853
- /** Versions strictly older than this, exclusive. */
854
- beforeVersion?: number;
855
- limit?: number;
856
- };
857
- /**
858
- * The coarse branch every subscriber makes, closed at four values. A
859
- * handler covering exactly these is complete, not merely adequate:
860
- * `changed` is an upsert signal carrying nine distinct verbs.
861
- * See docs/spec/events.md § The event shape.
862
- */
863
- export type ChangeKind = 'created' | 'changed' | 'removed' | 'purged';
864
- /**
865
- * The precise verb behind a ChangeKind, for consumers that distinguish a
866
- * reshare from an edit. Each is named after the call that produced it; see
867
- * docs/spec/events.md § The event shape for the three that aren't a method
868
- * name. `associate`, `dissociate`, `reshare`, `reparent`, `unlist` and
869
- * `list` don't bump; see docs/spec/versioning.md § Version history.
870
- */
871
- export type ChangeOp = 'create' | 'patch' | 'associate' | 'dissociate' | 'reshare' | 'migrate' | 'restore' | 'delete' | 'undelete' | 'purge'
872
- /**
873
- * Emitted even though the record's post-change state (`unlistedAt` now
874
- * set) would otherwise be excluded by the same filter it announces —
875
- * subscribers who already know the record need telling to drop it. See
876
- * docs/spec/events.md § The unlisted transition.
877
- */
878
- | 'unlist'
879
- /** The publish moment — mechanically an upsert, like `undelete`. */
880
- | 'list'
881
- /**
882
- * A move between containers. Matched by a `parentId` filter naming
883
- * *either* side of the move, so a subscriber watching the old container
884
- * learns the record left it — the record's post-change state alone
885
- * would answer only for the destination. See docs/spec/events.md
886
- * § The reparent transition.
887
- */
888
- | 'reparent';
889
- /**
890
- * Who performed a change — never who authored the record. Absent from a
891
- * RecordChange entirely when unknown, which means a write by an unscoped
892
- * `Stack`: absence is a fact of its own and never stands in for the
893
- * author. See docs/spec/events.md § Attribution.
894
- */
895
- export type ChangeActor = Actor & {
896
- /** Self-reported at create, never a trust input. See AppId. */
897
- appId?: AppId;
898
- };
899
- /**
900
- * One change to one record. The envelope describes the change; the record
901
- * describes the record, so nothing of the record's own provenance appears
902
- * here — a consumer that wants it asks for `record`.
903
- * See docs/spec/events.md § The event shape.
904
- */
905
- export type RecordChange = {
906
- kind: ChangeKind;
907
- /**
908
- * Every aspect this version moved, derived by diffing the record against
909
- * its prior state rather than read off the request — a change set that
910
- * names an aspect without moving it is not reported as moving it. Never
911
- * empty; multi-entry only for a mutate() change set, since every other op
912
- * names a whole-record transition and is emitted alone.
913
- * See docs/spec/events.md § The event shape.
914
- */
915
- ops: ChangeOp[];
916
- recordId: RecordId;
917
- /** As stored at the moment of the change. */
918
- typeId: TypeId;
919
- /**
920
- * The version this change produced; on `purged`, the version destroyed.
921
- * Unchanged from the record's prior version on every op that doesn't
922
- * bump — see ChangeOp.
923
- */
924
- version: number;
925
- /**
926
- * As persisted by this change; on `purged`, when the delete ran.
927
- * Unchanged from the record's prior `updatedAt` on every op that
928
- * doesn't bump.
929
- */
930
- updatedAt: Date;
931
- parentId?: RecordId;
932
- actor?: ChangeActor;
933
- /**
934
- * Associations now present that weren't before — current annotation
935
- * included, so a re-point appears here under its new
936
- * `attachmentRecordId`. Present whenever `ops` includes `associate`, on
937
- * the same "as of this change" convention as every other field here.
938
- */
939
- associationsAdded?: DataAssociation[];
940
- /**
941
- * Associations no longer present, identity only — kind and label, plus
942
- * `fileId` for an attachment — present whenever `ops` includes
943
- * `dissociate`. An attachment's `attachmentRecordId` is never repeated
944
- * here, the same way a `purged` frame never carries what it destroyed.
945
- */
946
- associationsRemoved?: DataAssociation[];
947
- /**
948
- * The record as of this change — present only when asked for and
949
- * available, never on `purged`. Shared with every other subscriber on
950
- * this emission, so a handler that needs to alter it copies first.
951
- */
952
- record?: StackRecord;
953
- /**
954
- * Resume cursor, minted by a server. Local changes carry none, this
955
- * stack's own writes included — so a consumer holding a cursor keeps
956
- * the last one that was present rather than the last change's.
957
- */
958
- cursor?: string;
959
- };
960
- /**
961
- * Applied by the emitter, exactly: a filtered subscription never receives
962
- * an event outside its filter, so filtering again is redundant rather than
963
- * defensive. Every key means what it means on `RecordFilter`, so one value
964
- * can drive both `query()` and `subscribe()`. See docs/spec/events.md
965
- * § Subscribing.
966
- */
967
- export type ChangeFilter = Pick<RecordFilter, 'typeId' | 'baseId' | 'parentId' | 'createdBy'> & {
968
- kinds?: ChangeKind[];
969
- };
970
- /** Ends a subscription. Safe to call more than once. */
971
- export type Unsubscribe = () => void;
972
- export type SubscribeOptions = {
973
- filter?: ChangeFilter;
974
- /** Ask the emitter to include `record`. Honored when it can; never assume it. */
975
- includeRecords?: boolean;
976
- /**
977
- * Receive events for unlisted records too. Owner-only under
978
- * `ScopedStack`, same authority as `RecordFilter.includeUnlisted` — the
979
- * feed excludes unlisted records by default so it never delivers more
980
- * than an equivalent `query()` would return. See
981
- * docs/spec/unlisted.md.
982
- */
983
- includeUnlisted?: boolean;
984
- /**
985
- * Resume from this cursor rather than the present — the last `cursor`
986
- * that was present on a delivered `RecordChange`. A relaying stack delivers
987
- * its own local writes through the same handler and those carry none, so
988
- * a consumer that stores every change's `cursor` unconditionally erases its
989
- * own cursor on its next write.
990
- *
991
- * Forwarded to the adapter as `SubscribeChangesOptions.since`, so it
992
- * only means something where a relay exists: a stack with none has no
993
- * third-party writes to have missed, and therefore no cursor it could
994
- * ever have minted. Passing `since` there throws `StackBadRequestError`
995
- * rather than silently starting from the present, which would let the
996
- * caller believe it resumed when it did not. A cursor outside the
997
- * framable charset is refused the same way, so a malformed one reports
998
- * identically whatever adapter is underneath. See
999
- * docs/spec/events.md § Subscribing.
1000
- */
1001
- since?: string;
1002
- /**
1003
- * Where a throwing handler's error goes. Without one the error is
1004
- * rethrown asynchronously rather than swallowed; either way it never
1005
- * reaches the caller of the mutation that produced the event.
1006
- */
1007
- onError?: (err: unknown) => void;
1008
- /**
1009
- * A gap opened that resumption could not close: reconcile by query.
1010
- * Never fires on a local stack, which has no gap to open. Can fire on
1011
- * the very first connection when `since` names a cursor the far end
1012
- * will not honor — that is a gap too, and the one an app most needs to
1013
- * hear about.
1014
- */
1015
- onReset?: () => void;
1016
- };
1017
- /**
1018
- * What a relay is asked for. The subscriber's options minus the ones only
1019
- * a local emitter answers: a relay reports changes it is told about and
1020
- * decides nothing, so there is no handler of its own to route errors from.
1021
- * `onError` here is the relay's own trouble — a connection it could not
1022
- * restore — and `onReset` the gap that leaves.
1023
- * See docs/spec/events.md § Where events come from.
1024
- */
1025
- export type SubscribeChangesOptions = {
1026
- filter?: ChangeFilter;
1027
- /** Resume from this cursor. A relay with none starts from the present. */
1028
- since?: string;
1029
- includeRecords?: boolean;
1030
- /** See SubscribeOptions.includeUnlisted. */
1031
- includeUnlisted?: boolean;
1032
- onError?: (err: unknown) => void;
1033
- onReset?: () => void;
1034
- };
1035
- /**
1036
- * The record-storage half of an adapter: structured data, queries,
1037
- * associations, versioning, type definitions, and stack identity.
1038
- */
1039
- export interface StackRecordAdapter {
1040
- readonly capabilities: StackCapabilities;
1041
- /** DID of the stack owner. Set during adapter initialization. */
1042
- readonly ownerEntityId: EntityId;
1043
- /**
1044
- * IANA timezone string for this stack, or undefined if never set.
1045
- * Passthrough app metadata with no default — see docs/spec.md § The
1046
- * `_config` record.
1047
- */
1048
- readonly timezone: string | undefined;
1049
- /**
1050
- * Throws StackConflictError if `record.id` already exists — never a
1051
- * silent overwrite. The check must be atomic with the write (a PK/unique
1052
- * constraint or equivalent); Stack itself doesn't pre-check, so a raw
1053
- * adapter that skips this enforces nothing.
1054
- */
1055
- createRecord(record: StackRecord, opts?: JournalOptions): Promise<StackRecord>;
1056
- getRecord(id: RecordId): Promise<StackRecord | null>;
1057
- /**
1058
- * Apply a change set — any combination of content patch, `parentId`,
1059
- * `permissions`, `associations` and `unlisted` — in one write. It is the
1060
- * only multi-aspect atomic write a record has: a publish is one act, so
1061
- * separate per-aspect methods would leave it able to half-land.
1062
- *
1063
- * The content patch merges at the top level only — each key it names is
1064
- * replaced whole. Never touches `typeId`; a type change goes through
1065
- * commitMigration() instead. `associations` replaces the stored set,
1066
- * where amendAssociations() amends it.
1067
- *
1068
- * `opts.bumpsVersion` says whether this call advances `version`/
1069
- * `updatedAt` and stores the snapshot — `false` for a change set that
1070
- * touches only association sets, `permissions` among them, matching
1071
- * amendAssociations() below, which never bumps. `Stack` computes it; an adapter never has to infer it
1072
- * from the change set's own keys.
1073
- *
1074
- * `Stack` owns everything above storage: validation, the acyclicity
1075
- * walk, the per-key gates, and deciding there is anything to write at
1076
- * all — an adapter is never handed a change set that changes nothing.
1077
- * See docs/spec/data-model.md § Mutations and docs/spec/versioning.md
1078
- * § Version history.
1079
- */
1080
- mutateRecord(id: RecordId, changes: RecordChangeSet, opts?: IfVersionOptions & SnapshotOptions & BumpVersionOptions & ActorOptions & JournalOptions): Promise<StackRecord>;
1081
- /**
1082
- * Returns the record this call acted on: as it now stands after a soft
1083
- * delete, and as it stood immediately before destruction after a
1084
- * purge — captured inside the same write, so nothing can observe or alter
1085
- * it in between. Null when there was no record to delete, which is the
1086
- * only case that mutates nothing.
1087
- */
1088
- deleteRecord(id: RecordId, opts?: {
1089
- purge?: boolean;
1090
- } & IfVersionOptions & SnapshotOptions & ActorOptions & JournalOptions): Promise<StackRecord | null>;
1091
- /** Reverse a soft delete. Returns the record as it now stands. */
1092
- undeleteRecord(id: RecordId, opts?: IfVersionOptions & SnapshotOptions & ActorOptions & JournalOptions): Promise<StackRecord>;
1093
- /**
1094
- * `query.sort`, when present, always carries a `direction`: `Stack`
1095
- * resolves the defaults before any adapter sees the query.
1096
- */
1097
- queryRecords(query: StackQuery): Promise<QueryResult>;
1098
- /**
1099
- * Apply a list of adds and removes as one write, all or none, removes
1100
- * before adds. Never bumps `version`/`updatedAt` and never snapshots — a
1101
- * set-add composes regardless of write order. A list mixing authority
1102
- * and data elements is refused.
1103
- * See docs/spec/adapters.md § Amending associations.
1104
- */
1105
- amendAssociations(id: RecordId, changes: AssociationEdit[], opts?: JournalOptions): Promise<StackRecord>;
1106
- /**
1107
- * Snapshots newest first; omitting `query` reads every version.
1108
- * See docs/spec/versioning.md § Version history.
1109
- */
1110
- getVersions(id: RecordId, query?: VersionsQuery): Promise<RecordVersion[]>;
1111
- getVersion(id: RecordId, version: number): Promise<RecordVersion | null>;
1112
- /**
1113
- * Standalone snapshot write, outside of a mutation's own atomic path.
1114
- * Mutating methods above take a `snapshot` option instead, so the
1115
- * snapshot and the mutation land in one write — see SnapshotOptions.
1116
- */
1117
- saveVersion(id: RecordId, version: RecordVersion): Promise<void>;
1118
- /**
1119
- * Read a record's change journal, oldest first — required, on the same
1120
- * footing as getVersions(). A recovery mechanism an app cannot rely on is
1121
- * most of the way to no mechanism at all, so an adapter with no journal
1122
- * refuses the call rather than declining to have the method.
1123
- * See docs/spec/journal.md § Reading it.
1124
- */
1125
- getJournal(id: RecordId, query?: JournalQuery): Promise<RecordJournalEntry[]>;
1126
- /**
1127
- * Restore a record to a previous version's `content` and `typeId` —
1128
- * everything a snapshot carries. Containment, listing and associations
1129
- * are left where they stand, there being nothing to restore them *from*.
1130
- * Bumps version internally; throws StackNotFoundError if the version
1131
- * doesn't exist.
1132
- */
1133
- restoreVersion(id: RecordId, version: number, opts?: IfVersionOptions & SnapshotOptions & ActorOptions & JournalOptions): Promise<StackRecord>;
1134
- /**
1135
- * Commit a migration: write new content under a new typeId in one step.
1136
- * This is the only way a record's typeId changes after creation — used by
1137
- * Stack.commitMigration() and Stack.migrateAll(); Stack.mutate() never
1138
- * changes typeId as a side effect. Bumps version internally.
1139
- */
1140
- commitMigration(id: RecordId, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions & SnapshotOptions & ActorOptions & JournalOptions): Promise<StackRecord>;
1141
- saveType(type: StackType): Promise<void>;
1142
- getType(id: TypeId): Promise<StackType | null>;
1143
- listTypes(): Promise<StackType[]>;
1144
- /**
1145
- * Atomically verify fileId is unreferenced, then purge its
1146
- * metadata records in the same adapter call, returning the deleted records.
1147
- * Throws StackConflictError if still referenced. Optional —
1148
- * Stack.deleteAttachment() has a non-atomic fallback. See
1149
- * docs/spec/attachments.md § Deleting attachments.
1150
- *
1151
- * `metadataTypeIds` is the resolved `_attachment` family, passed as
1152
- * concrete typeIds so an adapter never needs a baseId concept of its
1153
- * own — the same split Stack.query() makes for `filter.baseId`.
1154
- */
1155
- deleteUnreferencedAttachmentRecords?(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
1156
- /**
1157
- * Relay changes that originated elsewhere — the inverse direction from
1158
- * `Stack`, which emits every change made through it. Optional, and
1159
- * absent on every local adapter: exactly one process owns a stack's
1160
- * storage, so locally there is no third party whose writes could have
1161
- * been missed. See docs/spec/events.md § Where events come from.
1162
- */
1163
- subscribeChanges?(opts: SubscribeChangesOptions, handler: (change: RecordChange) => void): Promise<Unsubscribe>;
1164
- flush?(): Promise<void>;
1165
- close?(): Promise<void>;
1166
- }
1167
- /** One stored blob, as reported by StackBlobAdapter.listBlobs(). */
1168
- export type BlobInfo = {
1169
- fileId: FileId;
1170
- size: number;
1171
- /** When the blob was written. Used to apply a GC grace period to fresh, not-yet-associated uploads. */
1172
- modifiedAt: Date;
1173
- };
1174
- /**
1175
- * The blob-storage half of an adapter. Handles raw binary data only;
1176
- * attachment metadata lives on _attachment@1 records in the record adapter.
1177
- */
1178
- export interface StackBlobAdapter {
1179
- putBlob(data: Uint8Array): Promise<FileId>;
1180
- /**
1181
- * Returns a plain `Uint8Array` (never a subclass such as `Buffer`) that the
1182
- * caller owns: changing it never changes the stored bytes.
1183
- */
1184
- getBlob(fileId: FileId): Promise<Uint8Array>;
1185
- deleteBlob(fileId: FileId): Promise<void>;
1186
- /**
1187
- * Enumerate every blob currently in storage. Optional — without it,
1188
- * garbage collection can't find bare-bytes orphans (bytes with no
1189
- * metadata record at all). See docs/spec/attachments.md § Garbage
1190
- * collection.
1191
- */
1192
- listBlobs?(): Promise<BlobInfo[]>;
1193
- flush?(): Promise<void>;
1194
- close?(): Promise<void>;
1195
- }
1196
- /**
1197
- * A complete adapter: record storage and blob storage combined.
1198
- * Pass this to Stack.open(). Build one with combineAdapters() when you
1199
- * want different backends for records and blobs (e.g. SQLite + S3).
1200
- */
1201
- export type StackAdapter = StackRecordAdapter & StackBlobAdapter & {
1202
- /**
1203
- * Store bytes and create the accompanying _attachment@1 record as one
1204
- * atomic operation. Optional; implement only when bytes and records
1205
- * live behind a single boundary (today: APIAdapter alone), and never
1206
- * synthesized by combineAdapters(). A security boundary, not an
1207
- * efficiency shortcut — see docs/spec/adapters.md § Interface split.
1208
- */
1209
- putAttachmentWithMetadata?(data: Uint8Array, opts: PutAttachmentOptions): Promise<StackRecord>;
1210
- };
1211
- /**
1212
- * The two identities a token establishes — an Actor whose `principalId` is
1213
- * always populated, equal to `subjectId` on an undelegated token, so a
1214
- * server reading one never has to default it. Passes to `Stack.asActor()`
1215
- * as is. See docs/spec/access-control.md § Delegation: principal and subject.
1216
- */
1217
- export type TokenSession = Actor & {
1218
- principalId: EntityId;
1219
- };
1220
- export type TokenInfo = TokenSession & {
1221
- id: string;
1222
- label?: string;
1223
- createdAt: Date;
1224
- expiresAt?: Date;
1225
- };
1226
- /**
1227
- * Bearer-token issuance and lookup for server implementations — a
1228
- * standalone interface, not a slot on StackAdapter. createToken() trusts
1229
- * its caller: DID verification happens first, via the challenge-response
1230
- * handshake. Tokens SHOULD be stored outside the portable stack file.
1231
- * See docs/spec/wire-format.md § Authentication.
1232
- *
1233
- * The actor's `subjectId`, where it differs from `principalId`, is the
1234
- * delegation binding, and no handshake can establish it — proving key
1235
- * possession proves the principal and nothing about whom it may act for. It is asserted by the owner out of band, which is safe
1236
- * because effective authority is the intersection of both parties' grants.
1237
- */
1238
- export interface StackTokenStore {
1239
- createToken(actor: Actor, opts?: {
1240
- label?: string;
1241
- expiresAt?: Date;
1242
- }): Promise<{
1243
- id: string;
1244
- token: string;
1245
- }>;
1246
- lookupToken(token: string): Promise<TokenSession | null>;
1247
- listTokens(): Promise<TokenInfo[]>;
1248
- revokeToken(id: string): Promise<void>;
1249
- }
1250
- //# sourceMappingURL=types.d.ts.map