@substrat-run/kernel 0.139.0 → 0.141.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/capability.d.ts +5 -2
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +17 -8
- package/dist/capability.js.map +1 -1
- 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 +35 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -5
- 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-copy-ledger.d.ts +79 -0
- package/dist/scope-copy-ledger.d.ts.map +1 -0
- package/dist/scope-copy-ledger.js +82 -0
- package/dist/scope-copy-ledger.js.map +1 -0
- package/dist/scope-host.d.ts +402 -25
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +74 -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 +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derived-object.js","sourceRoot":"","sources":["../src/derived-object.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the kernel derived onto a module's tables, checked and put back (#2090).
|
|
3
|
+
*
|
|
4
|
+
* | Object | Is | When it is missing or not what the kernel emits |
|
|
5
|
+
* |---|---|---|
|
|
6
|
+
* | `_substrat_archived_at` / `_substrat_trashed_at` (#119) | state | fail closed: it cannot be invented again |
|
|
7
|
+
* | the `born` / `moved` archive/trash guard triggers (#119) | derived | put back |
|
|
8
|
+
* | the derived list indexes, partial per view on a stateful table (#811) | derived | put back |
|
|
9
|
+
| the purge sweep's partial index on a trashable table with a horizon (#119) | derived | put back |
|
|
10
|
+
* | the search index and the three triggers keeping it in step (#827) | derived | rebuilt from the rows |
|
|
11
|
+
*
|
|
12
|
+
* The search triggers are a privacy fact, not only a freshness one: subject erasure rewrites a
|
|
13
|
+
* row's text and relies on the update and delete triggers to take the old text out of the index.
|
|
14
|
+
* Without them, erased text stays searchable.
|
|
15
|
+
*
|
|
16
|
+
* **An object is judged by its definition, not its name.** `sqlite_master` holds each one's
|
|
17
|
+
* CREATE statement as it was run, and the kernel knows the statement it emits
|
|
18
|
+
* (`DerivedObject`), so a same-named trigger that does nothing, an index on the wrong columns or
|
|
19
|
+
* with the wrong partial predicate, and an object filed under another table are all "not
|
|
20
|
+
* there". The text is compared exactly: nothing but the kernel writes these objects, and a
|
|
21
|
+
* normalisation that forgave whitespace would forgive it inside a string literal too — a guard
|
|
22
|
+
* keyed on a different entity type. An object an older kernel worded differently is re-created
|
|
23
|
+
* once.
|
|
24
|
+
*
|
|
25
|
+
* Four places ask, and all four come here, so "what a table is owed" is written once:
|
|
26
|
+
*
|
|
27
|
+
* - **runtime DDL** (`ctx.sql`'s after-DDL hook, #1811) repairs inside the operation's
|
|
28
|
+
* transaction, whenever the scope has any derived plan. The text guard already refuses the DDL
|
|
29
|
+
* known to move a stateful table's rows; a search-only or list-only table has no such guard,
|
|
30
|
+
* and a module may rebuild it — this puts its triggers and indexes back before the operation
|
|
31
|
+
* commits. A lost state column fails the operation, and the DDL with it.
|
|
32
|
+
* - **a migration** checks the state columns after its own SQL, inside its own transaction, so a
|
|
33
|
+
* migration that drops one rolls back with nothing applied. The last migration of a pass then
|
|
34
|
+
* repairs the derived objects, inside that same transaction: SQLite's ordinary
|
|
35
|
+
* create-copy-rename rebuild of a table drops its triggers and indexes with it, and the
|
|
36
|
+
* migrations that made them are journaled, so they never run again. Only after the last,
|
|
37
|
+
* because a rebuild split over two migrations copies its rows in the second, and a guard put
|
|
38
|
+
* back in between would refuse the archived ones — which also means such a split must ship
|
|
39
|
+
* in ONE deploy: split over two, the guard is back before the copy runs.
|
|
40
|
+
* - **a wake with nothing pending** (and every `migrateScope`) repairs the same way, once per
|
|
41
|
+
* instance. That is what reaches a scope stripped before this existed, whose migrations are
|
|
42
|
+
* all journaled and will never run again.
|
|
43
|
+
* - **a restore** repairs after the rows, inside the load's transaction, which may legitimately
|
|
44
|
+
* bring them archived or trashed.
|
|
45
|
+
*
|
|
46
|
+
* ## What the journal says a table is owed
|
|
47
|
+
*
|
|
48
|
+
* Every expectation comes from `_substrat_migrations`, not only from the declarations: a scope
|
|
49
|
+
* mid-upgrade has declared a trash its column migration has not added yet, and a restored dump
|
|
50
|
+
* carries the journal of the scope it came from. A column is owed once the migration that added
|
|
51
|
+
* it is journaled; a derived object once the migration that derived it, for the CURRENT
|
|
52
|
+
* declaration, is. An object from a withdrawn declaration is not this module's business.
|
|
53
|
+
*/
|
|
54
|
+
import { SubstratError } from '@substrat-run/contracts';
|
|
55
|
+
import { type EntityStatePlan } from './entity-state.js';
|
|
56
|
+
import { type ListIndexPlan } from './list-index.js';
|
|
57
|
+
import type { ScopedSql } from './scope-host.js';
|
|
58
|
+
import { type SearchIndexPlan } from './search-index.js';
|
|
59
|
+
/** A scope's derivation inputs: every registered module's plans, entity type → plan. */
|
|
60
|
+
export interface DerivedPlans {
|
|
61
|
+
readonly state: ReadonlyMap<string, EntityStatePlan>;
|
|
62
|
+
readonly lists: ReadonlyMap<string, ListIndexPlan>;
|
|
63
|
+
readonly search: ReadonlyMap<string, SearchIndexPlan>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A table without a state column its journal says it has (`internal`). `migration` is the
|
|
67
|
+
* journaled `<module>@<version>` that added the column — what a scope failing closed on a wake,
|
|
68
|
+
* where no migration ran, records as its failure.
|
|
69
|
+
*/
|
|
70
|
+
export declare class StateColumnLost extends SubstratError {
|
|
71
|
+
readonly migration: string;
|
|
72
|
+
constructor(migration: string, message: string);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* After runtime DDL, inside the operation's transaction: the same repair a migration pass makes.
|
|
76
|
+
* A lost state column throws `internal`, and the operation and its DDL roll back.
|
|
77
|
+
*/
|
|
78
|
+
export declare function afterRuntimeDdl(sql: ScopedSql, run: (ddl: string) => void, plans: DerivedPlans): void;
|
|
79
|
+
/** Whether a scope has anything derived for `afterRuntimeDdl` to keep — the hook is installed only then. */
|
|
80
|
+
export declare const derivesAnything: (plans: DerivedPlans) => boolean;
|
|
81
|
+
/**
|
|
82
|
+
* A migration's half, after its own SQL and inside its own transaction: the state columns are
|
|
83
|
+
* still where the journal put them. A migration that dropped one throws here and rolls back —
|
|
84
|
+
* the rows keep their state, and the scope fails closed naming the migration.
|
|
85
|
+
*/
|
|
86
|
+
export declare function assertEntityStateColumns(sql: ScopedSql, plans: DerivedPlans, after: string): void;
|
|
87
|
+
/**
|
|
88
|
+
* Check the state columns, then re-create every derived set the journal says a table is owed
|
|
89
|
+
* and the catalogue does not hold exactly as emitted. Run inside the caller's transaction, so a
|
|
90
|
+
* failure — a lost column, or DDL that will not apply — rolls the caller back whole.
|
|
91
|
+
*
|
|
92
|
+
* `run` executes a multi-statement script (a Durable Object splits it first). `after` names what
|
|
93
|
+
* left the schema this way, for the message. `absentTable: 'skip'` is a restore's: a table the
|
|
94
|
+
* dump did not carry is left absent, and owed nothing. Everywhere else an owed table that is
|
|
95
|
+
* gone has lost its state, and fails closed.
|
|
96
|
+
*/
|
|
97
|
+
export declare function repairDerivedObjects(sql: ScopedSql, run: (ddl: string) => void, plans: DerivedPlans, opts: {
|
|
98
|
+
readonly after: string;
|
|
99
|
+
readonly absentTable?: 'fail' | 'skip';
|
|
100
|
+
}): void;
|
|
101
|
+
/**
|
|
102
|
+
* Both adapters' migration pass, after one migration's SQL and inside its transaction: the
|
|
103
|
+
* column check, and — after the last migration of the pass — the derived objects repaired.
|
|
104
|
+
* `key` is `<module>@<version>`, which a failure names. A last entry the pass skips (journaled
|
|
105
|
+
* by a pass that ran first) needs nothing here: that pass repaired them after it.
|
|
106
|
+
*/
|
|
107
|
+
export declare function afterMigration(sql: ScopedSql, run: (ddl: string) => void, plans: DerivedPlans, key: string, last: boolean): void;
|
|
108
|
+
//# sourceMappingURL=derived-objects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derived-objects.d.ts","sourceRoot":"","sources":["../src/derived-objects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAQL,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAoD,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACvG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAA0D,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEjH,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACvD;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,aAAa;IAE9C,QAAQ,CAAC,SAAS,EAAE,MAAM;IAD5B,YACW,SAAS,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM,EAGhB;CACF;AAsHD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAErG;AAED,4GAA4G;AAC5G,eAAO,MAAM,eAAe,UAAW,YAAY,KAAG,OACiB,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAC1B,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GACvE,IAAI,CAON;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAC1B,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,GACZ,IAAI,CAGN"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the kernel derived onto a module's tables, checked and put back (#2090).
|
|
3
|
+
*
|
|
4
|
+
* | Object | Is | When it is missing or not what the kernel emits |
|
|
5
|
+
* |---|---|---|
|
|
6
|
+
* | `_substrat_archived_at` / `_substrat_trashed_at` (#119) | state | fail closed: it cannot be invented again |
|
|
7
|
+
* | the `born` / `moved` archive/trash guard triggers (#119) | derived | put back |
|
|
8
|
+
* | the derived list indexes, partial per view on a stateful table (#811) | derived | put back |
|
|
9
|
+
| the purge sweep's partial index on a trashable table with a horizon (#119) | derived | put back |
|
|
10
|
+
* | the search index and the three triggers keeping it in step (#827) | derived | rebuilt from the rows |
|
|
11
|
+
*
|
|
12
|
+
* The search triggers are a privacy fact, not only a freshness one: subject erasure rewrites a
|
|
13
|
+
* row's text and relies on the update and delete triggers to take the old text out of the index.
|
|
14
|
+
* Without them, erased text stays searchable.
|
|
15
|
+
*
|
|
16
|
+
* **An object is judged by its definition, not its name.** `sqlite_master` holds each one's
|
|
17
|
+
* CREATE statement as it was run, and the kernel knows the statement it emits
|
|
18
|
+
* (`DerivedObject`), so a same-named trigger that does nothing, an index on the wrong columns or
|
|
19
|
+
* with the wrong partial predicate, and an object filed under another table are all "not
|
|
20
|
+
* there". The text is compared exactly: nothing but the kernel writes these objects, and a
|
|
21
|
+
* normalisation that forgave whitespace would forgive it inside a string literal too — a guard
|
|
22
|
+
* keyed on a different entity type. An object an older kernel worded differently is re-created
|
|
23
|
+
* once.
|
|
24
|
+
*
|
|
25
|
+
* Four places ask, and all four come here, so "what a table is owed" is written once:
|
|
26
|
+
*
|
|
27
|
+
* - **runtime DDL** (`ctx.sql`'s after-DDL hook, #1811) repairs inside the operation's
|
|
28
|
+
* transaction, whenever the scope has any derived plan. The text guard already refuses the DDL
|
|
29
|
+
* known to move a stateful table's rows; a search-only or list-only table has no such guard,
|
|
30
|
+
* and a module may rebuild it — this puts its triggers and indexes back before the operation
|
|
31
|
+
* commits. A lost state column fails the operation, and the DDL with it.
|
|
32
|
+
* - **a migration** checks the state columns after its own SQL, inside its own transaction, so a
|
|
33
|
+
* migration that drops one rolls back with nothing applied. The last migration of a pass then
|
|
34
|
+
* repairs the derived objects, inside that same transaction: SQLite's ordinary
|
|
35
|
+
* create-copy-rename rebuild of a table drops its triggers and indexes with it, and the
|
|
36
|
+
* migrations that made them are journaled, so they never run again. Only after the last,
|
|
37
|
+
* because a rebuild split over two migrations copies its rows in the second, and a guard put
|
|
38
|
+
* back in between would refuse the archived ones — which also means such a split must ship
|
|
39
|
+
* in ONE deploy: split over two, the guard is back before the copy runs.
|
|
40
|
+
* - **a wake with nothing pending** (and every `migrateScope`) repairs the same way, once per
|
|
41
|
+
* instance. That is what reaches a scope stripped before this existed, whose migrations are
|
|
42
|
+
* all journaled and will never run again.
|
|
43
|
+
* - **a restore** repairs after the rows, inside the load's transaction, which may legitimately
|
|
44
|
+
* bring them archived or trashed.
|
|
45
|
+
*
|
|
46
|
+
* ## What the journal says a table is owed
|
|
47
|
+
*
|
|
48
|
+
* Every expectation comes from `_substrat_migrations`, not only from the declarations: a scope
|
|
49
|
+
* mid-upgrade has declared a trash its column migration has not added yet, and a restored dump
|
|
50
|
+
* carries the journal of the scope it came from. A column is owed once the migration that added
|
|
51
|
+
* it is journaled; a derived object once the migration that derived it, for the CURRENT
|
|
52
|
+
* declaration, is. An object from a withdrawn declaration is not this module's business.
|
|
53
|
+
*/
|
|
54
|
+
import { SubstratError } from '@substrat-run/contracts';
|
|
55
|
+
import { entityStateGuardVersion, entityStateTriggerDdl, entityStateTriggerObjects, purgeIndexObjects, purgeIndexRepairDdl, purgeIndexVersion, stateColumnVersionsOf, } from './entity-state.js';
|
|
56
|
+
import { listIndexDdl, listIndexObjects, listIndexVersion } from './list-index.js';
|
|
57
|
+
import { searchIndexDdl, searchIndexObjects, searchIndexVersion } from './search-index.js';
|
|
58
|
+
/**
|
|
59
|
+
* A table without a state column its journal says it has (`internal`). `migration` is the
|
|
60
|
+
* journaled `<module>@<version>` that added the column — what a scope failing closed on a wake,
|
|
61
|
+
* where no migration ran, records as its failure.
|
|
62
|
+
*/
|
|
63
|
+
export class StateColumnLost extends SubstratError {
|
|
64
|
+
migration;
|
|
65
|
+
constructor(migration, message) {
|
|
66
|
+
super('internal', message);
|
|
67
|
+
this.migration = migration;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** `main`'s own catalogue by lowercased name — so a same-named temp object cannot stand in. */
|
|
71
|
+
function catalogueOf(sql) {
|
|
72
|
+
return new Map(sql
|
|
73
|
+
.query(`SELECT type, name, tbl_name, sql FROM main.sqlite_master`)
|
|
74
|
+
.map((r) => [r.name.toLowerCase(), { type: r.type, table: r.tbl_name, sql: r.sql }]));
|
|
75
|
+
}
|
|
76
|
+
/** The first object of `d` the catalogue does not hold exactly as the kernel emits it. */
|
|
77
|
+
const wrongIn = (catalogue, d) => d.objects.find((o) => {
|
|
78
|
+
const found = catalogue.get(o.name.toLowerCase());
|
|
79
|
+
return (found?.type !== o.type ||
|
|
80
|
+
found.table.toLowerCase() !== o.table.toLowerCase() ||
|
|
81
|
+
found.sql !== o.sql);
|
|
82
|
+
});
|
|
83
|
+
const columnsOn = (sql, table) => new Set(sql.query(`SELECT name FROM pragma_table_info(?, 'main')`, [table]).map((r) => r.name.toLowerCase()));
|
|
84
|
+
function expectations(sql, plans) {
|
|
85
|
+
const journal = new Set(sql.query(`SELECT module_id, version FROM _substrat_migrations`).map((r) => `${r.module_id}@${r.version}`));
|
|
86
|
+
const applied = (moduleId, version) => journal.has(`${moduleId}@${version}`);
|
|
87
|
+
const byTable = new Map();
|
|
88
|
+
const entry = (table) => {
|
|
89
|
+
const key = table.toLowerCase();
|
|
90
|
+
if (!byTable.has(key))
|
|
91
|
+
byTable.set(key, { table, derived: [] });
|
|
92
|
+
return byTable.get(key);
|
|
93
|
+
};
|
|
94
|
+
for (const plan of plans.state.values()) {
|
|
95
|
+
const e = entry(plan.table);
|
|
96
|
+
const columns = stateColumnVersionsOf(plan)
|
|
97
|
+
.filter((c) => applied(plan.moduleId, c.version))
|
|
98
|
+
.map((c) => ({ column: c.column, migration: `${plan.moduleId}@${c.version}` }));
|
|
99
|
+
if (columns.length)
|
|
100
|
+
e.columns = columns;
|
|
101
|
+
if (applied(plan.moduleId, entityStateGuardVersion(plan))) {
|
|
102
|
+
e.derived.push({ objects: entityStateTriggerObjects(plan), ddl: entityStateTriggerDdl(plan) });
|
|
103
|
+
}
|
|
104
|
+
const purge = purgeIndexObjects(plan);
|
|
105
|
+
if (purge.length && applied(plan.moduleId, purgeIndexVersion(plan))) {
|
|
106
|
+
e.derived.push({ objects: purge, ddl: purgeIndexRepairDdl(plan) });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const plan of plans.lists.values()) {
|
|
110
|
+
const objects = listIndexObjects(plan);
|
|
111
|
+
if (objects.length && applied(plan.moduleId, listIndexVersion(plan))) {
|
|
112
|
+
entry(plan.table).derived.push({ objects, ddl: listIndexDdl(plan) });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// The DDL ends in a full `rebuild` from the content table, which is also what repairs an index
|
|
116
|
+
// that went stale while its triggers were gone or wrong.
|
|
117
|
+
for (const plan of plans.search.values()) {
|
|
118
|
+
if (applied(plan.moduleId, searchIndexVersion(plan))) {
|
|
119
|
+
entry(plan.table).derived.push({ objects: searchIndexObjects(plan), ddl: searchIndexDdl(plan) });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return [...byTable.values()];
|
|
123
|
+
}
|
|
124
|
+
const lost = (after, table, what, migration) => {
|
|
125
|
+
throw new StateColumnLost(migration, `${after} left '${table.table}' without ${what} — its archive/trash guarantees depend on it; nothing was changed`);
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* The state half: the table holds every column its journal added. Throws `StateColumnLost` on
|
|
129
|
+
* the first one gone. An absent table counts as losing them all, unless `absentTable` is
|
|
130
|
+
* `'skip'` — a restore's case, where a dump that did not carry a table is not made to invent one.
|
|
131
|
+
*/
|
|
132
|
+
function assertColumns(sql, catalogue, table, after, absentTable = 'fail') {
|
|
133
|
+
if (!table.columns)
|
|
134
|
+
return;
|
|
135
|
+
if (catalogue.get(table.table.toLowerCase())?.type !== 'table') {
|
|
136
|
+
if (absentTable === 'skip')
|
|
137
|
+
return;
|
|
138
|
+
lost(after, table, 'its table', table.columns[0].migration);
|
|
139
|
+
}
|
|
140
|
+
const present = columnsOn(sql, table.table);
|
|
141
|
+
for (const c of table.columns)
|
|
142
|
+
if (!present.has(c.column))
|
|
143
|
+
lost(after, table, c.column, c.migration);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* After runtime DDL, inside the operation's transaction: the same repair a migration pass makes.
|
|
147
|
+
* A lost state column throws `internal`, and the operation and its DDL roll back.
|
|
148
|
+
*/
|
|
149
|
+
export function afterRuntimeDdl(sql, run, plans) {
|
|
150
|
+
repairDerivedObjects(sql, run, plans, { after: 'runtime DDL' });
|
|
151
|
+
}
|
|
152
|
+
/** Whether a scope has anything derived for `afterRuntimeDdl` to keep — the hook is installed only then. */
|
|
153
|
+
export const derivesAnything = (plans) => plans.state.size > 0 || plans.lists.size > 0 || plans.search.size > 0;
|
|
154
|
+
/**
|
|
155
|
+
* A migration's half, after its own SQL and inside its own transaction: the state columns are
|
|
156
|
+
* still where the journal put them. A migration that dropped one throws here and rolls back —
|
|
157
|
+
* the rows keep their state, and the scope fails closed naming the migration.
|
|
158
|
+
*/
|
|
159
|
+
export function assertEntityStateColumns(sql, plans, after) {
|
|
160
|
+
if (plans.state.size === 0)
|
|
161
|
+
return; // most scopes: nothing holds state, so no read at all
|
|
162
|
+
const catalogue = catalogueOf(sql);
|
|
163
|
+
for (const table of expectations(sql, plans))
|
|
164
|
+
assertColumns(sql, catalogue, table, after);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check the state columns, then re-create every derived set the journal says a table is owed
|
|
168
|
+
* and the catalogue does not hold exactly as emitted. Run inside the caller's transaction, so a
|
|
169
|
+
* failure — a lost column, or DDL that will not apply — rolls the caller back whole.
|
|
170
|
+
*
|
|
171
|
+
* `run` executes a multi-statement script (a Durable Object splits it first). `after` names what
|
|
172
|
+
* left the schema this way, for the message. `absentTable: 'skip'` is a restore's: a table the
|
|
173
|
+
* dump did not carry is left absent, and owed nothing. Everywhere else an owed table that is
|
|
174
|
+
* gone has lost its state, and fails closed.
|
|
175
|
+
*/
|
|
176
|
+
export function repairDerivedObjects(sql, run, plans, opts) {
|
|
177
|
+
const catalogue = catalogueOf(sql);
|
|
178
|
+
for (const table of expectations(sql, plans)) {
|
|
179
|
+
assertColumns(sql, catalogue, table, opts.after, opts.absentTable);
|
|
180
|
+
if (catalogue.get(table.table.toLowerCase())?.type !== 'table')
|
|
181
|
+
continue;
|
|
182
|
+
for (const d of table.derived)
|
|
183
|
+
if (wrongIn(catalogue, d))
|
|
184
|
+
run(d.ddl);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Both adapters' migration pass, after one migration's SQL and inside its transaction: the
|
|
189
|
+
* column check, and — after the last migration of the pass — the derived objects repaired.
|
|
190
|
+
* `key` is `<module>@<version>`, which a failure names. A last entry the pass skips (journaled
|
|
191
|
+
* by a pass that ran first) needs nothing here: that pass repaired them after it.
|
|
192
|
+
*/
|
|
193
|
+
export function afterMigration(sql, run, plans, key, last) {
|
|
194
|
+
if (last)
|
|
195
|
+
repairDerivedObjects(sql, run, plans, { after: `migration ${key}` });
|
|
196
|
+
else
|
|
197
|
+
assertEntityStateColumns(sql, plans, `migration ${key}`);
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=derived-objects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derived-objects.js","sourceRoot":"","sources":["../src/derived-objects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type PlatformOutcomeEvent, type PlatformRequestFailure, type PlatformRequestId, type PlatformRequestStatus, type ScopeId, type SendEmailRequest, type TenantId } from '@substrat-run/contracts';
|
|
2
|
+
import type { OperationContext } from './scope-host.js';
|
|
3
|
+
import type { SwitchSql } from './system-switch.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ask the platform to send one email, inside the operation (#2102).
|
|
6
|
+
*
|
|
7
|
+
* The send is a platform intent: a row written in this operation's transaction, so an
|
|
8
|
+
* operation that throws afterwards sends nothing, and one that commits is delivered by the
|
|
9
|
+
* platform's drain — retried while the provider defers it, and reported back as an
|
|
10
|
+
* `email.sent`, `email.refused` or `email.dead-lettered` event on `mail.about` (or on the
|
|
11
|
+
* intent, when the mail names no entity). The returned id is the `request` those events carry.
|
|
12
|
+
*
|
|
13
|
+
* Name the recipient's `subjectId` when they are a person in your data: the row holds their
|
|
14
|
+
* address and the message, and that id is how a subject erasure finds and cancels it.
|
|
15
|
+
*
|
|
16
|
+
* Parsed here rather than at drain time, so a malformed address fails the operation that wrote
|
|
17
|
+
* it, in front of the person who typed it, instead of a refusal event minutes later. Needs the
|
|
18
|
+
* vertical's `emailSender` grant, as the relay always has; without it the send is refused.
|
|
19
|
+
*/
|
|
20
|
+
export declare function requestEmail(ctx: Pick<OperationContext, 'requestPlatform'>, mail: Omit<SendEmailRequest, 'piiClass'>): PlatformRequestId;
|
|
21
|
+
/** What the scope stamps on an outcome event it writes: the drain names the rest. */
|
|
22
|
+
export interface OutcomeEventStamp {
|
|
23
|
+
tenantId: TenantId;
|
|
24
|
+
scopeId: ScopeId;
|
|
25
|
+
/** ISO 8601 — the settle's instant. */
|
|
26
|
+
now: string;
|
|
27
|
+
mintEventId(ms: number): string;
|
|
28
|
+
/** The deploy writing it, for the outbox's `version` column. */
|
|
29
|
+
version: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface PlatformRequestSettle {
|
|
32
|
+
status: PlatformRequestStatus;
|
|
33
|
+
result?: unknown;
|
|
34
|
+
lastError?: string | null;
|
|
35
|
+
failure?: PlatformRequestFailure | null;
|
|
36
|
+
/** #2102: the event this settle writes into the scope, when it moves the row out of `pending`. */
|
|
37
|
+
event?: PlatformOutcomeEvent | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Journal a platform-request outcome, and write its outcome event (#2102) — one step, so both
|
|
41
|
+
* adapters say the same thing and the event commits with the settle or not at all. Run it in a
|
|
42
|
+
* transaction.
|
|
43
|
+
*
|
|
44
|
+
* **Compare-and-set on `pending` (#1600 review).** The drain reads pending rows, runs a handler,
|
|
45
|
+
* then settles — and between the read and the settle a subject erasure can redact the row.
|
|
46
|
+
* Settling by `id` alone let that stale pass overwrite the redaction and write a provider's
|
|
47
|
+
* reply, which can quote the person, back into `last_error`. A settle that finds the row
|
|
48
|
+
* already terminal does nothing, deliberately silently — throwing would make the drain's
|
|
49
|
+
* blanket catch retry a row that is correctly over. The event follows the same rule: written
|
|
50
|
+
* only by the settle that actually moved the row, so a repeated settle never writes it twice.
|
|
51
|
+
*
|
|
52
|
+
* The event is refused (before anything is written) on a `pending` settle — an outcome event
|
|
53
|
+
* says the request is over — and when its `request` is not the row being settled.
|
|
54
|
+
*/
|
|
55
|
+
export declare function settlePlatformRequestIn(db: SwitchSql, id: PlatformRequestId, outcome: PlatformRequestSettle, stamp: OutcomeEventStamp): void;
|
|
56
|
+
//# sourceMappingURL=email-intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-intent.d.ts","sourceRoot":"","sources":["../src/email-intent.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,EAC9C,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GACvC,iBAAiB,CAKnB;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACxC,kGAAkG;IAClG,KAAK,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,SAAS,EACb,EAAE,EAAE,iBAAiB,EACrB,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,iBAAiB,GACvB,IAAI,CAsCN"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { SEND_EMAIL_KIND, domainEvent, eventId, platformOutcomeEvent, sendEmailRequest, } from '@substrat-run/contracts';
|
|
2
|
+
import { KERNEL_ACTOR, kernelOutboxInsertSql } from './kernel-outbox.js';
|
|
3
|
+
/**
|
|
4
|
+
* Ask the platform to send one email, inside the operation (#2102).
|
|
5
|
+
*
|
|
6
|
+
* The send is a platform intent: a row written in this operation's transaction, so an
|
|
7
|
+
* operation that throws afterwards sends nothing, and one that commits is delivered by the
|
|
8
|
+
* platform's drain — retried while the provider defers it, and reported back as an
|
|
9
|
+
* `email.sent`, `email.refused` or `email.dead-lettered` event on `mail.about` (or on the
|
|
10
|
+
* intent, when the mail names no entity). The returned id is the `request` those events carry.
|
|
11
|
+
*
|
|
12
|
+
* Name the recipient's `subjectId` when they are a person in your data: the row holds their
|
|
13
|
+
* address and the message, and that id is how a subject erasure finds and cancels it.
|
|
14
|
+
*
|
|
15
|
+
* Parsed here rather than at drain time, so a malformed address fails the operation that wrote
|
|
16
|
+
* it, in front of the person who typed it, instead of a refusal event minutes later. Needs the
|
|
17
|
+
* vertical's `emailSender` grant, as the relay always has; without it the send is refused.
|
|
18
|
+
*/
|
|
19
|
+
export function requestEmail(ctx, mail) {
|
|
20
|
+
const parsed = sendEmailRequest.parse(mail);
|
|
21
|
+
// Classified like an event naming the same person, so a subject erasure reaches the row.
|
|
22
|
+
const payload = parsed.subjectId ? { ...parsed, piiClass: 'direct' } : parsed;
|
|
23
|
+
return ctx.requestPlatform({ kind: SEND_EMAIL_KIND, payload });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Journal a platform-request outcome, and write its outcome event (#2102) — one step, so both
|
|
27
|
+
* adapters say the same thing and the event commits with the settle or not at all. Run it in a
|
|
28
|
+
* transaction.
|
|
29
|
+
*
|
|
30
|
+
* **Compare-and-set on `pending` (#1600 review).** The drain reads pending rows, runs a handler,
|
|
31
|
+
* then settles — and between the read and the settle a subject erasure can redact the row.
|
|
32
|
+
* Settling by `id` alone let that stale pass overwrite the redaction and write a provider's
|
|
33
|
+
* reply, which can quote the person, back into `last_error`. A settle that finds the row
|
|
34
|
+
* already terminal does nothing, deliberately silently — throwing would make the drain's
|
|
35
|
+
* blanket catch retry a row that is correctly over. The event follows the same rule: written
|
|
36
|
+
* only by the settle that actually moved the row, so a repeated settle never writes it twice.
|
|
37
|
+
*
|
|
38
|
+
* The event is refused (before anything is written) on a `pending` settle — an outcome event
|
|
39
|
+
* says the request is over — and when its `request` is not the row being settled.
|
|
40
|
+
*/
|
|
41
|
+
export function settlePlatformRequestIn(db, id, outcome, stamp) {
|
|
42
|
+
const event = outcome.event == null ? null : platformOutcomeEvent.parse(outcome.event);
|
|
43
|
+
if (event && outcome.status === 'pending') {
|
|
44
|
+
throw new Error(`settle of platform request ${id}: an outcome event needs a terminal status, not 'pending'`);
|
|
45
|
+
}
|
|
46
|
+
if (event && event.payload.request !== id) {
|
|
47
|
+
throw new Error(`settle of platform request ${id}: the outcome event names request ${event.payload.request}`);
|
|
48
|
+
}
|
|
49
|
+
const was = db.all('SELECT status FROM _substrat_platform_requests WHERE id = ?', id)[0]?.status;
|
|
50
|
+
db.run(`UPDATE _substrat_platform_requests
|
|
51
|
+
SET status = ?, result = COALESCE(?, result), last_error = ?, last_failure = ?,
|
|
52
|
+
attempts = attempts + 1, settled_at = ?
|
|
53
|
+
WHERE id = ? AND status = 'pending'`, outcome.status, outcome.result === undefined ? null : JSON.stringify(outcome.result), outcome.lastError ?? null, outcome.failure == null ? null : JSON.stringify(outcome.failure), outcome.status === 'pending' ? null : stamp.now, id);
|
|
54
|
+
if (!event || was !== 'pending')
|
|
55
|
+
return;
|
|
56
|
+
const full = domainEvent.parse({
|
|
57
|
+
id: eventId.parse(stamp.mintEventId(Date.parse(stamp.now))),
|
|
58
|
+
type: event.type,
|
|
59
|
+
schemaVersion: 1,
|
|
60
|
+
occurredAt: stamp.now,
|
|
61
|
+
tenantId: stamp.tenantId,
|
|
62
|
+
scopeId: stamp.scopeId,
|
|
63
|
+
actor: KERNEL_ACTOR,
|
|
64
|
+
entity: event.entity,
|
|
65
|
+
piiClass: 'none',
|
|
66
|
+
payload: event.payload,
|
|
67
|
+
});
|
|
68
|
+
const st = kernelOutboxInsertSql(full, stamp.version);
|
|
69
|
+
db.run(st.sql, ...st.params);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=email-intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-intent.js","sourceRoot":"","sources":["../src/email-intent.ts"],"names":[],"mappings":"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"}
|
package/dist/entity-edges.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DomainEventInput, type Instant } from '@substrat-run/contracts';
|
|
1
|
+
import { type DomainEventInput, type EntityRef, type Instant } from '@substrat-run/contracts';
|
|
2
2
|
import type { OperationContext, ScopedSql } from './scope-host.js';
|
|
3
3
|
/**
|
|
4
4
|
* `ctx.link` and `ctx.relink` (K-16, #1864), written once — both adapters hand the same
|
|
@@ -29,6 +29,11 @@ export interface EntityEdgeDeps {
|
|
|
29
29
|
emit: (event: DomainEventInput) => void;
|
|
30
30
|
/** K-42's read-only refusal, for the effecting verbs. */
|
|
31
31
|
assertWrites: (verb: string) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Is this entity out of reach as a parent (#119) — its type declares trash, and it is binned or
|
|
34
|
+
* missing (`isUnreachableParent`)? Refused by `link` and as `relink`'s `to`, one answer for both.
|
|
35
|
+
*/
|
|
36
|
+
isUnreachableParent: (entity: EntityRef) => boolean;
|
|
32
37
|
}
|
|
33
38
|
export type EntityEdgeVerbs = Pick<OperationContext, 'link' | 'relink'>;
|
|
34
39
|
export declare function createEntityEdgeVerbs(deps: EntityEdgeDeps): EntityEdgeVerbs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-edges.d.ts","sourceRoot":"","sources":["../src/entity-edges.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"entity-edges.d.ts","sourceRoot":"","sources":["../src/entity-edges.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEnE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,GAAG,EAAE,SAAS,CAAC;IACf,mFAAmF;IACnF,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,kEAAkE;IAClE,GAAG,EAAE,OAAO,CAAC;IACb,2EAA2E;IAC3E,IAAI,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxC,yDAAyD;IACzD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;;OAGG;IACH,mBAAmB,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC;CACrD;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;AAExE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,cAAc,GAAG,eAAe,CA4J3E"}
|
package/dist/entity-edges.js
CHANGED
|
@@ -7,6 +7,17 @@ export function createEntityEdgeVerbs(deps) {
|
|
|
7
7
|
`(declare it in a module manifest's entityRelations)`);
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* A binned entity is gone from everyone's view until it is restored (#119), so nothing new
|
|
12
|
+
* hangs off it. The refusal is the one a MISSING parent of the same type gets, word for word:
|
|
13
|
+
* `link` checks no permission, so a refusal that named the trash would tell a caller who may not
|
|
14
|
+
* see the parent that it exists. An ARCHIVED parent passes: archived is filed away and readable.
|
|
15
|
+
*/
|
|
16
|
+
const assertParentReachable = (verb, parent) => {
|
|
17
|
+
if (deps.isUnreachableParent(parent)) {
|
|
18
|
+
throw substratError('not_found', `${verb}: ${parent.entityType}:${parent.entityId} not found`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
10
21
|
/**
|
|
11
22
|
* Refuse when `ancestorCandidate` is `child` itself, or already lies beneath `child` in the
|
|
12
23
|
* live parent graph — shared by `link` (candidate = the new `parent`) and `relink` (candidate
|
|
@@ -54,6 +65,7 @@ export function createEntityEdgeVerbs(deps) {
|
|
|
54
65
|
const c = entityObjectRef(child, 'ctx.link'); // #1856: an edge the walk can read back
|
|
55
66
|
const p = entityObjectRef(parent, 'ctx.link');
|
|
56
67
|
assertDeclared('ctx.link', child, parent);
|
|
68
|
+
assertParentReachable('ctx.link', parent);
|
|
57
69
|
// Refuse a parent that is the child itself or already beneath it (#1875) — same question
|
|
58
70
|
// `relink` asks of `to`, same walk.
|
|
59
71
|
assertNoCycle('ctx.link', 'link', c, p);
|
|
@@ -80,6 +92,7 @@ export function createEntityEdgeVerbs(deps) {
|
|
|
80
92
|
// whether or not its relation is still declared, so an edge that grants must stay
|
|
81
93
|
// movable. What `from` must be is an edge that exists.
|
|
82
94
|
assertDeclared('ctx.relink', child, to);
|
|
95
|
+
assertParentReachable('ctx.relink', to);
|
|
83
96
|
// The walk's own `live` predicate, so "is a parent" means what a check means by it.
|
|
84
97
|
const live = deps.sql.query(`SELECT 1 AS live FROM _substrat_tuples
|
|
85
98
|
WHERE subject = ? AND relation = 'parent' AND object = ? AND ${liveTupleSql()}`, [c, f, deps.now]);
|
package/dist/entity-edges.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-edges.js","sourceRoot":"","sources":["../src/entity-edges.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,aAAa,GAId,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-edges.js","sourceRoot":"","sources":["../src/entity-edges.ts"],"names":[],"mappings":"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"}
|