@substrat-run/kernel 0.118.0 → 0.120.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/capability.d.ts +22 -1
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +20 -0
- package/dist/capability.js.map +1 -1
- package/dist/connector-calls.d.ts +205 -0
- package/dist/connector-calls.d.ts.map +1 -0
- package/dist/connector-calls.js +192 -0
- package/dist/connector-calls.js.map +1 -0
- package/dist/idempotency.d.ts +7 -1
- package/dist/idempotency.d.ts.map +1 -1
- package/dist/idempotency.js +8 -2
- package/dist/idempotency.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/peer.d.ts +132 -0
- package/dist/peer.d.ts.map +1 -0
- package/dist/peer.js +177 -0
- package/dist/peer.js.map +1 -0
- package/dist/permission-checker.d.ts +1 -1
- package/dist/permission-checker.d.ts.map +1 -1
- package/dist/permission-checker.js +4 -1
- package/dist/permission-checker.js.map +1 -1
- package/dist/platform-sweep.d.ts +116 -1
- package/dist/platform-sweep.d.ts.map +1 -1
- package/dist/platform-sweep.js +209 -0
- package/dist/platform-sweep.js.map +1 -1
- package/dist/scope-host.d.ts +233 -10
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js.map +1 -1
- package/dist/system-switch.d.ts +38 -0
- package/dist/system-switch.d.ts.map +1 -1
- package/dist/system-switch.js +50 -3
- package/dist/system-switch.js.map +1 -1
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +30 -3
- package/dist/timeline.js.map +1 -1
- package/dist/vertical-events.d.ts +162 -0
- package/dist/vertical-events.d.ts.map +1 -0
- package/dist/vertical-events.js +348 -0
- package/dist/vertical-events.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-vertical event delivery (#1705): the half both adapters share.
|
|
3
|
+
*
|
|
4
|
+
* A vertical exports an event type by declaring it (`events.exports`), and another vertical of
|
|
5
|
+
* the same tenant imports it by declaring `events.consumes: [{ from, … }]`. Nothing is written
|
|
6
|
+
* when an exported event commits. The producer's outbox is retained and is already the durable
|
|
7
|
+
* record, so an export is a READ over it, and a consumer's progress is a watermark the consumer
|
|
8
|
+
* keeps. Each platform sweep pass then:
|
|
9
|
+
*
|
|
10
|
+
* 1. asks the consumer scope for its watermark on this producer (`IMPORT_CURSORS_SQL`);
|
|
11
|
+
* 2. reads the producer's outbox after it, in the producer's own scope, through its own
|
|
12
|
+
* running `exports` (`exportReadQuery` + `planExportBatch`);
|
|
13
|
+
* 3. hands the batch to the consumer, which journals each event and runs its handlers,
|
|
14
|
+
* then moves the watermark in the same store (`IMPORT_CURSOR_ADVANCE_SQL`).
|
|
15
|
+
*
|
|
16
|
+
* The tables here are the consumer's half of that, and they hold NO payload. The event's only
|
|
17
|
+
* copy stays in the producer's outbox, where a subject erasure already reaches. An imported
|
|
18
|
+
* payload would be one more resting copy for the erasure to find, and #1600 and #1632 were
|
|
19
|
+
* each about a copy it had missed. What the consumer keeps is the envelope. That is enough to
|
|
20
|
+
* dedupe, to show a dead letter, and to resolve a `caused_by` that points across the edge.
|
|
21
|
+
*
|
|
22
|
+
* Shared from here for `CAPABILITY_DDL`'s reason: the self-host builds these tables and
|
|
23
|
+
* production builds them, and one definition is what keeps the two identical.
|
|
24
|
+
* `lint:spine-ddl` holds each adapter to including it.
|
|
25
|
+
*/
|
|
26
|
+
import { EXPORT_HOP_CAP, } from '@substrat-run/contracts';
|
|
27
|
+
import { domainEventOf } from './outbox-event.js';
|
|
28
|
+
export const VERTICAL_EVENTS_DDL = `
|
|
29
|
+
-- #1705: every event this scope has received from another vertical. The envelope only, and
|
|
30
|
+
-- never the payload. The producer's outbox keeps the one copy, where erasure already reaches.
|
|
31
|
+
CREATE TABLE IF NOT EXISTS _substrat_imports (
|
|
32
|
+
-- The PRODUCER's event id. A ULID the producer's host minted, so it is unique across
|
|
33
|
+
-- every source this scope imports from, and it is the dedupe key: a batch delivered
|
|
34
|
+
-- twice finds its events here and its handlers' journal rows in _substrat_deliveries.
|
|
35
|
+
event_id TEXT PRIMARY KEY,
|
|
36
|
+
source_scope_id TEXT NOT NULL,
|
|
37
|
+
source_vertical TEXT NOT NULL,
|
|
38
|
+
type TEXT NOT NULL,
|
|
39
|
+
schema_version INTEGER NOT NULL,
|
|
40
|
+
occurred_at TEXT NOT NULL,
|
|
41
|
+
entity_type TEXT NOT NULL,
|
|
42
|
+
entity_id TEXT NOT NULL,
|
|
43
|
+
-- How many vertical boundaries this event's cause chain has crossed, this one included.
|
|
44
|
+
-- An export read in THIS scope counts from here when one of its own events was caused by
|
|
45
|
+
-- an import, which is how a loop between two verticals is stopped.
|
|
46
|
+
hops INTEGER NOT NULL,
|
|
47
|
+
-- NULL: released, and handed to this scope's handlers. Otherwise the producer's reason
|
|
48
|
+
-- for NOT releasing it ('pii', 'version', 'cascade', 'undecodable'). Such a row was
|
|
49
|
+
-- named but never carried.
|
|
50
|
+
withheld TEXT,
|
|
51
|
+
imported_at TEXT NOT NULL
|
|
52
|
+
);
|
|
53
|
+
-- #1705: how far this scope has read each producer. Written in the same store as the
|
|
54
|
+
-- handlers' writes, so a restore of this scope rewinds the watermark with the data and the
|
|
55
|
+
-- producer re-delivers what the restore undid. A watermark the platform held instead would
|
|
56
|
+
-- skip it.
|
|
57
|
+
CREATE TABLE IF NOT EXISTS _substrat_import_cursors (
|
|
58
|
+
source_scope_id TEXT PRIMARY KEY,
|
|
59
|
+
source_vertical TEXT NOT NULL,
|
|
60
|
+
-- The id of the last producer row the reads walked (released or withheld). NULL is not
|
|
61
|
+
-- stored: a producer with no row here has never been read, which the absence says.
|
|
62
|
+
cursor TEXT NOT NULL,
|
|
63
|
+
updated_at TEXT NOT NULL
|
|
64
|
+
);
|
|
65
|
+
-- #1705: the export read, on the PRODUCER side. It asks for a few types after a watermark,
|
|
66
|
+
-- in id order. Without this index SQLite walks the primary key from the watermark forward and
|
|
67
|
+
-- steps over every row of every other type. A consumer that is caught up would then pay, on
|
|
68
|
+
-- every pass, for all the unexported activity since the last exported event. With it, the read
|
|
69
|
+
-- seeks each type's range and costs what it returns.
|
|
70
|
+
CREATE INDEX IF NOT EXISTS _substrat_outbox_type_id ON _substrat_outbox (type, id);
|
|
71
|
+
`;
|
|
72
|
+
/**
|
|
73
|
+
* The producer-side read: rows of the given types after the watermark, oldest first.
|
|
74
|
+
*
|
|
75
|
+
* `types` is already the intersection of what the consumer wants and what this scope exports.
|
|
76
|
+
* The caller computes it from its own registered manifests, never from the request alone, so
|
|
77
|
+
* nothing here can widen what leaves.
|
|
78
|
+
*/
|
|
79
|
+
export function exportReadQuery(types, after, limit) {
|
|
80
|
+
if (types.length === 0)
|
|
81
|
+
throw new Error('exportReadQuery: no types to read');
|
|
82
|
+
const placeholders = types.map(() => '?').join(', ');
|
|
83
|
+
return {
|
|
84
|
+
sql: `SELECT * FROM _substrat_outbox WHERE type IN (${placeholders})` +
|
|
85
|
+
(after === null ? '' : ' AND id > ?') +
|
|
86
|
+
' ORDER BY id LIMIT ?',
|
|
87
|
+
params: [...types, ...(after === null ? [] : [after]), limit],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* How many vertical boundaries the cause chain of an outbox row with this `caused_by` has
|
|
92
|
+
* already crossed. The answer is the `hops` of the NEAREST import in its chain, or no row when
|
|
93
|
+
* the chain never crossed one.
|
|
94
|
+
*
|
|
95
|
+
* Walks `caused_by` backwards through this scope's outbox. The chain ends at an import, which
|
|
96
|
+
* is in `_substrat_imports` and not in the outbox, or at an event nothing caused. Bounded at 64
|
|
97
|
+
* steps: a chain that long inside ONE scope has already been cut many times by the per-call
|
|
98
|
+
* cascade cap. Past the bound, the row counts as having crossed nothing. That under-counts, and
|
|
99
|
+
* it is stated rather than hidden. It can only let a loop run for more passes than the cap
|
|
100
|
+
* intends. It cannot release an event that should not cross.
|
|
101
|
+
*/
|
|
102
|
+
export const EXPORT_HOPS_SQL = `
|
|
103
|
+
WITH RECURSIVE chain(id, depth) AS (
|
|
104
|
+
SELECT ?, 0
|
|
105
|
+
UNION ALL
|
|
106
|
+
SELECT o.caused_by, c.depth + 1
|
|
107
|
+
FROM chain c JOIN _substrat_outbox o ON o.id = c.id
|
|
108
|
+
WHERE o.caused_by IS NOT NULL AND c.depth < 64
|
|
109
|
+
)
|
|
110
|
+
SELECT i.hops AS hops FROM chain c JOIN _substrat_imports i ON i.event_id = c.id
|
|
111
|
+
ORDER BY c.depth LIMIT 1
|
|
112
|
+
`;
|
|
113
|
+
/**
|
|
114
|
+
* The producer-side decision, over rows already read: what is released, what is withheld and
|
|
115
|
+
* why, and the watermark the consumer should hold afterwards.
|
|
116
|
+
*
|
|
117
|
+
* Pure, so both adapters make the same decision about the same rows. Authority is NOT decided
|
|
118
|
+
* here. The adapter checks the consumer's principal before it reads anything, and a missing
|
|
119
|
+
* key pauses the edge without walking a row. Every reason this function withholds for is
|
|
120
|
+
* fixed at write time (the classification, the version, the cause chain, the stored text),
|
|
121
|
+
* so stepping the watermark past a withheld row can never lose an event that a later pass
|
|
122
|
+
* would have released.
|
|
123
|
+
*/
|
|
124
|
+
export function planExportBatch(input) {
|
|
125
|
+
const cap = input.hopCap ?? EXPORT_HOP_CAP;
|
|
126
|
+
const events = [];
|
|
127
|
+
const withheld = [];
|
|
128
|
+
const withhold = (row, reason) => {
|
|
129
|
+
withheld.push({
|
|
130
|
+
id: row.id,
|
|
131
|
+
type: row.type,
|
|
132
|
+
schemaVersion: row.schema_version,
|
|
133
|
+
occurredAt: row.occurred_at,
|
|
134
|
+
entity: { entityType: row.entity_type, entityId: row.entity_id },
|
|
135
|
+
reason,
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
for (const row of input.rows) {
|
|
139
|
+
// Classification first: a classified row is withheld whatever else is true of it. The
|
|
140
|
+
// other reasons would also withhold it, but the one a reader is told must be the one
|
|
141
|
+
// that answers "why did this not cross".
|
|
142
|
+
if (row.pii_class !== 'none') {
|
|
143
|
+
withhold(row, 'pii');
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (input.wanted.get(row.type) !== row.schema_version) {
|
|
147
|
+
withhold(row, 'version');
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const hops = input.hopsBefore(row) + 1;
|
|
151
|
+
if (hops > cap) {
|
|
152
|
+
withhold(row, 'cascade');
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
let decoded;
|
|
156
|
+
try {
|
|
157
|
+
decoded = domainEventOf(row);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// The reason and nothing else. The decode's own message names columns, and it
|
|
161
|
+
// must not travel: it is written into the consumer's dead letter, in another
|
|
162
|
+
// vertical's scope.
|
|
163
|
+
withhold(row, 'undecodable');
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
events.push({
|
|
167
|
+
id: decoded.id,
|
|
168
|
+
type: decoded.type,
|
|
169
|
+
schemaVersion: decoded.schemaVersion,
|
|
170
|
+
occurredAt: decoded.occurredAt,
|
|
171
|
+
entity: decoded.entity,
|
|
172
|
+
payload: decoded.payload,
|
|
173
|
+
hops,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const last = input.rows[input.rows.length - 1];
|
|
177
|
+
return {
|
|
178
|
+
events,
|
|
179
|
+
withheld,
|
|
180
|
+
next: (last?.id ?? input.after),
|
|
181
|
+
more: input.rows.length === input.limit,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The read plan for one request: which types to read, which keys the consumer must hold for
|
|
186
|
+
* them, and what it asked for that this scope does not export.
|
|
187
|
+
*
|
|
188
|
+
* `exports` is THIS scope's running declaration, the union over its registered modules. A
|
|
189
|
+
* wanted type absent from it is reported and never read. That is the producer's own code
|
|
190
|
+
* deciding what leaves, whatever the platform asked for.
|
|
191
|
+
*/
|
|
192
|
+
export function exportReadPlan(exports, wants) {
|
|
193
|
+
const types = [];
|
|
194
|
+
const keys = new Set();
|
|
195
|
+
const wanted = new Map();
|
|
196
|
+
const unexported = [];
|
|
197
|
+
for (const w of wants) {
|
|
198
|
+
const e = exports.get(w.type);
|
|
199
|
+
if (!e) {
|
|
200
|
+
unexported.push(w);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (!wanted.has(w.type))
|
|
204
|
+
types.push(w.type);
|
|
205
|
+
wanted.set(w.type, w.schemaVersion);
|
|
206
|
+
keys.add(e.readPermission);
|
|
207
|
+
}
|
|
208
|
+
return { types: types.sort(), keys: [...keys].sort(), wanted, unexported };
|
|
209
|
+
}
|
|
210
|
+
/** Every (type → export) the given manifests declare. The caller's registration refuses conflicts. */
|
|
211
|
+
export function exportsOf(manifests) {
|
|
212
|
+
const out = new Map();
|
|
213
|
+
for (const m of manifests)
|
|
214
|
+
for (const e of m.events.exports ?? [])
|
|
215
|
+
if (!out.has(e.type))
|
|
216
|
+
out.set(e.type, e);
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
/** The consumer's watermark per producer, oldest source first. */
|
|
220
|
+
export const IMPORT_CURSORS_SQL = 'SELECT source_scope_id, source_vertical, cursor, updated_at FROM _substrat_import_cursors ORDER BY source_scope_id';
|
|
221
|
+
/** One producer's watermark, for the compare-and-set a batch is applied under. */
|
|
222
|
+
export const IMPORT_CURSOR_OF_SQL = 'SELECT cursor FROM _substrat_import_cursors WHERE source_scope_id = ?';
|
|
223
|
+
/**
|
|
224
|
+
* Move one producer's watermark. Params: (source_scope_id, source_vertical, cursor, updated_at).
|
|
225
|
+
*
|
|
226
|
+
* Only ever forward. ULIDs sort by time, and a batch's `next` is the last id its read walked,
|
|
227
|
+
* so a smaller value can only come from a pass that read less than one already applied. The
|
|
228
|
+
* batch's own compare-and-set normally refuses that pass before it gets here, and this is the
|
|
229
|
+
* second line.
|
|
230
|
+
*/
|
|
231
|
+
export const IMPORT_CURSOR_ADVANCE_SQL = `
|
|
232
|
+
INSERT INTO _substrat_import_cursors (source_scope_id, source_vertical, cursor, updated_at)
|
|
233
|
+
VALUES (?, ?, ?, ?)
|
|
234
|
+
ON CONFLICT (source_scope_id) DO UPDATE SET
|
|
235
|
+
source_vertical = excluded.source_vertical,
|
|
236
|
+
cursor = excluded.cursor,
|
|
237
|
+
updated_at = excluded.updated_at
|
|
238
|
+
WHERE excluded.cursor > _substrat_import_cursors.cursor
|
|
239
|
+
`;
|
|
240
|
+
/**
|
|
241
|
+
* Journal one received (or withheld) event's envelope. Params in column order. Ignored on a
|
|
242
|
+
* repeat: the first journaling is the fact, and a redelivered batch must not move
|
|
243
|
+
* `imported_at` forward or rewrite what was withheld and why.
|
|
244
|
+
*/
|
|
245
|
+
export const IMPORT_RECORD_SQL = `
|
|
246
|
+
INSERT OR IGNORE INTO _substrat_imports
|
|
247
|
+
(event_id, source_scope_id, source_vertical, type, schema_version, occurred_at,
|
|
248
|
+
entity_type, entity_id, hops, withheld, imported_at)
|
|
249
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
250
|
+
`;
|
|
251
|
+
/** The note a withheld event's dead letter carries at the consumer. Names the reason, never the content. */
|
|
252
|
+
export function withheldNote(reason, vertical) {
|
|
253
|
+
const why = {
|
|
254
|
+
pii: 'it is classified as carrying personal data, and only piiClass none crosses a vertical boundary',
|
|
255
|
+
version: 'its schemaVersion is not the one this module declares, so a parse here would reject it (K-39)',
|
|
256
|
+
cascade: `its cause chain has already crossed ${EXPORT_HOP_CAP} vertical boundaries`,
|
|
257
|
+
undecodable: 'the stored row does not decode (#1636)',
|
|
258
|
+
};
|
|
259
|
+
return `withheld by '${vertical}' (#1705): ${why[reason]} — nothing was delivered`;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A host's cross-vertical declarations (#1705), accumulated module by module. It is held by
|
|
263
|
+
* every place that registers modules (the pure host, the Cloudflare coordinator and the
|
|
264
|
+
* scope DO), so the three refuse the same wiring with the same words.
|
|
265
|
+
*
|
|
266
|
+
* What it refuses, and why each would otherwise be silent:
|
|
267
|
+
* - **An import handler for a (source, type) the manifest does not declare `from` it.** It
|
|
268
|
+
* would run for events nobody reviewed the module receiving, because the declaration is
|
|
269
|
+
* what the permission diff shows.
|
|
270
|
+
* - **A declared import with no handler.** The edge would be read, the events journaled and
|
|
271
|
+
* the watermark moved, and nothing would ever run: a delivery that reports success and
|
|
272
|
+
* does nothing. (A LOCAL consume without a handler stays allowed, as it always was.)
|
|
273
|
+
* - **Two modules importing one (source, type) at different versions**, or **exporting one
|
|
274
|
+
* type under different versions or keys**. The edge asks for one version and gates on one
|
|
275
|
+
* key, and which module won would depend on registration order.
|
|
276
|
+
*/
|
|
277
|
+
export class CrossVerticalRegistry {
|
|
278
|
+
exportsByType = new Map();
|
|
279
|
+
importVersion = new Map();
|
|
280
|
+
registered = [];
|
|
281
|
+
register(manifest, handlers) {
|
|
282
|
+
const declared = new Map();
|
|
283
|
+
for (const c of manifest.events.consumes)
|
|
284
|
+
if (c.from !== undefined)
|
|
285
|
+
declared.set(`${c.from}\u0000${c.type}`, c);
|
|
286
|
+
const imports = [];
|
|
287
|
+
for (const [from, byType] of Object.entries(handlers ?? {})) {
|
|
288
|
+
for (const [type, handler] of Object.entries(byType)) {
|
|
289
|
+
const decl = declared.get(`${from}\u0000${type}`);
|
|
290
|
+
if (!decl) {
|
|
291
|
+
throw new Error(`${manifest.id} registers an import handler for '${type}' from '${from}', which its manifest does not ` +
|
|
292
|
+
`declare — add { from: '${from}', type: '${type}', schemaVersion } to events.consumes`);
|
|
293
|
+
}
|
|
294
|
+
imports.push({ moduleId: manifest.id, from, type, schemaVersion: decl.schemaVersion, handler });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const [key, decl] of declared) {
|
|
298
|
+
if (!imports.some((i) => `${i.from}\u0000${i.type}` === key)) {
|
|
299
|
+
throw new Error(`${manifest.id} declares it consumes '${decl.type}' from '${decl.from}' but registers no handler for it ` +
|
|
300
|
+
`under \`imports['${decl.from}']\` — the edge would deliver into nothing`);
|
|
301
|
+
}
|
|
302
|
+
const prior = this.importVersion.get(key);
|
|
303
|
+
if (prior && prior.schemaVersion !== decl.schemaVersion) {
|
|
304
|
+
throw new Error(`'${decl.type}' from '${decl.from}' is imported at schemaVersion ${prior.schemaVersion} by ${prior.declaredBy} ` +
|
|
305
|
+
`and ${decl.schemaVersion} by ${manifest.id} — one edge asks for one version`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
for (const e of manifest.events.exports ?? []) {
|
|
309
|
+
const prior = this.exportsByType.get(e.type);
|
|
310
|
+
if (prior && (prior.schemaVersion !== e.schemaVersion || prior.readPermission !== e.readPermission)) {
|
|
311
|
+
throw new Error(`'${e.type}' is exported as v${prior.schemaVersion} under '${prior.readPermission}' by ${prior.declaredBy} ` +
|
|
312
|
+
`and as v${e.schemaVersion} under '${e.readPermission}' by ${manifest.id} — one export, one version, one key`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Validated whole before anything is kept, so a refused module leaves no half of itself.
|
|
316
|
+
for (const e of manifest.events.exports ?? []) {
|
|
317
|
+
if (!this.exportsByType.has(e.type))
|
|
318
|
+
this.exportsByType.set(e.type, { ...e, declaredBy: manifest.id });
|
|
319
|
+
}
|
|
320
|
+
for (const [key, decl] of declared) {
|
|
321
|
+
if (!this.importVersion.has(key))
|
|
322
|
+
this.importVersion.set(key, { schemaVersion: decl.schemaVersion, declaredBy: manifest.id });
|
|
323
|
+
}
|
|
324
|
+
this.registered.push(...imports);
|
|
325
|
+
}
|
|
326
|
+
/** type → export, over every registered module: what this deployment releases. */
|
|
327
|
+
exports() {
|
|
328
|
+
return new Map([...this.exportsByType].map(([t, { declaredBy: _, ...e }]) => [t, e]));
|
|
329
|
+
}
|
|
330
|
+
/** What this deployment imports, one row per (source, type), sorted. */
|
|
331
|
+
consumes() {
|
|
332
|
+
return [...this.importVersion.entries()]
|
|
333
|
+
.map(([key, v]) => {
|
|
334
|
+
const [from, type] = key.split('\u0000');
|
|
335
|
+
return { from, type, schemaVersion: v.schemaVersion };
|
|
336
|
+
})
|
|
337
|
+
.sort((a, b) => a.from.localeCompare(b.from) || a.type.localeCompare(b.type));
|
|
338
|
+
}
|
|
339
|
+
/** The handlers for one (source, type), in registration order. */
|
|
340
|
+
handlersFor(from, type) {
|
|
341
|
+
return this.registered.filter((i) => i.from === from && i.type === type);
|
|
342
|
+
}
|
|
343
|
+
/** Every module that imports anything from `from`: who a withheld event's dead letter is filed under. */
|
|
344
|
+
modulesImporting(from, type) {
|
|
345
|
+
return [...new Set(this.handlersFor(from, type).map((i) => i.moduleId))];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=vertical-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vertical-events.js","sourceRoot":"","sources":["../src/vertical-events.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/kernel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.0",
|
|
4
4
|
"description": "Substrat kernel contracts and services — pure TypeScript, no platform imports (D-14)",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@substrat-run/contracts": "^0.
|
|
25
|
+
"@substrat-run/contracts": "^0.120.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.0.0",
|