trellis 3.1.35 → 3.2.2
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/CHANGELOG.md +23 -5
- package/README.md +63 -156
- package/bin/trellis.mjs +2 -75
- package/dist/{better-sqlite-backend-ahx5p0br.js → better-sqlite-backend-2P4F6AOJ.js} +91 -29
- package/dist/boot-middleware-MHLHFCUC.js +9 -0
- package/dist/browser/index.d.ts +18 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +95 -0
- package/dist/{index-53f3b8p8.js → chunk-2W3MHTMG.js} +552 -213
- package/dist/chunk-44FFQKQX.js +142 -0
- package/dist/chunk-4HYPLFJD.js +150 -0
- package/dist/chunk-4XIVMVHC.js +361 -0
- package/dist/{index-yp88he8n.js → chunk-6CTA6MCE.js} +88 -64
- package/dist/chunk-6GEZ6DN5.js +394 -0
- package/dist/chunk-7ZFRVCUE.js +211 -0
- package/dist/chunk-CAG4FULI.js +121 -0
- package/dist/chunk-FF36CQHZ.js +61 -0
- package/dist/{index-h32txmxe.js → chunk-FKQO5A3H.js} +6 -13
- package/dist/{index-nq520y6k.js → chunk-FWRS4CSC.js} +46 -32
- package/dist/chunk-GFZCJ4EH.js +108 -0
- package/dist/chunk-GLM4HGN4.js +55 -0
- package/dist/{index-4cdr7x2x.js → chunk-GU374KWZ.js} +182 -111
- package/dist/chunk-H6JB3PZ3.js +45 -0
- package/dist/chunk-IZX2PLIB.js +1240 -0
- package/dist/chunk-J2TXUFCZ.js +289 -0
- package/dist/chunk-J3WYZO3Q.js +211 -0
- package/dist/{index-xzym9w0m.js → chunk-JA7AIHRK.js} +12 -7
- package/dist/chunk-JHHEXEDM.js +162 -0
- package/dist/chunk-JUEMDWLU.js +233 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-KIJITUZB.js +419 -0
- package/dist/{index-4wxa8xz4.js → chunk-KQ4A2D2P.js} +162 -23
- package/dist/chunk-KSU2GW22.js +958 -0
- package/dist/{index-rv1p47gp.js → chunk-L5N5PR2S.js} +3015 -2480
- package/dist/{index-h9e2efx4.js → chunk-LNUIGRDZ.js} +71 -55
- package/dist/{index-skhn0agf.js → chunk-MLC5BUYO.js} +19 -20
- package/dist/chunk-MPXUVGT3.js +104 -0
- package/dist/chunk-N6MYZT4O.js +68 -0
- package/dist/chunk-O2AV5WMN.js +48 -0
- package/dist/chunk-PZ4XYZ4J.js +143 -0
- package/dist/chunk-QB5ISE47.js +419 -0
- package/dist/chunk-RSWUFTO2.js +857 -0
- package/dist/chunk-S3XLFZ27.js +1095 -0
- package/dist/{index-w5wccer3.js → chunk-S6GBJ2ZB.js} +28 -12
- package/dist/{index-7pjz3tsy.js → chunk-SGMDTWJJ.js} +57 -15
- package/dist/chunk-SPSPV5BC.js +390 -0
- package/dist/chunk-TCKXSOBP.js +123 -0
- package/dist/chunk-UBGUDMUV.js +59 -0
- package/dist/{index-wt8rz4gn.js → chunk-UQISRW6T.js} +97 -26
- package/dist/chunk-USEINYUL.js +12 -0
- package/dist/{index-bmyt7k8n.js → chunk-UZRUP7QW.js} +12 -4
- package/dist/{index-xhcp6xrn.js → chunk-VBYUTRXX.js} +695 -514
- package/dist/{index-vkpkfwhq.js → chunk-VDAKEOML.js} +234 -194
- package/dist/chunk-WXSWA3MV.js +0 -0
- package/dist/{index-n9f2qyh5.js → chunk-X5FLTRUB.js} +55 -33
- package/dist/{index-h7zxhhhh.js → chunk-XMQ7G77X.js} +46 -38
- package/dist/{index-w7ng765c.js → chunk-YCM7ZYEZ.js} +102 -49
- package/dist/cli/deploy-cli.d.ts +18 -0
- package/dist/cli/deploy-cli.d.ts.map +1 -0
- package/dist/cli/errors.d.ts +6 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/examples.d.ts +23 -0
- package/dist/cli/examples.d.ts.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2426 -6225
- package/dist/client/index.browser.d.ts +29 -0
- package/dist/client/index.browser.d.ts.map +1 -0
- package/dist/client/index.browser.js +64 -0
- package/dist/client/index.d.ts +6 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +41 -21
- package/dist/client/live.d.ts +67 -0
- package/dist/client/live.d.ts.map +1 -0
- package/dist/client/reactive.d.ts +31 -0
- package/dist/client/reactive.d.ts.map +1 -0
- package/dist/client/sdk.browser.d.ts +104 -0
- package/dist/client/sdk.browser.d.ts.map +1 -0
- package/dist/client/sdk.browser.js +9 -0
- package/dist/client/sdk.d.ts +26 -2
- package/dist/client/sdk.d.ts.map +1 -1
- package/dist/client/sdk.js +9 -0
- package/dist/client/vcs-client.d.ts +129 -0
- package/dist/client/vcs-client.d.ts.map +1 -0
- package/dist/cms/index.js +171 -163
- package/dist/{config-8hczw0rs.js → config-PFTP67TR.js} +8 -9
- package/dist/core/computation/index.d.ts +5 -0
- package/dist/core/computation/index.d.ts.map +1 -0
- package/dist/core/computation/rollup.d.ts +18 -0
- package/dist/core/computation/rollup.d.ts.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +171 -104
- package/dist/core/kernel/boot-middleware.d.ts +10 -0
- package/dist/core/kernel/boot-middleware.d.ts.map +1 -0
- package/dist/core/kernel/logic-middleware.d.ts +7 -1
- package/dist/core/kernel/logic-middleware.d.ts.map +1 -1
- package/dist/core/kernel/trellis-kernel.d.ts.map +1 -1
- package/dist/core/ontology/types.d.ts +5 -0
- package/dist/core/ontology/types.d.ts.map +1 -1
- package/dist/core/persist/better-sqlite-backend.d.ts +9 -2
- package/dist/core/persist/better-sqlite-backend.d.ts.map +1 -1
- package/dist/core/persist/factory.d.ts +6 -7
- package/dist/core/persist/factory.d.ts.map +1 -1
- package/dist/core/persist/factory.js +2 -3
- package/dist/core/persist/sqljs-backend.js +2 -3
- package/dist/db/index.d.ts +4 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +67 -54
- package/dist/db/inspector.js +11 -19
- package/dist/db/trellis.css +1 -1
- package/dist/decisions/index.js +9 -10
- package/dist/deploy-LQ7GUGJ5.js +9 -0
- package/dist/deploy-YNVG5E4E.js +9 -0
- package/dist/deploy-cli-24BCHAXY.js +67 -0
- package/dist/deploy-cli-C35HTITO.js +67 -0
- package/dist/embeddings/auto-embed.d.ts +2 -2
- package/dist/embeddings/auto-embed.d.ts.map +1 -1
- package/dist/embeddings/index.js +20 -22
- package/dist/embeddings/search.d.ts +2 -1
- package/dist/embeddings/search.d.ts.map +1 -1
- package/dist/embeddings/store.d.ts +25 -4
- package/dist/embeddings/store.d.ts.map +1 -1
- package/dist/engine.d.ts +59 -19
- package/dist/engine.d.ts.map +1 -1
- package/dist/import-UZEKXU3B.js +10 -0
- package/dist/index.js +102 -93
- package/dist/launch-explorer-HZ4ZJA4X.js +95 -0
- package/dist/links/index.js +24 -26
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/plugins/agent-memory/index.js +316 -0
- package/dist/plugins/idea-garden/index.js +165 -0
- package/dist/plugins/plan-approval/index.js +579 -0
- package/dist/plugins/proactive-watcher/index.js +194 -0
- package/dist/{index-2r4jxwnb.js → query-XYLTJYSA.js} +14 -15
- package/dist/react/index.js +39 -70
- package/dist/react/realtime.js +11 -0
- package/dist/react/schema-hooks.js +87 -0
- package/dist/realtime/broadcast-channel-transport.d.ts +43 -0
- package/dist/realtime/broadcast-channel-transport.d.ts.map +1 -0
- package/dist/realtime/durable-object-relay-transport.d.ts +96 -0
- package/dist/realtime/durable-object-relay-transport.d.ts.map +1 -0
- package/dist/realtime/index.d.ts +44 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +40 -0
- package/dist/realtime/memory-hub.d.ts +32 -0
- package/dist/realtime/memory-hub.d.ts.map +1 -0
- package/dist/realtime/persistent-channel.d.ts +105 -0
- package/dist/realtime/persistent-channel.d.ts.map +1 -0
- package/dist/realtime/presence.d.ts +73 -0
- package/dist/realtime/presence.d.ts.map +1 -0
- package/dist/realtime/relay-persistence.d.ts +37 -0
- package/dist/realtime/relay-persistence.d.ts.map +1 -0
- package/dist/realtime/relay-persistence.js +107 -0
- package/dist/realtime/relay-server.d.ts +103 -0
- package/dist/realtime/relay-server.d.ts.map +1 -0
- package/dist/realtime/room.d.ts +98 -0
- package/dist/realtime/room.d.ts.map +1 -0
- package/dist/realtime/text.d.ts +73 -0
- package/dist/realtime/text.d.ts.map +1 -0
- package/dist/realtime/types.d.ts +89 -0
- package/dist/realtime/types.d.ts.map +1 -0
- package/dist/realtime/websocket-relay-transport.d.ts +28 -0
- package/dist/realtime/websocket-relay-transport.d.ts.map +1 -0
- package/dist/realtime.bundle.js +1100 -0
- package/dist/{remote-manager-xvbg4230.js → remote-manager-ODE3T2KR.js} +25 -15
- package/dist/scaffold/seed.d.ts.map +1 -1
- package/dist/scaffold/write.d.ts +1 -1
- package/dist/scaffold/write.d.ts.map +1 -1
- package/dist/schema/define.d.ts +137 -0
- package/dist/schema/define.d.ts.map +1 -0
- package/dist/schema/entity-projection.d.ts +34 -0
- package/dist/schema/entity-projection.d.ts.map +1 -0
- package/dist/schema/eql.d.ts +46 -0
- package/dist/schema/eql.d.ts.map +1 -0
- package/dist/schema/index.d.ts +21 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +46 -0
- package/dist/schema/kernel-resolve.d.ts +21 -0
- package/dist/schema/kernel-resolve.d.ts.map +1 -0
- package/dist/schema/mutations.d.ts +20 -0
- package/dist/schema/mutations.d.ts.map +1 -0
- package/dist/schema/resolve.d.ts +21 -0
- package/dist/schema/resolve.d.ts.map +1 -0
- package/dist/server/deploy-meta.d.ts +25 -0
- package/dist/server/deploy-meta.d.ts.map +1 -0
- package/dist/server/deploy.d.ts +7 -2
- package/dist/server/deploy.d.ts.map +1 -1
- package/dist/server/index.d.ts +7 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +100 -58
- package/dist/server/node-adapter.js +5 -104
- package/dist/server/realtime.d.ts +14 -3
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/server.d.ts +8 -16
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/sprites.d.ts +18 -1
- package/dist/server/sprites.d.ts.map +1 -1
- package/dist/server/tenancy.d.ts +4 -0
- package/dist/server/tenancy.d.ts.map +1 -1
- package/dist/server/usage-meter.d.ts +53 -0
- package/dist/server/usage-meter.d.ts.map +1 -0
- package/dist/server-MB3OPSPI.js +18 -0
- package/dist/server-ZVWGLLRF.js +18 -0
- package/dist/sprites-KQGTGM6W.js +27 -0
- package/dist/{sprites-vc4qbrp1.js → sprites-RU3E4XQN.js} +6 -7
- package/dist/svelte/index.d.ts +17 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +51 -0
- package/dist/svelte/schema-hooks.d.ts +45 -0
- package/dist/svelte/schema-hooks.d.ts.map +1 -0
- package/dist/svelte/schema-hooks.js +71 -0
- package/dist/svelte/stores.d.ts +78 -0
- package/dist/svelte/stores.d.ts.map +1 -0
- package/dist/sync/http-transport.d.ts +3 -3
- package/dist/sync/http-transport.d.ts.map +1 -1
- package/dist/sync/index.d.ts +15 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +617 -0
- package/dist/sync/memory-room.d.ts +37 -0
- package/dist/sync/memory-room.d.ts.map +1 -0
- package/dist/sync/memory-transport.d.ts +3 -3
- package/dist/sync/memory-transport.d.ts.map +1 -1
- package/dist/sync/partykit-room.d.ts +40 -0
- package/dist/sync/partykit-room.d.ts.map +1 -0
- package/dist/sync/partykit-transport.d.ts +84 -0
- package/dist/sync/partykit-transport.d.ts.map +1 -0
- package/dist/sync/room-core.d.ts +53 -0
- package/dist/sync/room-core.d.ts.map +1 -0
- package/dist/sync/sync-engine.d.ts +40 -2
- package/dist/sync/sync-engine.d.ts.map +1 -1
- package/dist/sync/sync-room-server.d.ts +39 -0
- package/dist/sync/sync-room-server.d.ts.map +1 -0
- package/dist/sync/types.d.ts +70 -3
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/vcs-sync-peer.d.ts +63 -0
- package/dist/sync/vcs-sync-peer.d.ts.map +1 -0
- package/dist/sync/ws-transport.d.ts +2 -2
- package/dist/sync/ws-transport.d.ts.map +1 -1
- package/dist/tenancy-NBLLGCNJ.js +14 -0
- package/dist/tenancy-POMWQP6M.js +16 -0
- package/dist/ui/client.html +355 -67
- package/dist/ui/launch-explorer.d.ts +22 -0
- package/dist/ui/launch-explorer.d.ts.map +1 -0
- package/dist/ui/server.d.ts +1 -1
- package/dist/ui/server.d.ts.map +1 -1
- package/dist/vcs/blob-store.d.ts.map +1 -1
- package/dist/vcs/idb-op-log.d.ts +67 -0
- package/dist/vcs/idb-op-log.d.ts.map +1 -0
- package/dist/vcs/index.d.ts +1 -0
- package/dist/vcs/index.d.ts.map +1 -1
- package/dist/vcs/index.js +81 -72
- package/dist/vcs/issue.d.ts +22 -5
- package/dist/vcs/issue.d.ts.map +1 -1
- package/dist/vcs/lane-materialize.d.ts +5 -0
- package/dist/vcs/lane-materialize.d.ts.map +1 -1
- package/dist/vcs/op-log.d.ts +28 -2
- package/dist/vcs/op-log.d.ts.map +1 -1
- package/dist/vcs/ops.d.ts +10 -0
- package/dist/vcs/ops.d.ts.map +1 -1
- package/dist/vcs/types.d.ts +2 -0
- package/dist/vcs/types.d.ts.map +1 -1
- package/dist/{vm-config-6xhsj6b3.js → vm-config-JI4OXKDQ.js} +9 -10
- package/dist/vue/hooks.d.ts +64 -0
- package/dist/vue/hooks.d.ts.map +1 -0
- package/dist/vue/index.d.ts +19 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +59 -0
- package/dist/vue/schema-hooks.d.ts +41 -0
- package/dist/vue/schema-hooks.d.ts.map +1 -0
- package/dist/vue/schema-hooks.js +64 -0
- package/package.json +130 -17
- package/dist/deploy-999q207z.js +0 -10
- package/dist/engine-y0724kjq.js +0 -8
- package/dist/import-s2b8e0ft.js +0 -11
- package/dist/index-a76rekgs.js +0 -67
- package/dist/index-c9h37r6h.js +0 -1
- package/dist/index-hr9qvv77.js +0 -860
- package/dist/index-k5b0xskw.js +0 -1
- package/dist/index-nzb3am4f.js +0 -80
- package/dist/index-wncptktd.js +0 -292
- package/dist/index-y6a4kj0p.js +0 -43
- package/dist/index-yhwjgfvj.js +0 -342
- package/dist/sdk-bepky0xs.js +0 -16
- package/dist/server-szdjx0nt.js +0 -13
- package/dist/sqlite-backend-0vsmc6qj.js +0 -8
- package/dist/tenancy-pjm32b4v.js +0 -14
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// src/client/sdk.browser.ts
|
|
2
|
+
function isRemote(opts) {
|
|
3
|
+
return "url" in opts;
|
|
4
|
+
}
|
|
5
|
+
function browserOnlyRemoteError(feature) {
|
|
6
|
+
return new Error(
|
|
7
|
+
`${feature} is not available in browser bundles. Use remote mode with \`new TrellisDb({ url })\`, or import \`trellis/client\` in Node.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
var TrellisDb = class {
|
|
11
|
+
opts;
|
|
12
|
+
_ws = null;
|
|
13
|
+
/** In-flight connect — concurrent subscribe() shares one socket open. */
|
|
14
|
+
_wsPromise = null;
|
|
15
|
+
_subCallbacks = /* @__PURE__ */ new Map();
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
if (!isRemote(opts)) {
|
|
18
|
+
throw browserOnlyRemoteError("TrellisDb local mode");
|
|
19
|
+
}
|
|
20
|
+
this.opts = opts;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Browser bundles cannot read `.trellis-db.json`.
|
|
24
|
+
*/
|
|
25
|
+
static async fromConfig(_dir = ".") {
|
|
26
|
+
throw browserOnlyRemoteError("TrellisDb.fromConfig()");
|
|
27
|
+
}
|
|
28
|
+
// -------------------------------------------------------------------------
|
|
29
|
+
// CRUD
|
|
30
|
+
// -------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Create a new entity.
|
|
33
|
+
* Returns the generated entity ID.
|
|
34
|
+
*/
|
|
35
|
+
async create(type, attributes = {}, links) {
|
|
36
|
+
const res = await this._fetch("POST", "/entities", {
|
|
37
|
+
type,
|
|
38
|
+
attributes,
|
|
39
|
+
links
|
|
40
|
+
});
|
|
41
|
+
return res.id;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read an entity by ID.
|
|
45
|
+
* Returns null if not found.
|
|
46
|
+
*/
|
|
47
|
+
async read(id) {
|
|
48
|
+
try {
|
|
49
|
+
return await this._fetch(
|
|
50
|
+
"GET",
|
|
51
|
+
`/entities/${encodeURIComponent(id)}`
|
|
52
|
+
);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (err?.status === 404) return null;
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Update an entity's attributes (partial update).
|
|
60
|
+
*/
|
|
61
|
+
async update(id, attributes) {
|
|
62
|
+
await this._fetch(
|
|
63
|
+
"PUT",
|
|
64
|
+
`/entities/${encodeURIComponent(id)}`,
|
|
65
|
+
attributes
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Delete an entity by ID.
|
|
70
|
+
*/
|
|
71
|
+
async delete(id) {
|
|
72
|
+
await this._fetch("DELETE", `/entities/${encodeURIComponent(id)}`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* List entities of a given type.
|
|
76
|
+
*/
|
|
77
|
+
async list(type, opts = {}) {
|
|
78
|
+
const params = new URLSearchParams();
|
|
79
|
+
if (type) params.set("type", type);
|
|
80
|
+
if (opts.limit) params.set("limit", String(opts.limit));
|
|
81
|
+
if (opts.offset) params.set("offset", String(opts.offset));
|
|
82
|
+
const qs = params.toString() ? `?${params}` : "";
|
|
83
|
+
return await this._fetch("GET", `/entities${qs}`);
|
|
84
|
+
}
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
// Query
|
|
87
|
+
// -------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Run an EQL-S query string.
|
|
90
|
+
*/
|
|
91
|
+
async query(eql) {
|
|
92
|
+
return await this._fetch("POST", "/query", {
|
|
93
|
+
query: eql
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// -------------------------------------------------------------------------
|
|
97
|
+
// Schema registration
|
|
98
|
+
// -------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* Register a user/system-tier ontology schema with a remote Trellis server.
|
|
101
|
+
*/
|
|
102
|
+
async registerType(schema) {
|
|
103
|
+
const def = "@id" in schema ? schema : schema.definition;
|
|
104
|
+
try {
|
|
105
|
+
await this._fetch("POST", "/ontologies", def);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (err instanceof FetchError && err.status === 409) return;
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// -------------------------------------------------------------------------
|
|
112
|
+
// File upload
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Upload a file to the blob store.
|
|
116
|
+
* Returns a content-addressed hash.
|
|
117
|
+
*/
|
|
118
|
+
async upload(data, contentType = "application/octet-stream") {
|
|
119
|
+
const raw = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
120
|
+
const cleanBuf = raw.buffer.slice(
|
|
121
|
+
raw.byteOffset,
|
|
122
|
+
raw.byteOffset + raw.byteLength
|
|
123
|
+
);
|
|
124
|
+
const res = await fetch(`${this.opts.url}/upload`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": contentType,
|
|
128
|
+
...this.opts.apiKey ? { Authorization: `Bearer ${this.opts.apiKey}` } : {}
|
|
129
|
+
},
|
|
130
|
+
body: cleanBuf
|
|
131
|
+
});
|
|
132
|
+
if (!res.ok) throw new FetchError(res.status, await res.text());
|
|
133
|
+
return await res.json();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Download a file by its blob hash.
|
|
137
|
+
*/
|
|
138
|
+
async getFile(hash) {
|
|
139
|
+
const res = await fetch(
|
|
140
|
+
`${this.opts.url}/files/${encodeURIComponent(hash)}`,
|
|
141
|
+
{
|
|
142
|
+
headers: this.opts.apiKey ? { Authorization: `Bearer ${this.opts.apiKey}` } : {}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
if (res.status === 404) return null;
|
|
146
|
+
if (!res.ok) throw new FetchError(res.status, await res.text());
|
|
147
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
148
|
+
}
|
|
149
|
+
// -------------------------------------------------------------------------
|
|
150
|
+
// Auth helpers
|
|
151
|
+
// -------------------------------------------------------------------------
|
|
152
|
+
async register(email, password, name) {
|
|
153
|
+
return await this._fetch("POST", "/auth/register", {
|
|
154
|
+
email,
|
|
155
|
+
password,
|
|
156
|
+
name
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async login(email, password) {
|
|
160
|
+
return await this._fetch("POST", "/auth/login", {
|
|
161
|
+
email,
|
|
162
|
+
password
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Set the active API key / JWT token for subsequent requests.
|
|
167
|
+
*/
|
|
168
|
+
setToken(token) {
|
|
169
|
+
this.opts.apiKey = token;
|
|
170
|
+
}
|
|
171
|
+
// -------------------------------------------------------------------------
|
|
172
|
+
// Realtime
|
|
173
|
+
// -------------------------------------------------------------------------
|
|
174
|
+
/**
|
|
175
|
+
* Subscribe to a live EQL-S query.
|
|
176
|
+
* Callback is fired immediately with the initial result, then on every update.
|
|
177
|
+
*/
|
|
178
|
+
subscribe(eql, callback, opts) {
|
|
179
|
+
const subId = `sub_${crypto.randomUUID()}`;
|
|
180
|
+
this._subCallbacks.set(subId, callback);
|
|
181
|
+
this._ensureWs().then((ws) => {
|
|
182
|
+
ws.send(
|
|
183
|
+
JSON.stringify({
|
|
184
|
+
type: "subscribe",
|
|
185
|
+
id: subId,
|
|
186
|
+
query: eql,
|
|
187
|
+
...opts?.entityType ? { entityType: opts.entityType } : {},
|
|
188
|
+
...opts?.resolve ? { resolve: opts.resolve } : {}
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
unsubscribe: () => {
|
|
194
|
+
this._subCallbacks.delete(subId);
|
|
195
|
+
this._ws?.send(JSON.stringify({ type: "unsubscribe", id: subId }));
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Close the WebSocket connection.
|
|
201
|
+
*/
|
|
202
|
+
disconnect() {
|
|
203
|
+
const ws = this._ws;
|
|
204
|
+
this._ws = null;
|
|
205
|
+
this._wsPromise = null;
|
|
206
|
+
ws?.close();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Close open client resources.
|
|
210
|
+
*/
|
|
211
|
+
close() {
|
|
212
|
+
this.disconnect();
|
|
213
|
+
}
|
|
214
|
+
// -------------------------------------------------------------------------
|
|
215
|
+
// Private
|
|
216
|
+
// -------------------------------------------------------------------------
|
|
217
|
+
async _fetch(method, path, body) {
|
|
218
|
+
const url = `${this.opts.url}${path}`;
|
|
219
|
+
const res = await fetch(url, {
|
|
220
|
+
method,
|
|
221
|
+
headers: {
|
|
222
|
+
"Content-Type": "application/json",
|
|
223
|
+
...this.opts.apiKey ? { Authorization: `Bearer ${this.opts.apiKey}` } : {}
|
|
224
|
+
},
|
|
225
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
226
|
+
});
|
|
227
|
+
const data = await res.json();
|
|
228
|
+
if (!res.ok)
|
|
229
|
+
throw new FetchError(
|
|
230
|
+
res.status,
|
|
231
|
+
data?.message ?? res.statusText,
|
|
232
|
+
data
|
|
233
|
+
);
|
|
234
|
+
return data;
|
|
235
|
+
}
|
|
236
|
+
_ensureWs() {
|
|
237
|
+
if (this._ws?.readyState === WebSocket.OPEN) {
|
|
238
|
+
return Promise.resolve(this._ws);
|
|
239
|
+
}
|
|
240
|
+
if (this._wsPromise) {
|
|
241
|
+
return this._wsPromise;
|
|
242
|
+
}
|
|
243
|
+
const wsUrl = this.opts.url.replace(
|
|
244
|
+
/^https?/,
|
|
245
|
+
this.opts.url.startsWith("https") ? "wss" : "ws"
|
|
246
|
+
) + "/realtime";
|
|
247
|
+
this._wsPromise = new Promise((resolve, reject) => {
|
|
248
|
+
const ws = new WebSocket(wsUrl);
|
|
249
|
+
ws.onopen = () => {
|
|
250
|
+
this._ws = ws;
|
|
251
|
+
resolve(ws);
|
|
252
|
+
};
|
|
253
|
+
ws.onerror = (e) => {
|
|
254
|
+
reject(e instanceof Error ? e : new Error("WebSocket error"));
|
|
255
|
+
};
|
|
256
|
+
ws.onmessage = (e) => {
|
|
257
|
+
try {
|
|
258
|
+
const msg = JSON.parse(e.data);
|
|
259
|
+
if (msg.type === "data" && this._subCallbacks.has(msg.id)) {
|
|
260
|
+
const meta = msg.resolved === true ? { resolved: true } : void 0;
|
|
261
|
+
this._subCallbacks.get(msg.id)(msg.result, msg.diff, meta);
|
|
262
|
+
}
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
ws.onclose = () => {
|
|
267
|
+
if (this._ws === ws) {
|
|
268
|
+
this._ws = null;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}).finally(() => {
|
|
272
|
+
this._wsPromise = null;
|
|
273
|
+
});
|
|
274
|
+
return this._wsPromise;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
var FetchError = class extends Error {
|
|
278
|
+
constructor(status, message, body) {
|
|
279
|
+
super(`HTTP ${status}: ${message}`);
|
|
280
|
+
this.status = status;
|
|
281
|
+
this.body = body;
|
|
282
|
+
this.name = "FetchError";
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
export {
|
|
287
|
+
TrellisDb,
|
|
288
|
+
FetchError
|
|
289
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bindingEntityId,
|
|
3
|
+
bindingToEntity,
|
|
4
|
+
entitiesQuery,
|
|
5
|
+
entityQuery,
|
|
6
|
+
isSparseBinding,
|
|
7
|
+
resolveRelations
|
|
8
|
+
} from "./chunk-JUEMDWLU.js";
|
|
9
|
+
import {
|
|
10
|
+
Signal
|
|
11
|
+
} from "./chunk-UBGUDMUV.js";
|
|
12
|
+
|
|
13
|
+
// src/client/live.ts
|
|
14
|
+
function liveQuery(client, eql) {
|
|
15
|
+
const signal = new Signal({
|
|
16
|
+
data: [],
|
|
17
|
+
loading: true,
|
|
18
|
+
error: null
|
|
19
|
+
});
|
|
20
|
+
let sub = null;
|
|
21
|
+
return {
|
|
22
|
+
signal,
|
|
23
|
+
start() {
|
|
24
|
+
if (sub) return () => stop();
|
|
25
|
+
try {
|
|
26
|
+
sub = client.subscribe(eql, (rows) => {
|
|
27
|
+
signal.value = { data: rows, loading: false, error: null };
|
|
28
|
+
});
|
|
29
|
+
} catch (err) {
|
|
30
|
+
signal.value = {
|
|
31
|
+
data: [],
|
|
32
|
+
loading: false,
|
|
33
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return () => stop();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
function stop() {
|
|
40
|
+
sub?.unsubscribe();
|
|
41
|
+
sub = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function normalizeLiveEntitiesOpts(whereOrOpts) {
|
|
45
|
+
if (!whereOrOpts) return {};
|
|
46
|
+
if ("resolve" in whereOrOpts || "where" in whereOrOpts) {
|
|
47
|
+
return whereOrOpts;
|
|
48
|
+
}
|
|
49
|
+
return { where: whereOrOpts };
|
|
50
|
+
}
|
|
51
|
+
function liveEntities(client, typeOrSchema, whereOrOpts) {
|
|
52
|
+
const schema = typeof typeOrSchema === "string" ? void 0 : typeOrSchema;
|
|
53
|
+
const type = typeof typeOrSchema === "string" ? typeOrSchema : typeOrSchema.type;
|
|
54
|
+
const { where, resolve } = normalizeLiveEntitiesOpts(whereOrOpts);
|
|
55
|
+
const signal = new Signal({
|
|
56
|
+
data: [],
|
|
57
|
+
loading: true,
|
|
58
|
+
error: null
|
|
59
|
+
});
|
|
60
|
+
let sub = null;
|
|
61
|
+
let token = 0;
|
|
62
|
+
return {
|
|
63
|
+
signal,
|
|
64
|
+
start() {
|
|
65
|
+
if (sub) return () => stop();
|
|
66
|
+
try {
|
|
67
|
+
const serverResolve = schema && resolve && Object.keys(resolve).length > 0;
|
|
68
|
+
sub = client.subscribe(
|
|
69
|
+
entitiesQuery(type, where),
|
|
70
|
+
(rows, _diff, meta) => {
|
|
71
|
+
const turn = ++token;
|
|
72
|
+
const hydrate = async () => hydrateSubscriptionRows(client, rows, meta);
|
|
73
|
+
hydrate().then(async (entities) => {
|
|
74
|
+
if (turn !== token) return;
|
|
75
|
+
let data = entities;
|
|
76
|
+
if (schema && resolve && Object.keys(resolve).length > 0 && !meta?.resolved) {
|
|
77
|
+
data = await resolveRelations(
|
|
78
|
+
client,
|
|
79
|
+
schema,
|
|
80
|
+
data,
|
|
81
|
+
resolve
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (turn !== token) return;
|
|
85
|
+
signal.value = { data, loading: false, error: null };
|
|
86
|
+
}).catch((err) => {
|
|
87
|
+
if (turn !== token) return;
|
|
88
|
+
signal.value = {
|
|
89
|
+
data: signal.peek().data,
|
|
90
|
+
loading: false,
|
|
91
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
serverResolve ? { entityType: type, resolve } : void 0
|
|
96
|
+
);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
signal.value = {
|
|
99
|
+
data: [],
|
|
100
|
+
loading: false,
|
|
101
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return () => stop();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function stop() {
|
|
108
|
+
token++;
|
|
109
|
+
sub?.unsubscribe();
|
|
110
|
+
sub = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function hydrateSubscriptionRows(client, rows, meta) {
|
|
114
|
+
if (rows.length === 0) return [];
|
|
115
|
+
if (meta?.resolved) return rows;
|
|
116
|
+
if (rows.some((r) => isSparseBinding(r))) {
|
|
117
|
+
const ids = rows.map((r) => bindingEntityId(r)).filter((id) => Boolean(id));
|
|
118
|
+
const loaded = await Promise.all(ids.map((id) => client.read(id)));
|
|
119
|
+
return loaded.filter((e) => e != null);
|
|
120
|
+
}
|
|
121
|
+
return rows.map((r) => bindingToEntity(r));
|
|
122
|
+
}
|
|
123
|
+
async function pickEntityFromRows(client, rows, entityId, meta) {
|
|
124
|
+
const hydrated = await hydrateSubscriptionRows(client, rows, meta);
|
|
125
|
+
const hit = hydrated.find((r) => r.id === entityId);
|
|
126
|
+
if (hit) return hit;
|
|
127
|
+
if (hydrated.length === 0) return client.read(entityId);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function liveEntity(client, typeOrSchema, id, opts) {
|
|
131
|
+
const schema = typeof typeOrSchema === "string" ? void 0 : typeOrSchema;
|
|
132
|
+
const type = typeof typeOrSchema === "string" ? typeOrSchema : typeOrSchema.type;
|
|
133
|
+
const { resolve } = opts ?? {};
|
|
134
|
+
const signal = new Signal({
|
|
135
|
+
data: null,
|
|
136
|
+
loading: Boolean(id),
|
|
137
|
+
error: null
|
|
138
|
+
});
|
|
139
|
+
let sub = null;
|
|
140
|
+
let token = 0;
|
|
141
|
+
return {
|
|
142
|
+
signal,
|
|
143
|
+
start() {
|
|
144
|
+
if (!id) {
|
|
145
|
+
signal.value = { data: null, loading: false, error: null };
|
|
146
|
+
return () => {
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (sub) return () => stop();
|
|
150
|
+
const entityId = id;
|
|
151
|
+
const serverResolve = Boolean(schema) && Boolean(resolve && Object.keys(resolve).length > 0);
|
|
152
|
+
const applyRow = async (row, turn) => {
|
|
153
|
+
if (turn !== token) return;
|
|
154
|
+
if (!row) {
|
|
155
|
+
signal.value = { data: null, loading: false, error: null };
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
let data = row;
|
|
159
|
+
if (schema && resolve && Object.keys(resolve).length > 0 && !serverResolve) {
|
|
160
|
+
const [resolved] = await resolveRelations(client, schema, [data], resolve);
|
|
161
|
+
data = resolved ?? data;
|
|
162
|
+
}
|
|
163
|
+
if (turn !== token) return;
|
|
164
|
+
signal.value = { data, loading: false, error: null };
|
|
165
|
+
};
|
|
166
|
+
const readGen = token;
|
|
167
|
+
client.read(entityId).then((row) => applyRow(row, readGen)).catch((err) => {
|
|
168
|
+
signal.value = {
|
|
169
|
+
data: signal.peek().data,
|
|
170
|
+
loading: false,
|
|
171
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
sub = client.subscribe(
|
|
176
|
+
entityQuery(type, entityId),
|
|
177
|
+
(rows, _diff, meta) => {
|
|
178
|
+
const turn = ++token;
|
|
179
|
+
pickEntityFromRows(client, rows, entityId, meta).then((row) => applyRow(row, turn)).catch((err) => {
|
|
180
|
+
if (turn !== token) return;
|
|
181
|
+
signal.value = {
|
|
182
|
+
data: signal.peek().data,
|
|
183
|
+
loading: false,
|
|
184
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
serverResolve ? { entityType: type, resolve } : void 0
|
|
189
|
+
);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
signal.value = {
|
|
192
|
+
data: null,
|
|
193
|
+
loading: false,
|
|
194
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return () => stop();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
function stop() {
|
|
201
|
+
token++;
|
|
202
|
+
sub?.unsubscribe();
|
|
203
|
+
sub = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export {
|
|
208
|
+
liveQuery,
|
|
209
|
+
liveEntities,
|
|
210
|
+
liveEntity
|
|
211
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @bun
|
|
2
1
|
// src/client/config.ts
|
|
3
2
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
4
3
|
import { resolve, join } from "path";
|
|
@@ -11,8 +10,7 @@ function hasConfig(dir = ".") {
|
|
|
11
10
|
}
|
|
12
11
|
function readConfig(dir = ".") {
|
|
13
12
|
const path = configPath(dir);
|
|
14
|
-
if (!existsSync(path))
|
|
15
|
-
return null;
|
|
13
|
+
if (!existsSync(path)) return null;
|
|
16
14
|
try {
|
|
17
15
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
18
16
|
} catch {
|
|
@@ -21,8 +19,7 @@ function readConfig(dir = ".") {
|
|
|
21
19
|
}
|
|
22
20
|
function writeConfig(config, dir = ".") {
|
|
23
21
|
const path = configPath(dir);
|
|
24
|
-
writeFileSync(path, JSON.stringify(config, null, 2) +
|
|
25
|
-
`, "utf8");
|
|
22
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
26
23
|
}
|
|
27
24
|
function updateConfig(updates, dir = ".") {
|
|
28
25
|
const existing = readConfig(dir) ?? { mode: "local" };
|
|
@@ -34,10 +31,18 @@ function defaultLocalConfig(dbPath, opts = {}) {
|
|
|
34
31
|
return {
|
|
35
32
|
mode: "local",
|
|
36
33
|
dbPath,
|
|
37
|
-
port:
|
|
34
|
+
port: 3e3,
|
|
38
35
|
multiTenant: false,
|
|
39
36
|
...opts
|
|
40
37
|
};
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
export {
|
|
40
|
+
export {
|
|
41
|
+
CONFIG_FILE,
|
|
42
|
+
configPath,
|
|
43
|
+
hasConfig,
|
|
44
|
+
readConfig,
|
|
45
|
+
writeConfig,
|
|
46
|
+
updateConfig,
|
|
47
|
+
defaultLocalConfig
|
|
48
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
attachStandardMiddleware
|
|
3
|
+
} from "./chunk-SPSPV5BC.js";
|
|
4
|
+
import {
|
|
5
|
+
SqliteKernelBackend
|
|
6
|
+
} from "./chunk-XMQ7G77X.js";
|
|
7
|
+
import {
|
|
8
|
+
TrellisKernel
|
|
9
|
+
} from "./chunk-KQ4A2D2P.js";
|
|
10
|
+
|
|
11
|
+
// src/server/tenancy.ts
|
|
12
|
+
import { existsSync, mkdirSync } from "fs";
|
|
13
|
+
import { join, resolve } from "path";
|
|
14
|
+
var DEFAULT_TENANT = "default";
|
|
15
|
+
var TENANTS_DIR = "tenants";
|
|
16
|
+
function resolvePoolBackendFromEnv() {
|
|
17
|
+
const backend = process.env.TRELLIS_BACKEND;
|
|
18
|
+
if (backend === "sqljs") return { backend: "sqljs" };
|
|
19
|
+
if (backend === "better-sqlite") return { backend: "better-sqlite" };
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
var TenantPool = class {
|
|
23
|
+
pool = /* @__PURE__ */ new Map();
|
|
24
|
+
dbPath;
|
|
25
|
+
agentId;
|
|
26
|
+
backendOpts;
|
|
27
|
+
constructor(dbPath, agentIdOrOpts = "trellis-db") {
|
|
28
|
+
this.dbPath = resolve(dbPath);
|
|
29
|
+
if (typeof agentIdOrOpts === "string") {
|
|
30
|
+
this.agentId = agentIdOrOpts;
|
|
31
|
+
this.backendOpts = void 0;
|
|
32
|
+
} else {
|
|
33
|
+
this.agentId = agentIdOrOpts.agentId ?? "trellis-db";
|
|
34
|
+
this.backendOpts = agentIdOrOpts.backend;
|
|
35
|
+
}
|
|
36
|
+
this._ensureDirs();
|
|
37
|
+
}
|
|
38
|
+
// -------------------------------------------------------------------------
|
|
39
|
+
// Public API
|
|
40
|
+
// -------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* Get (or create) the kernel for a tenant.
|
|
43
|
+
* Pass `null` or `undefined` to get the default tenant.
|
|
44
|
+
*
|
|
45
|
+
* Uses the synchronous bun:sqlite backend. To use an async backend
|
|
46
|
+
* (better-sqlite3, sql.js) call `preload(tenantId)` first; subsequent
|
|
47
|
+
* `get()` calls will return the preloaded kernel from the pool.
|
|
48
|
+
*/
|
|
49
|
+
get(tenantId) {
|
|
50
|
+
const id = tenantId ?? DEFAULT_TENANT;
|
|
51
|
+
if (!this.pool.has(id)) {
|
|
52
|
+
const isBun = typeof process !== "undefined" && Boolean(process.versions?.bun);
|
|
53
|
+
if (!isBun) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
"TenantPool.get() on Node requires `await pool.preload()` first. `trellis db serve` preloads automatically; custom servers should call preload before get()."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
this.pool.set(id, this._createKernelSync(id));
|
|
59
|
+
}
|
|
60
|
+
return this.pool.get(id);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Pre-create a tenant's kernel using the runtime-selected backend.
|
|
64
|
+
* Required when running with an async-only backend (sql.js / WebContainer
|
|
65
|
+
* / browser). Idempotent — safe to call multiple times.
|
|
66
|
+
*
|
|
67
|
+
* After `preload()`, subsequent `get(tenantId)` returns the same kernel
|
|
68
|
+
* synchronously from the pool.
|
|
69
|
+
*/
|
|
70
|
+
async preload(tenantId) {
|
|
71
|
+
const id = tenantId ?? DEFAULT_TENANT;
|
|
72
|
+
const cached = this.pool.get(id);
|
|
73
|
+
if (cached) return cached;
|
|
74
|
+
const kernel = await this._createKernelAsync(id);
|
|
75
|
+
this.pool.set(id, kernel);
|
|
76
|
+
return kernel;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check whether a tenant has been initialized (kernel created).
|
|
80
|
+
*/
|
|
81
|
+
has(tenantId) {
|
|
82
|
+
return this.pool.has(tenantId);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* List all active tenant IDs (those with open kernels).
|
|
86
|
+
*/
|
|
87
|
+
activeTenants() {
|
|
88
|
+
return Array.from(this.pool.keys());
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Close a specific tenant's kernel and remove it from the pool.
|
|
92
|
+
*/
|
|
93
|
+
close(tenantId) {
|
|
94
|
+
const kernel = this.pool.get(tenantId);
|
|
95
|
+
if (kernel) {
|
|
96
|
+
kernel.close();
|
|
97
|
+
this.pool.delete(tenantId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Close all kernels and clear the pool.
|
|
102
|
+
*/
|
|
103
|
+
closeAll() {
|
|
104
|
+
for (const kernel of this.pool.values()) {
|
|
105
|
+
kernel.close();
|
|
106
|
+
}
|
|
107
|
+
this.pool.clear();
|
|
108
|
+
}
|
|
109
|
+
/** Absolute path to the tenant data directory. */
|
|
110
|
+
dataPath() {
|
|
111
|
+
return this.dbPath;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Return the SQLite file path for a given tenant.
|
|
115
|
+
*/
|
|
116
|
+
dbFilePath(tenantId) {
|
|
117
|
+
const id = tenantId ?? DEFAULT_TENANT;
|
|
118
|
+
if (id === DEFAULT_TENANT) {
|
|
119
|
+
return join(this.dbPath, "default.sqlite");
|
|
120
|
+
}
|
|
121
|
+
return join(this.dbPath, TENANTS_DIR, `${id}.sqlite`);
|
|
122
|
+
}
|
|
123
|
+
// -------------------------------------------------------------------------
|
|
124
|
+
// Private
|
|
125
|
+
// -------------------------------------------------------------------------
|
|
126
|
+
_createKernelSync(tenantId) {
|
|
127
|
+
const sqlitePath = this.dbFilePath(tenantId);
|
|
128
|
+
const backend = new SqliteKernelBackend(sqlitePath);
|
|
129
|
+
return this._wrapKernel(backend);
|
|
130
|
+
}
|
|
131
|
+
async _createKernelAsync(tenantId) {
|
|
132
|
+
const sqlitePath = this.dbFilePath(tenantId);
|
|
133
|
+
const { createKernelBackend } = await import("./core/persist/factory.js");
|
|
134
|
+
const backend = await createKernelBackend(sqlitePath, this.backendOpts);
|
|
135
|
+
return this._wrapKernel(backend);
|
|
136
|
+
}
|
|
137
|
+
_wrapKernel(backend) {
|
|
138
|
+
const kernel = new TrellisKernel({
|
|
139
|
+
backend,
|
|
140
|
+
agentId: this.agentId,
|
|
141
|
+
snapshotThreshold: 1e3
|
|
142
|
+
});
|
|
143
|
+
kernel.boot();
|
|
144
|
+
attachStandardMiddleware(kernel);
|
|
145
|
+
return kernel;
|
|
146
|
+
}
|
|
147
|
+
_ensureDirs() {
|
|
148
|
+
if (!existsSync(this.dbPath)) {
|
|
149
|
+
mkdirSync(this.dbPath, { recursive: true });
|
|
150
|
+
}
|
|
151
|
+
const tenantsPath = join(this.dbPath, TENANTS_DIR);
|
|
152
|
+
if (!existsSync(tenantsPath)) {
|
|
153
|
+
mkdirSync(tenantsPath, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
DEFAULT_TENANT,
|
|
160
|
+
resolvePoolBackendFromEnv,
|
|
161
|
+
TenantPool
|
|
162
|
+
};
|