@substrat-run/kernel 0.139.0 → 0.140.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/dist/admin-log-ddl.d.ts +21 -0
- package/dist/admin-log-ddl.d.ts.map +1 -0
- package/dist/admin-log-ddl.js +32 -0
- package/dist/admin-log-ddl.js.map +1 -0
- package/dist/attachment-extractor.d.ts +12 -1
- package/dist/attachment-extractor.d.ts.map +1 -1
- package/dist/attachment-extractor.js +10 -5
- package/dist/attachment-extractor.js.map +1 -1
- package/dist/audit-outcome.d.ts +149 -0
- package/dist/audit-outcome.d.ts.map +1 -0
- package/dist/audit-outcome.js +233 -0
- package/dist/audit-outcome.js.map +1 -0
- package/dist/connect-links.d.ts +91 -0
- package/dist/connect-links.d.ts.map +1 -0
- package/dist/connect-links.js +189 -0
- package/dist/connect-links.js.map +1 -0
- package/dist/connect-state.d.ts +12 -3
- package/dist/connect-state.d.ts.map +1 -1
- package/dist/connect-state.js +8 -3
- package/dist/connect-state.js.map +1 -1
- package/dist/derived-object.d.ts +14 -0
- package/dist/derived-object.d.ts.map +1 -0
- package/dist/derived-object.js +2 -0
- package/dist/derived-object.js.map +1 -0
- package/dist/derived-objects.d.ts +108 -0
- package/dist/derived-objects.d.ts.map +1 -0
- package/dist/derived-objects.js +199 -0
- package/dist/derived-objects.js.map +1 -0
- package/dist/email-intent.d.ts +56 -0
- package/dist/email-intent.d.ts.map +1 -0
- package/dist/email-intent.js +71 -0
- package/dist/email-intent.js.map +1 -0
- package/dist/entity-edges.d.ts +6 -1
- package/dist/entity-edges.d.ts.map +1 -1
- package/dist/entity-edges.js +13 -0
- package/dist/entity-edges.js.map +1 -1
- package/dist/entity-grant-shape.d.ts +94 -0
- package/dist/entity-grant-shape.d.ts.map +1 -0
- package/dist/entity-grant-shape.js +329 -0
- package/dist/entity-grant-shape.js.map +1 -0
- package/dist/entity-grant.d.ts +45 -0
- package/dist/entity-grant.d.ts.map +1 -0
- package/dist/entity-grant.js +71 -0
- package/dist/entity-grant.js.map +1 -0
- package/dist/entity-state-reads.d.ts +4 -7
- package/dist/entity-state-reads.d.ts.map +1 -1
- package/dist/entity-state-reads.js +13 -57
- package/dist/entity-state-reads.js.map +1 -1
- package/dist/entity-state.d.ts +37 -10
- package/dist/entity-state.d.ts.map +1 -1
- package/dist/entity-state.js +80 -69
- package/dist/entity-state.js.map +1 -1
- package/dist/entity-trash.d.ts +245 -0
- package/dist/entity-trash.d.ts.map +1 -0
- package/dist/entity-trash.js +380 -0
- package/dist/entity-trash.js.map +1 -0
- package/dist/grant-scoped-read.d.ts +41 -0
- package/dist/grant-scoped-read.d.ts.map +1 -0
- package/dist/grant-scoped-read.js +100 -0
- package/dist/grant-scoped-read.js.map +1 -0
- package/dist/index.d.ts +31 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -4
- package/dist/index.js.map +1 -1
- package/dist/kernel-outbox.d.ts +14 -0
- package/dist/kernel-outbox.d.ts.map +1 -0
- package/dist/kernel-outbox.js +37 -0
- package/dist/kernel-outbox.js.map +1 -0
- package/dist/list-index.d.ts +11 -2
- package/dist/list-index.d.ts.map +1 -1
- package/dist/list-index.js +29 -21
- package/dist/list-index.js.map +1 -1
- package/dist/mail-sender.d.ts +78 -0
- package/dist/mail-sender.d.ts.map +1 -0
- package/dist/mail-sender.js +2 -0
- package/dist/mail-sender.js.map +1 -0
- package/dist/membership-executor.d.ts +0 -52
- package/dist/membership-executor.d.ts.map +1 -1
- package/dist/membership-executor.js +19 -59
- package/dist/membership-executor.js.map +1 -1
- package/dist/migration-digest.d.ts +161 -0
- package/dist/migration-digest.d.ts.map +1 -0
- package/dist/migration-digest.js +237 -0
- package/dist/migration-digest.js.map +1 -0
- package/dist/module-erasure.d.ts +156 -0
- package/dist/module-erasure.d.ts.map +1 -0
- package/dist/module-erasure.js +440 -0
- package/dist/module-erasure.js.map +1 -0
- package/dist/peer.d.ts +9 -1
- package/dist/peer.d.ts.map +1 -1
- package/dist/peer.js +29 -0
- package/dist/peer.js.map +1 -1
- package/dist/permission-checker.d.ts +6 -0
- package/dist/permission-checker.d.ts.map +1 -1
- package/dist/permission-checker.js.map +1 -1
- package/dist/permission-eval.d.ts +10 -1
- package/dist/permission-eval.d.ts.map +1 -1
- package/dist/permission-eval.js +51 -2
- package/dist/permission-eval.js.map +1 -1
- package/dist/platform-sweep.d.ts +1 -1
- package/dist/platform-sweep.d.ts.map +1 -1
- package/dist/platform-sweep.js +5 -0
- package/dist/platform-sweep.js.map +1 -1
- package/dist/scope-host.d.ts +301 -22
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +72 -5
- package/dist/scope-host.js.map +1 -1
- package/dist/search-index.d.ts +8 -0
- package/dist/search-index.d.ts.map +1 -1
- package/dist/search-index.js +51 -21
- package/dist/search-index.js.map +1 -1
- package/dist/spine-guard.d.ts +7 -0
- package/dist/spine-guard.d.ts.map +1 -1
- package/dist/spine-guard.js +54 -6
- package/dist/spine-guard.js.map +1 -1
- package/dist/spine-restore.d.ts.map +1 -1
- package/dist/spine-restore.js +5 -0
- package/dist/spine-restore.js.map +1 -1
- package/dist/sql-statements.d.ts +71 -0
- package/dist/sql-statements.d.ts.map +1 -0
- package/dist/sql-statements.js +191 -0
- package/dist/sql-statements.js.map +1 -0
- package/dist/subject-redaction.d.ts +9 -1
- package/dist/subject-redaction.d.ts.map +1 -1
- package/dist/subject-redaction.js.map +1 -1
- package/dist/system-switch-record.d.ts +3 -0
- package/dist/system-switch-record.d.ts.map +1 -1
- package/dist/system-switch-record.js +2 -0
- package/dist/system-switch-record.js.map +1 -1
- package/dist/table-ownership.d.ts +103 -0
- package/dist/table-ownership.d.ts.map +1 -0
- package/dist/table-ownership.js +285 -0
- package/dist/table-ownership.js.map +1 -0
- package/dist/timeline.d.ts +4 -4
- package/dist/timeline.js +4 -4
- package/dist/visible-continuation.d.ts +38 -0
- package/dist/visible-continuation.d.ts.map +1 -0
- package/dist/visible-continuation.js +162 -0
- package/dist/visible-continuation.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-trash.d.ts","sourceRoot":"","sources":["../src/entity-trash.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The trash, held by the host (#119 PR 2) — the half of K-45 a handler used to have to remember.
|
|
3
|
+
*
|
|
4
|
+
* Three things, written once for both adapters, the way `entity-state.ts` is:
|
|
5
|
+
*
|
|
6
|
+
* 1. **The refusal.** An operation that addresses an entity by id (`permission: { entity,
|
|
7
|
+
* idFrom }`) is refused on a TRASHED one before its guards and handler run, unless it
|
|
8
|
+
* declares `trashed: 'admits'` or `'purges'`. PR 1's review found handlers forgetting the
|
|
9
|
+
* per-handler check (`list-shares`, `revoke-share`); this makes it a mechanism.
|
|
10
|
+
* 2. **The purge horizon.** An entity declaring `trash.purgeAfterDays` is permanently deleted
|
|
11
|
+
* once it has been in the bin that long, by the sweep running the module's own
|
|
12
|
+
* `trashed: 'purges'` operation as a derived schedule (`purgeSchedulesOf` in contracts), one
|
|
13
|
+
* entity per transaction.
|
|
14
|
+
* 3. **Registration.** The checks that make both of those true of a module rather than of the
|
|
15
|
+
* modules that remembered a line.
|
|
16
|
+
*
|
|
17
|
+
* ## What the refusal does not reach, stated
|
|
18
|
+
*
|
|
19
|
+
* An operation whose check is `resolved` in the handler, narrowed by `refFrom`, or a `narrows`
|
|
20
|
+
* walk has no entity the host can read off its input, so it is not refused here and keeps its
|
|
21
|
+
* own `ctx.entityState` check. `trashRefusalGapsOf` names them and `lint:model` prints them.
|
|
22
|
+
*/
|
|
23
|
+
import { TRASHED_AT_COLUMN, declaredSurfaceOf, errorCodeOf, isStrictObjectSchema, substratError, } from '@substrat-run/contracts';
|
|
24
|
+
import { assertAllowed } from './permission-checker.js';
|
|
25
|
+
import { readStateRow } from './entity-state.js';
|
|
26
|
+
/** How many entities one purge pass deletes per entity type. A full batch leaves the schedule due. */
|
|
27
|
+
export const PURGE_BATCH = 50;
|
|
28
|
+
/**
|
|
29
|
+
* Is `entity` out of reach as a parent (#119): its type declares trash, and it is in the trash or
|
|
30
|
+
* does not exist. The two answer as ONE, so `ctx.link` cannot be used to tell a binned entity from
|
|
31
|
+
* a missing one. `false` for a type that declares no trash — the kernel never read those rows
|
|
32
|
+
* before, and linking to one is unchanged.
|
|
33
|
+
*/
|
|
34
|
+
export function isUnreachableParent(sql, plans, entity) {
|
|
35
|
+
const plan = plans.get(entity.entityType);
|
|
36
|
+
if (!plan?.trashPermission)
|
|
37
|
+
return false;
|
|
38
|
+
const row = readStateRow(sql, plan, entity.entityId);
|
|
39
|
+
return !row || row.trashed_at !== null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Refuse an operation reaching a trashed entity it did not declare it reaches (#119).
|
|
43
|
+
*
|
|
44
|
+
* Runs after `BEGIN` and before the guards and the handler, for every caller and every door.
|
|
45
|
+
*
|
|
46
|
+
* **The order is the security property.** On a trashed entity the operation's DECLARED key is
|
|
47
|
+
* checked first, exactly as the handler's first line would have: a caller without it gets the
|
|
48
|
+
* `forbidden` an active entity would have given them, so the bin is not something they can
|
|
49
|
+
* probe. Only a caller who holds the key learns it is gone — `not_found`, the answer the
|
|
50
|
+
* per-handler check gave. An active entity is never checked here; the handler does it, as
|
|
51
|
+
* always, so a pass is never recorded twice.
|
|
52
|
+
*
|
|
53
|
+
* `purge` is set only by the host's own purge sweep (`InvokeOptions` carries no such field, and an
|
|
54
|
+
* adapter refuses a call that tries to supply one). The CUTOFF is computed here, from `now` — the
|
|
55
|
+
* host's clock — and the entity's declared horizon, never taken from a caller: the entity must
|
|
56
|
+
* still be trashed at or before it, or the purge is `conflict` (`purge_not_due`). So a restore
|
|
57
|
+
* that landed between the sweep's selection and this turn wins, and nothing can purge early.
|
|
58
|
+
*/
|
|
59
|
+
export async function refuseTrashedTarget(deps, operation, target, input, purge) {
|
|
60
|
+
if (purge && target?.trashed !== 'purges') {
|
|
61
|
+
throw substratError('internal', `${operation} is not a purge — only a \`trashed: 'purges'\` operation is run by the purge sweep`);
|
|
62
|
+
}
|
|
63
|
+
// A purge reaches one entity, whoever runs it: its PARSED input is the id and nothing else.
|
|
64
|
+
// Registration requires a strict schema; this holds the parse result to it as well.
|
|
65
|
+
if (target?.trashed === 'purges')
|
|
66
|
+
assertPurgeInput(operation, target, input);
|
|
67
|
+
// An operation that opted in reaches the bin as it is; only a purge re-checks its cutoff.
|
|
68
|
+
if (!target || (target.trashed && !purge))
|
|
69
|
+
return;
|
|
70
|
+
const plan = deps.plans.get(target.entity);
|
|
71
|
+
if (!plan?.trashPermission)
|
|
72
|
+
return;
|
|
73
|
+
const id = input?.[target.idFrom];
|
|
74
|
+
if (typeof id !== 'string')
|
|
75
|
+
return;
|
|
76
|
+
const row = readStateRow(deps.sql, plan, id);
|
|
77
|
+
if (purge) {
|
|
78
|
+
// The sweep's gate again, inside this purge's own transaction: a switch pulled, a lifecycle
|
|
79
|
+
// hold or a copy classification that landed after the sweep began stops this purge here.
|
|
80
|
+
const held = purgeHeldBy(purge.gate);
|
|
81
|
+
if (held !== null)
|
|
82
|
+
throw substratError('conflict', `${operation}: ${held}`, { reason: 'purge_held' });
|
|
83
|
+
if (plan.purgeAfterDays === undefined) {
|
|
84
|
+
throw substratError('internal', `${operation}: '${target.entity}' declares no purge horizon`);
|
|
85
|
+
}
|
|
86
|
+
const purgeCutoff = purgeCutoffOf(purge.now, plan.purgeAfterDays);
|
|
87
|
+
if (!row)
|
|
88
|
+
throw substratError('not_found', `${target.entity} not found: ${id}`);
|
|
89
|
+
if (row.trashed_at === null || row.trashed_at > purgeCutoff) {
|
|
90
|
+
throw substratError('conflict', `${operation}: ${target.entity}:${id} is no longer due for purge`, {
|
|
91
|
+
reason: 'purge_not_due',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!row || row.trashed_at === null)
|
|
97
|
+
return;
|
|
98
|
+
assertAllowed(await deps.check(target.key, { entityType: target.entity, entityId: id }));
|
|
99
|
+
throw substratError('not_found', `${target.entity} not found: ${id}`);
|
|
100
|
+
}
|
|
101
|
+
/** The parsed input of a `trashed: 'purges'` operation: a plain object holding exactly its id. */
|
|
102
|
+
function assertPurgeInput(operation, target, input) {
|
|
103
|
+
const keys = typeof input === 'object' && input !== null && !Array.isArray(input) ? Object.keys(input) : undefined;
|
|
104
|
+
if (keys?.length === 1 && keys[0] === target.idFrom)
|
|
105
|
+
return;
|
|
106
|
+
throw substratError('validation_failed', `${operation} purges one ${target.entity}: its input is '${target.idFrom}' and nothing else`, {
|
|
107
|
+
errors: [{ path: '', message: `expected exactly { ${target.idFrom} }` }],
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The keys a system principal's checks refuse on this call (#119): its module's purge-only keys,
|
|
112
|
+
* unless the call is the host's own purge sweep invoking the purge operation. `undefined` —
|
|
113
|
+
* nothing withheld — for any other subject, and for a module with no purge-only key.
|
|
114
|
+
*/
|
|
115
|
+
export function withheldKeysFor(purgeOnlyKeys, target, purging) {
|
|
116
|
+
return purgeOnlyKeys && !(purging && target?.trashed === 'purges') ? purgeOnlyKeys : undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Refuse an invoke that tries to supply a purge from outside (#119). Purge authority comes only from
|
|
120
|
+
* the host's own sweep, through a path no caller can construct; an options object carrying the
|
|
121
|
+
* field is somebody trying, and is told so rather than silently ignored.
|
|
122
|
+
*/
|
|
123
|
+
export function assertNoCallerPurge(options) {
|
|
124
|
+
if (options && 'purgeCutoff' in options) {
|
|
125
|
+
throw substratError('validation_failed', 'purgeCutoff is not an invoke option — only the platform\'s purge sweep purges', {
|
|
126
|
+
errors: [{ path: 'purgeCutoff', message: 'not an invoke option' }],
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** The latest trash instant still due for purge at `now` — `now` minus the horizon. */
|
|
131
|
+
export function purgeCutoffOf(now, days) {
|
|
132
|
+
return new Date(Date.parse(now) - days * 86_400_000).toISOString();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The ids due for purge: trashed at or before `cutoff`, oldest trash first, at most `limit` —
|
|
136
|
+
* and, given `after`, only those strictly after it in that order (#2096). The predicate leads with
|
|
137
|
+
* the purge index's own `WHERE`, so the walk reads only the bin; `after` is compared as a VALUE,
|
|
138
|
+
* so a key whose entity has since been purged or restored still places the walk correctly.
|
|
139
|
+
*/
|
|
140
|
+
export function purgeCandidates(sql, plan, cutoff, limit = PURGE_BATCH, after = null) {
|
|
141
|
+
return sql
|
|
142
|
+
.query(`SELECT ${plan.idColumn} AS id, ${TRASHED_AT_COLUMN} AS at FROM ${plan.table} ` +
|
|
143
|
+
`WHERE ${TRASHED_AT_COLUMN} IS NOT NULL AND ${TRASHED_AT_COLUMN} <= ? ` +
|
|
144
|
+
(after ? `AND (${TRASHED_AT_COLUMN}, ${plan.idColumn}) > (?, ?) ` : '') +
|
|
145
|
+
`ORDER BY ${TRASHED_AT_COLUMN}, ${plan.idColumn} LIMIT ?`, after ? [cutoff, after.at, after.id, limit] : [cutoff, limit])
|
|
146
|
+
.map((r) => ({ id: String(r.id), at: String(r.at) }));
|
|
147
|
+
}
|
|
148
|
+
const LAP_START = { after: null, failed: 0 };
|
|
149
|
+
/**
|
|
150
|
+
* The lap a stored `purge_cursor` holds. NULL — no lap in progress, a row written before the
|
|
151
|
+
* column, or no row at all — is the start of a lap, and so is a value this code cannot read: the
|
|
152
|
+
* worst a lost cursor costs is walking from the oldest again.
|
|
153
|
+
*/
|
|
154
|
+
export function purgeLapOf(stored) {
|
|
155
|
+
if (stored === null || stored === undefined)
|
|
156
|
+
return LAP_START;
|
|
157
|
+
try {
|
|
158
|
+
const v = JSON.parse(stored);
|
|
159
|
+
if (typeof v.at !== 'string' || typeof v.id !== 'string')
|
|
160
|
+
return LAP_START;
|
|
161
|
+
const failed = typeof v.failed === 'number' && Number.isSafeInteger(v.failed) && v.failed > 0 ? v.failed : 0;
|
|
162
|
+
return { after: { at: v.at, id: v.id }, failed };
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return LAP_START;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Read the purge schedule's lap from its cadence row (`kind = 'schedule'`, keyed by the operation). */
|
|
169
|
+
export function readPurgeLap(sql, operation) {
|
|
170
|
+
const [row] = sql.query(`SELECT purge_cursor FROM _substrat_schedule_state WHERE kind = 'schedule' AND schedule_op = ?`, [operation]);
|
|
171
|
+
return purgeLapOf(row?.purge_cursor);
|
|
172
|
+
}
|
|
173
|
+
/** Store a lap's next start (NULL when none), touching `purge_cursor` alone — see `runPurgePass`. */
|
|
174
|
+
function writePurgeLap(sql, operation, lap) {
|
|
175
|
+
sql.exec(`INSERT INTO _substrat_schedule_state (kind, schedule_op, purge_cursor) VALUES ('schedule', ?, ?)
|
|
176
|
+
ON CONFLICT(kind, schedule_op) DO UPDATE SET purge_cursor = excluded.purge_cursor`, [operation, lap?.after ? JSON.stringify({ at: lap.after.at, id: lap.after.id, failed: lap.failed }) : null]);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* One purge horizon's due work on a scope (#119, #2096): the cutoff, the input field carrying the
|
|
180
|
+
* id, and the next `limit` due ids of the lap the cadence row records — read one past the batch, so
|
|
181
|
+
* `more` is known rather than guessed from a full batch. Registration has tied the schedule to a
|
|
182
|
+
* declared horizon and the entity's purge operation, so a miss here is a wiring fault.
|
|
183
|
+
*/
|
|
184
|
+
export function purgeDueOf(sql, plans, targets, operation, entityType, now, limit = PURGE_BATCH) {
|
|
185
|
+
const plan = plans.get(entityType);
|
|
186
|
+
const target = targets.get(operation);
|
|
187
|
+
if (plan?.purgeAfterDays === undefined || !target) {
|
|
188
|
+
throw new Error(`purge: '${operation}' is not the purge operation of a horizon on '${entityType}'`);
|
|
189
|
+
}
|
|
190
|
+
const cutoff = purgeCutoffOf(now, plan.purgeAfterDays);
|
|
191
|
+
const lap = readPurgeLap(sql, operation);
|
|
192
|
+
const keys = purgeCandidates(sql, plan, cutoff, limit + 1, lap.after);
|
|
193
|
+
const batch = keys.slice(0, limit);
|
|
194
|
+
return {
|
|
195
|
+
cutoff,
|
|
196
|
+
idFrom: target.idFrom,
|
|
197
|
+
ids: batch.map((k) => k.id),
|
|
198
|
+
lap,
|
|
199
|
+
next: keys.length > limit ? batch.at(-1) : null,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* THE purge sweep's gate (#119): why this scope may purge nothing now, or null when it may. The
|
|
204
|
+
* same holds the coordinator applies before any schedule fires, applied by the scope itself from
|
|
205
|
+
* whatever of them it records (below: on a directory-backed host, the kill switch). A tenant foreign to the scope is a REFUSAL (`not_found`,
|
|
206
|
+
* the doors' answer for a pair that does not hold), not a hold: nothing about it is "not yet".
|
|
207
|
+
*
|
|
208
|
+
* On a directory-backed host the scope DO enforces the kill switch; tenant, lifecycle, copy and
|
|
209
|
+
* rewind holds are enforced by the coordinator. The scope holds no tenant receipt, lifecycle or copy
|
|
210
|
+
* classification of its own there (the directory is their authority), so those facts read clear —
|
|
211
|
+
* an unrecorded tenant is `unknown`, which passes as at every door.
|
|
212
|
+
*/
|
|
213
|
+
export function purgeHeldBy(facts) {
|
|
214
|
+
if (facts.foreignTenant)
|
|
215
|
+
throw substratError('not_found', 'purge refused: the tenant named is not this scope\'s');
|
|
216
|
+
if (facts.switched !== 'on')
|
|
217
|
+
return `the module's schedules are ${facts.switched === 'off' ? 'switched off' : 'not granted'} on this scope`;
|
|
218
|
+
if (facts.lifecycle !== null)
|
|
219
|
+
return facts.lifecycle;
|
|
220
|
+
if (facts.copy)
|
|
221
|
+
return 'this scope is a copy, and a copy never purges';
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
/** A pass the scope's gate held: nothing selected, nothing tried, the lap where it was. */
|
|
225
|
+
export function heldPurgePass(held) {
|
|
226
|
+
return { purged: 0, skipped: 0, errors: [], more: false, lapFailed: 0, held };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Run one pass of a purge schedule over the ids the adapter selected (`purgeDueOf`), then record
|
|
230
|
+
* what it did to its lap (#2096). One invoke per entity, each its own transaction, so a crash or a
|
|
231
|
+
* failure loses nothing already committed and the next pass simply selects what is left. An entity
|
|
232
|
+
* that was restored (`purge_not_due`) or is already gone (`not_found`) is skipped, never a failure —
|
|
233
|
+
* the purge is idempotent by state.
|
|
234
|
+
*
|
|
235
|
+
* Then the lap moves on WHATEVER its purges did — that is the whole of #2096: resume after this
|
|
236
|
+
* batch with its failures added while more is due, or close the lap. After the pass, so a pass cut
|
|
237
|
+
* short repeats its batch rather than skipping it. `inScope` runs the write against the scope's
|
|
238
|
+
* spine, in the turn the adapter's storage needs.
|
|
239
|
+
*/
|
|
240
|
+
export async function runPurgePass(operation, due, purgeOne, inScope) {
|
|
241
|
+
const more = due.next !== null;
|
|
242
|
+
const pass = { purged: 0, skipped: 0, errors: [], more, lapFailed: more ? 0 : due.lap.failed };
|
|
243
|
+
for (const entityId of due.ids) {
|
|
244
|
+
try {
|
|
245
|
+
await purgeOne(entityId);
|
|
246
|
+
pass.purged += 1;
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
const code = errorCodeOf(err);
|
|
250
|
+
const reason = err.extensions?.reason;
|
|
251
|
+
if (code === 'not_found' || (code === 'conflict' && (reason === 'purge_not_due' || reason === 'purge_held'))) {
|
|
252
|
+
pass.skipped += 1;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
pass.errors.push({ entityId, error: err instanceof Error ? err.message : String(err) });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// A lap that began at the oldest and closes in this pass leaves the cursor NULL, as it found it.
|
|
259
|
+
if (more || due.lap.after !== null) {
|
|
260
|
+
const next = due.next ? { after: due.next, failed: due.lap.failed + pass.errors.length } : null;
|
|
261
|
+
await inScope((sql) => writePurgeLap(sql, operation, next));
|
|
262
|
+
}
|
|
263
|
+
return pass;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Whether a purge pass leaves its schedule due, so the next sweep pass continues its lap rather
|
|
267
|
+
* than waiting the cadence: more is due after its batch (#2096). It cannot spin (#2087): every pass
|
|
268
|
+
* moves the lap strictly forward, whatever its purges did, and the pass that runs out of due
|
|
269
|
+
* entities closes the lap and records the run — so a cadence window holds one lap, and an entity
|
|
270
|
+
* that keeps failing is tried once per window, not once per sweep tick. A scope running code older
|
|
271
|
+
* than the lap answers no `more`, and its pass waits its cadence.
|
|
272
|
+
*/
|
|
273
|
+
export function purgeStillDue(pass) {
|
|
274
|
+
return pass.more === true;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* The keys a module's system principal holds ONLY for its purge schedules (#119): declared by a
|
|
278
|
+
* purge schedule and by no other. Withheld from every system-principal check except the purge
|
|
279
|
+
* sweep's own invoke of the purge operation, so the scope-wide grant seated for a purge cannot
|
|
280
|
+
* run anything else — another operation checking the same key, a job step, a host-level call.
|
|
281
|
+
*/
|
|
282
|
+
export function purgeOnlyKeysOf(schedules) {
|
|
283
|
+
const purge = new Set(schedules.filter((s) => s.purge).flatMap((s) => s.permissions));
|
|
284
|
+
for (const s of schedules)
|
|
285
|
+
if (!s.purge)
|
|
286
|
+
for (const p of s.permissions)
|
|
287
|
+
purge.delete(p);
|
|
288
|
+
return purge.size > 0 ? purge : undefined;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* A purge pass, as the schedule run reports it: one error row per entity that failed (labelled
|
|
292
|
+
* with its id), and the error that marks the schedule's run `failed` when any did — in this pass,
|
|
293
|
+
* or in an earlier pass of the lap this one closed (#2096), so the cadence row keeps the failure.
|
|
294
|
+
*/
|
|
295
|
+
export function purgeReportOf(operation, entityType, pass) {
|
|
296
|
+
const errors = pass.errors.map((e) => ({ operation: `${operation} (${entityType}:${e.entityId})`, error: e.error }));
|
|
297
|
+
const earlier = pass.lapFailed ?? 0;
|
|
298
|
+
if (errors.length === 0 && earlier === 0)
|
|
299
|
+
return { errors };
|
|
300
|
+
const counts = [errors.length > 0 && `${errors.length} purge(s) of ${entityType} failed`, earlier > 0 && `${earlier} failed earlier in this lap`];
|
|
301
|
+
return { errors, failure: new Error(`${counts.filter(Boolean).join('; ')}; they stay in the bin and are retried`) };
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Hold one module's registration to the trash rules (#119), and answer the targets the host
|
|
305
|
+
* keeps. Refuses, never skips — each of these is a binned entity reachable while the module
|
|
306
|
+
* believes it is not:
|
|
307
|
+
*
|
|
308
|
+
* The targets are DERIVED, never handed over: `operationInputsOf(ops)` records the declared
|
|
309
|
+
* surface of the frozen map it returns (`declaredSurfaceOf`, which nothing else can write), and the
|
|
310
|
+
* host reads each operation's target off the same declaration it parses with. A module with a trashable entity must therefore
|
|
311
|
+
* - pass `operationInputs` built by `operationInputsOf`, so there is a declared surface to read;
|
|
312
|
+
* - declare every operation it binds there — an undeclared one could address a binned entity and
|
|
313
|
+
* the host could not see which;
|
|
314
|
+
* - declare a `trashed: 'purges'` operation whose input is a strict object holding the id and nothing else.
|
|
315
|
+
*
|
|
316
|
+
* Also refused: an opt-in on an entity that declares no trash here, a purge horizon without
|
|
317
|
+
* exactly the schedule `purgeSchedulesOf` derives for it, and a purge schedule that does not run
|
|
318
|
+
* the entity's `trashed: 'purges'` operation.
|
|
319
|
+
*/
|
|
320
|
+
export function registerTrashTargets(moduleId, ownOps, operationInputs, entityStates, schedules) {
|
|
321
|
+
const trashable = new Map((entityStates ?? []).filter((d) => d.trashPermission).map((d) => [d.entityType, d]));
|
|
322
|
+
const surface = declaredSurfaceOf(operationInputs);
|
|
323
|
+
if (trashable.size > 0) {
|
|
324
|
+
const where = `${moduleId} declares trashable entities (${[...trashable.keys()].sort().join(', ')})`;
|
|
325
|
+
if (!surface) {
|
|
326
|
+
throw new Error(`${where} but its \`operationInputs\` is not a map \`operationInputsOf\` returned — the host could not see which ` +
|
|
327
|
+
'operations address a binned one. A copy, a spread or an edited map is not one, and nor is a map built by a ' +
|
|
328
|
+
'second copy of @substrat-run/contracts.\n Remedy: `operationInputs: operationInputsOf(ops)`, as returned.');
|
|
329
|
+
}
|
|
330
|
+
const undeclared = [...ownOps].filter((name) => !surface.operations.includes(name)).sort();
|
|
331
|
+
if (undeclared.length > 0) {
|
|
332
|
+
throw new Error(`${where} and binds operation(s) its declarations do not name: ${undeclared.join(', ')} — the host could not ` +
|
|
333
|
+
'tell which entity they reach, so it could not refuse them on a binned one. Declare them.');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const out = new Map();
|
|
337
|
+
for (const [name, target] of Object.entries(surface?.targets ?? {})) {
|
|
338
|
+
if (!ownOps.has(name))
|
|
339
|
+
continue; // declared and not bound here: nothing to refuse
|
|
340
|
+
if (target.trashed === 'purges') {
|
|
341
|
+
const schema = operationInputs?.[name];
|
|
342
|
+
const fields = Object.keys(schema?.shape ?? {});
|
|
343
|
+
if (fields.length !== 1 || fields[0] !== target.idFrom) {
|
|
344
|
+
throw new Error(`${moduleId}: '${name}' purges '${target.entity}', and its parsed input takes ${fields.map((f) => `'${f}'`).join(', ') || 'nothing'} — ` +
|
|
345
|
+
`a purge's input is '${target.idFrom}' and nothing else, so it reaches only the entity it is run for`);
|
|
346
|
+
}
|
|
347
|
+
// The shape alone is not the parsed result: a passthrough object keeps whatever else the
|
|
348
|
+
// call carried. Strict, so an extra field is refused rather than kept or dropped.
|
|
349
|
+
if (!isStrictObjectSchema(schema)) {
|
|
350
|
+
throw new Error(`${moduleId}: '${name}' purges '${target.entity}', and its input is not a strict object — a passthrough one ` +
|
|
351
|
+
`keeps fields beside '${target.idFrom}'.\n Remedy: \`input: z.strictObject({ ${target.idFrom}: … })\`.`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (target.trashed && !trashable.has(target.entity)) {
|
|
355
|
+
throw new Error(`${moduleId}: '${name}' declares \`trashed: '${target.trashed}'\` over '${target.entity}', which declares no trash here`);
|
|
356
|
+
}
|
|
357
|
+
out.set(name, target);
|
|
358
|
+
}
|
|
359
|
+
for (const schedule of schedules ?? []) {
|
|
360
|
+
if (!schedule.purge)
|
|
361
|
+
continue;
|
|
362
|
+
const decl = trashable.get(schedule.purge.entityType);
|
|
363
|
+
const target = out.get(schedule.operation);
|
|
364
|
+
if (decl?.purgeAfterDays === undefined || target?.trashed !== 'purges' || target.entity !== schedule.purge.entityType) {
|
|
365
|
+
throw new Error(`${moduleId}: the purge schedule for '${schedule.purge.entityType}' runs '${schedule.operation}', which is not ` +
|
|
366
|
+
"that entity's `trashed: 'purges'` operation over a declared horizon — derive it with `purgeSchedulesOf`");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
for (const [entityType, decl] of trashable) {
|
|
370
|
+
if (decl.purgeAfterDays === undefined)
|
|
371
|
+
continue;
|
|
372
|
+
const count = (schedules ?? []).filter((s) => s.purge?.entityType === entityType).length;
|
|
373
|
+
if (count !== 1) {
|
|
374
|
+
throw new Error(`${moduleId}: '${entityType}' declares a purge horizon and ${count} purge schedules — ` +
|
|
375
|
+
'spread `purgeSchedulesOf(ops, entities)` into the manifest\'s `schedules` once');
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=entity-trash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-trash.js","sourceRoot":"","sources":["../src/entity-trash.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { EntityRef, PermissionKey } from '@substrat-run/contracts';
|
|
2
|
+
/** Added to both scope schemas and their on-wake upgrade path. */
|
|
3
|
+
export declare const GRANT_CHILDREN_INDEX_DDL = "CREATE INDEX IF NOT EXISTS _substrat_tuples_object_relation_subject ON _substrat_tuples (object, relation, subject)";
|
|
4
|
+
/** Ids can repeat across pages on multi-parent paths: only their union is complete. */
|
|
5
|
+
export type GrantedEntityIdsMayRepeatPage = {
|
|
6
|
+
kind: 'ids';
|
|
7
|
+
ids: string[];
|
|
8
|
+
nextCursor: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type GrantedEntitiesPage = {
|
|
11
|
+
kind: 'all';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'incomplete';
|
|
14
|
+
reason: 'capability' | 'checker';
|
|
15
|
+
} | GrantedEntityIdsMayRepeatPage;
|
|
16
|
+
export interface GrantWalkRow {
|
|
17
|
+
subject: string;
|
|
18
|
+
object: string;
|
|
19
|
+
expires_at: string | null;
|
|
20
|
+
revoked_at: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface GrantWalkStore {
|
|
23
|
+
/** The next grant for one subject, from the tuple primary key. */
|
|
24
|
+
nextGrant(subject: string, relation: string, after: string): GrantWalkRow | undefined;
|
|
25
|
+
/** The next reverse parent edge, from GRANT_CHILDREN_INDEX_DDL. */
|
|
26
|
+
nextChild(parent: string, after: string): GrantWalkRow | undefined;
|
|
27
|
+
}
|
|
28
|
+
export declare const GRANT_READ_MAX_LIMIT = 100;
|
|
29
|
+
export declare const GRANT_READ_WORK_BUDGET = 2000;
|
|
30
|
+
/**
|
|
31
|
+
* Depth-first over grant roots and reverse parent edges. A cursor stores only the current
|
|
32
|
+
* path (at most five refs), so neither graph width nor total result count grows it. A
|
|
33
|
+
* multi-parent graph can produce the same id on different pages; consumers combine pages
|
|
34
|
+
* as a set. Every id is rechecked by the caller before it leaves the kernel.
|
|
35
|
+
*/
|
|
36
|
+
export declare function walkGrantedEntities(store: GrantWalkStore, subjects: readonly string[], permission: PermissionKey, entityType: string, now: string, check: (entity: EntityRef) => Promise<boolean>, options?: {
|
|
37
|
+
limit?: number;
|
|
38
|
+
cursor?: string;
|
|
39
|
+
workBudget?: number;
|
|
40
|
+
}): Promise<GrantedEntityIdsMayRepeatPage>;
|
|
41
|
+
//# sourceMappingURL=grant-scoped-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant-scoped-read.d.ts","sourceRoot":"","sources":["../src/grant-scoped-read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAMxE,kEAAkE;AAClE,eAAO,MAAM,wBAAwB,wHACkF,CAAC;AAExH,uFAAuF;AACvF,MAAM,MAAM,6BAA6B,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEtG,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,GACxD,6BAA6B,CAAC;AAElC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IACtF,mEAAmE;IACnE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;CACpE;AAMD,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AA2B5C;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,UAAU,EAAE,aAAa,EACzB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,EAC9C,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,OAAO,CAAC,6BAA6B,CAAC,CA+DxC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { fromBase64url, toBase64url } from './base64url.js';
|
|
2
|
+
/** Added to both scope schemas and their on-wake upgrade path. */
|
|
3
|
+
export const GRANT_CHILDREN_INDEX_DDL = 'CREATE INDEX IF NOT EXISTS _substrat_tuples_object_relation_subject ON _substrat_tuples (object, relation, subject)';
|
|
4
|
+
const WALK_DEPTH = 4;
|
|
5
|
+
export const GRANT_READ_MAX_LIMIT = 100;
|
|
6
|
+
export const GRANT_READ_WORK_BUDGET = 2_000;
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
9
|
+
const live = (row, now) => row.revoked_at === null && (row.expires_at === null || row.expires_at > now);
|
|
10
|
+
function decode(cursor, permission, entityType) {
|
|
11
|
+
try {
|
|
12
|
+
const bytes = fromBase64url(cursor);
|
|
13
|
+
if (!bytes || bytes.length > 16_384)
|
|
14
|
+
throw new Error();
|
|
15
|
+
const value = JSON.parse(decoder.decode(bytes));
|
|
16
|
+
if (value.v !== 1 || value.permission !== permission || value.entityType !== entityType ||
|
|
17
|
+
!Number.isSafeInteger(value.subject) || value.subject < 0 ||
|
|
18
|
+
typeof value.rootAfter !== 'string' || !Array.isArray(value.stack) ||
|
|
19
|
+
value.stack.length > WALK_DEPTH + 1 ||
|
|
20
|
+
value.stack.some((f) => typeof f.ref !== 'string' || typeof f.after !== 'string' || typeof f.entered !== 'boolean'))
|
|
21
|
+
throw new Error();
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error('invalid grantedEntities cursor');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const encode = (position) => toBase64url(encoder.encode(JSON.stringify(position)));
|
|
29
|
+
/**
|
|
30
|
+
* Depth-first over grant roots and reverse parent edges. A cursor stores only the current
|
|
31
|
+
* path (at most five refs), so neither graph width nor total result count grows it. A
|
|
32
|
+
* multi-parent graph can produce the same id on different pages; consumers combine pages
|
|
33
|
+
* as a set. Every id is rechecked by the caller before it leaves the kernel.
|
|
34
|
+
*/
|
|
35
|
+
export async function walkGrantedEntities(store, subjects, permission, entityType, now, check, options = {}) {
|
|
36
|
+
const limit = options.limit ?? 50;
|
|
37
|
+
const workBudget = options.workBudget ?? GRANT_READ_WORK_BUDGET;
|
|
38
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > GRANT_READ_MAX_LIMIT) {
|
|
39
|
+
throw new Error(`grantedEntities limit must be 1..${GRANT_READ_MAX_LIMIT}`);
|
|
40
|
+
}
|
|
41
|
+
if (!Number.isSafeInteger(workBudget) || workBudget < 1 || workBudget > GRANT_READ_WORK_BUDGET) {
|
|
42
|
+
throw new Error(`grantedEntities work budget must be 1..${GRANT_READ_WORK_BUDGET}`);
|
|
43
|
+
}
|
|
44
|
+
const p = options.cursor
|
|
45
|
+
? decode(options.cursor, permission, entityType)
|
|
46
|
+
: { v: 1, permission, entityType, subject: 0, rootAfter: '', stack: [] };
|
|
47
|
+
const ids = [];
|
|
48
|
+
const seenOnPage = new Set();
|
|
49
|
+
let work = 0;
|
|
50
|
+
const relation = `granted:${permission}`;
|
|
51
|
+
while (p.subject < subjects.length) {
|
|
52
|
+
if (work >= workBudget || ids.length >= limit) {
|
|
53
|
+
return { kind: 'ids', ids, nextCursor: encode(p) };
|
|
54
|
+
}
|
|
55
|
+
if (p.stack.length === 0) {
|
|
56
|
+
const grant = store.nextGrant(subjects[p.subject], relation, p.rootAfter);
|
|
57
|
+
work++;
|
|
58
|
+
if (!grant) {
|
|
59
|
+
p.subject++;
|
|
60
|
+
p.rootAfter = '';
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (!live(grant, now)) {
|
|
64
|
+
p.rootAfter = grant.object;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
p.stack.push({ ref: grant.object, after: '', entered: false });
|
|
68
|
+
}
|
|
69
|
+
const frame = p.stack[p.stack.length - 1];
|
|
70
|
+
if (!frame.entered) {
|
|
71
|
+
frame.entered = true;
|
|
72
|
+
work++;
|
|
73
|
+
const colon = frame.ref.indexOf(':');
|
|
74
|
+
if (colon >= 0 && frame.ref.slice(0, colon) === entityType) {
|
|
75
|
+
const id = frame.ref.slice(colon + 1);
|
|
76
|
+
if (!seenOnPage.has(id) && await check({ entityType, entityId: id })) {
|
|
77
|
+
seenOnPage.add(id);
|
|
78
|
+
ids.push(id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (p.stack.length <= WALK_DEPTH) {
|
|
84
|
+
const child = store.nextChild(frame.ref, frame.after);
|
|
85
|
+
work++;
|
|
86
|
+
if (child) {
|
|
87
|
+
frame.after = child.subject;
|
|
88
|
+
if (live(child, now) && !p.stack.some((f) => f.ref === child.subject)) {
|
|
89
|
+
p.stack.push({ ref: child.subject, after: '', entered: false });
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
p.stack.pop();
|
|
95
|
+
if (p.stack.length === 0)
|
|
96
|
+
p.rootAfter = frame.ref;
|
|
97
|
+
}
|
|
98
|
+
return { kind: 'ids', ids, nextCursor: null };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=grant-scoped-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant-scoped-read.js","sourceRoot":"","sources":["../src/grant-scoped-read.ts"],"names":[],"mappings":"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"}
|