@haverstack/core 0.37.0 → 0.38.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 (76) hide show
  1. package/README.md +19 -15
  2. package/dist/access.d.ts +4 -6
  3. package/dist/access.d.ts.map +1 -1
  4. package/dist/access.js +7 -8
  5. package/dist/access.js.map +1 -1
  6. package/dist/adapter-entry.d.ts +7 -6
  7. package/dist/adapter-entry.d.ts.map +1 -1
  8. package/dist/adapter-entry.js +6 -5
  9. package/dist/adapter-entry.js.map +1 -1
  10. package/dist/attachment-download.d.ts +3 -1
  11. package/dist/attachment-download.d.ts.map +1 -1
  12. package/dist/attachment-download.js +1 -1
  13. package/dist/attachment-download.js.map +1 -1
  14. package/dist/changes.d.ts +22 -16
  15. package/dist/changes.d.ts.map +1 -1
  16. package/dist/changes.js +43 -32
  17. package/dist/changes.js.map +1 -1
  18. package/dist/combine.d.ts +1 -1
  19. package/dist/combine.js +6 -6
  20. package/dist/combine.js.map +1 -1
  21. package/dist/errors.d.ts +24 -12
  22. package/dist/errors.d.ts.map +1 -1
  23. package/dist/errors.js +32 -17
  24. package/dist/errors.js.map +1 -1
  25. package/dist/grants.d.ts +15 -24
  26. package/dist/grants.d.ts.map +1 -1
  27. package/dist/grants.js +14 -13
  28. package/dist/grants.js.map +1 -1
  29. package/dist/identity-bindings.js +2 -2
  30. package/dist/index.d.ts +12 -8
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +12 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/query-validation.d.ts +11 -11
  35. package/dist/query-validation.d.ts.map +1 -1
  36. package/dist/query-validation.js +41 -36
  37. package/dist/query-validation.js.map +1 -1
  38. package/dist/record-changes.d.ts +11 -10
  39. package/dist/record-changes.d.ts.map +1 -1
  40. package/dist/record-changes.js +33 -25
  41. package/dist/record-changes.js.map +1 -1
  42. package/dist/record-id.d.ts +3 -3
  43. package/dist/record-id.js +8 -8
  44. package/dist/record-id.js.map +1 -1
  45. package/dist/scoped-stack.d.ts +71 -34
  46. package/dist/scoped-stack.d.ts.map +1 -1
  47. package/dist/scoped-stack.js +107 -88
  48. package/dist/scoped-stack.js.map +1 -1
  49. package/dist/stack-reads.d.ts +2 -2
  50. package/dist/stack-reads.js +5 -5
  51. package/dist/stack-reads.js.map +1 -1
  52. package/dist/stack.d.ts +132 -161
  53. package/dist/stack.d.ts.map +1 -1
  54. package/dist/stack.js +287 -288
  55. package/dist/stack.js.map +1 -1
  56. package/dist/testing.d.ts +17 -17
  57. package/dist/testing.d.ts.map +1 -1
  58. package/dist/testing.js +46 -42
  59. package/dist/testing.js.map +1 -1
  60. package/dist/types.d.ts +169 -143
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js +2 -2
  63. package/dist/types.js.map +1 -1
  64. package/dist/wire-entry.d.ts +5 -6
  65. package/dist/wire-entry.d.ts.map +1 -1
  66. package/dist/wire-entry.js +5 -11
  67. package/dist/wire-entry.js.map +1 -1
  68. package/dist/wire-record.d.ts +4 -4
  69. package/dist/wire-record.d.ts.map +1 -1
  70. package/dist/wire-record.js +10 -10
  71. package/dist/wire-record.js.map +1 -1
  72. package/dist/wire-request.d.ts +2 -2
  73. package/dist/wire-request.d.ts.map +1 -1
  74. package/dist/wire-request.js +103 -61
  75. package/dist/wire-request.js.map +1 -1
  76. package/package.json +1 -1
package/dist/stack.d.ts CHANGED
@@ -9,13 +9,20 @@
9
9
  * - Content validation on write
10
10
  * - Migration registry and explicit, owner-driven migrateAll()
11
11
  * - Version snapshotting on update
12
- * - Soft and hard delete
12
+ * - Soft delete and purge
13
13
  *
14
14
  * Apps should never talk to a StackAdapter directly.
15
15
  */
16
- import type { StackRecord, StackType, TypeSchema, TypeId, StackAdapter, StackQuery, QueryResult, AuthorityAssociation, DataAssociation, Migration, RecordVersion, StackFeatures, GrantAction, AttachmentContent, FileId, EntityId, AppId, TokenSession, ActorOptions, RecordChange, RecordChanges, SubscribeOptions, Unsubscribe, JournalQuery, RecordJournalEntry } from './types.js';
17
- import type { GrantQuery, GrantTarget } from './grants.js';
16
+ import type { StackRecord, StackType, TypeSchema, TypeId, BaseId, StackAdapter, StackQuery, QueryResult, AuthorityAssociation, DataAssociation, Migration, RecordVersion, StackCapabilities, IfVersionOptions, GrantContent, TypeGrant, PutAttachmentOptions, AttachmentContent, FileId, EntityId, AppId, RecordId, Actor, ActorOptions, RecordChange, RecordChangeSet, SubscribeOptions, Unsubscribe, JournalQuery, RecordJournalEntry } from './types.js';
17
+ import type { GrantQuery } from './grants.js';
18
18
  import { ScopedStack } from './scoped-stack.js';
