@substrat-run/kernel 0.142.0 → 0.144.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/attachment-extractor.d.ts +0 -9
- package/dist/attachment-extractor.d.ts.map +1 -1
- package/dist/attachment-extractor.js +2 -2
- package/dist/attachment-extractor.js.map +1 -1
- package/dist/attachment-text.d.ts +0 -5
- package/dist/attachment-text.d.ts.map +1 -1
- package/dist/attachment-text.js +0 -4
- package/dist/attachment-text.js.map +1 -1
- package/dist/attribution.d.ts +0 -7
- package/dist/attribution.d.ts.map +1 -1
- package/dist/attribution.js +0 -7
- package/dist/attribution.js.map +1 -1
- package/dist/audit-outcome.d.ts +60 -2
- package/dist/audit-outcome.d.ts.map +1 -1
- package/dist/audit-outcome.js +63 -3
- package/dist/audit-outcome.js.map +1 -1
- package/dist/capability.d.ts +20 -13
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +76 -16
- package/dist/capability.js.map +1 -1
- package/dist/connector-calls.d.ts +0 -5
- package/dist/connector-calls.d.ts.map +1 -1
- package/dist/connector-calls.js +1 -1
- package/dist/connector-calls.js.map +1 -1
- package/dist/denial-query.d.ts +0 -14
- package/dist/denial-query.d.ts.map +1 -1
- package/dist/denial-query.js +1 -1
- package/dist/denial-query.js.map +1 -1
- package/dist/derived-handlers.d.ts +17 -0
- package/dist/derived-handlers.d.ts.map +1 -0
- package/dist/derived-handlers.js +198 -0
- package/dist/derived-handlers.js.map +1 -0
- package/dist/entity-binding.d.ts +121 -0
- package/dist/entity-binding.d.ts.map +1 -0
- package/dist/entity-binding.js +235 -0
- package/dist/entity-binding.js.map +1 -0
- package/dist/entity-grant-shape.d.ts +127 -12
- package/dist/entity-grant-shape.d.ts.map +1 -1
- package/dist/entity-grant-shape.js +399 -137
- package/dist/entity-grant-shape.js.map +1 -1
- package/dist/entity-grant.d.ts +5 -7
- package/dist/entity-grant.d.ts.map +1 -1
- package/dist/entity-grant.js +6 -7
- package/dist/entity-grant.js.map +1 -1
- package/dist/entity-state.d.ts +0 -17
- package/dist/entity-state.d.ts.map +1 -1
- package/dist/entity-state.js +2 -2
- package/dist/entity-state.js.map +1 -1
- package/dist/entity-trash.d.ts +2 -2
- package/dist/entity-trash.d.ts.map +1 -1
- package/dist/entity-trash.js +6 -6
- package/dist/entity-trash.js.map +1 -1
- package/dist/grant-scoped-read.d.ts +0 -2
- package/dist/grant-scoped-read.d.ts.map +1 -1
- package/dist/grant-scoped-read.js +2 -2
- package/dist/grant-scoped-read.js.map +1 -1
- package/dist/index.d.ts +53 -55
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -53
- package/dist/index.js.map +1 -1
- package/dist/job-run.d.ts +0 -39
- package/dist/job-run.d.ts.map +1 -1
- package/dist/job-run.js +6 -6
- package/dist/job-run.js.map +1 -1
- package/dist/list-index.d.ts +0 -45
- package/dist/list-index.d.ts.map +1 -1
- package/dist/list-index.js +2 -39
- package/dist/list-index.js.map +1 -1
- package/dist/membership-executor.d.ts +2 -1
- package/dist/membership-executor.d.ts.map +1 -1
- package/dist/membership-executor.js +1 -1
- package/dist/membership-executor.js.map +1 -1
- package/dist/migration-digest.d.ts +0 -12
- package/dist/migration-digest.d.ts.map +1 -1
- package/dist/migration-digest.js +1 -1
- package/dist/migration-digest.js.map +1 -1
- package/dist/module-erasure.d.ts +2 -1
- package/dist/module-erasure.d.ts.map +1 -1
- package/dist/module-erasure.js +12 -1
- package/dist/module-erasure.js.map +1 -1
- package/dist/module-operations.d.ts +146 -0
- package/dist/module-operations.d.ts.map +1 -0
- package/dist/module-operations.js +174 -0
- package/dist/module-operations.js.map +1 -0
- package/dist/peer.d.ts +0 -2
- package/dist/peer.d.ts.map +1 -1
- package/dist/peer.js +0 -4
- package/dist/peer.js.map +1 -1
- package/dist/platform-request-query.d.ts +6 -6
- package/dist/platform-request-query.d.ts.map +1 -1
- package/dist/platform-request-query.js +23 -7
- package/dist/platform-request-query.js.map +1 -1
- package/dist/platform-sweep.d.ts +26 -2
- package/dist/platform-sweep.d.ts.map +1 -1
- package/dist/platform-sweep.js +6 -2
- package/dist/platform-sweep.js.map +1 -1
- package/dist/refusal-query.d.ts +0 -11
- package/dist/refusal-query.d.ts.map +1 -1
- package/dist/refusal-query.js +1 -1
- package/dist/refusal-query.js.map +1 -1
- package/dist/refusals.d.ts +1 -3
- package/dist/refusals.d.ts.map +1 -1
- package/dist/refusals.js +0 -2
- package/dist/refusals.js.map +1 -1
- package/dist/row-decode.d.ts +16 -1
- package/dist/row-decode.d.ts.map +1 -1
- package/dist/row-decode.js +18 -6
- package/dist/row-decode.js.map +1 -1
- package/dist/scope-copy-ledger.d.ts +0 -2
- package/dist/scope-copy-ledger.d.ts.map +1 -1
- package/dist/scope-copy-ledger.js +1 -1
- package/dist/scope-copy-ledger.js.map +1 -1
- package/dist/scope-host.d.ts +47 -101
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +0 -17
- package/dist/scope-host.js.map +1 -1
- package/dist/scope-lifecycle.d.ts +0 -21
- package/dist/scope-lifecycle.d.ts.map +1 -1
- package/dist/scope-lifecycle.js +1 -1
- package/dist/scope-lifecycle.js.map +1 -1
- package/dist/scope-role-admin.d.ts +0 -6
- package/dist/scope-role-admin.d.ts.map +1 -1
- package/dist/scope-role-admin.js +1 -1
- package/dist/scope-role-admin.js.map +1 -1
- package/dist/search-index.d.ts +0 -23
- package/dist/search-index.d.ts.map +1 -1
- package/dist/search-index.js +2 -24
- package/dist/search-index.js.map +1 -1
- package/dist/spine-restore.d.ts +0 -22
- package/dist/spine-restore.d.ts.map +1 -1
- package/dist/spine-restore.js +2 -2
- package/dist/spine-restore.js.map +1 -1
- package/dist/sql-identifier.d.ts +3 -0
- package/dist/sql-identifier.d.ts.map +1 -1
- package/dist/sql-identifier.js +2 -0
- package/dist/sql-identifier.js.map +1 -1
- package/dist/sql-limits.d.ts +4 -0
- package/dist/sql-limits.d.ts.map +1 -1
- package/dist/sql-limits.js +4 -0
- package/dist/sql-limits.js.map +1 -1
- package/dist/subject-redaction.d.ts +0 -38
- package/dist/subject-redaction.d.ts.map +1 -1
- package/dist/subject-redaction.js +4 -4
- package/dist/subject-redaction.js.map +1 -1
- package/dist/system-switch-record.d.ts +0 -4
- package/dist/system-switch-record.d.ts.map +1 -1
- package/dist/system-switch-record.js +2 -2
- package/dist/system-switch-record.js.map +1 -1
- package/dist/system-switch.d.ts +5 -23
- package/dist/system-switch.d.ts.map +1 -1
- package/dist/system-switch.js +8 -1
- package/dist/system-switch.js.map +1 -1
- package/dist/table-ownership.d.ts +0 -32
- package/dist/table-ownership.d.ts.map +1 -1
- package/dist/table-ownership.js +5 -5
- package/dist/table-ownership.js.map +1 -1
- package/dist/testing.d.ts +17 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +15 -0
- package/dist/testing.js.map +1 -0
- package/dist/vertical-events.d.ts +1 -7
- package/dist/vertical-events.d.ts.map +1 -1
- package/dist/vertical-events.js +2 -10
- package/dist/vertical-events.js.map +1 -1
- package/package.json +6 -2
- package/dist/connect-state.d.ts +0 -103
- package/dist/connect-state.d.ts.map +0 -1
- package/dist/connect-state.js +0 -128
- package/dist/connect-state.js.map +0 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The handlers the platform writes from a declaration (#1773).
|
|
3
|
+
*
|
|
4
|
+
* `defineOperations` decides WHAT is derivable and records a plan for each `derive` declaration —
|
|
5
|
+
* the table, its columns, the permission, the event (`DerivationPlan`, contracts). This turns a
|
|
6
|
+
* plan into the handler. It takes the same steps a hand-written one does, through the same `ctx`
|
|
7
|
+
* a module has: the declared check first, then `ctx.sql` over the entity's declared columns
|
|
8
|
+
* (named, never `*`), `not_found` for a missing row, and one fat event per write.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here decides a shape. A handler written from a plan cannot read a column, check a key or
|
|
11
|
+
* emit an event the declaration did not name, because the plan holds nothing else.
|
|
12
|
+
*/
|
|
13
|
+
import { permissionKey, substratError, } from '@substrat-run/contracts';
|
|
14
|
+
import { assertAllowed } from './permission-checker.js';
|
|
15
|
+
import { ulid } from './ulid.js';
|
|
16
|
+
/** `"col"` — a declared column is an identifier, quoted so a keyword-named one still reads as a column. */
|
|
17
|
+
const quoted = (identifier) => `"${identifier.replace(/"/g, '""')}"`;
|
|
18
|
+
/** The handler a plan describes. */
|
|
19
|
+
export function derivedHandler(plan) {
|
|
20
|
+
const key = permissionKey.parse(plan.permission.key);
|
|
21
|
+
const ref = (id) => ({ entityType: plan.entity, entityId: id });
|
|
22
|
+
const rowOrThrow = lookup(plan.entity, plan.table, plan.primaryKey, plan.columns.map(quoted).join(', '));
|
|
23
|
+
/** The declared check: on the row the input names, or on its parent, or scope-wide. */
|
|
24
|
+
const check = async (ctx, input) => {
|
|
25
|
+
const { entity, idFrom } = plan.permission;
|
|
26
|
+
const decision = entity === undefined || idFrom === undefined
|
|
27
|
+
? await ctx.check(key)
|
|
28
|
+
: await ctx.check(key, { entityType: entity, entityId: String(input?.[idFrom]) });
|
|
29
|
+
assertAllowed(decision);
|
|
30
|
+
};
|
|
31
|
+
const body = bodyOf(plan, rowOrThrow, ref);
|
|
32
|
+
// The bin is checked row by row, by `ctx.pageTrashed` itself (#119): a scope-wide check of the
|
|
33
|
+
// trash key in front of it would deny the person whose key is narrowed to the rows they binned.
|
|
34
|
+
if (plan.kind === 'trashed')
|
|
35
|
+
return body;
|
|
36
|
+
return (async (ctx, input) => {
|
|
37
|
+
await check(ctx, input);
|
|
38
|
+
return body(ctx, input);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** What the handler does once the check has passed. */
|
|
42
|
+
function bodyOf(plan, rowOrThrow, ref) {
|
|
43
|
+
switch (plan.kind) {
|
|
44
|
+
case 'get':
|
|
45
|
+
return (ctx, input) => rowOrThrow(ctx, String(input?.[plan.idFrom]));
|
|
46
|
+
case 'list': {
|
|
47
|
+
const parentOrThrow = plan.parent && parentLookup(plan.parent);
|
|
48
|
+
return (ctx, input) => {
|
|
49
|
+
// A page of a parent's rows names the parent: one that does not exist is not an empty page.
|
|
50
|
+
parentOrThrow?.(ctx, input);
|
|
51
|
+
return ctx.page(plan.entity, {
|
|
52
|
+
...pageOf(input),
|
|
53
|
+
filters: filtersOf(plan.filters, input),
|
|
54
|
+
...(plan.total ? { total: true } : {}),
|
|
55
|
+
// The host parsed it against `z.enum(['active', 'archived'])` (#119).
|
|
56
|
+
...(plan.viewFrom !== undefined && input?.[plan.viewFrom] !== undefined
|
|
57
|
+
? { view: input[plan.viewFrom] }
|
|
58
|
+
: {}),
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
case 'update':
|
|
63
|
+
return (ctx, input) => {
|
|
64
|
+
const id = String(input?.[plan.idFrom]);
|
|
65
|
+
const existing = rowOrThrow(ctx, id);
|
|
66
|
+
// Absent is untouched, `null` clears: only the fields the caller sent are written.
|
|
67
|
+
const sent = plan.fields.filter(({ field }) => input?.[field] !== undefined);
|
|
68
|
+
// Nothing sent is nothing changed — no write, and no event to move the version.
|
|
69
|
+
if (sent.length === 0)
|
|
70
|
+
return existing;
|
|
71
|
+
ctx.sql.exec(`UPDATE ${quoted(plan.table)} SET ${sent.map(({ column }) => `${quoted(column)} = ?`).join(', ')} ` +
|
|
72
|
+
`WHERE ${quoted(plan.primaryKey)} = ?`,
|
|
73
|
+
// The plan admits only string and number columns, and the host parsed the input against them.
|
|
74
|
+
[...sent.map(({ field }) => input?.[field]), id]);
|
|
75
|
+
const row = rowOrThrow(ctx, id);
|
|
76
|
+
ctx.emit(eventOf(plan.emit, ref(id), row));
|
|
77
|
+
return row;
|
|
78
|
+
};
|
|
79
|
+
case 'delete':
|
|
80
|
+
return (ctx, input) => {
|
|
81
|
+
const id = String(input?.[plan.idFrom]);
|
|
82
|
+
rowOrThrow(ctx, id);
|
|
83
|
+
ctx.sql.exec(`DELETE FROM ${quoted(plan.table)} WHERE ${quoted(plan.primaryKey)} = ?`, [id]);
|
|
84
|
+
const result = { id, deleted: true };
|
|
85
|
+
ctx.emit(eventOf(plan.emit, ref(id), result));
|
|
86
|
+
return result;
|
|
87
|
+
};
|
|
88
|
+
case 'create': {
|
|
89
|
+
const parentOrThrow = plan.parent && parentLookup(plan.parent);
|
|
90
|
+
// Every column but the id, in the order the values are bound: the link, the input, the stamps.
|
|
91
|
+
const written = [
|
|
92
|
+
...(plan.parent ? [plan.parent.column] : []),
|
|
93
|
+
...plan.values.map(({ column }) => column),
|
|
94
|
+
...plan.stamps.map(({ column }) => column),
|
|
95
|
+
];
|
|
96
|
+
const insert = `INSERT INTO ${quoted(plan.table)} (${[plan.primaryKey, ...written].map(quoted).join(', ')}) ` +
|
|
97
|
+
`VALUES (${['?', ...written.map(() => '?')].join(', ')})`;
|
|
98
|
+
const keyAt = plan.key.map((column) => written.indexOf(column));
|
|
99
|
+
const byKey = `SELECT ${plan.columns.map(quoted).join(', ')} FROM ${quoted(plan.table)} WHERE ` +
|
|
100
|
+
plan.key.map((column) => `${quoted(column)} IS ?`).join(' AND ');
|
|
101
|
+
return (ctx, input) => {
|
|
102
|
+
const parentId = parentOrThrow?.(ctx, input);
|
|
103
|
+
const values = [
|
|
104
|
+
...(parentId !== undefined ? [parentId] : []),
|
|
105
|
+
// A field the caller left out is NULL — the plan admits it only over a nullable column.
|
|
106
|
+
...plan.values.map(({ field }) => (input?.[field] ?? null)),
|
|
107
|
+
...stamped(ctx, plan.stamps),
|
|
108
|
+
];
|
|
109
|
+
// Idempotent on the natural key: the row that already holds it is the answer, unchanged.
|
|
110
|
+
if (keyAt.length > 0) {
|
|
111
|
+
const existing = ctx.sql.query(byKey, keyAt.map((i) => values[i]))[0];
|
|
112
|
+
if (existing)
|
|
113
|
+
return existing;
|
|
114
|
+
}
|
|
115
|
+
const id = ulid();
|
|
116
|
+
ctx.sql.exec(insert, [id, ...values]);
|
|
117
|
+
// The edge the permission walk follows from the new row to the parent it was checked on.
|
|
118
|
+
if (plan.parent)
|
|
119
|
+
ctx.link(ref(id), { entityType: plan.parent.entity, entityId: String(parentId) });
|
|
120
|
+
const created = rowOrThrow(ctx, id);
|
|
121
|
+
ctx.emit(eventOf(plan.emit, ref(id), created));
|
|
122
|
+
return created;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// One kernel move (#119): the verb checks the entity's declared key again, refuses a wrong
|
|
126
|
+
// move as `conflict` and a missing row as `not_found`, and emits the move itself.
|
|
127
|
+
case 'archive':
|
|
128
|
+
case 'unarchive':
|
|
129
|
+
case 'trash':
|
|
130
|
+
case 'restore': {
|
|
131
|
+
const move = plan.kind;
|
|
132
|
+
return async (ctx, input) => {
|
|
133
|
+
const id = String(input?.[plan.idFrom]);
|
|
134
|
+
return { id, state: await ctx[move](ref(id)) };
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
case 'trashed':
|
|
138
|
+
return (ctx, input) => ctx.pageTrashed(plan.entity, { ...pageOf(input), filters: filtersOf(plan.filters, input) });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** A read by primary key that answers `not_found` in the words a hand-written read uses. */
|
|
142
|
+
function lookup(entity, table, primaryKey, columns) {
|
|
143
|
+
const select = `SELECT ${columns} FROM ${quoted(table)} WHERE ${quoted(primaryKey)} = ?`;
|
|
144
|
+
return (ctx, id) => {
|
|
145
|
+
const row = ctx.sql.query(select, [id])[0];
|
|
146
|
+
if (!row)
|
|
147
|
+
throw substratError('not_found', `${entity} not found: ${id}`);
|
|
148
|
+
return row;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** The parent the input names, or `not_found` — answered after the check, so it probes nothing. */
|
|
152
|
+
function parentLookup(parent) {
|
|
153
|
+
const exists = lookup(parent.entity, parent.table, parent.primaryKey, '1 AS found');
|
|
154
|
+
return (ctx, input) => {
|
|
155
|
+
const id = String(input?.[parent.idFrom]);
|
|
156
|
+
exists(ctx, id);
|
|
157
|
+
return id;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** Each stamp's value for this invocation, in the plan's order: the time, or the caller. */
|
|
161
|
+
function stamped(ctx, stamps) {
|
|
162
|
+
const now = ctx.now();
|
|
163
|
+
const caller = String(ctx.principal);
|
|
164
|
+
return stamps.map(({ kind }) => (kind === 'createdAt' ? now : caller));
|
|
165
|
+
}
|
|
166
|
+
/** The page the platform supplied with the input (`limit`, `cursor`, `sort`, `order`). */
|
|
167
|
+
function pageOf(input) {
|
|
168
|
+
return {
|
|
169
|
+
limit: input?.limit,
|
|
170
|
+
cursor: input?.cursor,
|
|
171
|
+
sort: input?.sort,
|
|
172
|
+
order: input?.order,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** The declared filters the caller sent, by column. */
|
|
176
|
+
function filtersOf(filters, input) {
|
|
177
|
+
const out = {};
|
|
178
|
+
for (const { field, column } of filters) {
|
|
179
|
+
if (input?.[field] !== undefined)
|
|
180
|
+
out[column] = input[field];
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
/** The declared event, its payload and subject drawn from what the operation answers with. */
|
|
185
|
+
function eventOf(emit, entity, output) {
|
|
186
|
+
const payload = {};
|
|
187
|
+
for (const field of emit.payload)
|
|
188
|
+
payload[field] = output[field];
|
|
189
|
+
return {
|
|
190
|
+
type: emit.type,
|
|
191
|
+
schemaVersion: emit.schemaVersion,
|
|
192
|
+
entity,
|
|
193
|
+
piiClass: emit.piiClass,
|
|
194
|
+
...(emit.subjectId !== undefined ? { subjectId: output[emit.subjectId] } : {}),
|
|
195
|
+
payload,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=derived-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derived-handlers.js","sourceRoot":"","sources":["../src/derived-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type BoundedUnbind, type CapabilityId, type Coverage, type DomainEventInput, type EntityRef, type PermissionKey, type PrincipalId } from '@substrat-run/contracts';
|
|
2
|
+
import type { ScopedSql } from './scope-host.js';
|
|
3
|
+
/**
|
|
4
|
+
* A BINDING shape (#1686): a record names its own person in a declared column, and the person
|
|
5
|
+
* it names holds the record's declared shape there. What makes a customer invite a binding
|
|
6
|
+
* rather than a delegation: the link names WHICH record, the reviewed registry says WHAT the
|
|
7
|
+
* person bound to it holds, and the minter vouches for the record with an ordinary key.
|
|
8
|
+
*
|
|
9
|
+
* Three writes, each one statement set shared by both adapters:
|
|
10
|
+
* - {@link bindIn}: inside a `become` exchange's transaction, once {@link judgeBind} said yes and
|
|
11
|
+
* the use is taken — the column set to the claimant, the shape granted (marker and keys), and
|
|
12
|
+
* `entity.holder-bound` emitted.
|
|
13
|
+
* - {@link unbindEntityHolderBounded}: the column cleared, and every holder of the shape on the
|
|
14
|
+
* record released from the SPINE — their marker and everything they hold there tombstoned (K-21).
|
|
15
|
+
* - {@link captureBoundOf} / {@link releaseBound}: the erasure's half — on every type that binds or
|
|
16
|
+
* ever bound here, the holders on the erased record and the erased person's own markers, read
|
|
17
|
+
* before any module erasure runs and released after the hooks whether or not a row survived.
|
|
18
|
+
*
|
|
19
|
+
* People already bound keep the shape until they are released — an unbind (while the shape still
|
|
20
|
+
* binds), an erasure, or the retirement of its keys — whatever a later version declares.
|
|
21
|
+
*
|
|
22
|
+
* Read from the scope's record of its reconciled registry (`shape-bind-*` tuples), never from the
|
|
23
|
+
* vertical's code: a scope not yet reconciled with a binding declaration binds nothing.
|
|
24
|
+
*/
|
|
25
|
+
interface EntityBinding {
|
|
26
|
+
entityType: string;
|
|
27
|
+
table: string;
|
|
28
|
+
idColumn: string;
|
|
29
|
+
principalColumn: string;
|
|
30
|
+
/**
|
|
31
|
+
* Who holds this on a record may bind its person with a link, and unbind them — null once a
|
|
32
|
+
* version dropped `bind` and kept the shape: nobody may, and an erasure still releases.
|
|
33
|
+
*/
|
|
34
|
+
permission: PermissionKey | null;
|
|
35
|
+
/** The shape's current keys — what a claim gives. */
|
|
36
|
+
keys: PermissionKey[];
|
|
37
|
+
/** Every key of the shape, current and retired — what an unbind takes back. */
|
|
38
|
+
every: PermissionKey[];
|
|
39
|
+
}
|
|
40
|
+
/** The bind key's refusal, in the shape every bounded verb answers a missing key with. */
|
|
41
|
+
declare const lacking: (permission: PermissionKey) => {
|
|
42
|
+
ok: false;
|
|
43
|
+
coverage: Extract<Coverage, {
|
|
44
|
+
covered: false;
|
|
45
|
+
}>;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* May `entity` be bound by a link its minter vouches for, now? The scope's recorded binding for
|
|
49
|
+
* the type, the record present, its column naming nobody, nobody holding its shape there on the
|
|
50
|
+
* spine (`bind-record-held`: unbind them first), and the minter holding the binding's
|
|
51
|
+
* permission on it (`allowed`, the checker's `check` for the minter). The same judgement at mint
|
|
52
|
+
* and at the exchange, so a link whose minter has since lost the key, or left, is refused.
|
|
53
|
+
*/
|
|
54
|
+
export declare function judgeBind(sql: ScopedSql, allowed: (permission: PermissionKey, entity: EntityRef) => Promise<boolean>, entity: EntityRef): Promise<{
|
|
55
|
+
ok: true;
|
|
56
|
+
binding: EntityBinding;
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
refused: 'bind-not-declared' | 'bind-record-missing' | 'bind-record-ambiguous' | 'bind-record-bound' | 'bind-record-held';
|
|
60
|
+
} | ReturnType<typeof lacking>>;
|
|
61
|
+
/**
|
|
62
|
+
* The claim's write, inside the exchange's transaction once {@link judgeBind} has said yes there
|
|
63
|
+
* and the use is taken: the record's column set to `principal` — only while it still names nobody —
|
|
64
|
+
* the shape's current keys given with its marker, and `entity.holder-bound` emitted with the
|
|
65
|
+
* capability as actor. Nothing can have moved since the judgement inside one transaction, so a
|
|
66
|
+
* column that no longer reads empty is a fault: thrown, so the use rolls back with it.
|
|
67
|
+
*/
|
|
68
|
+
export declare function bindIn(sql: ScopedSql, binding: EntityBinding, entity: EntityRef, principal: PrincipalId, link: {
|
|
69
|
+
capabilityId: CapabilityId;
|
|
70
|
+
mintedBy: PrincipalId;
|
|
71
|
+
}, emit: (event: DomainEventInput) => void): void;
|
|
72
|
+
/** The pseudo-operation an unbind's spine event names, on `capabilities.mint-become`'s pattern. */
|
|
73
|
+
export declare const ENTITY_UNBIND_OPERATION = "entities.unbind";
|
|
74
|
+
/**
|
|
75
|
+
* A principal's unbind of `entity` (#1686) — the lever beside a binding invite, run by a host in one
|
|
76
|
+
* scope task and transaction. Bounded here: the caller must hold the binding's key on the record
|
|
77
|
+
* (`allowed`, the checker's `check` for the caller). Clears the record's column (every row with
|
|
78
|
+
* its id) and releases every holder of the shape on it from the spine — whoever a link bound,
|
|
79
|
+
* whatever the column says now, and a holder given the shape before the column was written —
|
|
80
|
+
* with one `entity.holder-unbound` event each through `emit`, whose actor the host stamps as the
|
|
81
|
+
* caller. `unbound` names everyone released; a refusal writes nothing.
|
|
82
|
+
*/
|
|
83
|
+
export declare function unbindEntityHolderBounded(deps: {
|
|
84
|
+
sql: ScopedSql;
|
|
85
|
+
now: string;
|
|
86
|
+
allowed: (permission: PermissionKey, entity: EntityRef) => Promise<boolean>;
|
|
87
|
+
emit: (event: DomainEventInput) => void;
|
|
88
|
+
}, entity: EntityRef): Promise<BoundedUnbind>;
|
|
89
|
+
/** What an erasure releases, read before any of it runs. */
|
|
90
|
+
export interface ErasureBindings {
|
|
91
|
+
/** Shape holders to release: on a record that IS the subject (any holder), or the subject's own markers. */
|
|
92
|
+
holders: {
|
|
93
|
+
object: string;
|
|
94
|
+
only?: string;
|
|
95
|
+
}[];
|
|
96
|
+
/** Rows whose column names someone the erasure reaches, to clear where they survive. */
|
|
97
|
+
columns: {
|
|
98
|
+
binding: EntityBinding;
|
|
99
|
+
entityId: string;
|
|
100
|
+
principal: string;
|
|
101
|
+
}[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Every binding the erasure of `subjectId` reaches, by the spine and by the column: on each type the
|
|
105
|
+
* scope binds or has ever bound (`shape-bound`) — the id matched against every such type, as a
|
|
106
|
+
* module's declared erasure matches its subject columns whatever the type, so ids that are not
|
|
107
|
+
* ULIDs (an integer, say) can collide across types — the record that IS the subject (every holder on it) and every record the subject
|
|
108
|
+
* holds the shape on (their own marker), plus the rows whose column names the subject or that are
|
|
109
|
+
* the subject. Read BEFORE any declared blank, delete or hook runs, so a row a module's erasure
|
|
110
|
+
* deletes is still known. A scope that binds nothing pays the one read of its shape records.
|
|
111
|
+
*/
|
|
112
|
+
export declare function captureBoundOf(sql: ScopedSql, subjectId: string): ErasureBindings;
|
|
113
|
+
/**
|
|
114
|
+
* The erasure's unbind, after the module hooks: each captured column cleared where the row survived
|
|
115
|
+
* and still names that person, and each captured holder released from the spine whether or not
|
|
116
|
+
* its row survived. No event: an event naming the erased subject would be a new copy of what is
|
|
117
|
+
* being erased.
|
|
118
|
+
*/
|
|
119
|
+
export declare function releaseBound(sql: ScopedSql, captured: ErasureBindings, now: string): void;
|
|
120
|
+
export {};
|
|
121
|
+
//# sourceMappingURL=entity-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-binding.d.ts","sourceRoot":"","sources":["../src/entity-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { ENTITY_HOLDER_BOUND, ENTITY_HOLDER_UNBOUND, ENTITY_SHAPE_MARKER_RELATION, entityObjectRef, permissionKey, substratError, } from '@substrat-run/contracts';
|
|
2
|
+
import { BIND_HOLDER_RELATION, BIND_KEY_RELATION, BIND_PERMISSION_RELATION, BOUND_EVER_RELATION, grantEntityShapeIn } from './entity-grant-shape.js';
|
|
3
|
+
import { SHAPE_PROTECTED_KEY_RELATION } from './entity-grant.js';
|
|
4
|
+
import { assertSqlIdentifier, tableExists } from './sql-identifier.js';
|
|
5
|
+
import { switchSqlOf } from './system-switch.js';
|
|
6
|
+
const PRINCIPAL = 'principal:';
|
|
7
|
+
const GRANTED = 'granted:';
|
|
8
|
+
const BINDING_RELATIONS = [BIND_HOLDER_RELATION, BIND_PERMISSION_RELATION, BIND_KEY_RELATION, SHAPE_PROTECTED_KEY_RELATION];
|
|
9
|
+
/** One shape's recorded facts as a binding, or null when they do not make one. */
|
|
10
|
+
function bindingFrom(entityType, rows) {
|
|
11
|
+
const of = (relation) => rows.filter((r) => r.relation === relation).map((r) => r.object);
|
|
12
|
+
const holder = of(BIND_HOLDER_RELATION);
|
|
13
|
+
const permission = of(BIND_PERMISSION_RELATION);
|
|
14
|
+
if (holder.length !== 1 || permission.length > 1)
|
|
15
|
+
return null;
|
|
16
|
+
const parts = holder[0].split('.');
|
|
17
|
+
if (parts.length !== 3)
|
|
18
|
+
return null;
|
|
19
|
+
const [table, idColumn, principalColumn] = parts.map((n) => assertSqlIdentifier('entityBinding', 'an identifier', n, `the recorded binding of '${entityType}'`));
|
|
20
|
+
const keys = (relation) => of(relation).map((o) => permissionKey.parse(o.slice(GRANTED.length))).sort();
|
|
21
|
+
return {
|
|
22
|
+
entityType,
|
|
23
|
+
table,
|
|
24
|
+
idColumn,
|
|
25
|
+
principalColumn,
|
|
26
|
+
permission: permission[0] === undefined ? null : permissionKey.parse(permission[0]),
|
|
27
|
+
keys: keys(BIND_KEY_RELATION),
|
|
28
|
+
every: keys(SHAPE_PROTECTED_KEY_RELATION),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The scope's recorded binding for `entityType` that still declares its bind key — what a mint, a
|
|
33
|
+
* claim and an unbind need — or null when its reconciled registry binds nothing there.
|
|
34
|
+
*/
|
|
35
|
+
function entityBindingOf(sql, entityType) {
|
|
36
|
+
const binding = bindingFrom(entityType, sql.query(`SELECT relation, object FROM _substrat_tuples WHERE subject = ? AND relation IN (?, ?, ?, ?)`, [`shape:${entityType}`, ...BINDING_RELATIONS]));
|
|
37
|
+
return binding?.permission ? { ...binding, permission: binding.permission } : null;
|
|
38
|
+
}
|
|
39
|
+
/** Every binding the scope's reconciled registry records, bind key or not — what an erasure releases — in one read. */
|
|
40
|
+
function entityBindingsOf(sql) {
|
|
41
|
+
const bySubject = new Map();
|
|
42
|
+
for (const row of sql.query(`SELECT subject, relation, object FROM _substrat_tuples
|
|
43
|
+
WHERE subject >= 'shape:' AND subject < 'shape;' AND relation IN (?, ?, ?, ?)`, [...BINDING_RELATIONS])) {
|
|
44
|
+
bySubject.set(row.subject, [...(bySubject.get(row.subject) ?? []), row]);
|
|
45
|
+
}
|
|
46
|
+
return [...bySubject].flatMap(([subject, rows]) => bindingFrom(subject.slice('shape:'.length), rows) ?? []);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The record as the binding sees it: how many rows carry its id, and whom the first row that names
|
|
50
|
+
* someone names. Every row is read: the id column should be unique, and a record any of whose rows
|
|
51
|
+
* names someone is bound.
|
|
52
|
+
*/
|
|
53
|
+
function boundRecord(sql, b, entityId) {
|
|
54
|
+
if (!tableExists(switchSqlOf(sql), b.table))
|
|
55
|
+
return { rows: 0, principal: null };
|
|
56
|
+
const rows = sql.query(`SELECT "${b.principalColumn}" AS principal FROM "${b.table}" WHERE "${b.idColumn}" = ?`, [entityId]);
|
|
57
|
+
return { rows: rows.length, principal: rows.find((r) => r.principal !== null)?.principal ?? null };
|
|
58
|
+
}
|
|
59
|
+
/** Does anyone hold a live shape marker on `object`? */
|
|
60
|
+
const heldBySomeone = (sql, object) => sql.query(`SELECT 1 FROM _substrat_tuples WHERE object = ? AND relation = '${ENTITY_SHAPE_MARKER_RELATION}' AND revoked_at IS NULL
|
|
61
|
+
AND subject >= '${PRINCIPAL}' AND subject < 'principal;' LIMIT 1`, [object]).length > 0;
|
|
62
|
+
/** The bind key's refusal, in the shape every bounded verb answers a missing key with. */
|
|
63
|
+
const lacking = (permission) => ({
|
|
64
|
+
ok: false,
|
|
65
|
+
coverage: { covered: false, missing: [permission] },
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* May `entity` be bound by a link its minter vouches for, now? The scope's recorded binding for
|
|
69
|
+
* the type, the record present, its column naming nobody, nobody holding its shape there on the
|
|
70
|
+
* spine (`bind-record-held`: unbind them first), and the minter holding the binding's
|
|
71
|
+
* permission on it (`allowed`, the checker's `check` for the minter). The same judgement at mint
|
|
72
|
+
* and at the exchange, so a link whose minter has since lost the key, or left, is refused.
|
|
73
|
+
*/
|
|
74
|
+
export async function judgeBind(sql, allowed, entity) {
|
|
75
|
+
entityObjectRef(entity, 'bind');
|
|
76
|
+
const binding = entityBindingOf(sql, entity.entityType);
|
|
77
|
+
if (!binding)
|
|
78
|
+
return { ok: false, refused: 'bind-not-declared' };
|
|
79
|
+
// The key before anything about the record: a minter without it on this record learns nothing
|
|
80
|
+
// of whether it exists, names someone, or is held — the same refusal for every record they lack.
|
|
81
|
+
if (!(await allowed(binding.permission, entity)))
|
|
82
|
+
return lacking(binding.permission);
|
|
83
|
+
const record = boundRecord(sql, binding, entity.entityId);
|
|
84
|
+
if (record.rows === 0)
|
|
85
|
+
return { ok: false, refused: 'bind-record-missing' };
|
|
86
|
+
if (record.principal !== null)
|
|
87
|
+
return { ok: false, refused: 'bind-record-bound' };
|
|
88
|
+
// Two rows carrying one id are no one record: there is nothing a claim could bind exactly once.
|
|
89
|
+
if (record.rows > 1)
|
|
90
|
+
return { ok: false, refused: 'bind-record-ambiguous' };
|
|
91
|
+
// The spine as well as the column: someone already holding the record's shape there — given it
|
|
92
|
+
// before the column was written, or left by a holder column a later version moved — is a holder
|
|
93
|
+
// a new link would bind a second person beside. Unbind them first.
|
|
94
|
+
if (heldBySomeone(sql, entityObjectRef(entity, 'bind')))
|
|
95
|
+
return { ok: false, refused: 'bind-record-held' };
|
|
96
|
+
return { ok: true, binding };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The claim's write, inside the exchange's transaction once {@link judgeBind} has said yes there
|
|
100
|
+
* and the use is taken: the record's column set to `principal` — only while it still names nobody —
|
|
101
|
+
* the shape's current keys given with its marker, and `entity.holder-bound` emitted with the
|
|
102
|
+
* capability as actor. Nothing can have moved since the judgement inside one transaction, so a
|
|
103
|
+
* column that no longer reads empty is a fault: thrown, so the use rolls back with it.
|
|
104
|
+
*/
|
|
105
|
+
export function bindIn(sql, binding, entity, principal, link, emit) {
|
|
106
|
+
// Counted by RETURNING, never by `changes`: on a Durable Object that is `rowsWritten`, which
|
|
107
|
+
// counts index entries too, so an index on the principal column reads one row as two. Exactly one
|
|
108
|
+
// row: an id two rows carry is no one record, and the claim refuses it rather than bind both.
|
|
109
|
+
const set = sql.query(`UPDATE "${binding.table}" SET "${binding.principalColumn}" = ? WHERE "${binding.idColumn}" = ? AND "${binding.principalColumn}" IS NULL RETURNING 1 AS one`, [principal, entity.entityId]);
|
|
110
|
+
if (set.length !== 1) {
|
|
111
|
+
throw substratError('internal', `capability ${link.capabilityId}: its bind on ${entity.entityType} was refused after it was judged`);
|
|
112
|
+
}
|
|
113
|
+
grantEntityShapeIn(switchSqlOf(sql), principal, entity, binding.keys);
|
|
114
|
+
// Sticky (the reconcile that recorded the binding wrote it too): an erasure releases this type's
|
|
115
|
+
// holders whatever a later version declares.
|
|
116
|
+
sql.exec('INSERT OR IGNORE INTO _substrat_tuples (subject, relation, object) VALUES (?, ?, ?)', [
|
|
117
|
+
`shape:${entity.entityType}`,
|
|
118
|
+
BOUND_EVER_RELATION,
|
|
119
|
+
'bound',
|
|
120
|
+
]);
|
|
121
|
+
const payload = { entity, principal, ...link, granted: binding.keys };
|
|
122
|
+
emit({ type: ENTITY_HOLDER_BOUND, schemaVersion: 1, entity, piiClass: 'none', payload });
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Release the record's shape holders from the SPINE: every principal holding a live marker on
|
|
126
|
+
* `object` (or only `only`), and with each marker everything that principal holds on the record —
|
|
127
|
+
* every `granted:` tuple there, whatever the shape's keys are now. A binding shape's marker and its
|
|
128
|
+
* keys come only from its own grant (its keys are refused to every direct grant on a record of the
|
|
129
|
+
* type), so the marker is the person's holding of the record, and what they hold on it goes with it:
|
|
130
|
+
* a key the shape has since dropped without retiring it included. That is broader than the shape:
|
|
131
|
+
* it ALSO revokes a direct grant of a key the shape does not declare, made to the same holder on the
|
|
132
|
+
* same record (a `bind:member` granted to a bound person goes with the unbind) — reported in
|
|
133
|
+
* `removed`, never silent. Judged by the spine, never the column, so a column module code rewrote
|
|
134
|
+
* cannot strand the person a link bound. One tombstone statement per holder (K-21).
|
|
135
|
+
*/
|
|
136
|
+
function releaseHolders(sql, object, now, only) {
|
|
137
|
+
const holders = sql.query(`SELECT subject FROM _substrat_tuples
|
|
138
|
+
WHERE object = ? AND relation = '${ENTITY_SHAPE_MARKER_RELATION}' AND revoked_at IS NULL
|
|
139
|
+
AND subject >= '${PRINCIPAL}' AND subject < 'principal;'${only === undefined ? '' : ' AND subject = ?'}`, only === undefined ? [object] : [object, `${PRINCIPAL}${only}`]);
|
|
140
|
+
return holders.map(({ subject }) => ({
|
|
141
|
+
principal: subject.slice(PRINCIPAL.length),
|
|
142
|
+
removed: sql
|
|
143
|
+
.query(`UPDATE _substrat_tuples SET revoked_at = ?
|
|
144
|
+
WHERE subject = ? AND object = ? AND revoked_at IS NULL
|
|
145
|
+
AND (relation = '${ENTITY_SHAPE_MARKER_RELATION}' OR (relation >= '${GRANTED}' AND relation < 'granted;'))
|
|
146
|
+
RETURNING relation`, [now, subject, object])
|
|
147
|
+
.flatMap((r) => (r.relation === ENTITY_SHAPE_MARKER_RELATION ? [] : [permissionKey.parse(r.relation.slice(GRANTED.length))]))
|
|
148
|
+
.sort(),
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
/** Clear the record's column — every row carrying its id, or only where it still names `principal`. */
|
|
152
|
+
function clearColumn(sql, binding, entityId, principal) {
|
|
153
|
+
if (!tableExists(switchSqlOf(sql), binding.table))
|
|
154
|
+
return;
|
|
155
|
+
sql.exec(`UPDATE "${binding.table}" SET "${binding.principalColumn}" = NULL WHERE "${binding.idColumn}" = ? AND ${principal === undefined ? `"${binding.principalColumn}" IS NOT NULL` : `"${binding.principalColumn}" = ?`}`, principal === undefined ? [entityId] : [entityId, principal]);
|
|
156
|
+
}
|
|
157
|
+
/** The pseudo-operation an unbind's spine event names, on `capabilities.mint-become`'s pattern. */
|
|
158
|
+
export const ENTITY_UNBIND_OPERATION = 'entities.unbind';
|
|
159
|
+
/**
|
|
160
|
+
* A principal's unbind of `entity` (#1686) — the lever beside a binding invite, run by a host in one
|
|
161
|
+
* scope task and transaction. Bounded here: the caller must hold the binding's key on the record
|
|
162
|
+
* (`allowed`, the checker's `check` for the caller). Clears the record's column (every row with
|
|
163
|
+
* its id) and releases every holder of the shape on it from the spine — whoever a link bound,
|
|
164
|
+
* whatever the column says now, and a holder given the shape before the column was written —
|
|
165
|
+
* with one `entity.holder-unbound` event each through `emit`, whose actor the host stamps as the
|
|
166
|
+
* caller. `unbound` names everyone released; a refusal writes nothing.
|
|
167
|
+
*/
|
|
168
|
+
export async function unbindEntityHolderBounded(deps, entity) {
|
|
169
|
+
const object = entityObjectRef(entity, 'unbind');
|
|
170
|
+
const binding = entityBindingOf(deps.sql, entity.entityType);
|
|
171
|
+
if (!binding)
|
|
172
|
+
return { ok: false, refused: 'bind-not-declared' };
|
|
173
|
+
if (!(await deps.allowed(binding.permission, entity)))
|
|
174
|
+
return lacking(binding.permission);
|
|
175
|
+
clearColumn(deps.sql, binding, entity.entityId);
|
|
176
|
+
const released = releaseHolders(deps.sql, object, deps.now);
|
|
177
|
+
for (const { principal, removed } of released) {
|
|
178
|
+
const payload = { entity, principal, removed };
|
|
179
|
+
deps.emit({ type: ENTITY_HOLDER_UNBOUND, schemaVersion: 1, entity, piiClass: 'none', payload });
|
|
180
|
+
}
|
|
181
|
+
return { ok: true, unbound: released.map((r) => r.principal) };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Every binding the erasure of `subjectId` reaches, by the spine and by the column: on each type the
|
|
185
|
+
* scope binds or has ever bound (`shape-bound`) — the id matched against every such type, as a
|
|
186
|
+
* module's declared erasure matches its subject columns whatever the type, so ids that are not
|
|
187
|
+
* ULIDs (an integer, say) can collide across types — the record that IS the subject (every holder on it) and every record the subject
|
|
188
|
+
* holds the shape on (their own marker), plus the rows whose column names the subject or that are
|
|
189
|
+
* the subject. Read BEFORE any declared blank, delete or hook runs, so a row a module's erasure
|
|
190
|
+
* deletes is still known. A scope that binds nothing pays the one read of its shape records.
|
|
191
|
+
*/
|
|
192
|
+
export function captureBoundOf(sql, subjectId) {
|
|
193
|
+
const out = { holders: [], columns: [] };
|
|
194
|
+
const bindings = new Map(entityBindingsOf(sql).map((b) => [b.entityType, b]));
|
|
195
|
+
// Every type that binds now, and every type that ever bound here (`shape-bound`, sticky).
|
|
196
|
+
const types = new Set([
|
|
197
|
+
...bindings.keys(),
|
|
198
|
+
...sql
|
|
199
|
+
.query(`SELECT subject FROM _substrat_tuples WHERE subject >= 'shape:' AND subject < 'shape;' AND relation = ?`, [
|
|
200
|
+
BOUND_EVER_RELATION,
|
|
201
|
+
])
|
|
202
|
+
.map((r) => r.subject.slice('shape:'.length)),
|
|
203
|
+
]);
|
|
204
|
+
for (const entityType of types) {
|
|
205
|
+
const prefix = `${entityType}:`;
|
|
206
|
+
out.holders.push({ object: `${prefix}${subjectId}` });
|
|
207
|
+
for (const { object } of sql.query(`SELECT object FROM _substrat_tuples
|
|
208
|
+
WHERE subject = ? AND relation = '${ENTITY_SHAPE_MARKER_RELATION}' AND revoked_at IS NULL AND object >= ? AND object < ?`, [`${PRINCIPAL}${subjectId}`, prefix, `${entityType};`])) {
|
|
209
|
+
out.holders.push({ object, only: subjectId });
|
|
210
|
+
}
|
|
211
|
+
// The column only where the holder is still recorded: a type a later version stopped binding
|
|
212
|
+
// has no column the kernel still knows, and its holders are released from the spine alone.
|
|
213
|
+
const binding = bindings.get(entityType);
|
|
214
|
+
if (!binding || !tableExists(switchSqlOf(sql), binding.table))
|
|
215
|
+
continue;
|
|
216
|
+
for (const r of sql.query(`SELECT "${binding.idColumn}" AS id, "${binding.principalColumn}" AS principal FROM "${binding.table}"
|
|
217
|
+
WHERE ("${binding.idColumn}" = ? OR "${binding.principalColumn}" = ?) AND "${binding.principalColumn}" IS NOT NULL`, [subjectId, subjectId])) {
|
|
218
|
+
out.columns.push({ binding, entityId: String(r.id), principal: r.principal });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The erasure's unbind, after the module hooks: each captured column cleared where the row survived
|
|
225
|
+
* and still names that person, and each captured holder released from the spine whether or not
|
|
226
|
+
* its row survived. No event: an event naming the erased subject would be a new copy of what is
|
|
227
|
+
* being erased.
|
|
228
|
+
*/
|
|
229
|
+
export function releaseBound(sql, captured, now) {
|
|
230
|
+
for (const { binding, entityId, principal } of captured.columns)
|
|
231
|
+
clearColumn(sql, binding, entityId, principal);
|
|
232
|
+
for (const { object, only } of captured.holders)
|
|
233
|
+
releaseHolders(sql, object, now, only);
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=entity-binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-binding.js","sourceRoot":"","sources":["../src/entity-binding.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,4BAA4B,EAC5B,eAAe,EACf,aAAa,EACb,aAAa,GAUd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrJ,OAAO,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAwCjD,MAAM,SAAS,GAAG,YAAY,CAAC;AAC/B,MAAM,OAAO,GAAG,UAAU,CAAC;AAC3B,MAAM,iBAAiB,GAAG,CAAC,oBAAoB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,4BAA4B,CAAU,CAAC;AAErI,kFAAkF;AAClF,SAAS,WAAW,CAAC,UAAkB,EAAE,IAAqD;IAC5F,MAAM,EAAE,GAAG,CAAC,QAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAClG,MAAM,MAAM,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,EAAE,CAAC,wBAAwB,CAAC,CAAC;IAChD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACzD,mBAAmB,CAAC,eAAe,EAAE,eAAe,EAAE,CAAC,EAAE,4BAA4B,UAAU,GAAG,CAAC,CACxE,CAAC;IAC9B,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAChH,OAAO;QACL,UAAU;QACV,KAAK;QACL,QAAQ;QACR,eAAe;QACf,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACnF,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC;QAC7B,KAAK,EAAE,IAAI,CAAC,4BAA4B,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,GAAc,EAAE,UAAkB;IACzD,MAAM,OAAO,GAAG,WAAW,CACzB,UAAU,EACV,GAAG,CAAC,KAAK,CACP,8FAA8F,EAC9F,CAAC,SAAS,UAAU,EAAE,EAAE,GAAG,iBAAiB,CAAC,CAC9C,CACF,CAAC;IACF,OAAO,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACrF,CAAC;AAED,uHAAuH;AACvH,SAAS,gBAAgB,CAAC,GAAc;IACtC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkD,CAAC;IAC5E,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CACzB;oFACgF,EAChF,CAAC,GAAG,iBAAiB,CAAC,CACvB,EAAE,CAAC;QACF,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9G,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,GAAc,EAAE,CAAgB,EAAE,QAAgB;IACrE,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACjF,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CACpB,WAAW,CAAC,CAAC,eAAe,wBAAwB,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,QAAQ,OAAO,EACxF,CAAC,QAAQ,CAAC,CACX,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE,SAAS,IAAI,IAAI,EAAE,CAAC;AACrG,CAAC;AAED,wDAAwD;AACxD,MAAM,aAAa,GAAG,CAAC,GAAc,EAAE,MAAc,EAAW,EAAE,CAChE,GAAG,CAAC,KAAK,CACP,mEAAmE,4BAA4B;0BACzE,SAAS,sCAAsC,EACrE,CAAC,MAAM,CAAC,CACT,CAAC,MAAM,GAAG,CAAC,CAAC;AAEf,0FAA0F;AAC1F,MAAM,OAAO,GAAG,CAAC,UAAyB,EAAkE,EAAE,CAAC,CAAC;IAC9G,EAAE,EAAE,KAAK;IACT,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE;CACpD,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAc,EACd,OAA2E,EAC3E,MAAiB;IAMjB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IACjE,8FAA8F;IAC9F,iGAAiG;IACjG,IAAI,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1D,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC;IAC5E,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAClF,gGAAgG;IAChG,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IAC5E,+FAA+F;IAC/F,gGAAgG;IAChG,mEAAmE;IACnE,IAAI,aAAa,CAAC,GAAG,EAAE,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC3G,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CACpB,GAAc,EACd,OAAsB,EACtB,MAAiB,EACjB,SAAsB,EACtB,IAA2D,EAC3D,IAAuC;IAEvC,6FAA6F;IAC7F,kGAAkG;IAClG,8FAA8F;IAC9F,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CACnB,WAAW,OAAO,CAAC,KAAK,UAAU,OAAO,CAAC,eAAe,gBAAgB,OAAO,CAAC,QAAQ,cAAc,OAAO,CAAC,eAAe,8BAA8B,EAC5J,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,CAC7B,CAAC;IACF,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,aAAa,CAAC,UAAU,EAAE,cAAc,IAAI,CAAC,YAAY,iBAAiB,MAAM,CAAC,UAAU,kCAAkC,CAAC,CAAC;IACvI,CAAC;IACD,kBAAkB,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,iGAAiG;IACjG,6CAA6C;IAC7C,GAAG,CAAC,IAAI,CAAC,qFAAqF,EAAE;QAC9F,SAAS,MAAM,CAAC,UAAU,EAAE;QAC5B,mBAAmB;QACnB,OAAO;KACR,CAAC,CAAC;IACH,MAAM,OAAO,GAA6B,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;IAChG,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,cAAc,CAAC,GAAc,EAAE,MAAc,EAAE,GAAW,EAAE,IAAa;IAChF,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CACvB;yCACqC,4BAA4B;0BAC3C,SAAS,+BAA+B,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,EAAE,EAC5G,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,EAAE,CAAC,CAChE,CAAC;IACF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QACnC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAgB;QACzD,OAAO,EAAE,GAAG;aACT,KAAK,CACJ;;+BAEuB,4BAA4B,sBAAsB,OAAO;6BAC3D,EACrB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CACvB;aACA,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,4BAA4B,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5H,IAAI,EAAE;KACV,CAAC,CAAC,CAAC;AACN,CAAC;AAED,uGAAuG;AACvG,SAAS,WAAW,CAAC,GAAc,EAAE,OAAsB,EAAE,QAAgB,EAAE,SAAkB;IAC/F,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO;IAC1D,GAAG,CAAC,IAAI,CACN,WAAW,OAAO,CAAC,KAAK,UAAU,OAAO,CAAC,eAAe,mBAAmB,OAAO,CAAC,QAAQ,aAC1F,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,eAAe,eAAe,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,eAAe,OACpG,EAAE,EACF,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAC7D,CAAC;AACJ,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAEzD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAKC,EACD,MAAiB;IAEjB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IACjE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1F,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5D,KAAK,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC9C,MAAM,OAAO,GAA+B,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;QAC3E,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;AACjE,CAAC;AAUD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,GAAc,EAAE,SAAiB;IAC9D,MAAM,GAAG,GAAoB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,0FAA0F;IAC1F,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC;QACpB,GAAG,QAAQ,CAAC,IAAI,EAAE;QAClB,GAAG,GAAG;aACH,KAAK,CAAsB,wGAAwG,EAAE;YACpI,mBAAmB;SACpB,CAAC;aACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;KAChD,CAAC,CAAC;IACH,KAAK,MAAM,UAAU,IAAI,KAAK,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,GAAG,UAAU,GAAG,CAAC;QAChC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,EAAE,EAAE,CAAC,CAAC;QACtD,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,KAAK,CAChC;4CACsC,4BAA4B,yDAAyD,EAC3H,CAAC,GAAG,SAAS,GAAG,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,CAAC,CACvD,EAAE,CAAC;YACF,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,6FAA6F;QAC7F,2FAA2F;QAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;YAAE,SAAS;QACxE,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,CACvB,WAAW,OAAO,CAAC,QAAQ,aAAa,OAAO,CAAC,eAAe,wBAAwB,OAAO,CAAC,KAAK;kBACxF,OAAO,CAAC,QAAQ,aAAa,OAAO,CAAC,eAAe,eAAe,OAAO,CAAC,eAAe,eAAe,EACrH,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,EAAE,CAAC;YACF,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAc,EAAE,QAAyB,EAAE,GAAW;IACjF,KAAK,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,QAAQ,CAAC,OAAO;QAAE,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAChH,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,QAAQ,CAAC,OAAO;QAAE,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1F,CAAC"}
|