@substrat-run/adapter-sqlite 0.117.0 → 0.119.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.
@@ -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.117.0",
3
+ "version": "0.119.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.117.0",
27
- "@substrat-run/kernel": "^0.117.0"
33
+ "@substrat-run/contracts": "^0.119.0",
34
+ "@substrat-run/kernel": "^0.119.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.117.0"
40
+ "@substrat-run/contract-tests": "^0.119.0"
34
41
  },
35
42
  "publishConfig": {
36
43
  "access": "public"