@haverstack/core 0.29.0 → 0.30.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 (73) hide show
  1. package/dist/adapter-entry.d.ts +1 -1
  2. package/dist/adapter-entry.d.ts.map +1 -1
  3. package/dist/adapter-entry.js +1 -1
  4. package/dist/adapter-entry.js.map +1 -1
  5. package/dist/changes.d.ts +14 -0
  6. package/dist/changes.d.ts.map +1 -1
  7. package/dist/changes.js +35 -0
  8. package/dist/changes.js.map +1 -1
  9. package/dist/errors.d.ts +173 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/errors.js +208 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/grants.d.ts +84 -0
  14. package/dist/grants.d.ts.map +1 -0
  15. package/dist/grants.js +157 -0
  16. package/dist/grants.js.map +1 -0
  17. package/dist/id.d.ts +7 -5
  18. package/dist/id.d.ts.map +1 -1
  19. package/dist/id.js +17 -15
  20. package/dist/id.js.map +1 -1
  21. package/dist/identity-bindings.d.ts +13 -0
  22. package/dist/identity-bindings.d.ts.map +1 -0
  23. package/dist/identity-bindings.js +42 -0
  24. package/dist/identity-bindings.js.map +1 -0
  25. package/dist/index.d.ts +6 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/limits.d.ts +23 -0
  30. package/dist/limits.d.ts.map +1 -0
  31. package/dist/limits.js +35 -0
  32. package/dist/limits.js.map +1 -0
  33. package/dist/query-validation.d.ts +80 -0
  34. package/dist/query-validation.d.ts.map +1 -0
  35. package/dist/query-validation.js +223 -0
  36. package/dist/query-validation.js.map +1 -0
  37. package/dist/record-changes.d.ts +89 -0
  38. package/dist/record-changes.d.ts.map +1 -0
  39. package/dist/record-changes.js +201 -0
  40. package/dist/record-changes.js.map +1 -0
  41. package/dist/record-id.d.ts +70 -0
  42. package/dist/record-id.d.ts.map +1 -0
  43. package/dist/record-id.js +127 -0
  44. package/dist/record-id.js.map +1 -0
  45. package/dist/scoped-stack.d.ts +460 -0
  46. package/dist/scoped-stack.d.ts.map +1 -0
  47. package/dist/scoped-stack.js +1163 -0
  48. package/dist/scoped-stack.js.map +1 -0
  49. package/dist/stack-reads.d.ts +38 -0
  50. package/dist/stack-reads.d.ts.map +1 -0
  51. package/dist/stack-reads.js +79 -0
  52. package/dist/stack-reads.js.map +1 -0
  53. package/dist/stack.d.ts +37 -643
  54. package/dist/stack.d.ts.map +1 -1
  55. package/dist/stack.js +102 -2254
  56. package/dist/stack.js.map +1 -1
  57. package/dist/testing.d.ts.map +1 -1
  58. package/dist/testing.js +3 -1
  59. package/dist/testing.js.map +1 -1
  60. package/dist/types.d.ts +9 -2
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js +8 -0
  63. package/dist/types.js.map +1 -1
  64. package/dist/wire-entry.d.ts +2 -2
  65. package/dist/wire-entry.d.ts.map +1 -1
  66. package/dist/wire-entry.js +2 -2
  67. package/dist/wire-entry.js.map +1 -1
  68. package/dist/wire-record.js +1 -1
  69. package/dist/wire-record.js.map +1 -1
  70. package/dist/wire-request.d.ts.map +1 -1
  71. package/dist/wire-request.js +3 -2
  72. package/dist/wire-request.js.map +1 -1
  73. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Record ID and clock-field validation