19
+ /**
20
+ * An Actor in the one form `Stack` stores: a `principalId` equal to the
21
+ * subject is dropped, so "acted as itself" has a single spelling, and an
22
+ * empty id is refused rather than stored as a name for nobody.
23
+ * See docs/spec/data-model.md § Actor.
24
+ */
25
+ export declare function normalizeActor(actor: Actor | undefined): Actor | undefined;
19
26
  export type CreateRecordOptions = {
20
27
  /**
21
28
  * Client-minted record ID. Must be 12 lowercase Crockford base-32
@@ -23,17 +30,16 @@ export type CreateRecordOptions = {
23
30
  * library generate one. See Stack.create() and ScopedStack.create()
24
31
  * for the validation each applies.
25
32
  */
26
- id?: string;
27
- parentId?: string;
28
- entityId?: EntityId;
29
- /** Reverse-DNS identifier of the writing software — see AppId. */
30
- appId?: AppId;
33
+ id?: RecordId;
34
+ parentId?: RecordId;
31
35
  /**
32
- * The authenticated principal, when it isn't the author. ScopedStack sets
33
- * this from its own principal; callers of plain Stack supply it only when
34
- * reconstructing a delegated write. See StackRecord.principalId.
36
+ * The author. ScopedStack sets this from its own identities; callers of
37
+ * plain Stack supply it only when reconstructing an attributed write.
38
+ * See StackRecord.createdBy.
35
39
  */
36
- principalId?: EntityId;
40
+ createdBy?: Actor;
41
+ /** Reverse-DNS identifier of the writing software — see AppId. */
42
+ appId?: AppId;
37
43
  permissions?: AuthorityAssociation[];
38
44
  associations?: DataAssociation[];
39
45
  /**
@@ -69,14 +75,6 @@ export type BackdatableCreateRecordOptions = CreateRecordOptions & {
69
75
  */
70
76
  updatedAt?: Date;
71
77
  };
72
- export type ScopedStackOptions = {
73
- /**
74
- * The entity a delegated app acts for. Omit when the principal acts as
75
- * itself — the case for an app riding its user's identity, which needs
76
- * no delegation. See docs/spec/identity.md § App.
77
- */
78
- onBehalfOf?: EntityId;
79
- };
80
78
  export type StackOptions = {
81
79
  /**
82
80
  * Ensures the owner's own `_entity` profile record exists, creating it on
@@ -109,7 +107,8 @@ export type CollectAttachmentGarbageOptions = {
109
107
  dryRun?: boolean;
110
108
  };
111
109
  export type CollectAttachmentGarbageResult = {
112
- deleted: string[];
110
+ /** The files collected — or, on a dry run, the ones that would be. */
111
+ deletedFileIds: FileId[];
113
112
  reclaimedBytes: number;
114
113
  };
115
114
  export type GetRecordOptions = {
@@ -120,18 +119,9 @@ export type GetRecordOptions = {
120
119
  */
121
120
  presentAt?: 'stored' | 'latest';
122
121
  };
123
- /**
124
- * Opt-in optimistic-concurrency precondition, accepted by every mutation
125
- * that bumps a record's version. On mismatch the mutation throws
126
- * StackVersionConflictError and changes nothing; omit to keep
127
- * last-writer-wins. See docs/spec/versioning.md § Optimistic concurrency (`ifVersion`).
128
- */
129
- export type IfVersionOptions = {
130
- ifVersion?: number;
131
- };
132
122
  export type DeleteRecordOptions = IfVersionOptions & {
133
123
  /** If true, permanently remove the record and all its history. Default: false */
134
- hard?: boolean;
124
+ purge?: boolean;
135
125
  };
136
126
  /**
137
127
  * What a delete reports back. Information, not action: a purge never
@@ -157,14 +147,18 @@ export type DeleteResult = {
157
147
  export type DeleteAndReturnResult = DeleteResult & {
158
148
  /**
159
149
  * The record as it stood at the moment of deletion: immediately before
160
- * destruction for a hard delete, or the resulting tombstone for a soft
161
- * delete. Null only for the hard-delete no-op — there was nothing to
150
+ * destruction for a purge, or the resulting tombstone for a soft
151
+ * delete. Null only for the purge no-op — there was nothing to
162
152
  * delete, and nothing to report; every other outcome throws instead of
163
153
  * returning null (see Stack.delete()'s own no-op cases).
164
154
  */
165
155
  record: StackRecord | null;
166
156
  };
157
+ /** The argument to Stack.defineType(). */
167
158
  export type DefineTypeOptions = {
159
+ id: TypeId;
160
+ name: string;
161
+ schema: TypeSchema;
168
162
  migratesFrom?: TypeId;
169
163
  };
170
164
  /**
@@ -173,11 +167,11 @@ export type DefineTypeOptions = {
173
167
  * and work equally well with a full Stack or a permission-scoped view.
174
168
  */
