syncorejs 0.2.2 → 0.2.3
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/_vendor/cli/app.d.mts.map +1 -1
- package/dist/_vendor/cli/app.mjs +8 -5
- package/dist/_vendor/cli/app.mjs.map +1 -1
- package/dist/_vendor/cli/context.mjs.map +1 -1
- package/dist/_vendor/cli/dev-session.mjs.map +1 -1
- package/dist/_vendor/cli/doctor.mjs.map +1 -1
- package/dist/_vendor/cli/errors.mjs.map +1 -1
- package/dist/_vendor/cli/help.mjs.map +1 -1
- package/dist/_vendor/cli/index.mjs +9 -2
- package/dist/_vendor/cli/index.mjs.map +1 -1
- package/dist/_vendor/cli/messages.mjs.map +1 -1
- package/dist/_vendor/cli/preflight.mjs.map +1 -1
- package/dist/_vendor/cli/project.mjs +20 -20
- package/dist/_vendor/cli/project.mjs.map +1 -1
- package/dist/_vendor/cli/render.mjs.map +1 -1
- package/dist/_vendor/cli/targets.mjs.map +1 -1
- package/dist/_vendor/core/cli.d.mts +8 -2
- package/dist/_vendor/core/cli.d.mts.map +1 -1
- package/dist/_vendor/core/cli.mjs +238 -64
- package/dist/_vendor/core/cli.mjs.map +1 -1
- package/dist/_vendor/core/devtools-auth.mjs.map +1 -1
- package/dist/_vendor/core/runtime/components.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/components.mjs.map +1 -1
- package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/devtools.mjs +130 -23
- package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
- package/dist/_vendor/core/runtime/functions.d.mts +388 -6
- package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/functions.mjs +72 -1
- package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
- package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/id.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +11 -5
- package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +123 -20
- package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +56 -8
- package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +49 -14
- package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +4 -7
- package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs +76 -1
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +1 -0
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +4 -3
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +4 -0
- package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -1
- package/dist/_vendor/core/runtime/runtime.d.mts +1040 -9
- package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/runtime.mjs +63 -0
- package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
- package/dist/_vendor/core/transport.d.mts +2 -0
- package/dist/_vendor/core/transport.d.mts.map +1 -1
- package/dist/_vendor/core/transport.mjs +33 -24
- package/dist/_vendor/core/transport.mjs.map +1 -1
- package/dist/_vendor/devtools-protocol/index.d.ts +149 -4
- package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
- package/dist/_vendor/devtools-protocol/index.js.map +1 -1
- package/dist/_vendor/next/config.d.ts +3 -4
- package/dist/_vendor/next/config.d.ts.map +1 -1
- package/dist/_vendor/next/config.js +37 -19
- package/dist/_vendor/next/config.js.map +1 -1
- package/dist/_vendor/next/index.d.ts +109 -29
- package/dist/_vendor/next/index.d.ts.map +1 -1
- package/dist/_vendor/next/index.js +77 -17
- package/dist/_vendor/next/index.js.map +1 -1
- package/dist/_vendor/platform-expo/index.d.ts +146 -27
- package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
- package/dist/_vendor/platform-expo/index.js +76 -10
- package/dist/_vendor/platform-expo/index.js.map +1 -1
- package/dist/_vendor/platform-expo/react.js.map +1 -1
- package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
- package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
- package/dist/_vendor/platform-node/index.d.mts +173 -9
- package/dist/_vendor/platform-node/index.d.mts.map +1 -1
- package/dist/_vendor/platform-node/index.mjs +225 -94
- package/dist/_vendor/platform-node/index.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
- package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
- package/dist/_vendor/platform-web/external-change.d.ts +41 -0
- package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
- package/dist/_vendor/platform-web/external-change.js +30 -0
- package/dist/_vendor/platform-web/external-change.js.map +1 -1
- package/dist/_vendor/platform-web/index.d.ts +307 -35
- package/dist/_vendor/platform-web/index.d.ts.map +1 -1
- package/dist/_vendor/platform-web/index.js +189 -23
- package/dist/_vendor/platform-web/index.js.map +1 -1
- package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
- package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
- package/dist/_vendor/platform-web/indexeddb.js +10 -0
- package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
- package/dist/_vendor/platform-web/opfs.d.ts +13 -0
- package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
- package/dist/_vendor/platform-web/opfs.js +12 -0
- package/dist/_vendor/platform-web/opfs.js.map +1 -1
- package/dist/_vendor/platform-web/persistence.d.ts +54 -0
- package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
- package/dist/_vendor/platform-web/persistence.js +15 -0
- package/dist/_vendor/platform-web/persistence.js.map +1 -1
- package/dist/_vendor/platform-web/react.d.ts +1 -2
- package/dist/_vendor/platform-web/react.d.ts.map +1 -1
- package/dist/_vendor/platform-web/react.js +2 -4
- package/dist/_vendor/platform-web/react.js.map +1 -1
- package/dist/_vendor/platform-web/sqljs.js +10 -1
- package/dist/_vendor/platform-web/sqljs.js.map +1 -1
- package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
- package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
- package/dist/_vendor/platform-web/worker.d.ts +60 -9
- package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
- package/dist/_vendor/platform-web/worker.js +37 -4
- package/dist/_vendor/platform-web/worker.js.map +1 -1
- package/dist/_vendor/react/index.d.ts +196 -13
- package/dist/_vendor/react/index.d.ts.map +1 -1
- package/dist/_vendor/react/index.js +208 -17
- package/dist/_vendor/react/index.js.map +1 -1
- package/dist/_vendor/schema/definition.d.ts +129 -0
- package/dist/_vendor/schema/definition.d.ts.map +1 -1
- package/dist/_vendor/schema/definition.js +99 -0
- package/dist/_vendor/schema/definition.js.map +1 -1
- package/dist/_vendor/schema/planner.d.ts.map +1 -1
- package/dist/_vendor/schema/planner.js.map +1 -1
- package/dist/_vendor/schema/validators.d.ts +180 -4
- package/dist/_vendor/schema/validators.d.ts.map +1 -1
- package/dist/_vendor/schema/validators.js +35 -1
- package/dist/_vendor/schema/validators.js.map +1 -1
- package/dist/_vendor/svelte/index.d.ts +205 -7
- package/dist/_vendor/svelte/index.d.ts.map +1 -1
- package/dist/_vendor/svelte/index.js +199 -6
- package/dist/_vendor/svelte/index.js.map +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +24 -21
|
@@ -1,106 +1,43 @@
|
|
|
1
1
|
import { SyncoreRendererClient, attachNodeIpcRuntime, createNodeIpcMessageEndpoint, createRendererSyncoreBridgeClient, createRendererSyncoreClient, createRendererSyncoreWindowClient, installSyncoreWindowBridge } from "./ipc.mjs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { DatabaseSync } from "node:sqlite";
|
|
6
7
|
import WebSocket from "ws";
|
|
7
8
|
import { SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION, SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION, SYNCORE_DEVTOOLS_PROTOCOL_VERSION } from "../devtools-protocol/index.js";
|
|
8
9
|
import { SyncoreRuntime, createDevtoolsCommandHandler, createDevtoolsSubscriptionHost } from "../core/index.mjs";
|
|
9
10
|
//#region src/index.ts
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const nodeDevtoolsSqlSupport = {
|
|
13
|
-
analyzeSqlStatement(query) {
|
|
14
|
-
const ast = nodeSqlParser.astify(query, { database: "sqlite" });
|
|
15
|
-
if (Array.isArray(ast)) throw new Error("Only a single SQL statement is supported.");
|
|
16
|
-
switch (ast.type) {
|
|
17
|
-
case "select": return buildReadAnalysis(ast);
|
|
18
|
-
case "update":
|
|
19
|
-
case "delete":
|
|
20
|
-
case "insert":
|
|
21
|
-
case "replace": return buildWriteAnalysis(extractTables(ast.table), false);
|
|
22
|
-
case "create":
|
|
23
|
-
case "drop":
|
|
24
|
-
case "alter": return buildWriteAnalysis(extractTables(ast.table), true);
|
|
25
|
-
default: throw new Error(`Unsupported SQL statement type: ${String(ast.type)}`);
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
ensureSqlMode(analysis, expected) {
|
|
29
|
-
if (expected === "watch") {
|
|
30
|
-
if (analysis.mode !== "read") throw new Error("Live mode supports read-only SQL only.");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (analysis.mode !== expected) {
|
|
34
|
-
if (expected === "read") throw new Error("Use SQL Write for mutating statements.");
|
|
35
|
-
throw new Error("Use SQL Read or SQL Live for read-only statements.");
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
runReadonlyQuery(databasePath, query) {
|
|
39
|
-
const analysis = this.analyzeSqlStatement(query);
|
|
40
|
-
this.ensureSqlMode(analysis, "read");
|
|
41
|
-
const database = new DatabaseSync(databasePath, { readOnly: true });
|
|
42
|
-
try {
|
|
43
|
-
const statement = database.prepare(query);
|
|
44
|
-
const rows = statement.all();
|
|
45
|
-
const columnsMeta = statement.columns();
|
|
46
|
-
const columns = columnsMeta.map((column) => column.name);
|
|
47
|
-
const observedTables = Array.from(new Set(columnsMeta.map((column) => column.table).filter((table) => typeof table === "string")));
|
|
48
|
-
return {
|
|
49
|
-
columns,
|
|
50
|
-
rows: rows.map((row) => columns.map((column) => row[column])),
|
|
51
|
-
observedTables: observedTables.length > 0 ? observedTables : analysis.readTables
|
|
52
|
-
};
|
|
53
|
-
} finally {
|
|
54
|
-
database.close();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
};
|
|
11
|
+
const DEVTOOLS_META_DIRECTORY = ".syncore-devtools";
|
|
12
|
+
const DATA_SOURCE_ALIAS_PREFIX = "data-source-alias";
|
|
58
13
|
function normalizeData(input) {
|
|
59
14
|
if (typeof input === "string") return Buffer.from(input);
|
|
60
15
|
if (input instanceof Uint8Array) return input;
|
|
61
16
|
return new Uint8Array(input);
|
|
62
17
|
}
|
|
63
|
-
function buildReadAnalysis(ast) {
|
|
64
|
-
const readTables = Array.from(new Set(extractReadTables(ast)));
|
|
65
|
-
return {
|
|
66
|
-
mode: "read",
|
|
67
|
-
readTables,
|
|
68
|
-
writeTables: [],
|
|
69
|
-
schemaChanged: false,
|
|
70
|
-
observedScopes: readTables.length > 0 ? readTables.map((table) => `table:${table}`) : ["all"]
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function buildWriteAnalysis(tables, schemaChanged) {
|
|
74
|
-
const uniqueTables = Array.from(new Set(tables));
|
|
75
|
-
return {
|
|
76
|
-
mode: schemaChanged ? "ddl" : "write",
|
|
77
|
-
readTables: [],
|
|
78
|
-
writeTables: uniqueTables,
|
|
79
|
-
schemaChanged,
|
|
80
|
-
observedScopes: schemaChanged ? ["schema.tables", ...uniqueTables.map((table) => `table:${table}`)] : uniqueTables.length > 0 ? uniqueTables.map((table) => `table:${table}`) : ["all"]
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function extractReadTables(ast) {
|
|
84
|
-
return (ast.from ?? []).flatMap((entry) => {
|
|
85
|
-
if (entry.table) return [entry.table];
|
|
86
|
-
if (entry.expr?.ast) return extractReadTables(entry.expr.ast);
|
|
87
|
-
return [];
|
|
88
|
-
}).filter((table) => table !== "dual");
|
|
89
|
-
}
|
|
90
|
-
function extractTables(table) {
|
|
91
|
-
if (Array.isArray(table)) return table.map((entry) => entry?.table).filter((value) => typeof value === "string");
|
|
92
|
-
if (table && typeof table === "object") return typeof table.table === "string" ? [table.table] : [];
|
|
93
|
-
if (typeof table === "string") return [table];
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
18
|
function toSqlParameters(params) {
|
|
97
19
|
return params;
|
|
98
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* SQLite driver backed by Node.js’s built-in `node:sqlite` module (Node 22+).
|
|
23
|
+
*
|
|
24
|
+
* Opens the database at `databasePath` with `WAL` journal mode and foreign-key
|
|
25
|
+
* enforcement enabled. The file is created if it does not exist.
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* const driver = new NodeSqliteDriver("./data/app.db");
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* In most cases you should use {@link createNodeSyncoreRuntime} which
|
|
32
|
+
* instantiates this driver automatically from your `databasePath` option.
|
|
33
|
+
*/
|
|
99
34
|
var NodeSqliteDriver = class {
|
|
35
|
+
databasePath;
|
|
100
36
|
database;
|
|
101
37
|
transactionDepth = 0;
|
|
102
|
-
constructor(
|
|
103
|
-
this.
|
|
38
|
+
constructor(databasePath) {
|
|
39
|
+
this.databasePath = databasePath;
|
|
40
|
+
this.database = new DatabaseSync(databasePath);
|
|
104
41
|
this.database.exec("PRAGMA foreign_keys = ON;");
|
|
105
42
|
this.database.exec("PRAGMA journal_mode = WAL;");
|
|
106
43
|
}
|
|
@@ -152,7 +89,21 @@ var NodeSqliteDriver = class {
|
|
|
152
89
|
this.database.close();
|
|
153
90
|
}
|
|
154
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* Blob storage adapter that reads and writes files in a local directory.
|
|
94
|
+
*
|
|
95
|
+
* Each stored object is saved as a flat file named by its ID inside
|
|
96
|
+
* `directory`. The directory is created automatically on first write.
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* const storage = new NodeFileStorageAdapter("./data/storage");
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* In most cases you should use {@link createNodeSyncoreRuntime} which
|
|
103
|
+
* instantiates this adapter automatically from your `storageDirectory` option.
|
|
104
|
+
*/
|
|
155
105
|
var NodeFileStorageAdapter = class {
|
|
106
|
+
directory;
|
|
156
107
|
constructor(directory) {
|
|
157
108
|
this.directory = directory;
|
|
158
109
|
}
|
|
@@ -212,11 +163,137 @@ var NodeFileStorageAdapter = class {
|
|
|
212
163
|
}
|
|
213
164
|
}
|
|
214
165
|
};
|
|
166
|
+
const SESSION_ADJECTIVES = [
|
|
167
|
+
"Acrobatic",
|
|
168
|
+
"Bold",
|
|
169
|
+
"Cosmic",
|
|
170
|
+
"Daring",
|
|
171
|
+
"Electric",
|
|
172
|
+
"Fierce",
|
|
173
|
+
"Golden",
|
|
174
|
+
"Hidden",
|
|
175
|
+
"Iron",
|
|
176
|
+
"Jade",
|
|
177
|
+
"Keen",
|
|
178
|
+
"Lunar",
|
|
179
|
+
"Mystic",
|
|
180
|
+
"Noble",
|
|
181
|
+
"Orbital",
|
|
182
|
+
"Primal",
|
|
183
|
+
"Quick",
|
|
184
|
+
"Radiant",
|
|
185
|
+
"Shadow",
|
|
186
|
+
"Turbo",
|
|
187
|
+
"Ultra",
|
|
188
|
+
"Vivid",
|
|
189
|
+
"Wicked",
|
|
190
|
+
"Xenon",
|
|
191
|
+
"Zen",
|
|
192
|
+
"Arctic",
|
|
193
|
+
"Binary",
|
|
194
|
+
"Cyber",
|
|
195
|
+
"Digital",
|
|
196
|
+
"Ember",
|
|
197
|
+
"Frozen",
|
|
198
|
+
"Galactic",
|
|
199
|
+
"Hyper",
|
|
200
|
+
"Infra",
|
|
201
|
+
"Jumbo",
|
|
202
|
+
"Kinetic",
|
|
203
|
+
"Liquid",
|
|
204
|
+
"Magnetic",
|
|
205
|
+
"Neon",
|
|
206
|
+
"Onyx",
|
|
207
|
+
"Phantom",
|
|
208
|
+
"Quantum",
|
|
209
|
+
"Rapid",
|
|
210
|
+
"Sonic",
|
|
211
|
+
"Titan",
|
|
212
|
+
"Velvet",
|
|
213
|
+
"Wild",
|
|
214
|
+
"Blazing",
|
|
215
|
+
"Crystal",
|
|
216
|
+
"Dynamic"
|
|
217
|
+
];
|
|
218
|
+
const SESSION_NOUNS = [
|
|
219
|
+
"Phoenix",
|
|
220
|
+
"Dragon",
|
|
221
|
+
"Developer",
|
|
222
|
+
"Hacker",
|
|
223
|
+
"Wizard",
|
|
224
|
+
"Runner",
|
|
225
|
+
"Ranger",
|
|
226
|
+
"Maverick",
|
|
227
|
+
"Spartan",
|
|
228
|
+
"Viking",
|
|
229
|
+
"Sentinel",
|
|
230
|
+
"Guardian",
|
|
231
|
+
"Nomad",
|
|
232
|
+
"Cipher",
|
|
233
|
+
"Vector",
|
|
234
|
+
"Matrix",
|
|
235
|
+
"Prism",
|
|
236
|
+
"Nebula",
|
|
237
|
+
"Comet",
|
|
238
|
+
"Pulse",
|
|
239
|
+
"Vertex",
|
|
240
|
+
"Flux",
|
|
241
|
+
"Storm",
|
|
242
|
+
"Blaze",
|
|
243
|
+
"Frost",
|
|
244
|
+
"Thunder",
|
|
245
|
+
"Drift"
|
|
246
|
+
];
|
|
247
|
+
function generateUniqueSessionName() {
|
|
248
|
+
return `${SESSION_ADJECTIVES[Math.floor(Math.random() * SESSION_ADJECTIVES.length)]} ${SESSION_NOUNS[Math.floor(Math.random() * SESSION_NOUNS.length)]}`;
|
|
249
|
+
}
|
|
250
|
+
function resolvePersistedDataSourceAlias(storageDirectory, storageIdentity) {
|
|
251
|
+
const metaDirectory = path.join(storageDirectory, DEVTOOLS_META_DIRECTORY);
|
|
252
|
+
const aliasId = createHash("sha256").update(storageIdentity).digest("hex").slice(0, 16);
|
|
253
|
+
const aliasPath = path.join(metaDirectory, `${DATA_SOURCE_ALIAS_PREFIX}-${aliasId}.txt`);
|
|
254
|
+
try {
|
|
255
|
+
const existing = readFileSync(aliasPath, "utf8").trim();
|
|
256
|
+
if (existing.length > 0) return existing;
|
|
257
|
+
} catch {}
|
|
258
|
+
const nextValue = generateUniqueSessionName();
|
|
259
|
+
try {
|
|
260
|
+
mkdirSync(metaDirectory, { recursive: true });
|
|
261
|
+
writeFileSync(aliasPath, nextValue, "utf8");
|
|
262
|
+
} catch {}
|
|
263
|
+
return nextValue;
|
|
264
|
+
}
|
|
215
265
|
/**
|
|
216
|
-
* Create a Node or Electron
|
|
266
|
+
* Create a Syncore runtime for Node.js (or Electron’s main process) backed by
|
|
267
|
+
* the built-in `node:sqlite` driver and local file storage.
|
|
268
|
+
*
|
|
269
|
+
* This is the recommended entry point for Node and Electron apps. It wires up
|
|
270
|
+
* the SQL driver, storage adapter, devtools WebSocket connection, and
|
|
271
|
+
* cross-process change signals automatically.
|
|
272
|
+
*
|
|
273
|
+
* ```ts
|
|
274
|
+
* import path from "node:path";
|
|
275
|
+
* import { createNodeSyncoreRuntime } from "syncorejs/node";
|
|
276
|
+
* import schema from "./syncore/schema";
|
|
277
|
+
* import { functions } from "./syncore/_generated/functions";
|
|
278
|
+
*
|
|
279
|
+
* const runtime = createNodeSyncoreRuntime({
|
|
280
|
+
* databasePath: path.join(app.getPath("userData"), "db.sqlite"),
|
|
281
|
+
* storageDirectory: path.join(app.getPath("userData"), "storage"),
|
|
282
|
+
* schema,
|
|
283
|
+
* functions,
|
|
284
|
+
* });
|
|
285
|
+
*
|
|
286
|
+
* await runtime.start();
|
|
287
|
+
* const client = runtime.createClient();
|
|
288
|
+
* ```
|
|
289
|
+
*
|
|
290
|
+
* @param options - Configuration object. See {@link CreateNodeRuntimeOptions}.
|
|
291
|
+
* @returns A configured (but not yet started) {@link SyncoreRuntime}. Call
|
|
292
|
+
* `await runtime.start()` before using the client.
|
|
217
293
|
*/
|
|
218
294
|
function createNodeSyncoreRuntime(options) {
|
|
219
295
|
const resolvedDevtoolsUrl = options.devtoolsUrl ?? resolveDefaultNodeDevtoolsUrl();
|
|
296
|
+
const storageIdentity = `file::${path.resolve(options.databasePath)}`;
|
|
220
297
|
const websocketDevtools = options.devtools === void 0 && resolvedDevtoolsUrl && shouldAutoConnectNodeDevtools() ? createNodeWebSocketDevtoolsSink({
|
|
221
298
|
url: resolvedDevtoolsUrl,
|
|
222
299
|
...options.appName ? { appName: options.appName } : {},
|
|
@@ -225,7 +302,10 @@ function createNodeSyncoreRuntime(options) {
|
|
|
225
302
|
targetKind: "client",
|
|
226
303
|
storageProtocol: "file",
|
|
227
304
|
databaseLabel: path.basename(options.databasePath),
|
|
228
|
-
|
|
305
|
+
dataSourceAlias: resolvePersistedDataSourceAlias(options.storageDirectory, storageIdentity),
|
|
306
|
+
storageIdentity,
|
|
307
|
+
runtimeRole: "app",
|
|
308
|
+
capabilities: createNodeDevtoolsCapabilities()
|
|
229
309
|
}) : void 0;
|
|
230
310
|
const runtimeOptions = {
|
|
231
311
|
schema: options.schema,
|
|
@@ -238,6 +318,7 @@ function createNodeSyncoreRuntime(options) {
|
|
|
238
318
|
if (options.capabilities) runtimeOptions.capabilities = options.capabilities;
|
|
239
319
|
const resolvedDevtools = options.devtools === false ? void 0 : options.devtools ?? websocketDevtools;
|
|
240
320
|
if (resolvedDevtools) runtimeOptions.devtools = resolvedDevtools;
|
|
321
|
+
if (websocketDevtools?.externalChangeSignal) runtimeOptions.externalChangeSignal = websocketDevtools.externalChangeSignal;
|
|
241
322
|
if (options.scheduler) runtimeOptions.scheduler = options.scheduler;
|
|
242
323
|
const runtime = new SyncoreRuntime(runtimeOptions);
|
|
243
324
|
websocketDevtools?.attachRuntime(runtime);
|
|
@@ -246,15 +327,13 @@ function createNodeSyncoreRuntime(options) {
|
|
|
246
327
|
driver: runtimeOptions.driver,
|
|
247
328
|
schema: options.schema,
|
|
248
329
|
functions: options.functions,
|
|
249
|
-
admin: runtime.getAdmin()
|
|
250
|
-
sql: nodeDevtoolsSqlSupport
|
|
330
|
+
admin: runtime.getAdmin()
|
|
251
331
|
}));
|
|
252
332
|
websocketDevtools.attachSubscriptionHost(createDevtoolsSubscriptionHost({
|
|
253
333
|
driver: runtimeOptions.driver,
|
|
254
334
|
schema: options.schema,
|
|
255
335
|
functions: options.functions,
|
|
256
|
-
admin: runtime.getAdmin()
|
|
257
|
-
sql: nodeDevtoolsSqlSupport
|
|
336
|
+
admin: runtime.getAdmin()
|
|
258
337
|
}));
|
|
259
338
|
const stop = runtime.stop.bind(runtime);
|
|
260
339
|
runtime.stop = async () => {
|
|
@@ -363,6 +442,7 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
363
442
|
let getSummary;
|
|
364
443
|
let onCommand;
|
|
365
444
|
let subscriptionHost;
|
|
445
|
+
const externalChangeListeners = /* @__PURE__ */ new Set();
|
|
366
446
|
const pendingMessages = [];
|
|
367
447
|
let latestHello;
|
|
368
448
|
const connect = () => {
|
|
@@ -380,9 +460,12 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
380
460
|
...options.origin ? { origin: options.origin } : {},
|
|
381
461
|
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
382
462
|
...options.targetKind ? { targetKind: options.targetKind } : {},
|
|
463
|
+
...options.runtimeRole ? { runtimeRole: options.runtimeRole } : {},
|
|
383
464
|
...options.storageProtocol ? { storageProtocol: options.storageProtocol } : {},
|
|
384
465
|
...options.databaseLabel ? { databaseLabel: options.databaseLabel } : {},
|
|
385
|
-
...options.
|
|
466
|
+
...options.dataSourceAlias ? { dataSourceAlias: options.dataSourceAlias } : {},
|
|
467
|
+
...options.storageIdentity ? { storageIdentity: options.storageIdentity } : {},
|
|
468
|
+
capabilities: options.capabilities ?? createNodeDevtoolsCapabilities()
|
|
386
469
|
});
|
|
387
470
|
flushPendingMessages();
|
|
388
471
|
});
|
|
@@ -391,6 +474,7 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
391
474
|
if (rawPayload.length === 0) return;
|
|
392
475
|
const message = JSON.parse(rawPayload);
|
|
393
476
|
if (message.type === "ping") send({ type: "pong" });
|
|
477
|
+
else if (message.type === "external.change") for (const listener of externalChangeListeners) listener(message.event);
|
|
394
478
|
else if (message.type === "command" && onCommand) onCommand(message.payload).then((responsePayload) => {
|
|
395
479
|
const runtimeId = latestHello?.runtimeId ?? getSummary?.().runtimeId;
|
|
396
480
|
if (!runtimeId) return;
|
|
@@ -453,7 +537,7 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
453
537
|
pendingMessages.push(message);
|
|
454
538
|
};
|
|
455
539
|
connect();
|
|
456
|
-
|
|
540
|
+
const sink = {
|
|
457
541
|
emit(event) {
|
|
458
542
|
if (event.type === "runtime.connected") {
|
|
459
543
|
latestHello = {
|
|
@@ -471,9 +555,12 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
471
555
|
...options.origin ? { origin: options.origin } : {},
|
|
472
556
|
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
473
557
|
...options.targetKind ? { targetKind: options.targetKind } : {},
|
|
558
|
+
...options.runtimeRole ? { runtimeRole: options.runtimeRole } : {},
|
|
474
559
|
...options.storageProtocol ? { storageProtocol: options.storageProtocol } : {},
|
|
475
560
|
...options.databaseLabel ? { databaseLabel: options.databaseLabel } : {},
|
|
476
|
-
...options.
|
|
561
|
+
...options.dataSourceAlias ? { dataSourceAlias: options.dataSourceAlias } : {},
|
|
562
|
+
...options.storageIdentity ? { storageIdentity: options.storageIdentity } : {},
|
|
563
|
+
capabilities: options.capabilities ?? createNodeDevtoolsCapabilities()
|
|
477
564
|
});
|
|
478
565
|
}
|
|
479
566
|
send({
|
|
@@ -497,6 +584,28 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
497
584
|
socket?.close();
|
|
498
585
|
}
|
|
499
586
|
};
|
|
587
|
+
if (options.storageIdentity) sink.externalChangeSignal = {
|
|
588
|
+
subscribe(listener) {
|
|
589
|
+
externalChangeListeners.add(listener);
|
|
590
|
+
return () => {
|
|
591
|
+
externalChangeListeners.delete(listener);
|
|
592
|
+
};
|
|
593
|
+
},
|
|
594
|
+
publish(event) {
|
|
595
|
+
const runtimeId = latestHello?.runtimeId ?? getSummary?.().runtimeId;
|
|
596
|
+
if (!runtimeId) return;
|
|
597
|
+
send({
|
|
598
|
+
type: "external.change",
|
|
599
|
+
runtimeId,
|
|
600
|
+
storageIdentity: options.storageIdentity,
|
|
601
|
+
event
|
|
602
|
+
});
|
|
603
|
+
},
|
|
604
|
+
close() {
|
|
605
|
+
externalChangeListeners.clear();
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
return sink;
|
|
500
609
|
}
|
|
501
610
|
function withRuntimeSummaryMeta(summary, options) {
|
|
502
611
|
return {
|
|
@@ -505,9 +614,31 @@ function withRuntimeSummaryMeta(summary, options) {
|
|
|
505
614
|
...options.origin ? { origin: options.origin } : {},
|
|
506
615
|
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
507
616
|
...options.targetKind ? { targetKind: options.targetKind } : {},
|
|
617
|
+
...options.runtimeRole ? { runtimeRole: options.runtimeRole } : {},
|
|
508
618
|
...options.storageProtocol ? { storageProtocol: options.storageProtocol } : {},
|
|
509
619
|
...options.databaseLabel ? { databaseLabel: options.databaseLabel } : {},
|
|
510
|
-
...options.
|
|
620
|
+
...options.dataSourceAlias ? { dataSourceAlias: options.dataSourceAlias } : {},
|
|
621
|
+
...options.storageIdentity ? { storageIdentity: options.storageIdentity } : {},
|
|
622
|
+
capabilities: options.capabilities ?? createNodeDevtoolsCapabilities()
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function createNodeDevtoolsCapabilities() {
|
|
626
|
+
return {
|
|
627
|
+
sql: {
|
|
628
|
+
read: false,
|
|
629
|
+
write: false,
|
|
630
|
+
live: false,
|
|
631
|
+
reason: "SQL Console is provided by the Project Target for this data source."
|
|
632
|
+
},
|
|
633
|
+
data: {
|
|
634
|
+
browse: true,
|
|
635
|
+
mutate: true,
|
|
636
|
+
importExport: true
|
|
637
|
+
},
|
|
638
|
+
scheduler: {
|
|
639
|
+
read: true,
|
|
640
|
+
edit: true
|
|
641
|
+
}
|
|
511
642
|
};
|
|
512
643
|
}
|
|
513
644
|
function shouldAutoConnectNodeDevtools() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["createNodeRequire"],"sources":["../src/index.ts"],"sourcesContent":["import {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile\n} from \"node:fs/promises\";\nimport { createRequire as createNodeRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { DatabaseSync, type SQLInputValue } from \"node:sqlite\";\nimport WebSocket from \"ws\";\nimport {\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION,\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION,\n SYNCORE_DEVTOOLS_PROTOCOL_VERSION,\n} from \"@syncore/devtools-protocol\";\nimport type {\n SyncoreDevtoolsClientMessage,\n SyncoreDevtoolsMessage,\n SyncoreRuntimeSummary\n} from \"@syncore/devtools-protocol\";\nimport {\n createDevtoolsCommandHandler,\n createDevtoolsSubscriptionHost,\n type DevtoolsSqlAnalysis,\n type DevtoolsCommandHandler,\n type DevtoolsSqlMode,\n type DevtoolsSqlReadResult,\n type DevtoolsSqlSupport,\n type DevtoolsSink,\n type DevtoolsSubscriptionHost,\n type SchedulerOptions,\n type StorageObject,\n type StorageWriteInput,\n type SyncoreCapabilities,\n type SyncoreDataModel,\n SyncoreRuntime,\n type SyncoreRuntimeOptions,\n type SyncoreSqlDriver,\n type SyncoreStorageAdapter\n} from \"@syncore/core\";\nimport { attachNodeIpcRuntime, createNodeIpcMessageEndpoint } from \"./ipc.js\";\nexport * from \"./ipc.js\";\nexport type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent,\n SyncoreRuntimeSummary\n} from \"@syncore/devtools-protocol\";\n\nexport type NodeSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\n\nconst nodeRequire = createNodeRequire(import.meta.url);\nconst { Parser: NodeSqlParser } = nodeRequire(\"node-sql-parser\") as {\n Parser: new () => {\n astify(sql: string, options?: { database?: string }): unknown;\n };\n};\nconst nodeSqlParser = new NodeSqlParser();\n\ntype SqlAst = {\n type: string;\n from?: Array<{ table?: string; expr?: { ast?: SqlAst } }>;\n table?: Array<{ table?: string }> | { table?: string } | null | string;\n};\n\nconst nodeDevtoolsSqlSupport: DevtoolsSqlSupport = {\n analyzeSqlStatement(query: string): DevtoolsSqlAnalysis {\n const ast = nodeSqlParser.astify(query, {\n database: \"sqlite\"\n }) as SqlAst | SqlAst[];\n if (Array.isArray(ast)) {\n throw new Error(\"Only a single SQL statement is supported.\");\n }\n\n switch (ast.type) {\n case \"select\":\n return buildReadAnalysis(ast);\n case \"update\":\n case \"delete\":\n case \"insert\":\n case \"replace\":\n return buildWriteAnalysis(extractTables(ast.table), false);\n case \"create\":\n case \"drop\":\n case \"alter\":\n return buildWriteAnalysis(extractTables(ast.table), true);\n default:\n throw new Error(`Unsupported SQL statement type: ${String(ast.type)}`);\n }\n },\n ensureSqlMode(\n analysis: DevtoolsSqlAnalysis,\n expected: DevtoolsSqlMode | \"watch\"\n ): void {\n if (expected === \"watch\") {\n if (analysis.mode !== \"read\") {\n throw new Error(\"Live mode supports read-only SQL only.\");\n }\n return;\n }\n\n if (analysis.mode !== expected) {\n if (expected === \"read\") {\n throw new Error(\"Use SQL Write for mutating statements.\");\n }\n throw new Error(\"Use SQL Read or SQL Live for read-only statements.\");\n }\n },\n runReadonlyQuery(databasePath: string, query: string): DevtoolsSqlReadResult {\n const analysis = this.analyzeSqlStatement(query);\n this.ensureSqlMode(analysis, \"read\");\n\n const database = new DatabaseSync(databasePath, { readOnly: true });\n try {\n const statement = database.prepare(query);\n const rows = statement.all() as Array<Record<string, unknown>>;\n const columnsMeta = statement.columns();\n const columns = columnsMeta.map((column) => column.name);\n const observedTables = Array.from(\n new Set(\n columnsMeta\n .map((column) => column.table)\n .filter((table): table is string => typeof table === \"string\")\n )\n );\n\n return {\n columns,\n rows: rows.map((row) => columns.map((column) => row[column])),\n observedTables:\n observedTables.length > 0 ? observedTables : analysis.readTables\n };\n } finally {\n database.close();\n }\n }\n};\n\nfunction normalizeData(input: StorageWriteInput[\"data\"]): Uint8Array {\n if (typeof input === \"string\") {\n return Buffer.from(input);\n }\n if (input instanceof Uint8Array) {\n return input;\n }\n return new Uint8Array(input);\n}\n\nfunction buildReadAnalysis(ast: SqlAst): DevtoolsSqlAnalysis {\n const readTables = Array.from(new Set(extractReadTables(ast)));\n return {\n mode: \"read\",\n readTables,\n writeTables: [],\n schemaChanged: false,\n observedScopes:\n readTables.length > 0\n ? readTables.map((table) => `table:${table}` as const)\n : [\"all\"]\n };\n}\n\nfunction buildWriteAnalysis(\n tables: string[],\n schemaChanged: boolean\n): DevtoolsSqlAnalysis {\n const uniqueTables = Array.from(new Set(tables));\n return {\n mode: schemaChanged ? \"ddl\" : \"write\",\n readTables: [],\n writeTables: uniqueTables,\n schemaChanged,\n observedScopes: schemaChanged\n ? [\n \"schema.tables\",\n ...uniqueTables.map((table) => `table:${table}` as const)\n ]\n : uniqueTables.length > 0\n ? uniqueTables.map((table) => `table:${table}` as const)\n : [\"all\"]\n };\n}\n\nfunction extractReadTables(ast: SqlAst): string[] {\n return (ast.from ?? [])\n .flatMap((entry) => {\n if (entry.table) {\n return [entry.table];\n }\n if (entry.expr?.ast) {\n return extractReadTables(entry.expr.ast);\n }\n return [];\n })\n .filter((table) => table !== \"dual\");\n}\n\nfunction extractTables(table: SqlAst[\"table\"]): string[] {\n if (Array.isArray(table)) {\n return table\n .map((entry) => entry?.table)\n .filter((value): value is string => typeof value === \"string\");\n }\n if (table && typeof table === \"object\") {\n return typeof table.table === \"string\" ? [table.table] : [];\n }\n if (typeof table === \"string\") {\n return [table];\n }\n return [];\n}\n\nfunction toSqlParameters(params: unknown[]): SQLInputValue[] {\n return params as SQLInputValue[];\n}\n\nexport class NodeSqliteDriver implements SyncoreSqlDriver {\n private readonly database: DatabaseSync;\n private transactionDepth = 0;\n\n constructor(filename: string) {\n this.database = new DatabaseSync(filename);\n this.database.exec(\"PRAGMA foreign_keys = ON;\");\n this.database.exec(\"PRAGMA journal_mode = WAL;\");\n }\n\n async exec(sql: string): Promise<void> {\n this.database.exec(sql);\n }\n\n async run(\n sql: string,\n params: unknown[] = []\n ): Promise<{ changes: number; lastInsertRowid?: number | string }> {\n const statement = this.database.prepare(sql);\n const result = statement.run(...toSqlParameters(params));\n return {\n changes: Number(result.changes ?? 0),\n lastInsertRowid:\n typeof result.lastInsertRowid === \"bigint\"\n ? Number(result.lastInsertRowid)\n : result.lastInsertRowid\n };\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n const statement = this.database.prepare(sql);\n return statement.get(...toSqlParameters(params)) as T | undefined;\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n const statement = this.database.prepare(sql);\n return statement.all(...toSqlParameters(params)) as T[];\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n this.database.exec(\"BEGIN\");\n try {\n const result = await callback();\n this.database.exec(\"COMMIT\");\n return result;\n } catch (error) {\n this.database.exec(\"ROLLBACK\");\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.database.exec(`SAVEPOINT ${safeName}`);\n try {\n const result = await callback();\n this.database.exec(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n this.database.exec(`ROLLBACK TO SAVEPOINT ${safeName}`);\n this.database.exec(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n }\n }\n\n async close(): Promise<void> {\n this.database.close();\n }\n}\n\nexport class NodeFileStorageAdapter implements SyncoreStorageAdapter {\n constructor(private readonly directory: string) {}\n\n private filePath(id: string): string {\n return path.join(this.directory, id);\n }\n\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\n await mkdir(this.directory, { recursive: true });\n const filePath = this.filePath(id);\n const bytes = normalizeData(input.data);\n await writeFile(filePath, bytes);\n return {\n id,\n path: filePath,\n size: bytes.byteLength,\n contentType: input.contentType ?? null\n };\n }\n\n async get(id: string): Promise<StorageObject | null> {\n const filePath = this.filePath(id);\n try {\n const info = await stat(filePath);\n return {\n id,\n path: filePath,\n size: info.size,\n contentType: null\n };\n } catch {\n return null;\n }\n }\n\n async read(id: string): Promise<Uint8Array | null> {\n try {\n return await readFile(this.filePath(id));\n } catch {\n return null;\n }\n }\n\n async delete(id: string): Promise<void> {\n await rm(this.filePath(id), { force: true });\n }\n\n async list(): Promise<StorageObject[]> {\n try {\n const entries = await readdir(this.directory, { withFileTypes: true });\n const objects = await Promise.all(\n entries\n .filter((entry) => entry.isFile())\n .map(async (entry) => {\n const filePath = this.filePath(entry.name);\n const info = await stat(filePath);\n return {\n id: entry.name,\n path: filePath,\n size: info.size,\n contentType: null\n } satisfies StorageObject;\n })\n );\n return objects;\n } catch {\n return [];\n }\n }\n}\n\nexport interface CreateNodeRuntimeOptions<\n TSchema extends NodeSyncoreSchema = NodeSyncoreSchema\n> {\n databasePath: string;\n storageDirectory: string;\n schema: TSchema;\n functions: SyncoreRuntimeOptions<TSchema>[\"functions\"];\n components?: SyncoreRuntimeOptions<TSchema>[\"components\"];\n capabilities?: SyncoreCapabilities;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n platform?: string;\n devtools?: DevtoolsSink | false;\n devtoolsUrl?: string;\n scheduler?: SchedulerOptions;\n}\n\n/**\n * Options for creating a managed Node Syncore client.\n */\nexport type WithNodeSyncoreClientOptions<\n TSchema extends NodeSyncoreSchema = NodeSyncoreSchema\n> = CreateNodeRuntimeOptions<TSchema>;\n\n/**\n * A started local Node runtime paired with its client and a dispose helper.\n */\nexport interface ManagedNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema = NodeSyncoreSchema\n> {\n runtime: SyncoreRuntime<TSchema>;\n client: ReturnType<SyncoreRuntime<TSchema>[\"createClient\"]>;\n dispose(): Promise<void>;\n}\n\nexport interface SyncoreElectronIpcBinding {\n ready: Promise<void>;\n dispose(): Promise<void>;\n}\n\nexport interface SyncoreElectronBridgeWindow {\n isDestroyed(): boolean;\n webContents: {\n send(channel: string, message: unknown): void;\n };\n}\n\nexport interface CreateElectronSyncoreBridgeOptions {\n window: SyncoreElectronBridgeWindow;\n onRendererMessage(listener: (message: unknown) => void): () => void;\n channel?: string;\n}\n\n/**\n * The subset of Electron's `ipcMain` used by Syncore's main-process helper.\n */\nexport interface SyncoreElectronIpcMain {\n on(\n channel: string,\n listener: (event: { sender: unknown }, message: unknown) => void\n ): void;\n off(\n channel: string,\n listener: (event: { sender: unknown }, message: unknown) => void\n ): void;\n}\n\nexport interface CreateSyncoreRendererWindowClientOptions {\n bridgeName?: string;\n}\n\n/**\n * Create a Node or Electron runtime backed by SQLite and local file storage.\n */\nexport function createNodeSyncoreRuntime<\n TSchema extends NodeSyncoreSchema\n>(\n options: CreateNodeRuntimeOptions<TSchema>\n): SyncoreRuntime<TSchema> {\n const resolvedDevtoolsUrl =\n options.devtoolsUrl ?? resolveDefaultNodeDevtoolsUrl();\n const websocketDevtools =\n options.devtools === undefined &&\n resolvedDevtoolsUrl &&\n shouldAutoConnectNodeDevtools()\n ? createNodeWebSocketDevtoolsSink({\n url: resolvedDevtoolsUrl,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {}),\n targetKind: \"client\",\n storageProtocol: \"file\",\n databaseLabel: path.basename(options.databasePath),\n storageIdentity: `file::${path.resolve(options.databasePath)}`\n })\n : undefined;\n const runtimeOptions: SyncoreRuntimeOptions<TSchema> = {\n schema: options.schema,\n functions: options.functions,\n ...(options.components ? { components: options.components } : {}),\n driver: new NodeSqliteDriver(options.databasePath),\n storage: new NodeFileStorageAdapter(options.storageDirectory),\n platform: options.platform ?? \"node\"\n };\n if (options.capabilities) {\n runtimeOptions.capabilities = options.capabilities;\n }\n const resolvedDevtools =\n options.devtools === false\n ? undefined\n : (options.devtools ?? websocketDevtools);\n if (resolvedDevtools) {\n runtimeOptions.devtools = resolvedDevtools;\n }\n if (options.scheduler) {\n runtimeOptions.scheduler = options.scheduler;\n }\n const runtime = new SyncoreRuntime(runtimeOptions);\n websocketDevtools?.attachRuntime(runtime);\n if (websocketDevtools) {\n websocketDevtools.attachCommandHandler(\n createDevtoolsCommandHandler({\n driver: runtimeOptions.driver,\n schema: options.schema,\n functions: options.functions,\n admin: runtime.getAdmin(),\n sql: nodeDevtoolsSqlSupport\n })\n );\n websocketDevtools.attachSubscriptionHost(\n createDevtoolsSubscriptionHost({\n driver: runtimeOptions.driver,\n schema: options.schema,\n functions: options.functions,\n admin: runtime.getAdmin(),\n sql: nodeDevtoolsSqlSupport\n })\n );\n const stop = runtime.stop.bind(runtime);\n runtime.stop = async () => {\n websocketDevtools.dispose();\n await stop();\n };\n }\n return runtime;\n}\n\n/**\n * Create a same-process Syncore client from a started Node runtime.\n */\nexport function createNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema\n>(runtime: SyncoreRuntime<TSchema>) {\n return runtime.createClient();\n}\n\n/**\n * Start a Node Syncore runtime and return its client together with a dispose helper.\n */\nexport async function createManagedNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema\n>(\n options: WithNodeSyncoreClientOptions<TSchema>\n): Promise<ManagedNodeSyncoreClient<TSchema>> {\n const runtime = createNodeSyncoreRuntime(options);\n await runtime.start();\n return {\n runtime,\n client: runtime.createClient(),\n async dispose() {\n await runtime.stop();\n }\n };\n}\n\n/**\n * Run a callback with a started local Node Syncore client and always stop the runtime.\n *\n * @example\n * ```ts\n * await withNodeSyncoreClient(options, async (client) => {\n * console.log(await client.query(api.tasks.list));\n * });\n * ```\n */\nexport async function withNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema,\n TResult\n>(\n options: WithNodeSyncoreClientOptions<TSchema>,\n callback: (\n client: ReturnType<SyncoreRuntime<TSchema>[\"createClient\"]>,\n runtime: SyncoreRuntime<TSchema>\n ) => Promise<TResult> | TResult\n): Promise<TResult> {\n const managed = await createManagedNodeSyncoreClient(options);\n try {\n return await callback(managed.client, managed.runtime);\n } finally {\n await managed.dispose();\n }\n}\n\n/**\n * Create the default Electron main-process bridge used to connect a BrowserWindow\n * to a Syncore runtime.\n */\nexport function createElectronSyncoreBridge(\n options: CreateElectronSyncoreBridgeOptions\n) {\n const channel = options.channel ?? \"syncore:message\";\n return createNodeIpcMessageEndpoint({\n postMessage(message: unknown) {\n if (!options.window.isDestroyed()) {\n options.window.webContents.send(channel, message);\n }\n },\n onMessage(listener: (message: unknown) => void) {\n return options.onRendererMessage(listener);\n }\n });\n}\n\n/**\n * Bind a BrowserWindow to a Syncore runtime with the default Electron IPC transport.\n */\nexport function bindElectronWindowToSyncoreRuntime(options: {\n runtime: SyncoreRuntime<NodeSyncoreSchema>;\n window: SyncoreElectronBridgeWindow;\n onRendererMessage(listener: (message: unknown) => void): () => void;\n channel?: string;\n}): SyncoreElectronIpcBinding;\nexport function bindElectronWindowToSyncoreRuntime(options: {\n runtime: SyncoreRuntime<NodeSyncoreSchema>;\n window: SyncoreElectronBridgeWindow;\n ipcMain: SyncoreElectronIpcMain;\n channel?: string;\n}): SyncoreElectronIpcBinding;\nexport function bindElectronWindowToSyncoreRuntime(options: {\n runtime: SyncoreRuntime<NodeSyncoreSchema>;\n window: SyncoreElectronBridgeWindow;\n onRendererMessage?(listener: (message: unknown) => void): () => void;\n ipcMain?: SyncoreElectronIpcMain;\n channel?: string;\n}): SyncoreElectronIpcBinding {\n const cleanupCallbacks: Array<() => void> = [];\n const channel = options.channel ?? \"syncore:message\";\n let onRendererMessage:\n | ((listener: (message: unknown) => void) => () => void)\n | undefined;\n\n if (!options.onRendererMessage) {\n if (!options.ipcMain) {\n throw new Error(\n \"bindElectronWindowToSyncoreRuntime requires either onRendererMessage() or ipcMain.\"\n );\n }\n const listeners = new Set<(message: unknown) => void>();\n const handleRendererMessage = (\n event: { sender: unknown },\n message: unknown\n ) => {\n if (event.sender !== options.window.webContents) {\n return;\n }\n for (const listener of listeners) {\n listener(message);\n }\n };\n options.ipcMain.on(channel, handleRendererMessage);\n cleanupCallbacks.push(() => {\n options.ipcMain?.off(channel, handleRendererMessage);\n listeners.clear();\n });\n onRendererMessage = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n } else {\n onRendererMessage = (listener) => options.onRendererMessage!(listener);\n }\n\n const endpoint = createElectronSyncoreBridge({\n window: options.window,\n onRendererMessage,\n channel\n });\n const attachedRuntime = attachNodeIpcRuntime({\n endpoint,\n createRuntime: () => options.runtime\n });\n\n return {\n ready: attachedRuntime.ready,\n async dispose() {\n await attachedRuntime.dispose();\n endpoint.dispose();\n for (const cleanup of cleanupCallbacks) {\n cleanup();\n }\n }\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Devtools request handler */\n/* ------------------------------------------------------------------ */\n\nexport interface NodeWebSocketDevtoolsSinkOptions {\n url: string;\n reconnectDelayMs?: number;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n targetKind?: \"client\" | \"project\";\n storageProtocol?: string;\n databaseLabel?: string;\n storageIdentity?: string;\n}\n\nexport interface NodeWebSocketDevtoolsSink extends DevtoolsSink {\n attachRuntime(runtime: SyncoreRuntime<NodeSyncoreSchema>): void;\n attachCommandHandler(handler: DevtoolsCommandHandler): void;\n attachSubscriptionHost(host: DevtoolsSubscriptionHost): void;\n dispose(): void;\n}\n\nexport function createNodeWebSocketDevtoolsSink(\n options: NodeWebSocketDevtoolsSinkOptions\n): NodeWebSocketDevtoolsSink {\n let socket: WebSocket | undefined;\n let disposed = false;\n let connectTimer: ReturnType<typeof setTimeout> | undefined;\n let getSummary: (() => SyncoreRuntimeSummary) | undefined;\n let onCommand: DevtoolsCommandHandler | undefined;\n let subscriptionHost: DevtoolsSubscriptionHost | undefined;\n const pendingMessages: SyncoreDevtoolsMessage[] = [];\n let latestHello:\n | {\n runtimeId: string;\n platform: string;\n }\n | undefined;\n\n const connect = () => {\n if (disposed) {\n return;\n }\n socket = new WebSocket(options.url);\n socket.on(\"open\", () => {\n if (latestHello) {\n sendNow({\n type: \"hello\",\n protocolVersion: SYNCORE_DEVTOOLS_PROTOCOL_VERSION,\n minSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION,\n maxSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION,\n runtimeId: latestHello.runtimeId,\n platform: latestHello.platform,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {}),\n ...(options.targetKind ? { targetKind: options.targetKind } : {}),\n ...(options.storageProtocol\n ? { storageProtocol: options.storageProtocol }\n : {}),\n ...(options.databaseLabel ? { databaseLabel: options.databaseLabel } : {}),\n ...(options.storageIdentity\n ? { storageIdentity: options.storageIdentity }\n : {})\n });\n }\n flushPendingMessages();\n });\n socket.on(\"message\", (payload) => {\n const rawPayload =\n typeof payload === \"string\"\n ? payload\n : payload instanceof Buffer\n ? payload.toString(\"utf8\")\n : Array.isArray(payload)\n ? Buffer.concat(payload).toString(\"utf8\")\n : payload instanceof ArrayBuffer\n ? Buffer.from(payload).toString(\"utf8\")\n : Buffer.from(\n payload.buffer,\n payload.byteOffset,\n payload.byteLength\n ).toString(\"utf8\");\n if (rawPayload.length === 0) {\n return;\n }\n const message = JSON.parse(rawPayload) as\n | SyncoreDevtoolsMessage\n | SyncoreDevtoolsClientMessage;\n if (message.type === \"ping\") {\n send({ type: \"pong\" });\n } else if (message.type === \"command\" && onCommand) {\n onCommand(message.payload)\n .then((responsePayload) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"command.result\",\n commandId: message.commandId,\n runtimeId,\n payload: responsePayload\n });\n })\n .catch((err) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"command.result\",\n commandId: message.commandId,\n runtimeId,\n payload: {\n kind: \"error\",\n message: err instanceof Error ? err.message : \"Unknown error\"\n }\n });\n });\n } else if (message.type === \"subscribe\" && subscriptionHost) {\n void subscriptionHost.subscribe(\n message.subscriptionId,\n message.payload,\n (payload) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"subscription.data\",\n subscriptionId: message.subscriptionId,\n runtimeId,\n payload\n });\n }\n );\n } else if (message.type === \"unsubscribe\") {\n subscriptionHost?.unsubscribe(message.subscriptionId);\n }\n });\n socket.on(\"close\", scheduleReconnect);\n socket.on(\"error\", scheduleReconnect);\n };\n\n const scheduleReconnect = () => {\n if (disposed || connectTimer) {\n return;\n }\n connectTimer = setTimeout(() => {\n connectTimer = undefined;\n connect();\n }, options.reconnectDelayMs ?? 1200);\n };\n\n const sendNow = (message: SyncoreDevtoolsMessage) => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(message));\n }\n };\n\n const flushPendingMessages = () => {\n while (pendingMessages.length > 0) {\n const nextMessage = pendingMessages.shift();\n if (!nextMessage) {\n continue;\n }\n sendNow(nextMessage);\n }\n };\n\n const send = (message: SyncoreDevtoolsMessage) => {\n if (socket?.readyState === WebSocket.OPEN) {\n sendNow(message);\n return;\n }\n pendingMessages.push(message);\n };\n\n connect();\n\n return {\n emit(event) {\n if (event.type === \"runtime.connected\") {\n latestHello = {\n runtimeId: event.runtimeId,\n platform: event.platform\n };\n send({\n type: \"hello\",\n protocolVersion: SYNCORE_DEVTOOLS_PROTOCOL_VERSION,\n minSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION,\n maxSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION,\n runtimeId: event.runtimeId,\n platform: event.platform,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {}),\n ...(options.targetKind ? { targetKind: options.targetKind } : {}),\n ...(options.storageProtocol\n ? { storageProtocol: options.storageProtocol }\n : {}),\n ...(options.databaseLabel ? { databaseLabel: options.databaseLabel } : {}),\n ...(options.storageIdentity\n ? { storageIdentity: options.storageIdentity }\n : {})\n });\n }\n send({\n type: \"event\",\n event\n });\n },\n attachRuntime(runtime) {\n getSummary = () =>\n withRuntimeSummaryMeta(runtime.getAdmin().getRuntimeSummary(), options);\n },\n attachCommandHandler(handler) {\n onCommand = handler;\n },\n attachSubscriptionHost(host) {\n subscriptionHost = host;\n },\n dispose() {\n disposed = true;\n if (connectTimer) {\n clearTimeout(connectTimer);\n }\n subscriptionHost?.dispose();\n socket?.close();\n }\n };\n}\n\nfunction withRuntimeSummaryMeta(\n summary: SyncoreRuntimeSummary,\n options: NodeWebSocketDevtoolsSinkOptions\n): SyncoreRuntimeSummary {\n return {\n ...summary,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}),\n ...(options.targetKind ? { targetKind: options.targetKind } : {}),\n ...(options.storageProtocol\n ? { storageProtocol: options.storageProtocol }\n : {}),\n ...(options.databaseLabel ? { databaseLabel: options.databaseLabel } : {}),\n ...(options.storageIdentity\n ? { storageIdentity: options.storageIdentity }\n : {})\n };\n}\n\nfunction shouldAutoConnectNodeDevtools(): boolean {\n return process.env.NODE_ENV !== \"production\";\n}\n\nfunction resolveDefaultNodeDevtoolsUrl(): string | undefined {\n if (process.env.SYNCORE_DISABLE_DEVTOOLS === \"1\") {\n return undefined;\n }\n return process.env.SYNCORE_DEVTOOLS_URL ?? \"ws://127.0.0.1:4311\";\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile\n} from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { DatabaseSync, type SQLInputValue } from \"node:sqlite\";\nimport WebSocket from \"ws\";\nimport {\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION,\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION,\n SYNCORE_DEVTOOLS_PROTOCOL_VERSION,\n} from \"@syncore/devtools-protocol\";\nimport type {\n SyncoreDevtoolsClientMessage,\n SyncoreDevtoolsCapabilities,\n SyncoreDevtoolsExternalChangeEvent,\n SyncoreDevtoolsMessage,\n SyncoreRuntimeSummary\n} from \"@syncore/devtools-protocol\";\nimport {\n createDevtoolsCommandHandler,\n createDevtoolsSubscriptionHost,\n type DevtoolsCommandHandler,\n type DevtoolsSink,\n type DevtoolsSubscriptionHost,\n type SchedulerOptions,\n type StorageObject,\n type StorageWriteInput,\n type SyncoreCapabilities,\n type SyncoreDataModel,\n type SyncoreExternalChangeEvent,\n type SyncoreExternalChangeSignal,\n SyncoreRuntime,\n type SyncoreRuntimeOptions,\n type SyncoreSqlDriver,\n type SyncoreStorageAdapter\n} from \"@syncore/core\";\nimport { attachNodeIpcRuntime, createNodeIpcMessageEndpoint } from \"./ipc.js\";\nexport * from \"./ipc.js\";\nexport type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent,\n SyncoreRuntimeSummary\n} from \"@syncore/devtools-protocol\";\n\nexport type NodeSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\n\nconst DEVTOOLS_META_DIRECTORY = \".syncore-devtools\";\nconst DATA_SOURCE_ALIAS_PREFIX = \"data-source-alias\";\n\nfunction normalizeData(input: StorageWriteInput[\"data\"]): Uint8Array {\n if (typeof input === \"string\") {\n return Buffer.from(input);\n }\n if (input instanceof Uint8Array) {\n return input;\n }\n return new Uint8Array(input);\n}\n\nfunction toSqlParameters(params: unknown[]): SQLInputValue[] {\n return params as SQLInputValue[];\n}\n\n/**\n * SQLite driver backed by Node.js’s built-in `node:sqlite` module (Node 22+).\n *\n * Opens the database at `databasePath` with `WAL` journal mode and foreign-key\n * enforcement enabled. The file is created if it does not exist.\n *\n * ```ts\n * const driver = new NodeSqliteDriver(\"./data/app.db\");\n * ```\n *\n * In most cases you should use {@link createNodeSyncoreRuntime} which\n * instantiates this driver automatically from your `databasePath` option.\n */\nexport class NodeSqliteDriver implements SyncoreSqlDriver {\n private readonly database: DatabaseSync;\n private transactionDepth = 0;\n\n constructor(readonly databasePath: string) {\n this.database = new DatabaseSync(databasePath);\n this.database.exec(\"PRAGMA foreign_keys = ON;\");\n this.database.exec(\"PRAGMA journal_mode = WAL;\");\n }\n\n async exec(sql: string): Promise<void> {\n this.database.exec(sql);\n }\n\n async run(\n sql: string,\n params: unknown[] = []\n ): Promise<{ changes: number; lastInsertRowid?: number | string }> {\n const statement = this.database.prepare(sql);\n const result = statement.run(...toSqlParameters(params));\n return {\n changes: Number(result.changes ?? 0),\n lastInsertRowid:\n typeof result.lastInsertRowid === \"bigint\"\n ? Number(result.lastInsertRowid)\n : result.lastInsertRowid\n };\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n const statement = this.database.prepare(sql);\n return statement.get(...toSqlParameters(params)) as T | undefined;\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n const statement = this.database.prepare(sql);\n return statement.all(...toSqlParameters(params)) as T[];\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n this.database.exec(\"BEGIN\");\n try {\n const result = await callback();\n this.database.exec(\"COMMIT\");\n return result;\n } catch (error) {\n this.database.exec(\"ROLLBACK\");\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.database.exec(`SAVEPOINT ${safeName}`);\n try {\n const result = await callback();\n this.database.exec(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n this.database.exec(`ROLLBACK TO SAVEPOINT ${safeName}`);\n this.database.exec(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n }\n }\n\n async close(): Promise<void> {\n this.database.close();\n }\n}\n\n/**\n * Blob storage adapter that reads and writes files in a local directory.\n *\n * Each stored object is saved as a flat file named by its ID inside\n * `directory`. The directory is created automatically on first write.\n *\n * ```ts\n * const storage = new NodeFileStorageAdapter(\"./data/storage\");\n * ```\n *\n * In most cases you should use {@link createNodeSyncoreRuntime} which\n * instantiates this adapter automatically from your `storageDirectory` option.\n */\nexport class NodeFileStorageAdapter implements SyncoreStorageAdapter {\n constructor(private readonly directory: string) {}\n\n private filePath(id: string): string {\n return path.join(this.directory, id);\n }\n\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\n await mkdir(this.directory, { recursive: true });\n const filePath = this.filePath(id);\n const bytes = normalizeData(input.data);\n await writeFile(filePath, bytes);\n return {\n id,\n path: filePath,\n size: bytes.byteLength,\n contentType: input.contentType ?? null\n };\n }\n\n async get(id: string): Promise<StorageObject | null> {\n const filePath = this.filePath(id);\n try {\n const info = await stat(filePath);\n return {\n id,\n path: filePath,\n size: info.size,\n contentType: null\n };\n } catch {\n return null;\n }\n }\n\n async read(id: string): Promise<Uint8Array | null> {\n try {\n return await readFile(this.filePath(id));\n } catch {\n return null;\n }\n }\n\n async delete(id: string): Promise<void> {\n await rm(this.filePath(id), { force: true });\n }\n\n async list(): Promise<StorageObject[]> {\n try {\n const entries = await readdir(this.directory, { withFileTypes: true });\n const objects = await Promise.all(\n entries\n .filter((entry) => entry.isFile())\n .map(async (entry) => {\n const filePath = this.filePath(entry.name);\n const info = await stat(filePath);\n return {\n id: entry.name,\n path: filePath,\n size: info.size,\n contentType: null\n } satisfies StorageObject;\n })\n );\n return objects;\n } catch {\n return [];\n }\n }\n}\n\nconst SESSION_ADJECTIVES = [\n \"Acrobatic\",\n \"Bold\",\n \"Cosmic\",\n \"Daring\",\n \"Electric\",\n \"Fierce\",\n \"Golden\",\n \"Hidden\",\n \"Iron\",\n \"Jade\",\n \"Keen\",\n \"Lunar\",\n \"Mystic\",\n \"Noble\",\n \"Orbital\",\n \"Primal\",\n \"Quick\",\n \"Radiant\",\n \"Shadow\",\n \"Turbo\",\n \"Ultra\",\n \"Vivid\",\n \"Wicked\",\n \"Xenon\",\n \"Zen\",\n \"Arctic\",\n \"Binary\",\n \"Cyber\",\n \"Digital\",\n \"Ember\",\n \"Frozen\",\n \"Galactic\",\n \"Hyper\",\n \"Infra\",\n \"Jumbo\",\n \"Kinetic\",\n \"Liquid\",\n \"Magnetic\",\n \"Neon\",\n \"Onyx\",\n \"Phantom\",\n \"Quantum\",\n \"Rapid\",\n \"Sonic\",\n \"Titan\",\n \"Velvet\",\n \"Wild\",\n \"Blazing\",\n \"Crystal\",\n \"Dynamic\"\n] as const;\n\nconst SESSION_NOUNS = [\n \"Phoenix\",\n \"Dragon\",\n \"Developer\",\n \"Hacker\",\n \"Wizard\",\n \"Runner\",\n \"Ranger\",\n \"Maverick\",\n \"Spartan\",\n \"Viking\",\n \"Sentinel\",\n \"Guardian\",\n \"Nomad\",\n \"Cipher\",\n \"Vector\",\n \"Matrix\",\n \"Prism\",\n \"Nebula\",\n \"Comet\",\n \"Pulse\",\n \"Vertex\",\n \"Flux\",\n \"Storm\",\n \"Blaze\",\n \"Frost\",\n \"Thunder\",\n \"Drift\"\n] as const;\n\nfunction generateUniqueSessionName(): string {\n const adj =\n SESSION_ADJECTIVES[Math.floor(Math.random() * SESSION_ADJECTIVES.length)]!;\n const noun = SESSION_NOUNS[Math.floor(Math.random() * SESSION_NOUNS.length)]!;\n return `${adj} ${noun}`;\n}\n\nfunction resolvePersistedDataSourceAlias(\n storageDirectory: string,\n storageIdentity: string\n): string {\n const metaDirectory = path.join(storageDirectory, DEVTOOLS_META_DIRECTORY);\n const aliasId = createHash(\"sha256\")\n .update(storageIdentity)\n .digest(\"hex\")\n .slice(0, 16);\n const aliasPath = path.join(\n metaDirectory,\n `${DATA_SOURCE_ALIAS_PREFIX}-${aliasId}.txt`\n );\n\n try {\n const existing = readFileSync(aliasPath, \"utf8\").trim();\n if (existing.length > 0) {\n return existing;\n }\n } catch {\n // Missing metadata is expected for a new data source.\n }\n\n const nextValue = generateUniqueSessionName();\n try {\n mkdirSync(metaDirectory, { recursive: true });\n writeFileSync(aliasPath, nextValue, \"utf8\");\n } catch {\n // The alias is a dashboard convenience; runtime startup must not depend on it.\n }\n return nextValue;\n}\n\n/**\n * Options for {@link createNodeSyncoreRuntime}.\n *\n * At minimum supply `databasePath`, `storageDirectory`, `schema`, and\n * `functions`. Everything else has sensible defaults (auto-devtools connect in\n * development, Node SQLite driver, local file storage).\n *\n * ```ts\n * createNodeSyncoreRuntime({\n * databasePath: path.join(dataDir, \"app.db\"),\n * storageDirectory: path.join(dataDir, \"storage\"),\n * schema,\n * functions,\n * });\n * ```\n */\nexport interface CreateNodeRuntimeOptions<\n TSchema extends NodeSyncoreSchema = NodeSyncoreSchema\n> {\n /**\n * Absolute or relative path to the SQLite database file.\n *\n * The file is created if it does not exist. Use an absolute path in\n * production to avoid ambiguity about the current working directory.\n */\n databasePath: string;\n /**\n * Directory where blob storage objects (images, files, etc.) are persisted.\n *\n * The directory is created automatically if it does not exist.\n */\n storageDirectory: string;\n /** The data model that defines the available tables and indexes. */\n schema: TSchema;\n /**\n * The registered function map. Use the `functions` export from\n * `syncore/_generated/functions.ts`.\n */\n functions: SyncoreRuntimeOptions<TSchema>[\"functions\"];\n /**\n * Resolved Syncore component instances. Only required when your app\n * installs Syncore component packages.\n */\n components?: SyncoreRuntimeOptions<TSchema>[\"components\"];\n /**\n * Platform capabilities injected into `ctx.capabilities` inside function\n * handlers.\n */\n capabilities?: SyncoreCapabilities;\n /** Human-readable app name shown in the devtools dashboard. */\n appName?: string;\n /** Origin label (e.g. process name) shown in devtools. */\n origin?: string;\n /** Devtools session label. Auto-generated when omitted. */\n sessionLabel?: string;\n /**\n * Platform label reported to devtools. Defaults to `\"node\"`, or\n * `\"electron-main\"` when the runtime is used inside Electron's main process.\n */\n platform?: string;\n /**\n * Devtools event sink. Pass `false` to disable devtools entirely (recommended\n * for production). Omit to auto-connect to the local devtools server when\n * running in development.\n */\n devtools?: DevtoolsSink | false;\n /**\n * Explicit devtools WebSocket server URL. Defaults to\n * `ws://localhost:3099` (the Syncore devtools default port).\n */\n devtoolsUrl?: string;\n /** Scheduler configuration for background and recurring jobs. */\n scheduler?: SchedulerOptions;\n}\n\n/**\n * Alias of {@link CreateNodeRuntimeOptions} exposed for the managed-client\n * helper.\n * @see CreateNodeRuntimeOptions\n */\nexport type WithNodeSyncoreClientOptions<\n TSchema extends NodeSyncoreSchema = NodeSyncoreSchema\n> = CreateNodeRuntimeOptions<TSchema>;\n\n/**\n * A started local Node runtime paired with its client and a dispose helper.\n *\n * Returned by `withNodeSyncoreClient()`. Call `dispose()` when you are\n * finished (e.g. in tests or short-lived scripts) to stop the runtime and\n * close the database.\n */\nexport interface ManagedNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema = NodeSyncoreSchema\n> {\n /** The underlying runtime instance. */\n runtime: SyncoreRuntime<TSchema>;\n /** A ready-to-use client for calling Syncore functions. */\n client: ReturnType<SyncoreRuntime<TSchema>[\"createClient\"]>;\n /** Stop the runtime, flush pending jobs, and close the database. */\n dispose(): Promise<void>;\n}\n\n/**\n * Opaque handle returned by Syncore’s Electron IPC bridge setup.\n *\n * - `ready`: resolves when the bridge is connected and the renderer is ready to\n * receive messages.\n * - `dispose()`: tears down the bridge and removes IPC listeners.\n */\nexport interface SyncoreElectronIpcBinding {\n ready: Promise<void>;\n dispose(): Promise<void>;\n}\n\n/**\n * Minimal interface that Syncore requires from an Electron `BrowserWindow`\n * instance.\n *\n * Scoped to avoid importing Electron at the type level.\n */\nexport interface SyncoreElectronBridgeWindow {\n isDestroyed(): boolean;\n webContents: {\n send(channel: string, message: unknown): void;\n };\n}\n\n/**\n * Options for setting up Syncore’s Electron main-process IPC bridge.\n *\n * The bridge forwards database change events from the main-process runtime to\n * the renderer window over a named IPC channel.\n *\n * ```ts\n * createElectronSyncoreBridge(runtime, {\n * window: mainWindow,\n * onRendererMessage: (listener) => {\n * ipcMain.on(\"syncore\", (_e, msg) => listener(msg));\n * return () => ipcMain.removeAllListeners(\"syncore\");\n * },\n * });\n * ```\n */\nexport interface CreateElectronSyncoreBridgeOptions {\n /** The renderer window that will receive push messages. */\n window: SyncoreElectronBridgeWindow;\n /**\n * Register a listener for messages sent from the renderer. Must return an\n * unsubscribe function.\n */\n onRendererMessage(listener: (message: unknown) => void): () => void;\n /** IPC channel name. Defaults to `\"syncore\"`. */\n channel?: string;\n}\n\n/**\n * The subset of Electron’s `ipcMain` used by Syncore’s main-process helper.\n *\n * Using this narrowed interface avoids a hard runtime dependency on Electron.\n */\nexport interface SyncoreElectronIpcMain {\n on(\n channel: string,\n listener: (event: { sender: unknown }, message: unknown) => void\n ): void;\n off(\n channel: string,\n listener: (event: { sender: unknown }, message: unknown) => void\n ): void;\n}\n\n/**\n * Options for creating a client inside an Electron renderer process via the\n * preload IPC bridge.\n */\nexport interface CreateSyncoreRendererWindowClientOptions {\n /** Name of the bridge registered in the preload script. Defaults to `\"syncore\"`. */\n bridgeName?: string;\n}\n\n/**\n * Create a Syncore runtime for Node.js (or Electron’s main process) backed by\n * the built-in `node:sqlite` driver and local file storage.\n *\n * This is the recommended entry point for Node and Electron apps. It wires up\n * the SQL driver, storage adapter, devtools WebSocket connection, and\n * cross-process change signals automatically.\n *\n * ```ts\n * import path from \"node:path\";\n * import { createNodeSyncoreRuntime } from \"syncorejs/node\";\n * import schema from \"./syncore/schema\";\n * import { functions } from \"./syncore/_generated/functions\";\n *\n * const runtime = createNodeSyncoreRuntime({\n * databasePath: path.join(app.getPath(\"userData\"), \"db.sqlite\"),\n * storageDirectory: path.join(app.getPath(\"userData\"), \"storage\"),\n * schema,\n * functions,\n * });\n *\n * await runtime.start();\n * const client = runtime.createClient();\n * ```\n *\n * @param options - Configuration object. See {@link CreateNodeRuntimeOptions}.\n * @returns A configured (but not yet started) {@link SyncoreRuntime}. Call\n * `await runtime.start()` before using the client.\n */\nexport function createNodeSyncoreRuntime<\n TSchema extends NodeSyncoreSchema\n>(\n options: CreateNodeRuntimeOptions<TSchema>\n): SyncoreRuntime<TSchema> {\n const resolvedDevtoolsUrl =\n options.devtoolsUrl ?? resolveDefaultNodeDevtoolsUrl();\n const storageIdentity = `file::${path.resolve(options.databasePath)}`;\n const websocketDevtools =\n options.devtools === undefined &&\n resolvedDevtoolsUrl &&\n shouldAutoConnectNodeDevtools()\n ? createNodeWebSocketDevtoolsSink({\n url: resolvedDevtoolsUrl,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {}),\n targetKind: \"client\",\n storageProtocol: \"file\",\n databaseLabel: path.basename(options.databasePath),\n dataSourceAlias: resolvePersistedDataSourceAlias(\n options.storageDirectory,\n storageIdentity\n ),\n storageIdentity,\n runtimeRole: \"app\",\n capabilities: createNodeDevtoolsCapabilities()\n })\n : undefined;\n const runtimeOptions: SyncoreRuntimeOptions<TSchema> = {\n schema: options.schema,\n functions: options.functions,\n ...(options.components ? { components: options.components } : {}),\n driver: new NodeSqliteDriver(options.databasePath),\n storage: new NodeFileStorageAdapter(options.storageDirectory),\n platform: options.platform ?? \"node\"\n };\n if (options.capabilities) {\n runtimeOptions.capabilities = options.capabilities;\n }\n const resolvedDevtools =\n options.devtools === false\n ? undefined\n : (options.devtools ?? websocketDevtools);\n if (resolvedDevtools) {\n runtimeOptions.devtools = resolvedDevtools;\n }\n if (websocketDevtools?.externalChangeSignal) {\n runtimeOptions.externalChangeSignal = websocketDevtools.externalChangeSignal;\n }\n if (options.scheduler) {\n runtimeOptions.scheduler = options.scheduler;\n }\n const runtime = new SyncoreRuntime(runtimeOptions);\n websocketDevtools?.attachRuntime(runtime);\n if (websocketDevtools) {\n websocketDevtools.attachCommandHandler(\n createDevtoolsCommandHandler({\n driver: runtimeOptions.driver,\n schema: options.schema,\n functions: options.functions,\n admin: runtime.getAdmin()\n })\n );\n websocketDevtools.attachSubscriptionHost(\n createDevtoolsSubscriptionHost({\n driver: runtimeOptions.driver,\n schema: options.schema,\n functions: options.functions,\n admin: runtime.getAdmin()\n })\n );\n const stop = runtime.stop.bind(runtime);\n runtime.stop = async () => {\n websocketDevtools.dispose();\n await stop();\n };\n }\n return runtime;\n}\n\n/**\n * Create a same-process Syncore client from a started Node runtime.\n */\nexport function createNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema\n>(runtime: SyncoreRuntime<TSchema>) {\n return runtime.createClient();\n}\n\n/**\n * Start a Node Syncore runtime and return its client together with a dispose helper.\n */\nexport async function createManagedNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema\n>(\n options: WithNodeSyncoreClientOptions<TSchema>\n): Promise<ManagedNodeSyncoreClient<TSchema>> {\n const runtime = createNodeSyncoreRuntime(options);\n await runtime.start();\n return {\n runtime,\n client: runtime.createClient(),\n async dispose() {\n await runtime.stop();\n }\n };\n}\n\n/**\n * Run a callback with a started local Node Syncore client and always stop the runtime.\n *\n * @example\n * ```ts\n * await withNodeSyncoreClient(options, async (client) => {\n * console.log(await client.query(api.tasks.list));\n * });\n * ```\n */\nexport async function withNodeSyncoreClient<\n TSchema extends NodeSyncoreSchema,\n TResult\n>(\n options: WithNodeSyncoreClientOptions<TSchema>,\n callback: (\n client: ReturnType<SyncoreRuntime<TSchema>[\"createClient\"]>,\n runtime: SyncoreRuntime<TSchema>\n ) => Promise<TResult> | TResult\n): Promise<TResult> {\n const managed = await createManagedNodeSyncoreClient(options);\n try {\n return await callback(managed.client, managed.runtime);\n } finally {\n await managed.dispose();\n }\n}\n\n/**\n * Create the default Electron main-process bridge used to connect a BrowserWindow\n * to a Syncore runtime.\n */\nexport function createElectronSyncoreBridge(\n options: CreateElectronSyncoreBridgeOptions\n) {\n const channel = options.channel ?? \"syncore:message\";\n return createNodeIpcMessageEndpoint({\n postMessage(message: unknown) {\n if (!options.window.isDestroyed()) {\n options.window.webContents.send(channel, message);\n }\n },\n onMessage(listener: (message: unknown) => void) {\n return options.onRendererMessage(listener);\n }\n });\n}\n\n/**\n * Bind a BrowserWindow to a Syncore runtime with the default Electron IPC transport.\n */\nexport function bindElectronWindowToSyncoreRuntime(options: {\n runtime: SyncoreRuntime<NodeSyncoreSchema>;\n window: SyncoreElectronBridgeWindow;\n onRendererMessage(listener: (message: unknown) => void): () => void;\n channel?: string;\n}): SyncoreElectronIpcBinding;\nexport function bindElectronWindowToSyncoreRuntime(options: {\n runtime: SyncoreRuntime<NodeSyncoreSchema>;\n window: SyncoreElectronBridgeWindow;\n ipcMain: SyncoreElectronIpcMain;\n channel?: string;\n}): SyncoreElectronIpcBinding;\nexport function bindElectronWindowToSyncoreRuntime(options: {\n runtime: SyncoreRuntime<NodeSyncoreSchema>;\n window: SyncoreElectronBridgeWindow;\n onRendererMessage?(listener: (message: unknown) => void): () => void;\n ipcMain?: SyncoreElectronIpcMain;\n channel?: string;\n}): SyncoreElectronIpcBinding {\n const cleanupCallbacks: Array<() => void> = [];\n const channel = options.channel ?? \"syncore:message\";\n let onRendererMessage:\n | ((listener: (message: unknown) => void) => () => void)\n | undefined;\n\n if (!options.onRendererMessage) {\n if (!options.ipcMain) {\n throw new Error(\n \"bindElectronWindowToSyncoreRuntime requires either onRendererMessage() or ipcMain.\"\n );\n }\n const listeners = new Set<(message: unknown) => void>();\n const handleRendererMessage = (\n event: { sender: unknown },\n message: unknown\n ) => {\n if (event.sender !== options.window.webContents) {\n return;\n }\n for (const listener of listeners) {\n listener(message);\n }\n };\n options.ipcMain.on(channel, handleRendererMessage);\n cleanupCallbacks.push(() => {\n options.ipcMain?.off(channel, handleRendererMessage);\n listeners.clear();\n });\n onRendererMessage = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n } else {\n onRendererMessage = (listener) => options.onRendererMessage!(listener);\n }\n\n const endpoint = createElectronSyncoreBridge({\n window: options.window,\n onRendererMessage,\n channel\n });\n const attachedRuntime = attachNodeIpcRuntime({\n endpoint,\n createRuntime: () => options.runtime\n });\n\n return {\n ready: attachedRuntime.ready,\n async dispose() {\n await attachedRuntime.dispose();\n endpoint.dispose();\n for (const cleanup of cleanupCallbacks) {\n cleanup();\n }\n }\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Devtools request handler */\n/* ------------------------------------------------------------------ */\n\nexport interface NodeWebSocketDevtoolsSinkOptions {\n url: string;\n reconnectDelayMs?: number;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n targetKind?: \"client\" | \"project\";\n runtimeRole?: \"app\" | \"project-target\";\n storageProtocol?: string;\n databaseLabel?: string;\n dataSourceAlias?: string;\n storageIdentity?: string;\n capabilities?: SyncoreDevtoolsCapabilities;\n}\n\nexport interface NodeWebSocketDevtoolsSink extends DevtoolsSink {\n attachRuntime(runtime: SyncoreRuntime<NodeSyncoreSchema>): void;\n attachCommandHandler(handler: DevtoolsCommandHandler): void;\n attachSubscriptionHost(host: DevtoolsSubscriptionHost): void;\n externalChangeSignal?: SyncoreExternalChangeSignal;\n dispose(): void;\n}\n\nexport function createNodeWebSocketDevtoolsSink(\n options: NodeWebSocketDevtoolsSinkOptions\n): NodeWebSocketDevtoolsSink {\n let socket: WebSocket | undefined;\n let disposed = false;\n let connectTimer: ReturnType<typeof setTimeout> | undefined;\n let getSummary: (() => SyncoreRuntimeSummary) | undefined;\n let onCommand: DevtoolsCommandHandler | undefined;\n let subscriptionHost: DevtoolsSubscriptionHost | undefined;\n const externalChangeListeners = new Set<\n (event: SyncoreExternalChangeEvent) => void\n >();\n const pendingMessages: SyncoreDevtoolsMessage[] = [];\n let latestHello:\n | {\n runtimeId: string;\n platform: string;\n }\n | undefined;\n\n const connect = () => {\n if (disposed) {\n return;\n }\n socket = new WebSocket(options.url);\n socket.on(\"open\", () => {\n if (latestHello) {\n sendNow({\n type: \"hello\",\n protocolVersion: SYNCORE_DEVTOOLS_PROTOCOL_VERSION,\n minSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION,\n maxSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION,\n runtimeId: latestHello.runtimeId,\n platform: latestHello.platform,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {}),\n ...(options.targetKind ? { targetKind: options.targetKind } : {}),\n ...(options.runtimeRole ? { runtimeRole: options.runtimeRole } : {}),\n ...(options.storageProtocol\n ? { storageProtocol: options.storageProtocol }\n : {}),\n ...(options.databaseLabel ? { databaseLabel: options.databaseLabel } : {}),\n ...(options.dataSourceAlias\n ? { dataSourceAlias: options.dataSourceAlias }\n : {}),\n ...(options.storageIdentity\n ? { storageIdentity: options.storageIdentity }\n : {}),\n capabilities: options.capabilities ?? createNodeDevtoolsCapabilities()\n });\n }\n flushPendingMessages();\n });\n socket.on(\"message\", (payload) => {\n const rawPayload =\n typeof payload === \"string\"\n ? payload\n : payload instanceof Buffer\n ? payload.toString(\"utf8\")\n : Array.isArray(payload)\n ? Buffer.concat(payload).toString(\"utf8\")\n : payload instanceof ArrayBuffer\n ? Buffer.from(payload).toString(\"utf8\")\n : Buffer.from(\n payload.buffer,\n payload.byteOffset,\n payload.byteLength\n ).toString(\"utf8\");\n if (rawPayload.length === 0) {\n return;\n }\n const message = JSON.parse(rawPayload) as\n | SyncoreDevtoolsMessage\n | SyncoreDevtoolsClientMessage;\n if (message.type === \"ping\") {\n send({ type: \"pong\" });\n } else if (message.type === \"external.change\") {\n for (const listener of externalChangeListeners) {\n listener(message.event as SyncoreExternalChangeEvent);\n }\n } else if (message.type === \"command\" && onCommand) {\n onCommand(message.payload)\n .then((responsePayload) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"command.result\",\n commandId: message.commandId,\n runtimeId,\n payload: responsePayload\n });\n })\n .catch((err) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"command.result\",\n commandId: message.commandId,\n runtimeId,\n payload: {\n kind: \"error\",\n message: err instanceof Error ? err.message : \"Unknown error\"\n }\n });\n });\n } else if (message.type === \"subscribe\" && subscriptionHost) {\n void subscriptionHost.subscribe(\n message.subscriptionId,\n message.payload,\n (payload) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"subscription.data\",\n subscriptionId: message.subscriptionId,\n runtimeId,\n payload\n });\n }\n );\n } else if (message.type === \"unsubscribe\") {\n subscriptionHost?.unsubscribe(message.subscriptionId);\n }\n });\n socket.on(\"close\", scheduleReconnect);\n socket.on(\"error\", scheduleReconnect);\n };\n\n const scheduleReconnect = () => {\n if (disposed || connectTimer) {\n return;\n }\n connectTimer = setTimeout(() => {\n connectTimer = undefined;\n connect();\n }, options.reconnectDelayMs ?? 1200);\n };\n\n const sendNow = (message: SyncoreDevtoolsMessage) => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(message));\n }\n };\n\n const flushPendingMessages = () => {\n while (pendingMessages.length > 0) {\n const nextMessage = pendingMessages.shift();\n if (!nextMessage) {\n continue;\n }\n sendNow(nextMessage);\n }\n };\n\n const send = (message: SyncoreDevtoolsMessage) => {\n if (socket?.readyState === WebSocket.OPEN) {\n sendNow(message);\n return;\n }\n pendingMessages.push(message);\n };\n\n connect();\n\n const sink: NodeWebSocketDevtoolsSink = {\n emit(event) {\n if (event.type === \"runtime.connected\") {\n latestHello = {\n runtimeId: event.runtimeId,\n platform: event.platform\n };\n send({\n type: \"hello\",\n protocolVersion: SYNCORE_DEVTOOLS_PROTOCOL_VERSION,\n minSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION,\n maxSupportedProtocolVersion:\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION,\n runtimeId: event.runtimeId,\n platform: event.platform,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {}),\n ...(options.targetKind ? { targetKind: options.targetKind } : {}),\n ...(options.runtimeRole ? { runtimeRole: options.runtimeRole } : {}),\n ...(options.storageProtocol\n ? { storageProtocol: options.storageProtocol }\n : {}),\n ...(options.databaseLabel ? { databaseLabel: options.databaseLabel } : {}),\n ...(options.dataSourceAlias\n ? { dataSourceAlias: options.dataSourceAlias }\n : {}),\n ...(options.storageIdentity\n ? { storageIdentity: options.storageIdentity }\n : {}),\n capabilities: options.capabilities ?? createNodeDevtoolsCapabilities()\n });\n }\n send({\n type: \"event\",\n event\n });\n },\n attachRuntime(runtime) {\n getSummary = () =>\n withRuntimeSummaryMeta(runtime.getAdmin().getRuntimeSummary(), options);\n },\n attachCommandHandler(handler) {\n onCommand = handler;\n },\n attachSubscriptionHost(host) {\n subscriptionHost = host;\n },\n dispose() {\n disposed = true;\n if (connectTimer) {\n clearTimeout(connectTimer);\n }\n subscriptionHost?.dispose();\n socket?.close();\n }\n };\n if (options.storageIdentity) {\n sink.externalChangeSignal = {\n subscribe(listener) {\n externalChangeListeners.add(listener);\n return () => {\n externalChangeListeners.delete(listener);\n };\n },\n publish(event) {\n const runtimeId = latestHello?.runtimeId ?? getSummary?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"external.change\",\n runtimeId,\n storageIdentity: options.storageIdentity!,\n event: event as SyncoreDevtoolsExternalChangeEvent\n });\n },\n close() {\n externalChangeListeners.clear();\n }\n };\n }\n return sink;\n}\n\nfunction withRuntimeSummaryMeta(\n summary: SyncoreRuntimeSummary,\n options: NodeWebSocketDevtoolsSinkOptions\n): SyncoreRuntimeSummary {\n return {\n ...summary,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}),\n ...(options.targetKind ? { targetKind: options.targetKind } : {}),\n ...(options.runtimeRole ? { runtimeRole: options.runtimeRole } : {}),\n ...(options.storageProtocol\n ? { storageProtocol: options.storageProtocol }\n : {}),\n ...(options.databaseLabel ? { databaseLabel: options.databaseLabel } : {}),\n ...(options.dataSourceAlias ? { dataSourceAlias: options.dataSourceAlias } : {}),\n ...(options.storageIdentity\n ? { storageIdentity: options.storageIdentity }\n : {}),\n capabilities: options.capabilities ?? createNodeDevtoolsCapabilities()\n };\n}\n\nfunction createNodeDevtoolsCapabilities(): SyncoreDevtoolsCapabilities {\n return {\n sql: {\n read: false,\n write: false,\n live: false,\n reason: \"SQL Console is provided by the Project Target for this data source.\"\n },\n data: {\n browse: true,\n mutate: true,\n importExport: true\n },\n scheduler: {\n read: true,\n edit: true\n }\n };\n}\n\nfunction shouldAutoConnectNodeDevtools(): boolean {\n return process.env.NODE_ENV !== \"production\";\n}\n\nfunction resolveDefaultNodeDevtoolsUrl(): string | undefined {\n if (process.env.SYNCORE_DISABLE_DEVTOOLS === \"1\") {\n return undefined;\n }\n return process.env.SYNCORE_DEVTOOLS_URL ?? \"ws://127.0.0.1:4311\";\n}\n"],"mappings":"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"}
|