3
+ * -------------------------------------------------------
4
+ * The rules a caller-supplied `id`, `parentId`, `createdAt` or `updatedAt`
5
+ * is held to before any of them reaches storage. A client may mint its own
6
+ * id and backdate a record's clock, so both are untrusted input here even
7
+ * when the types say otherwise.
8
+ *
9
+ * See docs/spec/data-model.md § Record IDs.
10
+ */
11
+ import { isValidIdFormat, idTimestamp, MAX_ID_TIMESTAMP } from './id.js';
12
+ import { StackQueryError, StackValidationError } from './errors.js';
13
+ // -------------------------------------------------------
14
+ // Record ID validation
15
+ // -------------------------------------------------------
16
+ const RESERVED_ID_PREFIX = '_';
17
+ export const DEFAULT_ID_TIMESTAMP_SKEW_MS = 24 * 60 * 60 * 1000;
18
+ /**
19
+ * The same format rule applied to a `parentId` a caller names. Separate
20
+ * from validateRecordId() only for its message: the id under discussion is
21
+ * the destination, not the record being written, and a shared message would
22
+ * report the wrong one. See docs/spec/data-model.md § Reparenting.
23
+ */
24
+ export function validateParentId(parentId) {
25
+ if (parentId.startsWith(RESERVED_ID_PREFIX)) {
26
+ throw new StackQueryError(`Invalid parentId "${parentId}": uses the reserved "${RESERVED_ID_PREFIX}" prefix.`);
27
+ }
28
+ if (!isValidIdFormat(parentId)) {
29
+ throw new StackQueryError(`Invalid parentId "${parentId}": expected 12 lowercase Crockford base-32 characters.`);
30
+ }
31
+ }
32
+ /**
33
+ * Format and reserved-prefix checks — full-trust context (Stack.create()).
34
+ * Checked before the format check: the Crockford charset already excludes
35
+ * "_", so a reserved-looking id (e.g. "_config") would otherwise just fail
36
+ * as a generic format error instead of a specific, actionable one.
37
+ *
38
+ * Throws StackQueryError, not StackValidationError: a malformed id is
39
+ * structurally bad input the request never gets past — it doesn't reach
40
+ * type-schema validation — the same reasoning that makes an undecodable
41
+ * pagination cursor a StackQueryError rather than a content-validation
42
+ * failure. See StackQueryError's doc comment.
43
+ */
44
+ export function validateRecordId(id) {
45
+ if (id.startsWith(RESERVED_ID_PREFIX)) {
46
+ throw new StackQueryError(`ID "${id}" uses the reserved "${RESERVED_ID_PREFIX}" prefix.`);
47
+ }
48
+ if (!isValidIdFormat(id)) {
49
+ throw new StackQueryError(`Invalid ID "${id}": expected 12 lowercase Crockford base-32 characters.`);
50
+ }
51
+ }
52
+ /**
53
+ * Validity and range check for the backdating options on unscoped
54
+ * Stack.create(). A Date is only as good as what the caller parsed it
55
+ * from, and the two failure modes both need catching here rather than
56
+ * downstream:
57
+ *
58
+ * - **Invalid Date** (`new Date('13/45/2020')` off a malformed import row)
59
+ * has a NaN getTime(), and every comparison against NaN is false — so an
60
+ * unchecked Invalid Date passes the updatedAt/createdAt ordering check
61
+ * and the id/createdAt skew check by turning them off, mints the
62
+ * epoch-zero ID `000000000xxx`, and persists a record whose
63
+ * `createdAt.toISOString()` throws RangeError in serializeRecord() —
64
+ * making that record, and any wire response containing it,
65
+ * permanently unreadable.
66
+ * - **Out of encodable range** — before 1970 crockford32Encode() throws a
67
+ * bare RangeError from deep inside the ID encoder, and past
68
+ * MAX_ID_TIMESTAMP the derived ID silently grows to 13 characters and
69
+ * fails isValidIdFormat().
70
+ *
71
+ * Checked at the door for both, as a StackValidationError naming the
72
+ * field. Applies whether or not an `id` is supplied: a clock field outside
73
+ * this range is unrepresentable regardless of where the ID came from.
74
+ */
75
+ export function validateClockField(value, path) {
76
+ if (value === undefined)
77
+ return [];
78
+ // Type-checked callers always pass a Date, but this option is now
79
+ // reachable from the wire: JSON has no Date, so a server that forwards a
80
+ // parsed `POST /records` body hands us the ISO *string* it deserialized.
81
+ // Without this the very next line is `"2020-…".getTime()` — an unhandled
82
+ // TypeError, a 500 where the caller should have got a 400 naming the
83
+ // field.
84
+ if (!(value instanceof Date)) {
85
+ return [{ path, message: `${path} must be a Date.` }];
86
+ }
87
+ const ms = value.getTime();
88
+ if (Number.isNaN(ms)) {
89
+ return [{ path, message: `${path} is not a valid Date.` }];
90
+ }
91
+ if (ms < 0 || ms > MAX_ID_TIMESTAMP) {
92
+ return [
93
+ {
94
+ path,
95
+ message: `${path} is outside the representable range ` +
96
+ `(1970-01-01T00:00:00.000Z…${new Date(MAX_ID_TIMESTAMP).toISOString()}).`,
97
+ },
98
+ ];
99
+ }
100
+ return [];
101
+ }
102
+ /**
103
+ * Timestamp-prefix plausibility check, shared by callers that compare an
104
+ * ID's embedded millisecond against a different reference each:
105
+ * ScopedStack.create() against the current time for any non-backdated
106
+ * create (a grantee is untrusted and could otherwise mint an ID that
107
+ * forges its sort position, and this is the one check standing between a
108
+ * delegated or grantee caller and doing so), and Stack.create() — reached
109
+ * directly when unscoped, or via ScopedStack.create() when the requester
110
+ * is the owner acting alone with an explicit `createdAt` — against that
111
+ * `createdAt` instead (the two must agree, not silently diverge — see
112
+ * docs/spec/data-model.md § Record IDs). Pass null to disable.
113
+ */
114
+ export function validateIdTimestampSkew(id, toleranceMs, referenceMs, referenceLabel) {
115
+ if (toleranceMs === null)
116
+ return;
117
+ const skew = Math.abs(referenceMs - idTimestamp(id));
118
+ if (skew > toleranceMs) {
119
+ throw new StackValidationError([
120
+ {
121
+ path: 'id',
122
+ message: `ID "${id}" timestamp disagrees with ${referenceLabel} by more than the allowed clock-skew tolerance (${toleranceMs}ms).`,
123
+ },
124
+ ]);
125
+ }
126
+ }
127
+ //# sourceMappingURL=record-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-id.js","sourceRoot":"","sources":["../src/record-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGpE,0DAA0D;AAC1D,uBAAuB;AACvB,0DAA0D;AAE1D,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,QAAQ,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,eAAe,CACvB,qBAAqB,QAAQ,yBAAyB,kBAAkB,WAAW,CACpF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,eAAe,CACvB,qBAAqB,QAAQ,wDAAwD,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,IAAI,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,wBAAwB,kBAAkB,WAAW,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,eAAe,CACvB,eAAe,EAAE,wDAAwD,CAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAuB,EAAE,IAAY;IACtE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,kEAAkE;IAClE,yEAAyE;IACzE,yEAAyE;IACzE,yEAAyE;IACzE,qEAAqE;IACrE,SAAS;IACT,IAAI,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,kBAAkB,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,uBAAuB,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,gBAAgB,EAAE,CAAC;QACpC,OAAO;YACL;gBACE,IAAI;gBACJ,OAAO,EACL,GAAG,IAAI,sCAAsC;oBAC7C,6BAA6B,IAAI,IAAI,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,IAAI;aAC5E;SACF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CACrC,EAAU,EACV,WAA0B,EAC1B,WAAmB,EACnB,cAAsB;IAEtB,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IACrD,IAAI,IAAI,GAAG,WAAW,EAAE,CAAC;QACvB,MAAM,IAAI,oBAAoB,CAAC;YAC7B;gBACE,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,OAAO,EAAE,8BAA8B,cAAc,mDAAmD,WAAW,MAAM;aACnI;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -0,0 +1,460 @@
1
+ /**
2
+ * ScopedStack — the permission-enforcing view
3
+ * -------------------------------------------------------
4
+ * A `Stack` answers every question; a `ScopedStack` answers only the ones a
5
+ * particular requester is entitled to. It wraps a Stack rather than an
6
+ * adapter, so every invariant the Stack layer holds is already in force by
7
+ * the time a check here runs, and the gates below only ever narrow.
8
+ *
9
+ * Two identities, one rule: **the principal governs authority, the subject
10
+ * governs attribution.** Both are DIDs and both are often equal, which is
11
+ * what makes them easy to confuse — so the split is stated on every gate
12
+ * that depends on it.
13
+ *
14
+ * Also holds the feed side of the same rule: a subscription's deliveries
15
+ * run through the same canRead() a get() answers with, so a feed can never
16
+ * disagree with a read about what a session sees.
17
+ *
18
+ * See docs/spec/access-control.md and docs/spec/events.md § Permission scoping.
19
+ */
20
+ import { ChangeEmitter } from './changes.js';
21
+ import type { AppId, Association, AttachmentContent, EntityId, QueryResult, RecordChange, RecordVersion, StackAdapter, StackFeatures, StackQuery, StackRecord, SubscribeOptions, TypeId, Unsubscribe, RecordChanges } from './types.js';
22
+ import type { Stack, BackdatableCreateRecordOptions, CollectAttachmentGarbageOptions, CollectAttachmentGarbageResult, DeleteRecordOptions, GetRecordOptions, IfVersionOptions, StackClient } from './stack.js';
23
+ /**
24
+ * A permission-enforcing view of a Stack for a single (principal, subject)
25
+ * pair, obtained via `stack.asEntity(entityId)`. A record the request
26
+ * cannot read answers exactly as a missing one does — null on reads,
27
+ * StackNotFoundError on the verbs that name one — so only a requester who
28
+ * could have read it is told a refusal was about access.
29
+ * See docs/spec/access-control.md § Errors and information exposure.
30
+ *
31
+ * Two identities, one rule: **the principal governs authority, the subject
32
+ * governs attribution.** Grant lookup and the privilege-bearing gates that
33
+ * no grant reaches (resharing, group management, hard delete, widening
34
+ * access at create time) key on `principalEntityId`; authorship, `-own`
35
+ * matching, record-level permission resolution, and "files I uploaded"
36
+ * lookups key on `subjectEntityId`.
37
+ *
38
+ * Unconditional owner access follows that same split rather than one
39
+ * identity: it answers *what data is reachable* for the subject (an owner
40
+ * subject resolves past every permission check) and *who may exercise a
41
+ * privileged verb* for the principal (an owner app is not bounded by
42
+ * grants). Under delegation both halves apply, and a mistake on the
43
+ * authority side is an escalation rather than a preference.
44
+ */
45
+ export declare class ScopedStack implements StackClient {
46
+ private readonly stack;
47
+ private readonly principalEntityId;
48
+ private readonly subjectEntityId;
49
+ private readonly idTimestampSkewMs;
50
+ private readonly adapter;
51
+ private readonly changes;
52
+ constructor(stack: Stack, principalEntityId: EntityId | null, subjectEntityId: EntityId | null, idTimestampSkewMs: number | null, adapter: StackAdapter, changes: ChangeEmitter);
53
+ get features(): StackFeatures;
54
+ private resolveRecord;
55
+ /** Every `_grant` Record — see loadGrantRecords(). */
56
+ private loadGrants;
57
+ /** Whether a delegated app is acting for someone other than itself. */
58
+ private get delegated();
59
+ /**
60
+ * Who this request is, for stamping onto whatever it mutates. Attribution
61
+ * follows record-level authorship: the subject is the actor, and the
62
+ * principal is named beside it only when the two differ.
63
+ * See docs/spec/data-model.md § Authorship and attribution.
64
+ */
65
+ private get actor();
66
+ /**
67
+ * Whether unconditional owner authority applies — the owner acting as
68
+ * itself. The verbs that rest on it are irreversible or disclose the
69
+ * sharing graph, so delegation never carries one to a subject, whichever
70
+ * side the owner is on. Shared with the predicate a server applies to a
71
+ * session, which decides the same tier one step earlier. See
72
+ * docs/spec/access-control.md § Delegation: principal and subject.
73
+ */
74
+ private get ownerActingAlone();
75
+ /**
76
+ * Refuse anything but the owner acting as itself. The verbs resting on
77
+ * this tier are irreversible or disclose the sharing graph, so delegation
78
+ * never carries one to a subject — see ownerActingAlone.
79
+ */
80
+ private requireOwnerActingAlone;
81
+ private checkRead;
82
+ private checkWrite;
83
+ /**
84
+ * Whether `grantee` holds a _grant covering one of `actions` for the
85
+ * type's family (grants match by baseId, so a version bump never orphans
86
+ * one). -own actions additionally require record.entityId === grantee,
87
+ * unless `matchOwn` is false — on the principal side of a delegated
88
+ * request the suffix is read as the bare verb, since which records are
89
+ * reachable is the subject's business. `allowDefault` decides whether a
90
+ * grant naming nobody counts. Anonymous grantees always return false.
91
+ *
92
+ * Reached only through subjectAllows()/principalAllows(), which fix those
93
+ * two flags per side of the intersection. Call one of those instead.
94
+ * See docs/spec/access-control.md § Type-level grants.
95
+ */
96
+ private hasGrant;
97
+ /**
98
+ * The principal half of a delegated request's authority: does the app
99
+ * hold any grant permitting these verbs on this type at all. Bounds what
100
+ * the subject's own authority can reach through it, so a powerful app
101
+ * can never lend its reach to a weaker subject — nor the reverse.
102
+ * Vacuously true when there's no delegation, where the principal and
103
+ * subject checks would be the same question asked twice.
104
+ *
105
+ * Default grants don't count here. "Any authenticated entity" is about
106
+ * people who turn up, not software the owner installed — an app reaches
107
+ * only the types named to it, which is the whole of what containment
108
+ * promises.
109
+ *
110
+ * Group-targeted grants don't count here either, one step removed: a
111
+ * roster is editable by any of the group's admins, so authority reaching
112
+ * a principal through one would let someone other than the owner name an
113
+ * app to a type. See docs/spec/access-control.md § Type-level grants.
114
+ */
115
+ private principalAllows;
116
+ /**
117
+ * The subject half: which records are reachable, answered with `-own`
118
+ * matching and default grants both in force — the ordinary reading of a
119
+ * grant, since the subject is the entity a grant is written about.
120
+ *
121
+ * Paired with principalAllows() so that the two halves of the
122
+ * intersection are the only callers of hasGrant(): its flags differ per
123
+ * side and mean nothing on their own, so no call site sets them by hand.
124
+ */
125
+ private subjectAllows;
126
+ /**
127
+ * How to refuse a record this request addressed by ID. A requester who
128
+ * can read the record is told it exists and the verb was refused;
129
+ * everyone else is told what a missing ID is told, so no one learns an ID
130
+ * is live who could not have learned it by reading. `message` therefore
131
+ * only ever reaches someone holding the record already.
132
+ * See docs/spec/access-control.md § Errors and information exposure.
133
+ */
134
+ private denialFor;
135
+ private canRead;
136
+ private checkCreateGrant;
137
+ /**
138
+ * `_group` records are managed, not merely written: only the owner or an
139
+ * `admin` roster holder may mutate them — ordinary write permissions and
140
+ * grants don't apply. Asked of both identities under delegation, like
141
+ * a reshare. See docs/spec/identity.md § Group.
142
+ */
143
+ private isGroupManager;
144
+ /** Whether one identity, on its own, manages `record` — see isGroupManager(). */
145
+ private managesGroup;
146
+ /**
147
+ * Fetch a record the subject can reach and the principal holds `update` on
148
+ * (via permissions or an update grant), or throw. `mutating: false` marks
149
+ * the history readers, which borrow this gate without changing anything —
150
+ * the one way through it a `_grant` Record stays open to.
151
+ */
152
+ private requireUpdatable;
153
+ /**
154
+ * The write gate both requireUpdatable() and requireDeletable() are: the
155
+ * subject can reach the record (record-level permission or a grant) and
156
+ * the principal holds the same verb, intersected — or, for a `_group`, the
157
+ * stricter management rule that no grant reaches.
158
+ * See docs/spec/access-control.md § Delegation: principal and subject.
159
+ */
160
+ private requireVerb;
161
+ /**
162
+ * Refuse a mutation aimed at a soft-deleted Record. Asked only of a
163
+ * mutating caller, so the history readers borrowing this gate still work,
164
+ * and only after the authority decision, so "exists but deleted" is never
165
+ * a probe a stranger can run. See docs/spec/versioning.md § Mutations are
166
+ * refused, not applied to a tombstone.
167
+ */
168
+ private refuseIfDeleted;
169
+ /**
170
+ * Fetch a record the subject can reach and the principal holds `delete` on
171
+ * (via permissions or a delete grant), or throw.
172
+ */
173
+ private requireDeletable;
174
+ /**
175
+ * Whether this request may reference `recordId` (as a parentId or
176
+ * relationship target). Missing and unreadable both return false —
177
+ * indistinguishable, so this can't probe for a record's existence.
178
+ */
179
+ private canReadReferent;
180
+ /**
181
+ * Whether this request can read some record referencing `fileId` —
182
+ * shared by canAccessFile() and the non-owner _attachment@1 create()
183
+ * carve-out, which deliberately excludes the uploader clause. Walks
184
+ * every referencing record, short-circuiting on the first readable one.
185
+ * `_attachment@1` records are excluded from matching outright: the
186
+ * carve-out must be satisfied by some *other* record referencing the
187
+ * file, never by the requester's own prior metadata record for it —
188
+ * allowing that would let one successful guess unlock unlimited further
189
+ * metadata records for the same fileId, reintroducing the circularity
190
+ * the carve-out's uploader-clause exclusion closes.
191
+ * See docs/spec/attachments.md § Creating `_attachment@1` records directly.
192
+ */
193
+ private hasReadableReference;
194
+ /**
195
+ * Whether this request may reference or download `fileId` — the dual of
196
+ * getAttachment()'s access rule. Nonexistent and inaccessible are
197
+ * indistinguishable (both false), so no confirmation oracle for guessed
198
+ * hashes. See docs/spec/access-control.md § Reference-creation gating.
199
+ */
200
+ private canAccessFile;
201
+ /** Names of the type's top-level file-ref fields — the content-reference half of attachmentFileId matching. */
202
+ private fileRefFieldNames;
203
+ /**
204
+ * Gates file-ref content fields on file access, mirroring the
205
+ * attachment-association gate — a file-ref field conveys attachment
206
+ * access exactly like an `attachment` association. Only fields present
207
+ * in `content` are checked (a content patch is a merge patch; untouched fields
208
+ * carry no new reference).
209
+ */
210
+ private requireFileRefAccess;
211
+ /**
212
+ * Reference-creation gate for one association: `attachment` requires
213
+ * file access, and a `relationship` naming a record in this stack
214
+ * requires read access to it. `tag` is unchecked, and `_group` roster
215
+ * associations are gated by the stricter isGroupManager() instead.
216
+ *
217
+ * The other target arms are ungated because the gate's purpose —
218
+ * refusing a reference that would convey access to, or confirm the
219
+ * existence of, an unreadable record — has nothing to bite on: core
220
+ * never resolves them, so no access flows through one.
221
+ * See docs/spec/access-control.md § Reference-creation gating.
222
+ */
223
+ private requireAssociationAccess;
224
+ /**
225
+ * Create a record on behalf of the subject: create grant required,
226
+ * anonymous denied, entityId set to the subject, client IDs skew-checked,
227
+ * reference-creating options gated, and non-owner `_attachment@1`
228
+ * creation refused save one carve-out. A scoped create always stamps
229
+ * authorship — an absent entityId means an unscoped `Stack` wrote it.
230
+ * `createdAt`/`updatedAt` are refused to everyone but the owner acting
231
+ * alone — see the guard below and docs/spec/data-model.md § Record IDs.
232
+ * See also docs/spec/access-control.md and docs/spec/attachments.md.
233
+ */
234
+ create<T extends Record<string, unknown> = Record<string, unknown>>(typeId: TypeId, content: T, opts?: BackdatableCreateRecordOptions): Promise<StackRecord & {
235
+ content: T;
236
+ }>;
237
+ /**
238
+ * Whether this request may decide who else reaches a record — the rule
239
+ * a reshare enforces, asked at create time too so the reach it
240
+ * withholds can't be taken one step earlier while authoring. A delegated
241
+ * app is denied it: widening access is the one thing containment most
242
+ * needs to hold. Refused rather than silently ignored, so an app never
243
+ * believes it published something it didn't. Not `ownerActingAlone`:
244
+ * the record is the subject's own, so an owner principal grants it no
245
+ * reach the subject lacks.
246
+ * See docs/spec/access-control.md § Delegation: principal and subject.
247
+ */
248
+ private mayGrantAccess;
249
+ /**
250
+ * Whether one identity, on its own, may decide who else reaches `record`
251
+ * — the owner-or-creator rule a reshare enforces, asked of one
252
+ * side at a time. See
253
+ * docs/spec/access-control.md § Delegation: principal and subject.
254
+ */
255
+ private mayReshare;
256
+ /**
257
+ * A record this request may read, or null. Never throws
258
+ * StackPermissionError: an unreadable record answers exactly as a missing
259
+ * one does, so a caller learns only what it may read.
260
+ * See docs/spec/access-control.md § Errors and information exposure.
261
+ */
262
+ get(id: string, opts?: GetRecordOptions): Promise<StackRecord | null>;
263
+ /**
264
+ * Runs through this.query(), so canRead() still applies — this answers a
265
+ * question the caller asks on its own behalf. includeUnlisted is passed
266
+ * only when the request is the owner acting alone, per
267
+ * docs/spec/identity.md § DID bindings.
268
+ */
269
+ getEntityByDid(did: EntityId): Promise<StackRecord | null>;
270
+ getOwnerEntity(): Promise<StackRecord | null>;
271
+ /**
272
+ * Query records, filtered to those this request can read. Pages are
273
+ * filtered then refilled, so a page may slightly overshoot `limit` but
274
+ * never skips a record. Grants are prefetched once, cursor-walked to
275
+ * exhaustion.
276
+ */
277
+ query(query?: StackQuery): Promise<QueryResult>;
278
+ /**
279
+ * Apply a change set on behalf of the subject. Authority is resolved
280
+ * **per key** and every gate reads the record as it stands, never as the
281
+ * change set would leave it: a widened `permissions` never satisfies the
282
+ * read check on a `parentId` named in the same call, and a `_group`
283
+ * roster never satisfies the admin check that same call has to pass.
284
+ * One refused key refuses the whole call, so nothing is partially
285
+ * applied and no key is silently dropped.
286
+ * See docs/spec/access-control.md § Composing a change set.
287
+ */
288
+ mutate(id: string, changes: RecordChanges, opts?: IfVersionOptions): Promise<StackRecord>;
289
+ patchContent(id: string, patch: Record<string, unknown | null>, opts?: IfVersionOptions): Promise<StackRecord>;
290
+ /**
291
+ * The record, having established that this request may decide who else
292
+ * reaches it — the owner-or-creator rule, asked of both sides of a
293
+ * delegation, or a Group's admin rule for a `_group`. The gate the
294
+ * `permissions` and `unlisted` keys carry, which the write bit
295
+ * deliberately does not confer.
296
+ */
297
+ private requireReshareable;
298
+ /** The reshare decision alone, for a record already read and write-gated. */
299
+ private requireReshareOf;
300
+ /**
301
+ * Naming the software behind a key is the trust decision the `_app`
302
+ * registry exists to record, so both halves of that binding — `did` and
303
+ * `appId` — belong to the owner alone. Same reasoning that makes `_app`
304
+ * ungrantable, applied to the fields a lookup reads. Registering a card
305
+ * is already owner-only; without this, record-level `write` shared on a
306
+ * card would be a second way in: a card carrying no DID yet could be
307
+ * pointed at a write-holder's own key while keeping the name the owner
308
+ * gave it, or relabelled to claim another app's `appId`. `name` and
309
+ * `version` stay writable — they are display, not lookup.
310
+ *
311
+ * Owner *acting alone*, in both directions: a delegated app never holds
312
+ * it, and an owner principal doesn't lend it to a subject holding
313
+ * record-level `write` on a card — which would reopen the same route
314
+ * from the other side.
315
+ *
316
+ * `_entity` deliberately does not get this rule: naming people is what a
317
+ * contacts app does, so its cards stay writable by grant. Uniqueness and
318
+ * immutability still bind them. See docs/spec/identity.md § DID bindings.
319
+ */
320
+ private requireOwnerForAppIdentity;
321
+ /**
322
+ * A self-reported `appId` must agree with the `_app` card naming the
323
+ * principal's DID, where the owner registered one. `principalId` is
324
+ * verified, so letting the pair disagree would leave a verified principal
325
+ * claiming a name the owner gave different software — the cross-check the
326
+ * registry exists for, refused at the write instead of left to each reader.
327
+ * A principal with no card keeps `appId` as the bare self-report it is for
328
+ * every undelegated writer.
329
+ * See docs/spec/identity.md § Attribution and what can be trusted.
330
+ */
331
+ private requireAppIdMatchesPrincipal;
332
+ /**
333
+ * The owner's own DID is the one `_entity` binding a grantee may not claim.
334
+ * `ownerProfile` adopts whichever card holds it, so a card minted by
335
+ * someone else becomes the stack's own profile, and uniqueness then makes
336
+ * that permanent. Every other DID stays open to a contacts app, which is
337
+ * the reach `_entity` is grantable for.
338
+ * See docs/spec/identity.md § DID bindings.
339
+ */
340
+ private requireOwnerForOwnerDid;
341
+ /**
342
+ * A `_grant` Record *is* authority, so rewriting one is the escalation
343
+ * UNGRANTABLE_SYSTEM_TYPES refuses at evaluation, reached by editing an
344
+ * existing grant rather than minting a fresh one. `grant()` and `revoke()`
345
+ * live on `Stack`, never `StackClient`, so no scoped write is lost. Writes
346
+ * only: reading a grant Record and its history stays on the ordinary gate.
347
+ * See docs/spec/access-control.md § Type-level grants.
348
+ */
349
+ private requireOwnerForGrantRecord;
350
+ associate(id: string, association: Association, opts?: IfVersionOptions): Promise<StackRecord>;
351
+ dissociate(id: string, association: Association, opts?: IfVersionOptions): Promise<StackRecord>;
352
+ /**
353
+ * Hard delete is owner-only: it is irreversible and destroys version
354
+ * history, so neither the write bit nor delete-own/delete-any grants
355
+ * reach it, and delegation doesn't carry it either. Everyone else is
356
+ * limited to soft delete.
357
+ */
358
+ delete(id: string, opts?: DeleteRecordOptions): Promise<void>;
359
+ /**
360
+ * Reverse a soft delete. Gated the same as delete() — undelete is the
361
+ * inverse of soft delete, so granting one direction without the other
362
+ * would be backwards. Idempotent, per Stack.undelete().
363
+ */
364
+ undelete(id: string, opts?: IfVersionOptions): Promise<StackRecord>;
365
+ /**
366
+ * History is the mutation/recovery surface, not a read surface — gated
367
+ * like patchContent(), with snapshot `permissions` stripped for everyone but
368
+ * the owner acting alone — a snapshot's permissions are the stack's
369
+ * sharing graph, which delegation is not a route to. Reading history
370
+ * changes nothing, so it is the one path the `_grant` write fence leaves
371
+ * alone: seeing how a Record you can already read got that way is not
372
+ * the escalation that fence exists to stop, and losing it would leave a
373
+ * write-holder unable to audit the Record they hold.
374
+ * See docs/spec/versioning.md § History access.
375
+ */
376
+ getVersions(id: string): Promise<RecordVersion[]>;
377
+ /** See getVersions() — same mutate-surface gate, same permissions stripping. */
378
+ getVersion(id: string, version: number): Promise<RecordVersion | null>;
379
+ /**
380
+ * Re-runs the reference-creation checks against the snapshot, so a
381
+ * restore can't re-convey access to a file or record the subject can no
382
+ * longer reach today — the snapshot's `parentId` among them, gated
383
+ * exactly as a change set's `parentId` gates a destination named directly. Only the
384
+ * owner acting alone is exempt: under delegation the checks resolve
385
+ * against the subject, which is whose reach the restore would widen.
386
+ * See docs/spec/versioning.md § Restore semantics.
387
+ */
388
+ restoreVersion(id: string, version: number, opts?: IfVersionOptions): Promise<StackRecord>;
389
+ /**
390
+ * Commit a per-record migration — **the owner acting alone, only**.
391
+ *
392
+ * Migration is owner-driven by design: `migrateAll()`, the bulk path,
393
+ * lives on `Stack` and is deliberately absent from `StackClient`, the
394
+ * same way `grant()`/`revoke()` are. This is its per-record counterpart
395
+ * and carries the same restriction, rather than inventing a grant model
396
+ * that the bulk path deliberately doesn't have.
397
+ *
398
+ * The restriction is what makes the verb safe to expose at all. Migrate
399
+ * replaces `content` and `typeId` wholesale, so a grant-based version
400
+ * would have to re-derive every gate `create()` applies at the
401
+ * destination *and* every gate `mutate()` applies over the existing
402
+ * content, and would reopen each one it missed. The sharpest is the
403
+ * non-owner `_attachment@1` refusal create() carries: without it, a
404
+ * requester holding a create grant on `_attachment@1` and write access
405
+ * to any record they authored could migrate that record into the family
406
+ * naming any `fileId`, then read the bytes through canAccessFile()'s
407
+ * uploader clause — the exact escalation that carve-out exists to refuse
408
+ * (see docs/spec/attachments.md § Creating `_attachment@1` records
409
+ * directly). Ordinary write access to a record is not consent to move it
410
+ * between families.
411
+ *
412
+ * A server implementing `POST /records/:id/migrate` therefore serves it
413
+ * to the stack owner and answers 403 otherwise. See
414
+ * docs/spec/data-model.md § Type migrations.
415
+ */
416
+ commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: IfVersionOptions): Promise<StackRecord>;
417
+ /**
418
+ * Store bytes and create an _attachment@1 metadata record (create grant
419
+ * on `_attachment@1` required; anonymous denied), returning that record.
420
+ * Authorship and principal are stamped exactly as create() does.
421
+ */
422
+ putAttachment(data: Uint8Array, mimeType: string, filename?: string, appId?: AppId): Promise<StackRecord & {
423
+ content: AttachmentContent;
424
+ }>;
425
+ /**
426
+ * Download attachment bytes. Accessible to the owner, a reader of any
427
+ * referencing record, or the uploader pre-association — the same
428
+ * predicate as the reference-creation gate (canAccessFile).
429
+ */
430
+ getAttachment(fileId: string): Promise<Uint8Array>;
431
+ /**
432
+ * Delete an attachment. Only the stack owner may delete attachments.
433
+ * Delegates to Stack.deleteAttachment(), which enforces the "not referenced" check.
434
+ */
435
+ deleteAttachment(fileId: string): Promise<void>;
436
+ /**
437
+ * Sweep for unreferenced attachment bytes and delete them. Only the stack
438
+ * owner may run this. Delegates to Stack.collectAttachmentGarbage().
439
+ */
440
+ collectAttachmentGarbage(opts?: CollectAttachmentGarbageOptions): Promise<CollectAttachmentGarbageResult>;
441
+ /**
442
+ * Observe the changes this request may read. The predicate is canRead
443
+ * applied per event — the same one get() and query() answer with, so a
444
+ * feed can't disagree with them about what this session sees.
445
+ *
446
+ * A record the subscriber cannot read produces no event at all, rather
447
+ * than an empty or redacted one: the existence of a change is itself a
448
+ * disclosure, the same reasoning that keeps a count of the whole match
449
+ * off a query result. See docs/spec/events.md § Permission scoping.
450
+ */
451
+ subscribe(handler: (change: RecordChange) => void, opts?: SubscribeOptions): Promise<Unsubscribe>;
452
+ /**
453
+ * canRead for one event, through the subscription's own cache. Grants
454
+ * are prefetched once and refilled after an invalidation rather than
455
+ * re-queried per event, which would be a `_grant` scan for every change
456
+ * the stack makes.
457
+ */
458
+ private canReadCached;
459
+ }
460
+ //# sourceMappingURL=scoped-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-stack.d.ts","sourceRoot":"","sources":["../src/scoped-stack.ts"],"names":[],"mappings":"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"}