175
169
  export interface StackClient {
176
- readonly features: StackFeatures;
170
+ readonly capabilities: StackCapabilities;
177
171
  create<T extends Record<string, unknown> = Record<string, unknown>>(typeId: TypeId, content: T, opts?: CreateRecordOptions): Promise<StackRecord & {
178
172
  content: T;
179
173
  }>;
180
- get(id: string, opts?: GetRecordOptions): Promise<StackRecord | null>;
174
+ get(id: RecordId, opts?: GetRecordOptions): Promise<StackRecord | null>;
181
175
  query(query?: StackQuery): Promise<QueryResult>;
182
176
  /**
183
177
  * Resolve a DID to its `_entity` card — family-wide and soft-deleted
@@ -195,42 +189,42 @@ export interface StackClient {
195
189
  * producing one version where it names `contentPatch`, and none where it
196
190
  * doesn't. Keys are read for presence, so `unlisted: false`
197
191
  * and `parentId: null` are changes; a change set naming no key at all is
198
- * a StackQueryError. Under ScopedStack each key carries its own gate and
192
+ * a StackBadRequestError. Under ScopedStack each key carries its own gate and
199
193
  * one refused key refuses the call.
200
194
  * See docs/spec/data-model.md § Mutations.
201
195
  */
202
- mutate(id: string, changes: RecordChanges, opts?: IfVersionOptions): Promise<StackRecord>;
196
+ mutate(id: RecordId, changes: RecordChangeSet, opts?: IfVersionOptions): Promise<StackRecord>;
203
197
  /** mutate() with `contentPatch` alone — the common case, named for it. */
204
- patchContent(id: string, patch: Record<string, unknown | null>, opts?: IfVersionOptions): Promise<StackRecord>;
198
+ patchContent(id: RecordId, patch: Record<string, unknown | null>, opts?: IfVersionOptions): Promise<StackRecord>;
205
199
  /**
206
200
  * Add an association. Never bumps `version`/`updatedAt` and takes no
207
201
  * `ifVersion` — a set-add composes regardless of write order.
208
202
  * See docs/spec/versioning.md § Version history.
209
203
  */
210
- associate(id: string, association: DataAssociation): Promise<StackRecord>;
204
+ associate(id: RecordId, association: DataAssociation): Promise<StackRecord>;
211
205
  /** Remove an association. Never bumps `version`/`updatedAt` — see associate(). */
212
- dissociate(id: string, association: DataAssociation): Promise<StackRecord>;
206
+ dissociate(id: RecordId, association: DataAssociation): Promise<StackRecord>;
213
207
  /**
214
208
  * Extend who reaches a record by one element — the record-level mirror of
215
- * the type-level `grant()`, and the amending spelling of the
209
+ * the type-level `grantType()`, and the amending spelling of the
216
210
  * `permissions` key, which replaces the whole set. No-bump, like
217
211
  * associate(). See docs/spec/access-control.md § Record-level permissions.
218
212
  */
219
- grantAccess(id: string, permission: AuthorityAssociation): Promise<StackRecord>;
213
+ grantAccess(id: RecordId, permission: AuthorityAssociation): Promise<StackRecord>;
220
214
  /** Withdraw one element of who reaches a record — see grantAccess(). */
221
- revokeAccess(id: string, permission: AuthorityAssociation): Promise<StackRecord>;
222
- delete(id: string, opts?: DeleteRecordOptions): Promise<DeleteResult>;
215
+ revokeAccess(id: RecordId, permission: AuthorityAssociation): Promise<StackRecord>;
216
+ delete(id: RecordId, opts?: DeleteRecordOptions): Promise<DeleteResult>;
223
217
  /**
224
218
  * delete(), plus the record it acted on — read and destroyed as one
225
219
  * atomic operation, so a caller that needs the record for its own
226
- * response (e.g. a server building a hard delete's body) never opens a
220
+ * response (e.g. a server building a purge's body) never opens a
227
221
  * gap between reading it and destroying it. See Stack.deleteAndReturn().
228
222
  */
229
- deleteAndReturn(id: string, opts?: DeleteRecordOptions): Promise<DeleteAndReturnResult>;
230
- undelete(id: string, opts?: IfVersionOptions): Promise<StackRecord>;
231
- getVersions(id: string): Promise<RecordVersion[]>;
232
- getVersion(id: string, version: number): Promise<RecordVersion | null>;
233
- restoreVersion(id: string, version: number, opts?: IfVersionOptions): Promise<StackRecord>;
223
+ deleteAndReturn(id: RecordId, opts?: DeleteRecordOptions): Promise<DeleteAndReturnResult>;
224
+ undelete(id: RecordId, opts?: IfVersionOptions): Promise<StackRecord>;
225
+ getVersions(id: RecordId): Promise<RecordVersion[]>;
226
+ getVersion(id: RecordId, version: number): Promise<RecordVersion | null>;
227
+ restoreVersion(id: RecordId, version: number, opts?: IfVersionOptions): Promise<StackRecord>;
234
228
  /**
235
229
  * A record's change journal, oldest first. On the mutate surface, on the
236
230
  * same footing as getVersions() — a plain reader is refused.
@@ -240,7 +234,7 @@ export interface StackClient {
240
234
  * unconditionally, so an adapter with nothing to read refuses and names
241
235
  * why instead. See docs/spec/journal.md § Reading it.
242
236
  */
243
- getJournal(id: string, query?: JournalQuery): Promise<RecordJournalEntry[]>;
237
+ getJournal(id: RecordId, query?: JournalQuery): Promise<RecordJournalEntry[]>;
244
238
  /**
245
239
  * Commit a per-record migration: change `typeId` and `content` together,
246
240
  * validated against `toTypeId`'s schema. The only way a record's typeId
@@ -249,12 +243,12 @@ export interface StackClient {
249
243
  * record's version (see docs/spec/versioning.md § Optimistic
250
244
  * concurrency); over the wire that is `If-Match`.
251
245
  */
252
- commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions): Promise<StackRecord>;
253
- getAttachment(fileId: string): Promise<Uint8Array>;
254
- putAttachment(data: Uint8Array, mimeType: string, filename?: string, appId?: AppId): Promise<StackRecord & {
246
+ commitMigration(id: RecordId, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions): Promise<StackRecord>;
247
+ getAttachment(fileId: FileId): Promise<Uint8Array>;
248
+ putAttachment(data: Uint8Array, opts: PutAttachmentOptions): Promise<StackRecord & {
255
249
  content: AttachmentContent;
256
250
  }>;
257
- deleteAttachment(fileId: string): Promise<void>;
251
+ deleteAttachment(fileId: FileId): Promise<void>;
258
252
  collectAttachmentGarbage(opts?: CollectAttachmentGarbageOptions): Promise<CollectAttachmentGarbageResult>;
259
253
  subscribe(handler: (change: RecordChange) => void, opts?: SubscribeOptions): Promise<Unsubscribe>;
260
254
  }
