@substrat-run/adapter-sqlite 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/index.d.ts +60 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +554 -65
- package/dist/index.js.map +1 -1
- package/dist/vertical-broker.d.ts +62 -0
- package/dist/vertical-broker.d.ts.map +1 -0
- package/dist/vertical-broker.js +87 -0
- package/dist/vertical-broker.js.map +1 -0
- package/package.json +11 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The LOCAL peer broker (#1706) — the pure host's stand-in for the platform hop that says which
|
|
3
|
+
* vertical is calling. DEV AND TEST ONLY, and built so it cannot be anything else:
|
|
4
|
+
*
|
|
5
|
+
* - It is an in-process object, not an endpoint. Nothing reaches it over a network; code reaches
|
|
6
|
+
* it only by importing it, and it takes the pure host (`SqliteScopeHost`, better-sqlite3) as its
|
|
7
|
+
* input — which no Workers bundle can carry.
|
|
8
|
+
* - It is exported from its own subpath, `@substrat-run/adapter-sqlite/vertical-broker`, whose
|
|
9
|
+
* `workerd` / `worker` / `browser` conditions resolve to nothing, so a worker build that imports
|
|
10
|
+
* it fails to resolve rather than shipping it.
|
|
11
|
+
* - `node tools/boundary-lint.mjs` (R9) refuses the import anywhere but a test or a node harness
|
|
12
|
+
* (`server.ts`, `seed.ts`, `dev.ts`, `scripts/`), so a `worker.ts` or module code that names it
|
|
13
|
+
* is red before it builds.
|
|
14
|
+
*
|
|
15
|
+
* **What asserts the caller here.** The harness that wires the broker: it builds one client per
|
|
16
|
+
* calling scope with `clientFor`, and that client can only ever speak as that scope. In a dev
|
|
17
|
+
* process the harness IS the whole trust domain — the same stance as `@substrat-run/dev-issuer`'s
|
|
18
|
+
* `/dev/token`. What the broker keeps identical to production is everything else: the caller must
|
|
19
|
+
* be a live, primary instance of its vertical in the tenant, the target is resolved by the
|
|
20
|
+
* kernel's one rule (`resolveVerticalInstance`: same tenant, primary, active, exactly one), and the
|
|
21
|
+
* call goes through the target's peer door (`getVerticalScope`), whose admission and checks are
|
|
22
|
+
* the ones a hosted call meets. On the hosted path the router plays this part, from facts the
|
|
23
|
+
* calling deployment cannot influence.
|
|
24
|
+
*
|
|
25
|
+
* **What it deliberately cannot do.** Name a tenant (the target is searched in the caller's own
|
|
26
|
+
* tenant only), name a principal (there is no parameter for one: a peer acts as itself), or reach
|
|
27
|
+
* a preview (never resolved as a target, and refused as a caller).
|
|
28
|
+
*/
|
|
29
|
+
import { type PlatformActorId, type ScopeId, type TenantId } from '@substrat-run/contracts';
|
|
30
|
+
import { type InvokeOptions } from '@substrat-run/kernel';
|
|
31
|
+
import type { SqliteScopeHost } from './index.js';
|
|
32
|
+
/** The platform actor the broker reads the directory as — the platform, on the pure host. */
|
|
33
|
+
export declare const LOCAL_BROKER_ACTOR: PlatformActorId;
|
|
34
|
+
/** A calling scope, as the harness that owns it names it. */
|
|
35
|
+
export interface LocalVerticalCallerRef {
|
|
36
|
+
/** The calling vertical's registry slug — a key of the broker's host map. */
|
|
37
|
+
vertical: string;
|
|
38
|
+
tenantId: TenantId;
|
|
39
|
+
scopeId: ScopeId;
|
|
40
|
+
}
|
|
41
|
+
/** What a calling harness holds: calls out as ONE scope of ONE vertical, and nothing else. */
|
|
42
|
+
export interface LocalVerticalClient {
|
|
43
|
+
/**
|
|
44
|
+
* Invoke `operation` on the instance of vertical `target` in the caller's tenant, as the
|
|
45
|
+
* caller. Throws `forbidden` when the caller is not a live primary instance or the target's
|
|
46
|
+
* door refuses it, `not_found` when the target is not installed in the tenant, `conflict`
|
|
47
|
+
* when the tenant runs more than one instance of it.
|
|
48
|
+
*/
|
|
49
|
+
invoke<O = unknown>(target: string, operation: string, input?: unknown, options?: InvokeOptions): Promise<O>;
|
|
50
|
+
}
|
|
51
|
+
export interface LocalVerticalBroker {
|
|
52
|
+
/** A client that speaks as `from` — the harness's own scope, and only that. */
|
|
53
|
+
clientFor(from: LocalVerticalCallerRef): LocalVerticalClient;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Stand two (or more) verticals side by side on the pure host, each on its own `SqliteScopeHost`
|
|
57
|
+
* — one vertical's code per host, as one script per vertical in production — keyed by registry
|
|
58
|
+
* slug. Each host holds its own directory, so the tenant a test or dev harness provisions must be
|
|
59
|
+
* created on every host that serves one of its instances.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createLocalVerticalBroker(hosts: Readonly<Record<string, SqliteScopeHost>>): LocalVerticalBroker;
|
|
62
|
+
//# sourceMappingURL=vertical-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vertical-broker.d.ts","sourceRoot":"","sources":["../src/vertical-broker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,EAAE,eAAqE,CAAC;AAEvG,6DAA6D;AAC7D,MAAM,WAAW,sBAAsB;IACrC,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,8FAA8F;AAC9F,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC9G;AAED,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,SAAS,CAAC,IAAI,EAAE,sBAAsB,GAAG,mBAAmB,CAAC;CAC9D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,GAAG,mBAAmB,CA8D/G"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The LOCAL peer broker (#1706) — the pure host's stand-in for the platform hop that says which
|
|
3
|
+
* vertical is calling. DEV AND TEST ONLY, and built so it cannot be anything else:
|
|
4
|
+
*
|
|
5
|
+
* - It is an in-process object, not an endpoint. Nothing reaches it over a network; code reaches
|
|
6
|
+
* it only by importing it, and it takes the pure host (`SqliteScopeHost`, better-sqlite3) as its
|
|
7
|
+
* input — which no Workers bundle can carry.
|
|
8
|
+
* - It is exported from its own subpath, `@substrat-run/adapter-sqlite/vertical-broker`, whose
|
|
9
|
+
* `workerd` / `worker` / `browser` conditions resolve to nothing, so a worker build that imports
|
|
10
|
+
* it fails to resolve rather than shipping it.
|
|
11
|
+
* - `node tools/boundary-lint.mjs` (R9) refuses the import anywhere but a test or a node harness
|
|
12
|
+
* (`server.ts`, `seed.ts`, `dev.ts`, `scripts/`), so a `worker.ts` or module code that names it
|
|
13
|
+
* is red before it builds.
|
|
14
|
+
*
|
|
15
|
+
* **What asserts the caller here.** The harness that wires the broker: it builds one client per
|
|
16
|
+
* calling scope with `clientFor`, and that client can only ever speak as that scope. In a dev
|
|
17
|
+
* process the harness IS the whole trust domain — the same stance as `@substrat-run/dev-issuer`'s
|
|
18
|
+
* `/dev/token`. What the broker keeps identical to production is everything else: the caller must
|
|
19
|
+
* be a live, primary instance of its vertical in the tenant, the target is resolved by the
|
|
20
|
+
* kernel's one rule (`resolveVerticalInstance`: same tenant, primary, active, exactly one), and the
|
|
21
|
+
* call goes through the target's peer door (`getVerticalScope`), whose admission and checks are
|
|
22
|
+
* the ones a hosted call meets. On the hosted path the router plays this part, from facts the
|
|
23
|
+
* calling deployment cannot influence.
|
|
24
|
+
*
|
|
25
|
+
* **What it deliberately cannot do.** Name a tenant (the target is searched in the caller's own
|
|
26
|
+
* tenant only), name a principal (there is no parameter for one: a peer acts as itself), or reach
|
|
27
|
+
* a preview (never resolved as a target, and refused as a caller).
|
|
28
|
+
*/
|
|
29
|
+
import { platformActorId, scopeId as scopeIdOf, substratError, tenantId as tenantIdOf, verticalSlug, } from '@substrat-run/contracts';
|
|
30
|
+
import { isPrimaryScope } from '@substrat-run/kernel';
|
|
31
|
+
/** The platform actor the broker reads the directory as — the platform, on the pure host. */
|
|
32
|
+
export const LOCAL_BROKER_ACTOR = platformActorId.parse('01JZ00000000000000PEERBRKR');
|
|
33
|
+
/**
|
|
34
|
+
* Stand two (or more) verticals side by side on the pure host, each on its own `SqliteScopeHost`
|
|
35
|
+
* — one vertical's code per host, as one script per vertical in production — keyed by registry
|
|
36
|
+
* slug. Each host holds its own directory, so the tenant a test or dev harness provisions must be
|
|
37
|
+
* created on every host that serves one of its instances.
|
|
38
|
+
*/
|
|
39
|
+
export function createLocalVerticalBroker(hosts) {
|
|
40
|
+
const hostOf = (vertical) => Object.prototype.hasOwnProperty.call(hosts, vertical) ? hosts[vertical] : undefined;
|
|
41
|
+
return {
|
|
42
|
+
clientFor(from) {
|
|
43
|
+
const caller = {
|
|
44
|
+
vertical: verticalSlug.parse(from.vertical),
|
|
45
|
+
tenantId: tenantIdOf.parse(from.tenantId),
|
|
46
|
+
scopeId: scopeIdOf.parse(from.scopeId),
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
async invoke(target, operation, input, options) {
|
|
50
|
+
// 1. The caller, re-read from ITS host's directory on every call: a live, primary
|
|
51
|
+
// instance of the vertical it says it is, in the tenant it says. Suspended,
|
|
52
|
+
// archived, forked, preview — all refused here, before any target is looked up.
|
|
53
|
+
// This is the router's per-call gate; nothing downstream re-checks it.
|
|
54
|
+
const callerHost = hostOf(caller.vertical);
|
|
55
|
+
const record = await callerHost?.admin.getScopeRecord(LOCAL_BROKER_ACTOR, caller.tenantId, caller.scopeId);
|
|
56
|
+
if (!record || record.vertical !== caller.vertical) {
|
|
57
|
+
throw substratError('forbidden', `scope ${caller.scopeId} is not an instance of '${caller.vertical}' in this tenant — a peer ` +
|
|
58
|
+
'call is made by a live instance of the calling vertical');
|
|
59
|
+
}
|
|
60
|
+
const tenant = await callerHost.admin.getTenant(LOCAL_BROKER_ACTOR, caller.tenantId);
|
|
61
|
+
if (tenant?.status !== 'active' || record.status !== 'active' || !isPrimaryScope(record)) {
|
|
62
|
+
throw substratError('forbidden', `scope ${caller.scopeId} of '${caller.vertical}' may not call other verticals: it is ` +
|
|
63
|
+
(tenant?.status !== 'active' ? `in a ${tenant?.status ?? 'missing'} tenant` : record.status !== 'active' ? `${record.status}` : 'a preview or a fork') +
|
|
64
|
+
' — only a live primary instance acts as its vertical');
|
|
65
|
+
}
|
|
66
|
+
// 2. The target, by slug, in the CALLER's tenant only — the kernel's one rule.
|
|
67
|
+
const target_ = verticalSlug.parse(target);
|
|
68
|
+
const targetHost = hostOf(target_);
|
|
69
|
+
const resolution = targetHost
|
|
70
|
+
? await targetHost.admin.resolveVerticalInstance(caller.tenantId, target_)
|
|
71
|
+
: { outcome: 'not-installed', tenantId: caller.tenantId, vertical: target_ };
|
|
72
|
+
if (resolution.outcome === 'not-installed') {
|
|
73
|
+
throw substratError('not_found', `vertical '${target_}' is not installed in this tenant`);
|
|
74
|
+
}
|
|
75
|
+
if (resolution.outcome === 'ambiguous') {
|
|
76
|
+
throw substratError('conflict', `this tenant runs ${resolution.count} instances of '${target_}' — a call cannot pick one; ` +
|
|
77
|
+
'bind the instance first');
|
|
78
|
+
}
|
|
79
|
+
// 3. The target's peer door, as the caller — admission and every check happen there.
|
|
80
|
+
const stub = await targetHost.getVerticalScope({ vertical: caller.vertical, scope: caller.scopeId }, resolution.instance.tenantId, resolution.instance.scopeId);
|
|
81
|
+
return stub.invoke(operation, input, options);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=vertical-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vertical-broker.js","sourceRoot":"","sources":["../src/vertical-broker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/adapter-sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.0",
|
|
4
4
|
"description": "Pure-SQLite scope host — real kernel semantics with no Cloudflare dependency (D-14): local dev, CI, self-host/escrow",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./vertical-broker": {
|
|
21
|
+
"workerd": null,
|
|
22
|
+
"worker": null,
|
|
23
|
+
"browser": null,
|
|
24
|
+
"types": "./dist/vertical-broker.d.ts",
|
|
25
|
+
"default": "./dist/vertical-broker.js"
|
|
19
26
|
}
|
|
20
27
|
},
|
|
21
28
|
"files": [
|
|
@@ -23,14 +30,14 @@
|
|
|
23
30
|
],
|
|
24
31
|
"dependencies": {
|
|
25
32
|
"better-sqlite3": "^13.0.3",
|
|
26
|
-
"@substrat-run/contracts": "^0.
|
|
27
|
-
"@substrat-run/kernel": "^0.
|
|
33
|
+
"@substrat-run/contracts": "^0.120.0",
|
|
34
|
+
"@substrat-run/kernel": "^0.120.0"
|
|
28
35
|
},
|
|
29
36
|
"devDependencies": {
|
|
30
37
|
"@types/better-sqlite3": "^9.6.0",
|
|
31
38
|
"typescript": "^7.0.0",
|
|
32
39
|
"vitest": "^3.2.7",
|
|
33
|
-
"@substrat-run/contract-tests": "^0.
|
|
40
|
+
"@substrat-run/contract-tests": "^0.120.0"
|
|
34
41
|
},
|
|
35
42
|
"publishConfig": {
|
|
36
43
|
"access": "public"
|