@haverstack/core 0.8.0 → 0.9.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.
- package/README.md +30 -14
- package/dist/access.d.ts +20 -7
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +37 -16
- package/dist/access.js.map +1 -1
- package/dist/adapter-entry.d.ts +12 -0
- package/dist/adapter-entry.d.ts.map +1 -0
- package/dist/adapter-entry.js +11 -0
- package/dist/adapter-entry.js.map +1 -0
- package/dist/attachment-download.d.ts +75 -0
- package/dist/attachment-download.d.ts.map +1 -0
- package/dist/attachment-download.js +128 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/auth.d.ts +88 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +145 -0
- package/dist/auth.js.map +1 -0
- package/dist/combine.d.ts.map +1 -1
- package/dist/combine.js +20 -3
- package/dist/combine.js.map +1 -1
- package/dist/did-entry.d.ts +12 -0
- package/dist/did-entry.d.ts.map +1 -0
- package/dist/did-entry.js +16 -0
- package/dist/did-entry.js.map +1 -0
- package/dist/did.d.ts +51 -0
- package/dist/did.d.ts.map +1 -0
- package/dist/did.js +153 -0
- package/dist/did.js.map +1 -0
- package/dist/id.d.ts +18 -6
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +39 -10
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +15 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -9
- package/dist/index.js.map +1 -1
- package/dist/merge.d.ts +11 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +38 -0
- package/dist/merge.js.map +1 -0
- package/dist/schema.d.ts +31 -9
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +134 -11
- package/dist/schema.js.map +1 -1
- package/dist/stack.d.ts +748 -152
- package/dist/stack.d.ts.map +1 -1
- package/dist/stack.js +1805 -328
- package/dist/stack.js.map +1 -1
- package/dist/testing.d.ts +84 -23
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +260 -19
- package/dist/testing.js.map +1 -1
- package/dist/types.d.ts +281 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/dist/validate.d.ts +26 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +51 -1
- package/dist/validate.js.map +1 -1
- package/dist/wire-entry.d.ts +17 -0
- package/dist/wire-entry.d.ts.map +1 -0
- package/dist/wire-entry.js +24 -0
- package/dist/wire-entry.js.map +1 -0
- package/package.json +13 -1
package/dist/types.d.ts
CHANGED
|
@@ -15,6 +15,20 @@ export type RecordId = string;
|
|
|
15
15
|
export type TypeId = string;
|
|
16
16
|
/** Opaque file identifier returned by putAttachment */
|
|
17
17
|
export type FileId = string;
|
|
18
|
+
/**
|
|
19
|
+
* Reverse-DNS identifier for the software that wrote a Record, e.g.
|
|
20
|
+
* "com.example.myapp" — not a RecordId. Self-reported and never a
|
|
21
|
+
* permission input; `StackRecord.principalId` is the verified counterpart.
|
|
22
|
+
* See docs/spec/identity.md § App.
|
|
23
|
+
*/
|
|
24
|
+
export type AppId = string;
|
|
25
|
+
/**
|
|
26
|
+
* Identifies a "who" — a DID string, e.g. "did:key:z6Mk...". A
|
|
27
|
+
* self-certifying identifier that means the same thing in every stack,
|
|
28
|
+
* unlike a RecordId. did:key is the mandatory floor method (see did.ts).
|
|
29
|
+
* See docs/spec/identity.md.
|
|
30
|
+
*/
|
|
31
|
+
export type EntityId = string;
|
|
18
32
|
export type TagAssociation = {
|
|
19
33
|
kind: 'tag';
|
|
20
34
|
label: string;
|
|
@@ -23,7 +37,6 @@ export type AttachmentAssociation = {
|
|
|
23
37
|
kind: 'attachment';
|
|
24
38
|
label: string;
|
|
25
39
|
fileId: FileId;
|
|
26
|
-
mimeType: string;
|
|
27
40
|
};
|
|
28
41
|
export type RelationshipAssociation = {
|
|
29
42
|
kind: 'relationship';
|
|
@@ -40,12 +53,14 @@ export type Permission = {
|
|
|
40
53
|
access: 'public';
|
|
41
54
|
} | {
|
|
42
55
|
access: 'entity';
|
|
43
|
-
entityId:
|
|
56
|
+
entityId: EntityId;
|
|
44
57
|
read: boolean;
|
|
45
58
|
write: boolean;
|
|
46
59
|
} | {
|
|
47
60
|
access: 'group';
|
|
48
61
|
groupId: RecordId;
|
|
62
|
+
/** Restricts this entry to group admins. Absent = any member (member or admin). */
|
|
63
|
+
role?: 'admin';
|
|
49
64
|
read: boolean;
|
|
50
65
|
write: boolean;
|
|
51
66
|
};
|
|
@@ -57,19 +72,31 @@ export type StackRecord = {
|
|
|
57
72
|
content: Record<string, unknown>;
|
|
58
73
|
version: number;
|
|
59
74
|
parentId?: RecordId;
|
|
60
|
-
entityId?:
|
|
61
|
-
appId?:
|
|
75
|
+
entityId?: EntityId;
|
|
76
|
+
appId?: AppId;
|
|
77
|
+
/**
|
|
78
|
+
* The authenticated principal behind the write, when it isn't the author
|
|
79
|
+
* itself — a delegated app's own DID. Absent means the writer
|
|
80
|
+
* authenticated as the author, so `appId` is an unverifiable self-report;
|
|
81
|
+
* present means `appId` can be checked against the `_app` record naming
|
|
82
|
+
* this DID. See docs/spec/identity.md § App.
|
|
83
|
+
*/
|
|
84
|
+
principalId?: EntityId;
|
|
62
85
|
deletedAt?: Date;
|
|
63
86
|
permissions?: Permission[];
|
|
64
87
|
associations?: Association[];
|
|
65
88
|
};
|
|
66
89
|
export type RecordVersion = {
|
|
67
90
|
version: number;
|
|
91
|
+
/** The record's typeId at the moment this version was snapshotted. */
|
|
92
|
+
typeId: TypeId;
|
|
68
93
|
content: Record<string, unknown>;
|
|
69
94
|
updatedAt: Date;
|
|
70
|
-
entityId?:
|
|
95
|
+
entityId?: EntityId;
|
|
96
|
+
associations?: Association[];
|
|
97
|
+
permissions?: Permission[];
|
|
71
98
|
};
|
|
72
|
-
export type ScalarFieldKind = 'string' | 'number' | 'boolean' | 'date' | 'text' | 'record-ref';
|
|
99
|
+
export type ScalarFieldKind = 'string' | 'number' | 'boolean' | 'date' | 'text' | 'record-ref' | 'file-ref';
|
|
73
100
|
export type ScalarFieldDef = {
|
|
74
101
|
kind: ScalarFieldKind;
|
|
75
102
|
required?: boolean;
|
|
@@ -104,41 +131,82 @@ export type StackType = {
|
|
|
104
131
|
migratesFrom?: TypeId;
|
|
105
132
|
createdAt: Date;
|
|
106
133
|
};
|
|
107
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Content for _entity records: a stack-local profile card *about* a DID,
|
|
136
|
+
* not the identity itself. `name`/`handle` are this owner's local labels
|
|
137
|
+
* for that DID (the petname pattern), so two stacks holding different
|
|
138
|
+
* names for the same DID is correct. See docs/spec/identity.md § Entity.
|
|
139
|
+
*/
|
|
108
140
|
export type EntityContent = {
|
|
141
|
+
/**
|
|
142
|
+
* The identity this profile is about. e.g. "did:key:z6Mk...". A binding,
|
|
143
|
+
* not a value — a Record's `entityId` resolves through it to the name
|
|
144
|
+
* this card carries, so it is unique per stack and immutable once set.
|
|
145
|
+
* See docs/spec/identity.md § DID bindings.
|
|
146
|
+
*/
|
|
147
|
+
did: string;
|
|
109
148
|
/** Display name — human-friendly, not necessarily unique. May contain spaces and punctuation. e.g. "Jane Smith" */
|
|
110
149
|
name: string;
|
|
111
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Short, conventionally URL-safe label. e.g. "janesmith". A label, not a
|
|
152
|
+
* key: duplicates are legitimate and `did` is what identifies this
|
|
153
|
+
* profile. See docs/spec/identity.md § Entity.
|
|
154
|
+
*/
|
|
112
155
|
handle?: string;
|
|
113
156
|
};
|
|
114
157
|
/** Content for _app records */
|
|
115
158
|
export type AppContent = {
|
|
159
|
+
/**
|
|
160
|
+
* The software this card is about, in reverse-DNS form — the value a
|
|
161
|
+
* Record's `appId` is checked against. Distinct from the card Record's
|
|
162
|
+
* own `appId`, which names whatever wrote the card (an admin console
|
|
163
|
+
* registering a third-party app is the ordinary case, and the two differ
|
|
164
|
+
* there). See docs/spec/identity.md § App.
|
|
165
|
+
*/
|
|
166
|
+
appId: AppId;
|
|
116
167
|
/** Display name of the app e.g. "My Notes App" */
|
|
117
168
|
name: string;
|
|
169
|
+
/** Semver string e.g. "1.0.0". `appId` is the machine-readable identity, so no handle is needed here. */
|
|
170
|
+
version?: string;
|
|
118
171
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
172
|
+
* The DID this app authenticates with, when it holds a key of its own.
|
|
173
|
+
* Lets an attribution UI resolve a record's `principalId` to this card,
|
|
174
|
+
* and a server check a self-reported `appId` against the principal that
|
|
175
|
+
* wrote it. Absent for apps that ride their user's identity. Unique per
|
|
176
|
+
* stack and immutable once set, like `appId` above — see
|
|
177
|
+
* docs/spec/identity.md § DID bindings.
|
|
122
178
|
*/
|
|
123
|
-
|
|
179
|
+
did?: string;
|
|
124
180
|
};
|
|
125
181
|
/** Content for _group records */
|
|
126
182
|
export type GroupContent = {
|
|
127
183
|
/** Display name — human-friendly, not necessarily unique. May contain spaces and punctuation. e.g. "Jane's Book Club" */
|
|
128
184
|
name: string;
|
|
129
|
-
/**
|
|
185
|
+
/**
|
|
186
|
+
* Short, conventionally URL-safe label. e.g. "janes-book-club". A label,
|
|
187
|
+
* not a key: duplicates are legitimate, and a group is addressed by
|
|
188
|
+
* `stackUrl` when it has one and by its record id otherwise. See
|
|
189
|
+
* docs/spec/identity.md § Group.
|
|
190
|
+
*/
|
|
130
191
|
handle?: string;
|
|
131
192
|
/** If present, this group owns a shared collaborative stack at this URL. Absent = permission-only group. */
|
|
132
193
|
stackUrl?: string;
|
|
133
194
|
};
|
|
134
|
-
/**
|
|
135
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Actions that can be granted via a _grant record. The array is the source
|
|
197
|
+
* of truth — GrantAction is derived from it so runtime validation (see
|
|
198
|
+
* Stack.grant()) can't drift from the type.
|
|
199
|
+
*/
|
|
200
|
+
export declare const GRANT_ACTIONS: readonly ["create", "read-own", "read-any", "update-own", "update-any", "delete-own", "delete-any"];
|
|
201
|
+
export type GrantAction = (typeof GRANT_ACTIONS)[number];
|
|
136
202
|
/** Content for _grant records */
|
|
137
203
|
export type GrantContent = {
|
|
138
204
|
/** Which record type the grant applies to. */
|
|
139
205
|
typeId: TypeId;
|
|
140
206
|
/** Which actions are permitted. */
|
|
141
207
|
actions: GrantAction[];
|
|
208
|
+
/** Who the grant applies to. Absent = default grant, applies to any authenticated entity. */
|
|
209
|
+
granteeEntityId?: EntityId;
|
|
142
210
|
};
|
|
143
211
|
/** Content for _attachment records — one per upload, tracks file metadata. */
|
|
144
212
|
export type AttachmentContent = {
|
|
@@ -153,10 +221,17 @@ export type AttachmentContent = {
|
|
|
153
221
|
};
|
|
154
222
|
/** Content for _config records — one singleton per stack, created on initialization. */
|
|
155
223
|
export type ConfigContent = {
|
|
156
|
-
/**
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
224
|
+
/**
|
|
225
|
+
* DID of the stack owner. Immutable — see docs/spec.md § The `_config`
|
|
226
|
+
* record for why ownership transfer isn't a field write.
|
|
227
|
+
*/
|
|
228
|
+
entityId: EntityId;
|
|
229
|
+
/**
|
|
230
|
+
* IANA timezone string e.g. "America/New_York". Optional passthrough app
|
|
231
|
+
* metadata — nothing in core reads it for behavior, and there is no
|
|
232
|
+
* default. See docs/spec.md § The `_config` record.
|
|
233
|
+
*/
|
|
234
|
+
timezone?: string;
|
|
160
235
|
};
|
|
161
236
|
/** Reserved system type IDs */
|
|
162
237
|
export declare const SYSTEM_TYPES: {
|
|
@@ -176,9 +251,18 @@ export type DateRange = {
|
|
|
176
251
|
};
|
|
177
252
|
export type RecordFilter = {
|
|
178
253
|
typeId?: TypeId | TypeId[];
|
|
254
|
+
/**
|
|
255
|
+
* Match every version of a type family — e.g. baseId: "com.example/note"
|
|
256
|
+
* matches both "com.example/note@1" and "com.example/note@2" records.
|
|
257
|
+
* Resolved against registered Types, not parsed from typeId strings, so
|
|
258
|
+
* it works regardless of which versions happen to exist. Combined with
|
|
259
|
+
* typeId (if both given) as an intersection.
|
|
260
|
+
*/
|
|
261
|
+
baseId?: string | string[];
|
|
179
262
|
parentId?: RecordId | null;
|
|
180
|
-
appId?:
|
|
181
|
-
entityId?:
|
|
263
|
+
appId?: AppId | AppId[];
|
|
264
|
+
entityId?: EntityId | EntityId[];
|
|
265
|
+
principalId?: EntityId | EntityId[];
|
|
182
266
|
createdAt?: DateRange;
|
|
183
267
|
updatedAt?: DateRange;
|
|
184
268
|
tags?: string[];
|
|
@@ -201,6 +285,13 @@ export type StackQuery = {
|
|
|
201
285
|
sort?: QuerySort;
|
|
202
286
|
limit?: number;
|
|
203
287
|
cursor?: string;
|
|
288
|
+
/**
|
|
289
|
+
* Records are returned exactly as stored by default ("stored"). Pass
|
|
290
|
+
* "latest" to apply the registered migration chain in memory before
|
|
291
|
+
* returning — never written back. Throws StackMigrationError if any
|
|
292
|
+
* matched record has no registered path to the latest version.
|
|
293
|
+
*/
|
|
294
|
+
presentAt?: 'stored' | 'latest';
|
|
204
295
|
};
|
|
205
296
|
export type QueryResult = {
|
|
206
297
|
records: StackRecord[];
|
|
@@ -222,39 +313,136 @@ export type Migration = {
|
|
|
222
313
|
};
|
|
223
314
|
export type AdapterCapabilities = {
|
|
224
315
|
fullTextSearch: boolean;
|
|
316
|
+
/**
|
|
317
|
+
* Required `true` for local/in-process adapters; wire adapters may
|
|
318
|
+
* report `false`, driven by the server's discovery response.
|
|
319
|
+
* Stack.query() throws StackQueryError rather than silently widening.
|
|
320
|
+
* See docs/spec/adapters.md § Adapter capabilities.
|
|
321
|
+
*/
|
|
225
322
|
contentFieldQuery: boolean;
|
|
226
323
|
sortableFields: Array<QuerySort['field']>;
|
|
324
|
+
/**
|
|
325
|
+
* Maximum attachment upload size in bytes, or `null` if unbounded. Lets
|
|
326
|
+
* apps pre-check and surface limits before burning an upload on a 413.
|
|
327
|
+
*/
|
|
328
|
+
maxAttachmentBytes: number | null;
|
|
329
|
+
/**
|
|
330
|
+
* Maximum serialized size in bytes of a Record's content — a create body
|
|
331
|
+
* or a merge patch — or `null` if unbounded. The counterpart to
|
|
332
|
+
* maxAttachmentBytes for the JSON side of a write, which had no stated
|
|
333
|
+
* ceiling anywhere despite the spec bounding every other resource
|
|
334
|
+
* (validation depth, queryAllPages, GC grace).
|
|
335
|
+
*
|
|
336
|
+
* Local adapters declare `null`: nothing at the storage layer imposes
|
|
337
|
+
* one, and a caller with in-process access to the database can spend its
|
|
338
|
+
* own memory however it likes. A server declares its request-size limit
|
|
339
|
+
* here so apps can pre-check rather than discover it as a 413.
|
|
340
|
+
* See docs/spec/adapters.md § Adapter capabilities.
|
|
341
|
+
*/
|
|
342
|
+
maxContentBytes: number | null;
|
|
227
343
|
};
|
|
228
344
|
/** What a Stack can do, as seen by app and plugin code. */
|
|
229
345
|
export type StackFeatures = AdapterCapabilities;
|
|
230
346
|
/**
|
|
231
|
-
*
|
|
347
|
+
* Opt-in optimistic-concurrency precondition, accepted by every mutation
|
|
348
|
+
* that bumps a record's version. On mismatch the adapter throws
|
|
349
|
+
* StackVersionConflictError without applying anything. The check is atomic
|
|
350
|
+
* inside the adapter's write, never a read-then-write. See
|
|
351
|
+
* docs/spec/versioning.md § Optimistic concurrency (`ifVersion`).
|
|
352
|
+
*/
|
|
353
|
+
export type ExpectedVersionOptions = {
|
|
354
|
+
expectedVersion?: number;
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Accepted by every mutating StackRecordAdapter method. The adapter
|
|
358
|
+
* persists this prior-state snapshot as part of the SAME atomic write as
|
|
359
|
+
* the mutation, so a crash between the two can't leave an orphan versions
|
|
360
|
+
* row. Server-backed adapters (already atomic per-request) can ignore it.
|
|
361
|
+
*/
|
|
362
|
+
export type SnapshotOptions = {
|
|
363
|
+
snapshot?: RecordVersion;
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* The record-storage half of an adapter: structured data, queries,
|
|
232
367
|
* associations, versioning, type definitions, and stack identity.
|
|
233
368
|
*/
|
|
234
369
|
export interface StackRecordAdapter {
|
|
235
370
|
readonly capabilities: AdapterCapabilities;
|
|
236
|
-
/**
|
|
237
|
-
readonly ownerEntityId:
|
|
238
|
-
/**
|
|
239
|
-
|
|
371
|
+
/** DID of the stack owner. Set during adapter initialization. */
|
|
372
|
+
readonly ownerEntityId: EntityId;
|
|
373
|
+
/**
|
|
374
|
+
* IANA timezone string for this stack, or undefined if never set.
|
|
375
|
+
* Passthrough app metadata with no default — see docs/spec.md § The
|
|
376
|
+
* `_config` record.
|
|
377
|
+
*/
|
|
378
|
+
readonly timezone: string | undefined;
|
|
379
|
+
/**
|
|
380
|
+
* Throws StackConflictError if `record.id` already exists — never a
|
|
381
|
+
* silent overwrite. The check must be atomic with the write (a PK/unique
|
|
382
|
+
* constraint or equivalent); Stack itself doesn't pre-check, so a raw
|
|
383
|
+
* adapter that skips this enforces nothing.
|
|
384
|
+
*/
|
|
240
385
|
createRecord(record: StackRecord): Promise<StackRecord>;
|
|
241
386
|
getRecord(id: RecordId): Promise<StackRecord | null>;
|
|
242
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Apply a content-only RFC 7396 merge patch: `null` removes a field,
|
|
389
|
+
* omitted fields are retained. Bumps `version`/`updatedAt` in the same
|
|
390
|
+
* write. Never touches `typeId` — a type change goes through
|
|
391
|
+
* commitMigration() instead.
|
|
392
|
+
*/
|
|
393
|
+
patchContent(id: RecordId, patch: Record<string, unknown | null>, opts?: ExpectedVersionOptions & SnapshotOptions): Promise<StackRecord>;
|
|
243
394
|
deleteRecord(id: RecordId, opts?: {
|
|
244
395
|
hard?: boolean;
|
|
245
|
-
}): Promise<void>;
|
|
396
|
+
} & ExpectedVersionOptions & SnapshotOptions): Promise<void>;
|
|
397
|
+
/** Reverse a soft delete. Returns the record as it now stands. */
|
|
398
|
+
undeleteRecord(id: RecordId, opts?: ExpectedVersionOptions & SnapshotOptions): Promise<StackRecord>;
|
|
246
399
|
queryRecords(query: StackQuery): Promise<QueryResult>;
|
|
247
|
-
associate(id: RecordId, association: Association): Promise<void>;
|
|
248
|
-
dissociate(id: RecordId, association: Association): Promise<void>;
|
|
400
|
+
associate(id: RecordId, association: Association, opts?: ExpectedVersionOptions & SnapshotOptions): Promise<void>;
|
|
401
|
+
dissociate(id: RecordId, association: Association, opts?: ExpectedVersionOptions & SnapshotOptions): Promise<void>;
|
|
402
|
+
/** Replace all permissions on a record. Bumps version internally. */
|
|
403
|
+
setPermissions(id: RecordId, permissions: Permission[], opts?: ExpectedVersionOptions & SnapshotOptions): Promise<void>;
|
|
249
404
|
getVersions(id: RecordId): Promise<RecordVersion[]>;
|
|
250
405
|
getVersion(id: RecordId, version: number): Promise<RecordVersion | null>;
|
|
406
|
+
/**
|
|
407
|
+
* Standalone snapshot write, outside of a mutation's own atomic path.
|
|
408
|
+
* Mutating methods above take a `snapshot` option instead, so the
|
|
409
|
+
* snapshot and the mutation land in one write — see SnapshotOptions.
|
|
410
|
+
*/
|
|
251
411
|
saveVersion(id: RecordId, version: RecordVersion): Promise<void>;
|
|
412
|
+
/**
|
|
413
|
+
* Restore a record to a previous version's content (and associations,
|
|
414
|
+
* when the snapshot has them). Never restores permissions. Bumps version
|
|
415
|
+
* internally. Throws StackNotFoundError if the version doesn't exist.
|
|
416
|
+
*/
|
|
417
|
+
restoreVersion(id: RecordId, version: number, opts?: ExpectedVersionOptions & SnapshotOptions): Promise<StackRecord>;
|
|
418
|
+
/**
|
|
419
|
+
* Commit a migration: write new content under a new typeId in one step.
|
|
420
|
+
* This is the only way a record's typeId changes after creation — used
|
|
421
|
+
* exclusively by Stack.migrateAll(); Stack.update() never changes typeId
|
|
422
|
+
* as a side effect. Bumps version internally.
|
|
423
|
+
*/
|
|
424
|
+
commitMigration(id: RecordId, toTypeId: TypeId, content: Record<string, unknown>, opts?: SnapshotOptions): Promise<StackRecord>;
|
|
252
425
|
saveType(type: StackType): Promise<void>;
|
|
253
426
|
getType(id: TypeId): Promise<StackType | null>;
|
|
254
427
|
listTypes(): Promise<StackType[]>;
|
|
428
|
+
/**
|
|
429
|
+
* Atomically verify fileId is unreferenced, then hard-delete its
|
|
430
|
+
* metadata records in the same adapter call, returning their ids.
|
|
431
|
+
* Throws StackConflictError if still referenced. Optional —
|
|
432
|
+
* Stack.deleteAttachment() has a non-atomic fallback. See
|
|
433
|
+
* docs/spec/attachments.md § Deleting attachments.
|
|
434
|
+
*/
|
|
435
|
+
deleteUnreferencedAttachmentRecords?(fileId: FileId, metadataTypeId: TypeId): Promise<RecordId[]>;
|
|
255
436
|
flush?(): Promise<void>;
|
|
256
437
|
close?(): Promise<void>;
|
|
257
438
|
}
|
|
439
|
+
/** One stored blob, as reported by StackBlobAdapter.listFiles(). */
|
|
440
|
+
export type BlobFileInfo = {
|
|
441
|
+
fileId: FileId;
|
|
442
|
+
size: number;
|
|
443
|
+
/** When the blob was written. Used to apply a GC grace period to fresh, not-yet-associated uploads. */
|
|
444
|
+
modifiedAt: Date;
|
|
445
|
+
};
|
|
258
446
|
/**
|
|
259
447
|
* The blob-storage half of an adapter. Handles raw binary data only;
|
|
260
448
|
* attachment metadata lives on _attachment@1 records in the record adapter.
|
|
@@ -263,6 +451,13 @@ export interface StackBlobAdapter {
|
|
|
263
451
|
putAttachment(data: Uint8Array): Promise<FileId>;
|
|
264
452
|
getAttachment(fileId: FileId): Promise<Uint8Array>;
|
|
265
453
|
deleteAttachment(fileId: FileId): Promise<void>;
|
|
454
|
+
/**
|
|
455
|
+
* Enumerate every blob currently in storage. Optional — without it,
|
|
456
|
+
* garbage collection can't find bare-bytes orphans (bytes with no
|
|
457
|
+
* metadata record at all). See docs/spec/attachments.md § Garbage
|
|
458
|
+
* collection.
|
|
459
|
+
*/
|
|
460
|
+
listFiles?(): Promise<BlobFileInfo[]>;
|
|
266
461
|
flush?(): Promise<void>;
|
|
267
462
|
close?(): Promise<void>;
|
|
268
463
|
}
|
|
@@ -271,5 +466,60 @@ export interface StackBlobAdapter {
|
|
|
271
466
|
* Pass this to Stack.create(). Build one with combineAdapters() when you
|
|
272
467
|
* want different backends for records and blobs (e.g. SQLite + S3).
|
|
273
468
|
*/
|
|
274
|
-
export type StackAdapter = StackRecordAdapter & StackBlobAdapter
|
|
469
|
+
export type StackAdapter = StackRecordAdapter & StackBlobAdapter & {
|
|
470
|
+
/**
|
|
471
|
+
* Store bytes and create the accompanying _attachment@1 record as one
|
|
472
|
+
* atomic operation. Optional; implement only when bytes and records
|
|
473
|
+
* live behind a single boundary (today: APIAdapter alone), and never
|
|
474
|
+
* synthesized by combineAdapters(). A security boundary, not an
|
|
475
|
+
* efficiency shortcut — see docs/spec/adapters.md § Interface split.
|
|
476
|
+
*/
|
|
477
|
+
putAttachmentWithMetadata?(data: Uint8Array, mimeType: string, filename?: string, appId?: AppId): Promise<StackRecord>;
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
480
|
+
* The two identities a token establishes. Both are always populated: on an
|
|
481
|
+
* undelegated token they are the same DID, and spelling that out is what
|
|
482
|
+
* keeps the mapping onto asEntity() mechanical.
|
|
483
|
+
*
|
|
484
|
+
* stack.asEntity(session.principalId, { onBehalfOf: session.subjectId })
|
|
485
|
+
*
|
|
486
|
+
* See docs/spec/access-control.md § Delegation: principal and subject.
|
|
487
|
+
*/
|
|
488
|
+
export type TokenSession = {
|
|
489
|
+
/** Who authenticated — the DID that proved key possession. Governs authority. */
|
|
490
|
+
principalId: EntityId;
|
|
491
|
+
/** Who the principal acts for. Governs attribution. Equal to principalId unless delegated. */
|
|
492
|
+
subjectId: EntityId;
|
|
493
|
+
};
|
|
494
|
+
export type TokenInfo = TokenSession & {
|
|
495
|
+
id: string;
|
|
496
|
+
label?: string;
|
|
497
|
+
createdAt: Date;
|
|
498
|
+
expiresAt?: Date;
|
|
499
|
+
};
|
|
500
|
+
/**
|
|
501
|
+
* Bearer-token issuance and lookup for server implementations — a
|
|
502
|
+
* standalone interface, not a slot on StackAdapter. createToken() trusts
|
|
503
|
+
* its caller: DID verification happens first, via the challenge-response
|
|
504
|
+
* handshake. Tokens SHOULD be stored outside the portable stack file.
|
|
505
|
+
* See docs/spec/wire-format.md § Authentication.
|
|
506
|
+
*
|
|
507
|
+
* `onBehalfOf` is the delegation binding, and no handshake can establish
|
|
508
|
+
* it — proving key possession proves the principal and nothing about whom
|
|
509
|
+
* it may act for. It is asserted by the owner out of band, which is safe
|
|
510
|
+
* because effective authority is the intersection of both parties' grants.
|
|
511
|
+
*/
|
|
512
|
+
export interface StackTokenStore {
|
|
513
|
+
createToken(principalId: EntityId, opts?: {
|
|
514
|
+
onBehalfOf?: EntityId;
|
|
515
|
+
label?: string;
|
|
516
|
+
expiresAt?: Date;
|
|
517
|
+
}): Promise<{
|
|
518
|
+
id: string;
|
|
519
|
+
token: string;
|
|
520
|
+
}>;
|
|
521
|
+
lookupToken(token: string): Promise<TokenSession | null>;
|
|
522
|
+
listTokens(): Promise<TokenInfo[]>;
|
|
523
|
+
revokeToken(id: string): Promise<void>;
|
|
524
|
+
}
|
|
275
525
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,4EAA4E;AAC5E,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,uDAAuD;AACvD,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
* This file is the source of truth for all types in the
|
|
5
5
|
* Stack library. No logic lives here — just shapes.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Actions that can be granted via a _grant record. The array is the source
|
|
9
|
+
* of truth — GrantAction is derived from it so runtime validation (see
|
|
10
|
+
* Stack.grant()) can't drift from the type.
|
|
11
|
+
*/
|
|
12
|
+
export const GRANT_ACTIONS = [
|
|
13
|
+
'create',
|
|
14
|
+
'read-own',
|
|
15
|
+
'read-any',
|
|
16
|
+
'update-own',
|
|
17
|
+
'update-any',
|
|
18
|
+
'delete-own',
|
|
19
|
+
'delete-any',
|
|
20
|
+
];
|
|
7
21
|
/** Reserved system type IDs */
|
|
8
22
|
export const SYSTEM_TYPES = {
|
|
9
23
|
ENTITY: '_entity',
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsPH;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ;IACR,UAAU;IACV,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;CACJ,CAAC;AAyCX,+BAA+B;AAC/B,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,SAAS;IACjB,GAAG,EAAE,MAAM;IACX,KAAK,EAAE,QAAQ;IACf,oDAAoD;IACpD,KAAK,EAAE,QAAQ;IACf,0DAA0D;IAC1D,UAAU,EAAE,aAAa;IACzB,8DAA8D;IAC9D,MAAM,EAAE,SAAS;CACT,CAAC"}
|
package/dist/validate.d.ts
CHANGED
|
@@ -20,6 +20,32 @@ export type ValidationError = {
|
|
|
20
20
|
* @param errors - Internal: error accumulator for recursive calls
|
|
21
21
|
*/
|
|
22
22
|
export declare const validateContent: (content: Record<string, unknown>, schema: TypeSchema, prefix?: string, errors?: ValidationError[], depth?: number) => ValidationError[];
|
|
23
|
+
/**
|
|
24
|
+
* Content keys that name JavaScript's object machinery rather than a
|
|
25
|
+
* field. Undeclared content fields are allowed by design, so without this
|
|
26
|
+
* they reach `merged[key] = value` in applyMergePatch — where `__proto__`
|
|
27
|
+
* invokes the prototype setter instead of setting a field.
|
|
28
|
+
*
|
|
29
|
+
* That is not a prototype-pollution gadget today: JSON.stringify writes
|
|
30
|
+
* only own-enumerable properties, so a reassigned prototype never reaches
|
|
31
|
+
* storage, and JSON.parse recreates `__proto__` as an own property rather
|
|
32
|
+
* than invoking the setter, so a stored one round-trips inertly. The
|
|
33
|
+
* defect is that the two write paths disagree — a merge patch to
|
|
34
|
+
* `__proto__` silently vanishes, while the same key through create()
|
|
35
|
+
* stores as an ordinary field — and that a future refactor deepening the
|
|
36
|
+
* shallow merge would turn a quiet inconsistency into a real one.
|
|
37
|
+
*
|
|
38
|
+
* Rejecting is more honest than skipping: a caller who sends one of these
|
|
39
|
+
* finds out, rather than watching a write appear to succeed and do
|
|
40
|
+
* nothing. See docs/spec/data-model.md § Reserved content keys.
|
|
41
|
+
*/
|
|
42
|
+
export declare const RESERVED_CONTENT_KEYS: readonly string[];
|
|
43
|
+
/**
|
|
44
|
+
* Top-level only. Nested occurrences survive the JSON round trip as inert
|
|
45
|
+
* own properties (see above), and walking undeclared subtrees to find them
|
|
46
|
+
* would cost a traversal the schema walk doesn't otherwise do.
|
|
47
|
+
*/
|
|
48
|
+
export declare const validateReservedKeys: (content: Record<string, unknown>) => ValidationError[];
|
|
23
49
|
/**
|
|
24
50
|
* Returns true if the content is valid against the schema.
|
|
25
51
|
* Use validateContent() directly to get error details.
|
package/dist/validate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAA6B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAA6B,MAAM,YAAY,CAAC;AAmBxE,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA6FF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,UAAU,EAClB,eAAW,EACX,SAAQ,eAAe,EAAO,EAC9B,cAAS,KACR,eAAe,EAyBjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAA8C,CAAC;AAElG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,eAAe,EAIlF,CAAC;AAEN;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,UAAU,KAAG,OAChC,CAAC"}
|
package/dist/validate.js
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
* Coercion is never performed — types must match exactly.
|
|
9
9
|
*/
|
|
10
10
|
const MAX_VALIDATION_DEPTH = 32;
|
|
11
|
+
/** fileId format: SHA-256 hex, lowercase — matches blob-adapter-disk's assertFileId(). */
|
|
12
|
+
const FILE_ID_RE = /^[0-9a-f]{64}$/;
|
|
13
|
+
/**
|
|
14
|
+
* ISO 8601 date or date-time shape. The regex pins the shape (bare
|
|
15
|
+
* `Date.parse` accepts engine-dependent non-ISO formats); `Date.parse`
|
|
16
|
+
* still runs afterward as a calendar sanity check. See
|
|
17
|
+
* docs/spec/data-model.md § Types.
|
|
18
|
+
*/
|
|
19
|
+
const ISO_8601_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/;
|
|
11
20
|
// -------------------------------------------------------
|
|
12
21
|
// Internal helpers
|
|
13
22
|
// -------------------------------------------------------
|
|
@@ -16,6 +25,7 @@ const jsTypeForScalar = (kind) => {
|
|
|
16
25
|
case 'string':
|
|
17
26
|
case 'text':
|
|
18
27
|
case 'record-ref':
|
|
28
|
+
case 'file-ref':
|
|
19
29
|
return 'string';
|
|
20
30
|
case 'number':
|
|
21
31
|
return 'number';
|
|
@@ -51,7 +61,9 @@ const validateField = (value, def, path, errors, depth = 0) => {
|
|
|
51
61
|
}
|
|
52
62
|
// Scalar validation
|
|
53
63
|
if (def.kind === 'date') {
|
|
54
|
-
if (typeof value !== 'string' ||
|
|
64
|
+
if (typeof value !== 'string' ||
|
|
65
|
+
!ISO_8601_RE.test(value) ||
|
|
66
|
+
isNaN(Date.parse(value))) {
|
|
55
67
|
errors.push({
|
|
56
68
|
path,
|
|
57
69
|
message: `Expected ISO 8601 date string, got ${typeof value}`,
|
|
@@ -59,6 +71,15 @@ const validateField = (value, def, path, errors, depth = 0) => {
|
|
|
59
71
|
}
|
|
60
72
|
return;
|
|
61
73
|
}
|
|
74
|
+
if (def.kind === 'file-ref') {
|
|
75
|
+
if (typeof value !== 'string' || !FILE_ID_RE.test(value)) {
|
|
76
|
+
errors.push({
|
|
77
|
+
path,
|
|
78
|
+
message: 'Expected a 64-character lowercase hex fileId (SHA-256)',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
62
83
|
const expected = jsTypeForScalar(def.kind);
|
|
63
84
|
if (typeof value !== expected) {
|
|
64
85
|
errors.push({
|
|
@@ -99,6 +120,35 @@ export const validateContent = (content, schema, prefix = '', errors = [], depth
|
|
|
99
120
|
}
|
|
100
121
|
return errors;
|
|
101
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* Content keys that name JavaScript's object machinery rather than a
|
|
125
|
+
* field. Undeclared content fields are allowed by design, so without this
|
|
126
|
+
* they reach `merged[key] = value` in applyMergePatch — where `__proto__`
|
|
127
|
+
* invokes the prototype setter instead of setting a field.
|
|
128
|
+
*
|
|
129
|
+
* That is not a prototype-pollution gadget today: JSON.stringify writes
|
|
130
|
+
* only own-enumerable properties, so a reassigned prototype never reaches
|
|
131
|
+
* storage, and JSON.parse recreates `__proto__` as an own property rather
|
|
132
|
+
* than invoking the setter, so a stored one round-trips inertly. The
|
|
133
|
+
* defect is that the two write paths disagree — a merge patch to
|
|
134
|
+
* `__proto__` silently vanishes, while the same key through create()
|
|
135
|
+
* stores as an ordinary field — and that a future refactor deepening the
|
|
136
|
+
* shallow merge would turn a quiet inconsistency into a real one.
|
|
137
|
+
*
|
|
138
|
+
* Rejecting is more honest than skipping: a caller who sends one of these
|
|
139
|
+
* finds out, rather than watching a write appear to succeed and do
|
|
140
|
+
* nothing. See docs/spec/data-model.md § Reserved content keys.
|
|
141
|
+
*/
|
|
142
|
+
export const RESERVED_CONTENT_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
143
|
+
/**
|
|
144
|
+
* Top-level only. Nested occurrences survive the JSON round trip as inert
|
|
145
|
+
* own properties (see above), and walking undeclared subtrees to find them
|
|
146
|
+
* would cost a traversal the schema walk doesn't otherwise do.
|
|
147
|
+
*/
|
|
148
|
+
export const validateReservedKeys = (content) => RESERVED_CONTENT_KEYS.filter((key) => Object.hasOwn(content, key)).map((key) => ({
|
|
149
|
+
path: key,
|
|
150
|
+
message: `"${key}" is a reserved content key and cannot be used as a field name`,
|
|
151
|
+
}));
|
|
102
152
|
/**
|
|
103
153
|
* Returns true if the content is valid against the schema.
|
|
104
154
|
* Use validateContent() directly to get error details.
|