@@ -299,20 +293,11 @@ export declare class Stack implements StackClient {
299
293
  * carries `appId`, which ActorOptions has no field for.
300
294
  */
301
295
  private static createActor;
302
- /**
303
- * The requester behind a write that stamps nothing on the record itself —
304
- * a hard delete, which leaves no record to stamp, and associate()/
305
- * dissociate(), which don't bump and so don't touch updatedBy/updatedVia.
306
- * Hard delete is owner-acting-alone only, so it never has a principal to
307
- * name beside the subject; associate()/dissociate() can run delegated,
308
- * so `updatedVia` rides along here when present.
309
- */
310
- private static actorFrom;
311
296
  private getTypeCached;
312
297
  /**
313
- * Create a Stack instance. Reads ownerEntityId and timezone from the adapter.
298
+ * Open a Stack over an adapter. Reads ownerEntityId and timezone from the adapter.
314
299
  */
315
- static create(adapter: StackAdapter, opts?: StackOptions): Promise<Stack>;
300
+ static open(adapter: StackAdapter, opts?: StackOptions): Promise<Stack>;
316
301
  /**
317
302
  * Idempotent bootstrap for StackOptions.ownerProfile. Filters by family
318
303
  * only (content filtering is capability-gated) and matches `content.did`
@@ -328,33 +313,25 @@ export declare class Stack implements StackClient {
328
313
  getEntityByDid(did: EntityId): Promise<StackRecord | null>;
329
314
  getOwnerEntity(): Promise<StackRecord | null>;
330
315
  get timezone(): string | undefined;
331
- get features(): StackFeatures;
316
+ get capabilities(): StackCapabilities;
332
317
  /**
333
318
  * Get a permission-scoped view of this Stack, as if requests came from
334
- * the given principal (null = anonymous). Plain Stack methods are
335
- * unscoped; use asEntity() when one Stack serves multiple, possibly
336
- * untrusted, entities.
337
- *
338
- * `onBehalfOf` names the subject a delegated app acts for: authority is
339
- * then the intersection of both parties' grants, while authorship and
340
- * `-own` resolve against the subject. See
319
+ * the given entity acting as itself (null = anonymous). Plain Stack
320
+ * methods are unscoped; use asEntity() when one Stack serves multiple,
321
+ * possibly untrusted, entities. See
341
322
  * docs/spec/access-control.md § Enforcement: Stack.asEntity().
342
323
  */
343
- asEntity(entityId: EntityId | null, opts?: ScopedStackOptions): ScopedStack;
324
+ asEntity(entityId: EntityId | null): ScopedStack;
344
325
  /**
345
- * Scope to an authenticated session — what StackTokenStore.lookupToken()
346
- * returns. Equivalent to asEntity(principalId, { onBehalfOf: subjectId }),
347
- * and what a server should reach for at its request boundary.
348
- *
349
- * Both identities are DIDs, so passing them positionally leaves nothing
350
- * to catch a swap, and a swapped pair is undetectable in the undelegated
351
- * case where they are equal — it would surface only once delegation is in
352
- * use, as authority no longer fenced by the app's grants and every write
353
- * attributed to the app rather than the person. Taking the pair whole
354
- * removes the order to get wrong. See
355
- * docs/spec/access-control.md § Delegation: principal and subject.
326
+ * Scope to an Actor — a delegated app acting for its user, or a
327
+ * TokenSession exactly as StackTokenStore.lookupToken() returns it, which
328
+ * is what a server should reach for at its request boundary. Authority is
329
+ * the intersection of both parties' grants, while authorship and `-own`
330
+ * resolve against the subject. Taking the pair whole leaves no order to
331
+ * swap. See docs/spec/access-control.md § Delegation: principal and subject.
356
332
  */
357
- forSession(session: TokenSession): ScopedStack;
333
+ asActor(actor: Actor): ScopedStack;
334
+ private scope;
358
335
  /**
359
336
  * Define and persist a Type; call at app startup before creating records
360
337
  * of the type. Redefining an existing typeId is checked against the
@@ -362,7 +339,7 @@ export declare class Stack implements StackClient {
362
339
  * anything beyond additive evolution throws StackSchemaDriftError. See
363
340
  * docs/spec/data-model.md § Schema drift detection.
364
341
  */
365
- defineType(id: TypeId, name: string, schema: TypeSchema, opts?: DefineTypeOptions): Promise<StackType>;
342
+ defineType({ id, name, schema, migratesFrom }: DefineTypeOptions): Promise<StackType>;
366
343
  getType(id: TypeId): Promise<StackType | null>;
367
344
  /** Refreshes typeCache wholesale — the explicit way to see a rename made by another writer. */
368
345
  listTypes(): Promise<StackType[]>;
@@ -394,7 +371,7 @@ export declare class Stack implements StackClient {
394
371
  * too, validates each result before writing, and aborts on the first
395
372
  * validation failure. See docs/spec/data-model.md § Type migrations.
396
373
  */
397
- migrateAll(baseTypeId: string): Promise<{
374
+ migrateAll(baseId: BaseId): Promise<{
398
375
  migrated: number;
399
376
  }>;
400
377
  /**
@@ -424,7 +401,7 @@ export declare class Stack implements StackClient {
424
401
  * { presentAt: 'latest' } to migrate in memory; only migrateAll()
425
402
  * commits migrations to disk.
426
403
  */
427
- get(id: string, opts?: GetRecordOptions): Promise<StackRecord | null>;
404
+ get(id: RecordId, opts?: GetRecordOptions): Promise<StackRecord | null>;
428
405
  /**
429
406
  * Apply a change set as one atomic write — see the StackClient
430
407
  * declaration above for what it accepts. Atomicity is what it is for: a
@@ -435,13 +412,13 @@ export declare class Stack implements StackClient {
435
412
  * against the record's *current* stored type; `typeId` never changes here.
436
413
  * See docs/spec/data-model.md § Mutations.
437
414
  */
438
- mutate(id: string, changes: RecordChanges, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
415
+ mutate(id: RecordId, changes: RecordChangeSet, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
439
416
  /**
440
417
  * mutate() with `contentPatch` alone. The common case by a wide margin,
441
418
  * and named for what it does rather than for a symmetry with create()
442
419
  * that a patch does not have.
443
420
  */
444
- patchContent(id: string, patch: Record<string, unknown | null>, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
421
+ patchContent(id: RecordId, patch: Record<string, unknown | null>, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
445
422
  /**
446
423
  * Every check a change set owes before anything is written, in one pass:
447
424
  * one refused key refuses the call, so none of them may run after a
@@ -477,7 +454,7 @@ export declare class Stack implements StackClient {
477
454
  * file. What a re-point overwrote is kept only by the journal, on that
478
455
  * entry's `repoint`. See docs/spec/journal.md § The entry.
479
456
  */
480
- associate(id: string, association: DataAssociation, opts?: ActorOptions): Promise<StackRecord>;
457
+ associate(id: RecordId, association: DataAssociation, opts?: ActorOptions): Promise<StackRecord>;
481
458
  /**
482
459
  * Remove an association from a record — see associate(). Matched by kind,
483
460
  * label and payload; a no-op if not found. The emitted event names the
@@ -485,7 +462,7 @@ export declare class Stack implements StackClient {
485
462
  * `attachmentRecordId` describes nothing current once it is gone; the
486
463
  * journal is where it survives.
487
464
  */
488
- dissociate(id: string, association: DataAssociation, opts?: ActorOptions): Promise<StackRecord>;
465
+ dissociate(id: RecordId, association: DataAssociation, opts?: ActorOptions): Promise<StackRecord>;
489
466
  /**
490
467
  * Extend who reaches a record by one element. The spelling that survives
491
468
  * two admins sharing a record at once: a `permissions` key write replaces
@@ -493,13 +470,13 @@ export declare class Stack implements StackClient {
493
470
  * earlier granted. No-bump, like associate(); an element the record
494
471
  * already carries is a no-op.
495
472
  */
496
- grantAccess(id: string, permission: AuthorityAssociation, opts?: ActorOptions): Promise<StackRecord>;
473
+ grantAccess(id: RecordId, permission: AuthorityAssociation, opts?: ActorOptions): Promise<StackRecord>;
497
474
  /**
498
475
  * Withdraw one element of who reaches a record — see grantAccess() for
499
476
  * why this is a verb rather than a key write. An element the record does
500
477
  * not carry is a no-op. Returns the record as it now stands.
501
478
  */
502
- revokeAccess(id: string, permission: AuthorityAssociation, opts?: ActorOptions): Promise<StackRecord>;
479
+ revokeAccess(id: RecordId, permission: AuthorityAssociation, opts?: ActorOptions): Promise<StackRecord>;
503
480
  /**
504
481
  * Hold a permission set to the cross-element invariant — see
505
482
  * validatePermissions() in access.ts for why it reads the post-state.
@@ -536,7 +513,7 @@ export declare class Stack implements StackClient {
536
513
  */
537
514
  private assertNoParentCycle;
538
515
  /**
539
- * Soft-delete a record (default) or hard-delete it permanently, removing
516
+ * Soft-delete a record (default) or purge it permanently, removing
540
517
  * the record and its version history. Soft delete snapshots and bumps
541
518
  * version; a no-op if already deleted. `_config` is never deletable
542
519
  * (docs/spec.md § The `_config` record). See docs/spec/versioning.md
@@ -553,17 +530,17 @@ export declare class Stack implements StackClient {
553
530
  * body from a separate read beforehand is exactly the race and the extra
554
531
  * round trip it exists to remove.
555
532
  */
556
- delete(id: string, opts?: DeleteRecordOptions & ActorOptions): Promise<DeleteResult>;
533
+ delete(id: RecordId, opts?: DeleteRecordOptions & ActorOptions): Promise<DeleteResult>;
557
534
  /**
558
535
  * delete(), reporting the record it acted on alongside the files it
559
- * stranded: as it stood immediately before destruction for a hard
560
- * delete, or as the resulting tombstone for a soft delete. Both are
536
+ * stranded: as it stood immediately before destruction for a
537
+ * purge, or as the resulting tombstone for a soft delete. Both are
561
538
  * captured inside the same write that destroys or tombstones the record,
562
539
  * never a read beforehand — a read-then-delete would leave a window
563
540
  * where a concurrent write can land, get destroyed or overwritten by
564
541
  * this call, and never appear in the record this returns.
565
542
  */
566
- deleteAndReturn(id: string, opts?: DeleteRecordOptions & ActorOptions): Promise<DeleteAndReturnResult>;
543
+ deleteAndReturn(id: RecordId, opts?: DeleteRecordOptions & ActorOptions): Promise<DeleteAndReturnResult>;
567
544
  /**
568
545
  * The files a record references, by the same definition
569
546
  * deleteAttachment() and the garbage sweep use: attachment associations
@@ -574,11 +551,11 @@ export declare class Stack implements StackClient {
574
551
  private referencedFileIds;
575
552
  /**
576
553
  * Reverse a soft delete. Idempotent — undeleting a record that isn't
577
- * deleted returns it unchanged. Hard-deleted records are gone, so this
554
+ * deleted returns it unchanged. Purged records are gone, so this
578
555
  * throws StackNotFoundError for them just like any other missing record.
579
556
  * Snapshots and bumps version, same as delete().
580
557
  */
581
- undelete(id: string, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
558
+ undelete(id: RecordId, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
582
559
  /**
583
560
  * Query records. filter.baseId matches every version of a type family,
584
561
  * resolved against registered Types. Results come back exactly as
@@ -593,7 +570,7 @@ export declare class Stack implements StackClient {
593
570
  * the caller can short-circuit without an adapter round trip.
594
571
  */
595
572
  private resolveBaseIdFilter;
596
- getVersions(id: string): Promise<RecordVersion[]>;
573
+ getVersions(id: RecordId): Promise<RecordVersion[]>;
597
574
  /**
598
575
  * A record's change journal, oldest first — what moved, who moved it, and
599
576
  * the association deltas nothing else retains. `ScopedStack` applies the
@@ -603,15 +580,15 @@ export declare class Stack implements StackClient {
603
580
  * a destroyed log and an empty one are not the same answer.
604
581
  * See docs/spec/journal.md § Reading it.
605
582
  */
606
- getJournal(id: string, query?: JournalQuery): Promise<RecordJournalEntry[]>;
607
- getVersion(id: string, version: number): Promise<RecordVersion | null>;
583
+ getJournal(id: RecordId, query?: JournalQuery): Promise<RecordJournalEntry[]>;
584
+ getVersion(id: RecordId, version: number): Promise<RecordVersion | null>;
608
585
  /**
609
586
  * Restore a record to a previous version by creating a new version —
610
587
  * never rewrites history. The snapshot is validated against its own
611
588
  * stored typeId, and puts back `content` and `typeId` alone: a snapshot
612
589
  * carries nothing else. See docs/spec/versioning.md § Restore semantics.
613
590
  */
614
- restoreVersion(id: string, version: number, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
591
+ restoreVersion(id: RecordId, version: number, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
615
592
  /**
616
593
  * Commit a per-record migration — the single-record counterpart to
617
594
  * migrateAll(), with `content` supplied by the caller rather than by a
@@ -624,7 +601,7 @@ export declare class Stack implements StackClient {
624
601
  * either half would make migrate a second, unguarded write path to a
625
602
  * state create()/mutate() refuse to reach.
626
603
  */
627
- commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
604
+ commitMigration(id: RecordId, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions & ActorOptions): Promise<StackRecord>;
628
605
  /**
629
606
  * The checked migration write, shared by commitMigration() and
630
607
  * migrateAll(). Takes the record already in hand rather than an id: a
@@ -662,8 +639,8 @@ export declare class Stack implements StackClient {
662
639
  */
663
640
  private checkBindingsOnMigrate;
664
641
  /**
665
- * A unique binding field is what a lookup resolves *by* — a record's
666
- * `principalId` by `_app.did`, its `entityId` by `_entity.did`. Two cards
642
+ * A unique binding field is what a lookup resolves *by* — an Actor's
643
+ * `principalId` by `_app.did`, its `subjectId` by `_entity.did`. Two cards
667
644
  * claiming one value would leave that lookup without a single answer, and
668
645
  * ambiguity is all an impersonating card needs. Enforced here rather than
669
646
  * by schema, since uniqueness is a property of the set, not of the value.
@@ -734,16 +711,16 @@ export declare class Stack implements StackClient {
734
711
  private checkConfigEntityIdUnchanged;
735
712
  /**
736
713
  * Store bytes and create an _attachment@1 metadata record (owner-
737
- * attributed, no entityId), returning that record — `content.fileId`
714
+ * attributed, no createdBy), returning that record — `content.fileId`
738
715
  * addresses the bytes, `id` addresses the metadata. Delegates to the
739
716
  * adapter's atomic putAttachmentWithMetadata() when implemented, trusting
740
717
  * the returned record as backend-authoritative; otherwise falls back to
741
718
  * bytes-then-create(). See docs/spec/wire-format.md § Attachments.
742
719
  */
743
- putAttachment(data: Uint8Array, mimeType: string, filename?: string, appId?: AppId): Promise<StackRecord & {
720
+ putAttachment(data: Uint8Array, opts: PutAttachmentOptions): Promise<StackRecord & {
744
721
  content: AttachmentContent;
745
722
  }>;
746
- getAttachment(fileId: string): Promise<Uint8Array>;
723
+ getAttachment(fileId: FileId): Promise<Uint8Array>;
747
724
  /**
748
725
  * Every `_attachment` record describing `fileId`, earliest-recorded
749
726
  * first — so `records[0]` is the one that establishes the file's
@@ -771,7 +748,7 @@ export declare class Stack implements StackClient {
771
748
  * still references the file, StackNotFoundError if neither metadata
772
749
  * records nor bytes exist.
773
750
  */
774
- deleteAttachment(fileId: string, opts?: ActorOptions): Promise<void>;
751
+ deleteAttachment(fileId: FileId, opts?: ActorOptions): Promise<void>;
775
752
  /**
776
753
  * Non-atomic fallback for adapters that don't implement
777
754
  * deleteUnreferencedAttachmentRecords(): a concurrent associate() can
@@ -803,16 +780,13 @@ export declare class Stack implements StackClient {
803
780
  *
804
781
  * The subscription's own filter goes to the relay rather than being
805
782
  * applied on the way back: the emitter at the far end holds the record,
806
- * so it can answer `entityId` and `parentId`, which the envelope
783
+ * so it can answer `createdBy` and `parentId`, which the envelope
807
784
  * deliberately does not carry. That is also why a relay is opened per
808
785
  * subscription rather than shared.
809
786
  */
810
787
  private openRelay;
811
- /**
812
- * Whether changes reach this stack from elsewhere. Public only so
813
- * ScopedStack can refuse a scope it cannot honor; not an app-facing API.
814
- */
815
- get relaysChanges(): boolean;
788
+ /** Whether changes reach this stack from elsewhere. */
789
+ private get relaysChanges();
816
790
  /**
817
791
  * Flush pending writes to the underlying storage. A no-op for adapters
818
792
  * that commit on every call (SQLite, the API adapter); meaningful for
@@ -827,17 +801,14 @@ export declare class Stack implements StackClient {
827
801
  * See docs/spec/adapters.md § Lifecycle.
828
802
  */
829
803
  close(): Promise<void>;
830
- /**
831
- * Throws once close() has run. Public only so ScopedStack can gate the
832
- * one path it takes to the adapter directly; not an app-facing API.
833
- */
834
- assertOpen(): void;
835
- /**
836
- * Create _grant records authorizing entities to act on records of
837
- * specific types. The target is the grantee the record carries:
838
- * `{ kind: 'entity' }` for one DID, `{ kind: 'group' }` for a `_group`
839
- * Record's roster at a role, `{ kind: 'authenticated' }` for any
840
- * authenticated entity.
804
+ /** Throws once close() has run. */
805
+ private assertOpen;
806
+ /**
807
+ * Create a _grant record authorizing `grantee` to take `actions` on
808
+ * records of `typeId`: `{ kind: 'entity' }` for one DID, `{ kind: 'group' }`
809
+ * for a `_group` Record's roster at a role, `{ kind: 'authenticated' }` for
810
+ * any authenticated entity. The type-level mirror of grantAccess() —
811
+ * subject first, grantee inside the element.
841
812
  *
842
813
  * Granting an **app** a `-own` action does not contain it the way the
843
814
  * suffix suggests: when that app acts for someone, `-own` is read as the
@@ -847,13 +818,13 @@ export declare class Stack implements StackClient {
847
818
  * needs, not the suffix that looks narrowest. See
848
819
  * docs/spec/access-control.md § Delegation: principal and subject.
849
820
  *
850
- * The grantee lives in content.grantee, not record.entityId. See
821
+ * `typeOrBaseId` is a versioned TypeId (`"baseId@version"`) or a bare
822
+ * baseId; either names the whole family. See
851
823
  * docs/spec/access-control.md § Type-level grants.
852
824
  */
853
- grant(target: GrantTarget, grants: Array<{
854
- actions: GrantAction[];
855
- typeId: TypeId;
856
- }>): Promise<StackRecord[]>;
825
+ grantType(typeOrBaseId: TypeId | BaseId, grant: TypeGrant): Promise<StackRecord & {
826
+ content: GrantContent;
827
+ }>;
857
828
  /**
858
829
  * List _grant records. Omit `query` for all grants;
859
830
  * `{ kind: 'authenticated' }` for only default grants;
@@ -863,36 +834,36 @@ export declare class Stack implements StackClient {
863
834
  * (ones naming them, ones naming a group they belong to at a role they
864
835
  * hold, plus every default grant) — the same resolution hasGrant() uses.
865
836
  *
866
- * Every arm but `entity` answers identity — what `grant()` would have
837
+ * Every arm but `entity` answers identity — what `grantType()` would have
867
838
  * written with the same argument. The `entity` arm answers coverage, so
868
- * its result is not a preview of what `revoke()` would withdraw.
839
+ * its result is not a preview of what `revokeType()` would withdraw.
869
840
  * See docs/spec/access-control.md § Type-level grants.
870
841
  */
871
- listGrants(query?: GrantQuery): Promise<StackRecord[]>;
842
+ listTypeGrants(query?: GrantQuery): Promise<(StackRecord & {
843
+ content: GrantContent;
844
+ })[]>;
872
845
  /**
873
- * The inverse of grant(): soft-deletes _grant records matching `target`
874
- * and each `{ typeId, actions }` pair, at the same granularity grant()
875
- * writes — the grantee is matched whole, role included. A soft delete
876
- * like any other — the owner can undelete a revocation.
846
+ * The inverse of grantType(): soft-deletes the _grant records on
847
+ * `typeOrBaseId`'s family matching `grant`, at the same granularity
848
+ * grantType() writes — the grantee is matched whole, role included, and
849
+ * the actions exactly. A soft delete like any other — the owner can
850
+ * undelete a revocation.
877
851
  *
878
- * Returns the grants it withdrew, as they stood, so a revocation that
879
- * matched nothing says so rather than passing in silence. An empty result
880
- * is not an error — a grant already withdrawn or never written is the
881
- * ordinary case, and re-running a revocation has to stay safe.
882
- * See docs/spec/access-control.md § Type-level grants.
883
- */
884
- revoke(target: GrantTarget, grants: Array<{
885
- actions: GrantAction[];
886
- typeId: TypeId;
887
- }>): Promise<StackRecord[]>;
852
+ * Returns the grants it withdrew, as they stood: an array, where
853
+ * revokeAccess() returns one Record, because one target can match several
854
+ * _grant records. An empty result is not an error — re-running a
855
+ * revocation has to stay safe. See
856
+ * docs/spec/access-control.md § Listing and revoking.
857
+ */
858
+ revokeType(typeOrBaseId: TypeId | BaseId, grant: TypeGrant): Promise<(StackRecord & {
859
+ content: GrantContent;
860
+ })[]>;
888
861
  /**
889
- * Validates the whole grant batch before any record is created, so a bad
890
- * entry fails clean rather than leaving a partial set written. Actions
891
- * must be known GrantAction values, typeIds must be well-formed, and the
892
- * _grant/_config families are refused — see docs/spec/access-control.md
893
- * § Type-level grants.
862
+ * Actions must be known GrantAction values, typeIds must be well-formed,
863
+ * and the _grant/_config families are refused — see
864
+ * docs/spec/access-control.md § Type-level grants.
894
865
  */
895
- private checkGrantsValid;
866
+ private checkGrantValid;
896
867
  private seedSystemTypes;
897
868
  /**
898
869
  * Fast-fail for the ifVersion precondition using the already-fetched
@@ -905,8 +876,8 @@ export declare class Stack implements StackClient {
905
876
  * The options every version-bumping adapter write carries: the `ifVersion`
906
877
  * precondition, the prior-state snapshot that has to land in the same
907
878
  * atomic write, and who to attribute the change to. Taken together so a
908
- * new mutating verb cannot quietly omit one — `updatedVia` most of all,
909
- * whose absence reads as an undelegated write.
879
+ * new mutating verb cannot quietly omit one — the actor's `principalId`
880
+ * most of all, whose absence reads as an undelegated write.
910
881
  * See docs/spec/versioning.md § Version history.
911
882
  */
912
883
  private writeOptions;