@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/README.md CHANGED
@@ -46,40 +46,46 @@ const adapter = await LocalAdapter.open({
46
46
  // safe to keep passing on every open, it's a no-op once the record exists.
47
47
  const stack = await Stack.open(adapter, { ownerProfile: { name: 'Jane Smith' } });
48
48
 
49
- // Define a type. The handle carries the id and schema, and the compiler
49
+ // Define a type. The handle carries the id, name and schema, and the compiler
50
50
  // derives the content type from it — no separate interface to keep in step.
51
- const Note = typeHandle('com.example.myapp/note@1', {
52
- text: { kind: 'text', required: true },
53
- title: { kind: 'string' },
51
+ const Note = typeHandle({
52
+ id: 'com.example.myapp/note@1',
53
+ name: 'Note',
54
+ schema: {
55
+ text: { kind: 'text', required: true },
56
+ title: { kind: 'string' },
57
+ },
54
58
  });
55
- await stack.defineType({ ...Note, name: 'Note' });
59
+ await stack.defineType(Note);
60
+
61
+ // Typed access to the Note family
62
+ const notes = stack.collection(Note);
56
63
 
57
64
  // Create a record
58
- const note = await stack.create(Note, {
65
+ const note = await notes.create({
59
66
  text: 'Hello, Haverstack!',
60
67
  title: 'My first note',
61
68
  });
62
69
 
63
70
  // Update its content (partial merge — only changed fields needed)
64
- await stack.patchContent(Note, note.id, { title: 'Updated title' });
71
+ await notes.patchContent(note.id, { title: 'Updated title' });
65
72
 
66
73
  // Read it back, typed: `content.text` is a string
67
- const same = await stack.get(Note, note.id);
74
+ const same = await notes.get(note.id);
68
75
 
69
76
  // Tag it
70
- await stack.associate(note.id, [{ kind: 'tag', label: 'favourite' }]);
77
+ await notes.associate(note.id, [{ kind: 'tag', label: 'favourite' }]);
71
78
 
72
79
  // Or change several things at once — one version, one atomic write
73
- await stack.mutate(note.id, {
80
+ await notes.mutate(note.id, {
74
81
  contentPatch: { title: 'Final title' },
75
82
  permissions: [{ kind: 'anyone', label: 'read' }],
76
83
  unlisted: false,
77
84
  });
78
85
 
79
- // Query. With no filter, query() returns every record you can read, from every
80
- // app and system types included, so filter by typeId, baseId or appId to get your own.
81
- const notes = await stack.query({
82
- filter: { typeId: 'com.example.myapp/note@1', tags: ['favourite'] },
86
+ // Query the family. stack.query() reads across every type you can see instead.
87
+ const { records: favourites } = await notes.query({
88
+ filter: { tags: ['favourite'] },
83
89
  sort: { field: 'createdAt', direction: 'desc' },
84
90
  });
85
91
 
@@ -92,60 +98,117 @@ await stack.close();
92
98
  An app has two layers, because the stack draws a line between them:
93
99
 
94
100
  - **A data layer that takes a `StackClient`** — the record API `Stack` and `ScopedStack` both implement. The same code then runs embedded as the owner, or behind a server as a requester who reaches only what they were granted.
95
- - **An install function that takes a `Stack`**, run by the owner. Defining types, `migrateAll()` and `grantType()` change the whole stack rather than one record, so they live on `Stack` alone and are absent from `StackClient`. Over the wire, `POST /types` and `POST /records/:id/migrate` are served to the owner acting alone.
101
+ - **An install that the owner approves.** Defining types, `migrateAll()` and `grantType()` change the whole stack rather than one record, so they live on `Stack` alone and are absent from `StackClient`.
96
102
 
97
- `registerMigration()` belongs to neither. Its registry lives in memory on each `Stack` instance, so it runs at **every startup**, right after `Stack.open()` — an install function that registers migrations and runs once leaves every later start without them.
103
+ Migrations belong to neither. They are passed to `Stack.open()` on **every startup**, so the chain is complete before the first read.
98
104
 
99
105
  ```ts
100
- import { Stack, typeHandle, type StackClient } from '@haverstack/core';
106
+ import { Stack, map, migration, typeHandle } from '@haverstack/core';
107
+ import type { AppManifest, Collection, ContentOf, StackClient } from '@haverstack/core';
101
108
 
102
- const NoteV1 = typeHandle('com.example.myapp/note@1', {
103
- text: { kind: 'text', required: true },
109
+ export const Shelf = typeHandle({
110
+ id: 'com.example.reading/shelf@1',
111
+ name: 'Shelf',
112
+ schema: { name: { kind: 'string', required: true } },
104
113
  });
105
- export const Note = typeHandle('com.example.myapp/note@2', {
106
- text: { kind: 'text', required: true },
107
- pinned: { kind: 'boolean', required: true },
114
+ const BookV1 = typeHandle({
115
+ id: 'com.example.reading/book@1',
116
+ name: 'Book',
117
+ schema: {
118
+ title: { kind: 'string', required: true },
119
+ status: { kind: 'enum', values: ['reading', 'done'], required: true },
120
+ },
121
+ });
122
+ export const Book = typeHandle({
123
+ id: 'com.example.reading/book@2',
124
+ name: 'Book',
125
+ migratesFrom: BookV1,
126
+ schema: {
127
+ title: { kind: 'string', required: true },
128
+ status: { kind: 'enum', values: ['reading', 'finished'], required: true },
129
+ shelfId: { kind: 'record-ref' },
130
+ },
108
131
  });
132
+ export const Review = typeHandle({
133
+ id: 'com.example.reading/review@1',
134
+ name: 'Review',
135
+ schema: {
136
+ bookId: { kind: 'record-ref', required: true },
137
+ rating: { kind: 'number', required: true },
138
+ text: { kind: 'text' },
139
+ },
140
+ });
141
+
142
+ export interface ShelfContent extends ContentOf<typeof Shelf.schema> {}
143
+ export interface BookContent extends ContentOf<typeof Book.schema> {}
144
+ export interface ReviewContent extends ContentOf<typeof Review.schema> {}
145
+
146
+ // Startup: pass to every Stack.open(). Each step is checked against both
147
+ // handles' schemas when migration() runs.
148
+ export const migrations = [migration(BookV1, Book, [map('status', { done: 'finished' })])];
149
+
150
+ // Data layer: one collection per type, and typed records from every verb.
151
+ export class Library {
152
+ readonly shelves: Collection<ShelfContent>;
153
+ readonly books: Collection<BookContent>;
154
+ readonly reviews: Collection<ReviewContent>;
109
155
 
110
- // Data layer: whoever the stack lets in.
111
- export class Notes {
112
- constructor(private readonly client: StackClient) {}
113
- add(text: string) {
114
- return this.client.create(Note, { text, pinned: false });
156
+ constructor(client: StackClient) {
157
+ this.shelves = client.collection(Shelf);
158
+ this.books = client.collection(Book);
159
+ this.reviews = client.collection(Review);
115
160
  }
116
- pin(id: string) {
117
- return this.client.patchContent(Note, id, { pinned: true });
161
+
162
+ addBook(title: string, shelfId: string) {
163
+ return this.books.create({ title, status: 'reading', shelfId });
118
164
  }
119
- list() {
120
- return this.client.query(Note);
165
+
166
+ finish(id: string) {
167
+ return this.books.patchContent(id, { status: 'finished' });
121
168
  }
122
- }
123
169
 
124
- // Startup: every open, every Stack instance.
125
- export function registerNoteMigrations(stack: Stack) {
126
- stack.registerMigration({
127
- from: NoteV1.id,
128
- to: Note.id,
129
- migrate: (content) => ({ ...content, pinned: false }),
130
- });
131
- }
170
+ review(bookId: string, rating: number, text?: string) {
171
+ return this.reviews.create({ bookId, rating, ...(text && { text }) });
172
+ }
132
173
 
133
- // Install: the owner, once per stack and again after a schema change.
134
- // defineType() is a no-op for a schema already stored, so re-running is safe.
135
- export async function installNotes(stack: Stack, appDid?: string) {
136
- await stack.defineType({ ...NoteV1, name: 'Note' });
137
- await stack.defineType({ ...Note, name: 'Note', migratesFrom: NoteV1.id });
138
- await stack.migrateAll(Note.baseId);
139
- if (appDid) {
140
- await stack.grantType(Note.baseId, {
141
- actions: ['create', 'read-own', 'update-own', 'delete-own'],
142
- grantee: { kind: 'entity', entityId: appDid },
174
+ // Records at @1 come back migrated to @2. One written by a newer release
175
+ // of the app can't be migrated down, so it is reported, not thrown.
176
+ async onShelf(shelfId: string) {
177
+ const page = await this.books.query({
178
+ filter: { content: { shelfId } },
179
+ sort: { contentField: 'title' },
143
180
  });
181
+ if (page.misfits.length > 0) {
182
+ console.warn(`${page.misfits.length} books need a newer version of this app`);
183
+ }
184
+ return page.records;
144
185
  }
145
186
  }
187
+
188
+ // Install, when the owner runs the app. defineType() is a no-op for a
189
+ // schema already stored, so re-running after an upgrade is safe.
190
+ export async function install(stack: Stack) {
191
+ for (const handle of [Shelf, BookV1, Book, Review]) await stack.defineType(handle);
192
+ await stack.migrateAll(Book.baseId);
193
+ }
194
+
195
+ // Install, when the owner doesn't: the same handles, as a request.
196
+ export const manifest: AppManifest = {
197
+ appId: 'com.example.reading',
198
+ name: 'Reading',
199
+ types: [Shelf, BookV1, Book, Review],
200
+ requests: [Shelf, Book, Review].map((handle) => ({
201
+ baseId: handle.baseId,
202
+ actions: ['create', 'read-own', 'update-own', 'delete-own'],
203
+ })),
204
+ };
146
205
  ```
147
206
 
148
- The owner's own app calls `registerNoteMigrations(stack)` and `installNotes(stack)`, then `new Notes(stack)`. A server hands each requester `new Notes(stack.asActor(session))`. An app that isn't the owner has no way to install its own types yet; the owner runs its install function for it.
207
+ **When the owner runs the app**, it opens with `Stack.open(adapter, { migrations })`, calls `install(stack)`, then `new Library(stack)`. A server hands each requester `new Library(stack.asActor(session))`.
208
+
209
+ **When the app runs elsewhere** — holding its own key and reaching the stack through `APIAdapter` — it can't define types, so it asks: `adapter.requestInstall(manifest)` resolves to `pending` until the owner approves. The owner reviews `stack.planInstall(manifest, { did })` and applies it with `stack.installApp(plan)`, which defines the types, registers the app's key and grants it the requests. The app still opens with its `migrations`, then builds `new Library(stack)` over its own connection. See [App installs](https://github.com/haverstack/core/blob/main/docs/spec/apps.md).
210
+
211
+ **Name content with an interface**, as above. `ContentOf` flattens its result, so a `type` alias shows the expanded object in editor hovers, while an interface keeps its name. typescript-eslint's recommended `no-empty-object-type` rule rejects an empty interface; its `allowInterfaces: 'with-single-extends'` option admits this one.
149
212
 
150
213
  ## Core concepts
151
214
 
@@ -186,24 +249,23 @@ Tags, attachments, and relationships are unified under a single model:
186
249
 
187
250
  ### Migrations
188
251
 
189
- Types can evolve over time. Register migration functions between adjacent versions and the library composes them into chains automatically:
252
+ Types can evolve over time. A new version names the one it migrates from, and `migration()` builds the step between two handles from declared steps — `rename`, `map`, `add`, `drop` and `derive` — checked against both schemas when it runs. Pass every step to `Stack.open()`; the library composes adjacent steps into chains:
190
253
 
191
254
  ```ts
192
- await stack.defineType({
255
+ const NoteV2 = typeHandle({
193
256
  id: 'com.example.myapp/note@2',
194
257
  name: 'Note',
258
+ migratesFrom: NoteV1,
195
259
  schema: { text: { kind: 'text', required: true }, title: { kind: 'string' } },
196
- migratesFrom: 'com.example.myapp/note@1',
197
260
  });
198
261
 
199
- stack.registerMigration({
200
- from: 'com.example.myapp/note@1',
201
- to: 'com.example.myapp/note@2',
202
- migrate: (content) => ({ ...content, title: '' }),
262
+ const stack = await Stack.open(adapter, {
263
+ migrations: [migration(NoteV1, NoteV2, [add('title', '')])],
203
264
  });
265
+ await stack.defineType(NoteV2);
204
266
  ```
205
267
 
206
- Records stay at the version they were written at: `get()` and `query()` return them as stored, `presentAt: 'latest'` migrates them in memory for one read, and `stack.migrateAll()` commits a family to disk. Register migrations at every startup — see [Writing an app](#writing-an-app).
268
+ Records stay at the version they were written at: `get()` and `query()` return them as stored, `presentAt: 'latest'` migrates them in memory for one read, and `stack.migrateAll()` commits a family to disk. Because the steps are declared rather than a function, a collection carries a content filter or sort across the versions they translate to — a filter on `title` reaches records still at `@1`. Pass migrations at every startup — see [Writing an app](#writing-an-app).
207
269
 
208
270
  ## License
209
271
 
package/dist/access.d.ts CHANGED
@@ -3,16 +3,17 @@
3
3
  * -------------------------------------------------------
4
4
  * Implements the Permissions model from the spec: a pure predicate over a
5
5
  * Record's `permissions` field, with no dependency on a transport layer or
6
- * storage backend. Used by ScopedStack (see scoped-stack.ts) to enforce
6
+ * storage backend. Used by ScopedStack (see scoped-stack/scoped-stack.ts) to enforce
7
7
  * access control; exported standalone for callers that want the raw
8
- * predicate.
8
+ * predicate. Also the one home of the `_group` roster rules that predicate
9
+ * and the grant path resolve against, and of the owner-acting-alone tier.
9
10
  *
10
11
  * A permission set is read as data throughout, never as the type: it can
11
12
  * arrive from an import or a foreign server, where no compiler has seen
12
13
  * it. Every tier is affirmative, so an element read past its own kind —
13
14
  * or carrying no kind at all — confers nothing.
14
15
  */
15
- import type { Association, AuthorityAssociation, DataAssociation, EntityId, GroupRole, RecordId, StackRecord } from './types.js';
16
+ import type { AuthorityAssociation, DataAssociation, EntityId, GroupRole, RecordId, StackRecord } from './types/index.js';
16
17
  import type { ValidationError } from './validate.js';
17
18
  export type AccessMode = 'read' | 'write';
18
19
  /**
@@ -21,13 +22,10 @@ export type AccessMode = 'read' | 'write';
21
22
  */
22
23
  export type RecordResolver = (id: RecordId) => Promise<StackRecord | null>;
23
24
  /**
24
- * Whether an entity has read or write access to a Record.
25
- *
26
- * The entity here is the **subject** — record-level permissions are written
27
- * about who data is for, never about the software that carried the request.
28
- * A delegated app's own authority is a separate question, asked of the
29
- * principal. See docs/spec/access-control.md § Record-level permissions and
30
- * § Delegation: principal and subject.
25
+ * Whether an entity may read or write a Record. The entity is the
26
+ * **subject**: record-level permissions are written about who data is for,
27
+ * never the software carrying the request, whose authority is asked of the
28
+ * principal. See docs/spec/access-control.md § Delegation: principal and subject.
31
29
  */
32
30
  export declare function checkAccess(record: StackRecord, subjectEntityId: EntityId | null, ownerEntityId: EntityId | null, mode: AccessMode, resolveRecord: RecordResolver): Promise<boolean>;
33
31
  /**
@@ -39,40 +37,40 @@ export declare function checkAccess(record: StackRecord, subjectEntityId: Entity
39
37
  */
40
38
  export declare function validatePermissions(permissions: AuthorityAssociation[] | undefined, path?: string): ValidationError[];
41
39
  /**
42
- * Whether a Record is a roster anything may resolve against, on two counts.
43
- * The family check: without it an app modelling its own `member` links
44
- * would turn every record it points a permission at into an ACL. The
45
- * tombstone check: deleting a Group is how a Group is withdrawn. Shared
46
- * with the grant path, so the two layers cannot disagree about which Groups
47
- * still reach anyone. See docs/spec/identity.md § Group.
40
+ * An entity's role on the `_group` roster `groupId` names. `memo`, when
41
+ * given, is built per operation, so no resolved role outlives the operation
42
+ * that resolved it — removal from a group must never go stale.
43
+ */
44
+ export declare function resolveGroupRole(groupId: RecordId, entityId: EntityId, resolveRecord: RecordResolver, memo?: Map<string, GroupRole | null>): Promise<GroupRole | null>;
45
+ /** Whether a held role meets a grantee's: an admin is also a member. */
46
+ export declare function roleSatisfies(held: GroupRole | null, required: GroupRole): boolean;
47
+ /**
48
+ * Whether a Record is a `_group`, in any of its type versions — the family
49
+ * whose roster rules apply.
50
+ */
51
+ export declare function isGroupRecord(record: StackRecord): boolean;
52
+ /**
53
+ * Whether a Record is a roster anything may resolve against: a `_group`,
54
+ * or an app's own `member` links would turn records into ACLs, and not
55
+ * deleted, since deleting a Group withdraws it. Shared with the grant path
56
+ * so the two cannot disagree. See docs/spec/identity.md § Group.
48
57
  */
49
58
  export declare function carriesRoster(record: StackRecord): boolean;
50
59
  /**
51
- * An entity's role within a `_group` Record's roster. `admin`
52
- * short-circuits, so a matching admin association wins regardless of order.
53
- *
54
- * Reads associations alone and cannot tell whose they are: callers pass
55
- * carriesRoster() first, or an app's own `member` relationships become a
56
- * roster.
60
+ * An entity's role on a `_group` roster; a matching `admin` wins regardless
61
+ * of order. Reads associations alone and cannot tell whose they are, so
62
+ * callers pass carriesRoster() first.
57
63
  */
58
64
  export declare function groupRoleFromAssociations(associations: DataAssociation[] | undefined, entityId: EntityId): GroupRole | null;
59
65
  /**
60
- * Whether a roster carries at least one `admin` — the invariant every write
61
- * to a `_group` Record must leave standing. Asked of the roster a write
62
- * would *produce*, never the one it started from: that is what lets an
63
- * admin remove themselves while another remains, and refuses the same
64
- * removal when they are the last, without either case naming who is going.
65
- * Reads associations alone, on the same terms as
66
- * groupRoleFromAssociations() above.
66
+ * Whether a roster carries at least one `admin` — the invariant every
67
+ * `_group` write must leave standing. Asked of the roster the write would
68
+ * *produce*, which lets an admin leave while another remains and refuses
69
+ * the last. Reads associations alone, like groupRoleFromAssociations().
67
70
  */
68
71
  export declare function hasGroupAdmin(associations: DataAssociation[] | undefined): boolean;
69
- /**
70
- * Whether one association is an `admin` roster entry. The `entity` target is
71
- * what makes it one — a `record` target carrying the same label confers
72
- * nothing, so it must not count toward the invariant either.
73
- * See docs/spec/data-model.md § Relationship targets.
74
- */
75
- export declare function isGroupAdminAssociation(association: Association): boolean;
72
+ /** Bootstraps a `_group` record's first admin at create time. */
73
+ export declare function stampGroupAdmin(associations: DataAssociation[] | undefined, creator: EntityId): DataAssociation[];
76
74
  /**
77
75
  * Whether a request is the stack owner acting alone — the tier behind every
78
76
  * unconditional owner authority. Being the owner is never sufficient on its
@@ -1 +1 @@
1
- {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,QAAQ,EACR,SAAS,EAET,QAAQ,EACR,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAE3E;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,QAAQ,GAAG,IAAI,EAChC,aAAa,EAAE,QAAQ,GAAG,IAAI,EAC9B,IAAI,EAAE,UAAU,EAChB,aAAa,EAAE,cAAc,GAC5B,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAyCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,oBAAoB,EAAE,GAAG,SAAS,EAC/C,IAAI,SAAgB,GACnB,eAAe,EAAE,CAanB;AAaD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,QAAQ,EAAE,QAAQ,GACjB,SAAS,GAAG,IAAI,CASlB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,OAAO,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAMzE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE;IAAE,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,EACpE,aAAa,EAAE,QAAQ,GACtB,OAAO,CAGT"}
1
+ {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EACf,QAAQ,EAER,SAAS,EAET,QAAQ,EAER,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,QAAQ,GAAG,IAAI,EAChC,aAAa,EAAE,QAAQ,GAAG,IAAI,EAC9B,IAAI,EAAE,UAAU,EAChB,aAAa,EAAE,cAAc,GAC5B,OAAO,CAAC,OAAO,CAAC,CA4BlB;AA0CD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,oBAAoB,EAAE,GAAG,SAAS,EAC/C,IAAI,SAAgB,GACnB,eAAe,EAAE,CAanB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,cAAc,EAC7B,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAS3B;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,EAAE,QAAQ,EAAE,SAAS,GAAG,OAAO,CAElF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,QAAQ,EAAE,QAAQ,GACjB,SAAS,GAAG,IAAI,CASlB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,OAAO,CAElF;AAkBD,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,OAAO,EAAE,QAAQ,GAChB,eAAe,EAAE,CAUnB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE;IAAE,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,EACpE,aAAa,EAAE,QAAQ,GACtB,OAAO,CAGT"}
package/dist/access.js CHANGED
@@ -3,26 +3,24 @@
3
3
  * -------------------------------------------------------
4
4
  * Implements the Permissions model from the spec: a pure predicate over a
5
5
  * Record's `permissions` field, with no dependency on a transport layer or
6
- * storage backend. Used by ScopedStack (see scoped-stack.ts) to enforce
6
+ * storage backend. Used by ScopedStack (see scoped-stack/scoped-stack.ts) to enforce
7
7
  * access control; exported standalone for callers that want the raw
8
- * predicate.
8
+ * predicate. Also the one home of the `_group` roster rules that predicate
9
+ * and the grant path resolve against, and of the owner-acting-alone tier.
9
10
  *
10
11
  * A permission set is read as data throughout, never as the type: it can
11
12
  * arrive from an import or a foreign server, where no compiler has seen
12
13
  * it. Every tier is affirmative, so an element read past its own kind —
13
14
  * or carrying no kind at all — confers nothing.
14
15
  */
15
- import { SYSTEM_TYPES } from './types.js';
16
- import { granteeEqual } from './record-changes.js';
16
+ import { SYSTEM_TYPES } from './types/index.js';
17
+ import { granteeEqual } from './associations/identity.js';
17
18
  import { baseIdOf } from './schema.js';
18
19
  /**
19
- * Whether an entity has read or write access to a Record.
20
- *
21
- * The entity here is the **subject** — record-level permissions are written
22
- * about who data is for, never about the software that carried the request.
23
- * A delegated app's own authority is a separate question, asked of the
24
- * principal. See docs/spec/access-control.md § Record-level permissions and
25
- * § Delegation: principal and subject.
20
+ * Whether an entity may read or write a Record. The entity is the
21
+ * **subject**: record-level permissions are written about who data is for,
22
+ * never the software carrying the request, whose authority is asked of the
23
+ * principal. See docs/spec/access-control.md § Delegation: principal and subject.
26
24
  */
27
25
  export async function checkAccess(record, subjectEntityId, ownerEntityId, mode, resolveRecord) {
28
26
  if (subjectEntityId && subjectEntityId === ownerEntityId)
@@ -31,6 +29,9 @@ export async function checkAccess(record, subjectEntityId, ownerEntityId, mode,
31
29
  // No permissions = private.
32
30
  if (!perms || perms.length === 0)
33
31
  return false;
32
+ // Two elements can name one group (a `member` and an `admin` read), and
33
+ // the roster answers both the same way within one check.
34
+ const roles = new Map();
34
35
  for (const p of perms) {
35
36
  if (p?.kind === 'anyone') {
36
37
  if (mode === 'read' && isWorldRead(p))
@@ -48,7 +49,7 @@ export async function checkAccess(record, subjectEntityId, ownerEntityId, mode,
48
49
  // See docs/spec/access-control.md § Write implies read.
49
50
  if (mode === 'write' && !holdsRead(perms, p.grantee))
50
51
  continue;
51
- if (await granteeCovers(p.grantee, subjectEntityId, resolveRecord))
52
+ if (await granteeCovers(p.grantee, subjectEntityId, resolveRecord, roles))
52
53
  return true;
53
54
  }
54
55
  return false;
@@ -77,13 +78,13 @@ function isWorldRead(association) {
77
78
  * Whether a grantee reaches this subject. `role: 'member'` is the wider
78
79
  * set — an admin satisfies it — and `role: 'admin'` the narrower.
79
80
  */
80
- async function granteeCovers(grantee, subjectEntityId, resolveRecord) {
81
+ async function granteeCovers(grantee, subjectEntityId, resolveRecord, roles) {
81
82
  if (!subjectEntityId)
82
83
  return false;
83
84
  if (grantee.kind === 'entity')
84
85
  return grantee.entityId === subjectEntityId;
85
- const role = await resolveGroupRole(grantee.groupId, subjectEntityId, resolveRecord);
86
- return grantee.role === 'admin' ? role === 'admin' : role !== null;
86
+ const role = await resolveGroupRole(grantee.groupId, subjectEntityId, resolveRecord, roles);
87
+ return roleSatisfies(role, grantee.role);
87
88
  }
88
89
  /**
89
90
  * Rejects a permission set where some grantee holds `write` with no `read`
@@ -107,31 +108,45 @@ export function validatePermissions(permissions, path = 'permissions') {
107
108
  });
108
109
  return errors;
109
110
  }
110
- /** Resolve a role from the `_group` Record a permission's `groupId` names. */
111
- async function resolveGroupRole(groupRecordId, entityId, resolveRecord) {
112
- const group = await resolveRecord(groupRecordId);
113
- if (!group || !carriesRoster(group))
114
- return null;
115
- return groupRoleFromAssociations(group.associations, entityId);
111
+ /**
112
+ * An entity's role on the `_group` roster `groupId` names. `memo`, when
113
+ * given, is built per operation, so no resolved role outlives the operation
114
+ * that resolved it — removal from a group must never go stale.
115
+ */
116
+ export async function resolveGroupRole(groupId, entityId, resolveRecord, memo) {
117
+ const key = `${groupId}:${entityId}`;
118
+ const cached = memo?.get(key);
119
+ if (cached !== undefined)
120
+ return cached;
121
+ const group = await resolveRecord(groupId);
122
+ const role = group && carriesRoster(group) ? groupRoleFromAssociations(group.associations, entityId) : null;
123
+ memo?.set(key, role);
124
+ return role;
125
+ }
126
+ /** Whether a held role meets a grantee's: an admin is also a member. */
127
+ export function roleSatisfies(held, required) {
128
+ return required === 'admin' ? held === 'admin' : held !== null;
116
129
  }
117
130
  /**
118
- * Whether a Record is a roster anything may resolve against, on two counts.
119
- * The family check: without it an app modelling its own `member` links
120
- * would turn every record it points a permission at into an ACL. The
121
- * tombstone check: deleting a Group is how a Group is withdrawn. Shared
122
- * with the grant path, so the two layers cannot disagree about which Groups
123
- * still reach anyone. See docs/spec/identity.md § Group.
131
+ * Whether a Record is a `_group`, in any of its type versions — the family
132
+ * whose roster rules apply.
133
+ */
134
+ export function isGroupRecord(record) {
135
+ return baseIdOf(record.typeId) === SYSTEM_TYPES.GROUP;
136
+ }
137
+ /**
138
+ * Whether a Record is a roster anything may resolve against: a `_group`,
139
+ * or an app's own `member` links would turn records into ACLs, and not
140
+ * deleted, since deleting a Group withdraws it. Shared with the grant path
141
+ * so the two cannot disagree. See docs/spec/identity.md § Group.
124
142
  */
125
143
  export function carriesRoster(record) {
126
- return baseIdOf(record.typeId) === SYSTEM_TYPES.GROUP && !record.deletedAt;
144
+ return isGroupRecord(record) && !record.deletedAt;
127
145
  }
128
146
  /**
129
- * An entity's role within a `_group` Record's roster. `admin`
130
- * short-circuits, so a matching admin association wins regardless of order.
131
- *
132
- * Reads associations alone and cannot tell whose they are: callers pass
133
- * carriesRoster() first, or an app's own `member` relationships become a
134
- * roster.
147
+ * An entity's role on a `_group` roster; a matching `admin` wins regardless
148
+ * of order. Reads associations alone and cannot tell whose they are, so
149
+ * callers pass carriesRoster() first.
135
150
  */
136
151
  export function groupRoleFromAssociations(associations, entityId) {
137
152
  let role = null;
@@ -146,13 +161,10 @@ export function groupRoleFromAssociations(associations, entityId) {
146
161
  return role;
147
162
  }
148
163
  /**
149
- * Whether a roster carries at least one `admin` — the invariant every write
150
- * to a `_group` Record must leave standing. Asked of the roster a write
151
- * would *produce*, never the one it started from: that is what lets an
152
- * admin remove themselves while another remains, and refuses the same
153
- * removal when they are the last, without either case naming who is going.
154
- * Reads associations alone, on the same terms as
155
- * groupRoleFromAssociations() above.
164
+ * Whether a roster carries at least one `admin` — the invariant every
165
+ * `_group` write must leave standing. Asked of the roster the write would
166
+ * *produce*, which lets an admin leave while another remains and refuses
167
+ * the last. Reads associations alone, like groupRoleFromAssociations().
156
168
  */
157
169
  export function hasGroupAdmin(associations) {
158
170
  return (associations ?? []).some(isGroupAdminAssociation);
@@ -163,11 +175,22 @@ export function hasGroupAdmin(associations) {
163
175
  * nothing, so it must not count toward the invariant either.
164
176
  * See docs/spec/data-model.md § Relationship targets.
165
177
  */
166
- export function isGroupAdminAssociation(association) {
178
+ function isGroupAdminAssociation(association) {
167
179
  return (association.kind === 'relationship' &&
168
180
  association.label === 'admin' &&
169
181
  association.target.kind === 'entity');
170
182
  }
183
+ /** Bootstraps a `_group` record's first admin at create time. */
184
+ export function stampGroupAdmin(associations, creator) {
185
+ const list = associations ?? [];
186
+ const alreadyAdmin = list.some((a) => isGroupAdminAssociation(a) && a.target.entityId === creator);
187
+ if (alreadyAdmin)
188
+ return list;
189
+ return [
190
+ ...list,
191
+ { kind: 'relationship', label: 'admin', target: { kind: 'entity', entityId: creator } },
192
+ ];
193
+ }
171
194
  /**
172
195
  * Whether a request is the stack owner acting alone — the tier behind every
173
196
  * unconditional owner authority. Being the owner is never sufficient on its
@@ -1 +1 @@
1
- {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,11 @@
1
- import type { StackAdapter, StackRecordAdapter, StackBlobAdapter } from './types.js';
1
+ /**
2
+ * Combining adapters
3
+ * -------------------------------------------------------
4
+ * A full StackAdapter assembled from a record half and a blob half, so a
5
+ * deployment can keep records and bytes in different backends without
6
+ * either adapter knowing about the other.
7
+ */
8
+ import type { StackAdapter, StackRecordAdapter, StackBlobAdapter } from '../types/index.js';
2
9
  /**
3
10
  * Compose a StackRecordAdapter and a StackBlobAdapter into a single StackAdapter.
4
11
  * Use this when you want different backends for records and blobs, e.g.:
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combine.d.ts","sourceRoot":"","sources":["../../src/adapter/combine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAEV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,gBAAgB,CAAC;CACxB,GAAG,YAAY,CAsEf"}
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Combining adapters
3
+ * -------------------------------------------------------
4
+ * A full StackAdapter assembled from a record half and a blob half, so a
5
+ * deployment can keep records and bytes in different backends without
6
+ * either adapter knowing about the other.
7
+ */
1
8
  /**
2
9
  * Compose a StackRecordAdapter and a StackBlobAdapter into a single StackAdapter.
3
10
  * Use this when you want different backends for records and blobs, e.g.:
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combine.js","sourceRoot":"","sources":["../../src/adapter/combine.ts"],"names":[],"mappings":"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"}
@@ -7,10 +7,11 @@
7
7
  * adapters). Types an app reads too, such as StackCapabilities, live in
8
8
  * the root entry point instead: every export has exactly one home.
9
9
  */
10
- export type { StackAdapter, StackRecordAdapter, StackBlobAdapter, BlobInfo, BumpVersionOptions, SnapshotOptions, JournalEntryInput, JournalOptions, SubscribeChangesOptions, } from './types.js';
11
- export { OwnerMismatchError } from './errors.js';
10
+ export type { StackAdapter, StackRecordAdapter, StackBlobAdapter, BlobInfo, BumpVersionOptions, SnapshotOptions, JournalEntryInput, JournalOptions, MutateOptions, } from '../types/index.js';
11
+ export { OwnerMismatchError } from '../errors.js';
12
12
  export { combineAdapters } from './combine.js';
13
- export { assertQueryCapabilities, assertSortCapability, assertValidSort, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from './query-validation.js';
14
- export { contentSortEntry, contentSortKey, compareSortEntries } from './sort.js';
13
+ export { assertQueryCapabilities, assertSortCapability, assertValidSort, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '../query-validation.js';
14
+ export { contentSortEntry, contentSortKey, compareSortEntries, constantSortEntry, sortConstant, } from './sort.js';
15
15
  export type { SortEntry } from './sort.js';
16
- //# sourceMappingURL=adapter-entry.d.ts.map
16
+ export { AUTHORITY_ASSOCIATION_KINDS, partitionAssociations } from '../associations/identity.js';
17
+ //# sourceMappingURL=index.d.ts.map