syncorejs 0.2.2 → 0.2.4
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 +510 -71
- package/dist/_vendor/core/cli.mjs.map +1 -1
- package/dist/_vendor/core/devtools-auth.mjs.map +1 -1
- package/dist/_vendor/core/index.d.mts +3 -3
- 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 +261 -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 +12 -6
- 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 +81 -2
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +100 -13
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +42 -7
- 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 +1100 -12
- 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 +61 -27
- package/dist/_vendor/core/transport.mjs.map +1 -1
- package/dist/_vendor/devtools-protocol/index.d.ts +223 -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 +86 -18
- 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 +81 -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 +174 -9
- package/dist/_vendor/platform-node/index.d.mts.map +1 -1
- package/dist/_vendor/platform-node/index.mjs +251 -95
- package/dist/_vendor/platform-node/index.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc-react.mjs +4 -0
- 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 +312 -37
- package/dist/_vendor/platform-web/index.d.ts.map +1 -1
- package/dist/_vendor/platform-web/index.js +247 -25
- 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 +16 -1
- package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
- package/dist/_vendor/platform-web/opfs.js +41 -3
- package/dist/_vendor/platform-web/opfs.js.map +1 -1
- package/dist/_vendor/platform-web/persistence.d.ts +85 -1
- 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 +11 -5
- 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 +197 -13
- package/dist/_vendor/react/index.d.ts.map +1 -1
- package/dist/_vendor/react/index.js +209 -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 +207 -7
- package/dist/_vendor/svelte/index.d.ts.map +1 -1
- package/dist/_vendor/svelte/index.js +201 -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 +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 open,\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 readRange(\n id: string,\n offset: number,\n length: number\n ): Promise<Uint8Array | null> {\n let handle;\n try {\n handle = await open(this.filePath(id), \"r\");\n const buffer = Buffer.alloc(Math.max(length, 0));\n const result = await handle.read(\n buffer,\n 0,\n buffer.byteLength,\n Math.max(offset, 0)\n );\n return buffer.subarray(0, result.bytesRead);\n } catch {\n return null;\n } finally {\n await handle?.close();\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) SyncoreRuntime. Call\n * `await runtime.start()` before using the client.\n */\nexport function createNodeSyncoreRuntime<TSchema extends NodeSyncoreSchema>(\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 runtimeCapabilities: {\n storage: {\n available: true,\n protocol: \"file\",\n supportsRange: true\n }\n },\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 =\n 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<TSchema extends NodeSyncoreSchema>(\n runtime: SyncoreRuntime<TSchema>\n) {\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\n ? { databaseLabel: options.databaseLabel }\n : {}),\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\n ? { databaseLabel: options.databaseLabel }\n : {}),\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\n ? { dataSourceAlias: options.dataSourceAlias }\n : {}),\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:\n \"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 storage: {\n browse: true,\n download: true,\n readRange: true,\n delete: true,\n maxPreviewBytes: 80_000\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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"}
|
|
@@ -16,6 +16,10 @@ function SyncoreElectronProvider({ children, bridgeName, windowObject }) {
|
|
|
16
16
|
return createUnavailableSyncoreClient({
|
|
17
17
|
kind: "unavailable",
|
|
18
18
|
reason: "ipc-unavailable",
|
|
19
|
+
capabilities: { storage: {
|
|
20
|
+
available: false,
|
|
21
|
+
reason: "Syncore IPC bridge is unavailable."
|
|
22
|
+
} },
|
|
19
23
|
...error instanceof Error ? { error } : {}
|
|
20
24
|
});
|
|
21
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ipc-react.mjs","names":[],"sources":["../src/ipc-react.tsx"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createUnavailableSyncoreClient } from \"@syncore/core\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport { createRendererSyncoreWindowClient } from \"./ipc.js\";\n\n/**\n * Props for {@link SyncoreElectronProvider}.\n */\nexport interface SyncoreElectronProviderProps {\n /** The React subtree that should receive the renderer Syncore client. */\n children: ReactNode;\n\n /** Optional custom bridge name exposed on `window`. */\n bridgeName?: string;\n\n /** Optional window-like object for tests or custom shells. */\n windowObject?: Window & typeof globalThis;\n}\n\n/**\n * Create a renderer Syncore client from `window.syncoreBridge` and provide it to React.\n */\nexport function SyncoreElectronProvider({\n children,\n bridgeName,\n windowObject\n}: SyncoreElectronProviderProps): ReactNode {\n const resolvedWindow = windowObject ?? window;\n const client = useMemo(\n () => {\n try {\n return createRendererSyncoreWindowClient(\n resolvedWindow,\n bridgeName ?? \"syncoreBridge\"\n );\n } catch (error) {\n return createUnavailableSyncoreClient({\n kind: \"unavailable\",\n reason: \"ipc-unavailable\",\n ...(error instanceof Error ? { error } : {})\n });\n }\n },\n [bridgeName, resolvedWindow]\n );\n\n useEffect(\n () => () => {\n if (\"dispose\" in client && typeof client.dispose === \"function\") {\n client.dispose();\n }\n },\n [client]\n );\n\n return <SyncoreProvider client={client}>{children}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,wBAAwB,EACtC,UACA,YACA,gBAC0C;CAC1C,MAAM,iBAAiB,gBAAgB;CACvC,MAAM,SAAS,cACP;
|
|
1
|
+
{"version":3,"file":"ipc-react.mjs","names":[],"sources":["../src/ipc-react.tsx"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createUnavailableSyncoreClient } from \"@syncore/core\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport { createRendererSyncoreWindowClient } from \"./ipc.js\";\n\n/**\n * Props for {@link SyncoreElectronProvider}.\n */\nexport interface SyncoreElectronProviderProps {\n /** The React subtree that should receive the renderer Syncore client. */\n children: ReactNode;\n\n /** Optional custom bridge name exposed on `window`. */\n bridgeName?: string;\n\n /** Optional window-like object for tests or custom shells. */\n windowObject?: Window & typeof globalThis;\n}\n\n/**\n * Create a renderer Syncore client from `window.syncoreBridge` and provide it to React.\n */\nexport function SyncoreElectronProvider({\n children,\n bridgeName,\n windowObject\n}: SyncoreElectronProviderProps): ReactNode {\n const resolvedWindow = windowObject ?? window;\n const client = useMemo(\n () => {\n try {\n return createRendererSyncoreWindowClient(\n resolvedWindow,\n bridgeName ?? \"syncoreBridge\"\n );\n } catch (error) {\n return createUnavailableSyncoreClient({\n kind: \"unavailable\",\n reason: \"ipc-unavailable\",\n capabilities: {\n storage: {\n available: false,\n reason: \"Syncore IPC bridge is unavailable.\"\n }\n },\n ...(error instanceof Error ? { error } : {})\n });\n }\n },\n [bridgeName, resolvedWindow]\n );\n\n useEffect(\n () => () => {\n if (\"dispose\" in client && typeof client.dispose === \"function\") {\n client.dispose();\n }\n },\n [client]\n );\n\n return <SyncoreProvider client={client}>{children}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,wBAAwB,EACtC,UACA,YACA,gBAC0C;CAC1C,MAAM,iBAAiB,gBAAgB;CACvC,MAAM,SAAS,cACP;EACJ,IAAI;GACF,OAAO,kCACL,gBACA,cAAc,eAChB;EACF,SAAS,OAAO;GACd,OAAO,+BAA+B;IACpC,MAAM;IACN,QAAQ;IACR,cAAc,EACZ,SAAS;KACP,WAAW;KACX,QAAQ;IACV,EACF;IACA,GAAI,iBAAiB,QAAQ,EAAE,MAAM,IAAI,CAAC;GAC5C,CAAC;EACH;CACF,GACA,CAAC,YAAY,cAAc,CAC7B;CAEA,sBACc;EACV,IAAI,aAAa,UAAU,OAAO,OAAO,YAAY,YACnD,OAAO,QAAQ;CAEnB,GACA,CAAC,MAAM,CACT;CAEA,OAAO,oBAAC,iBAAD;EAAyB;EAAS;CAA0B,CAAA;AACrE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ipc.d.mts","names":[],"sources":["../src/ipc.ts"],"mappings":";;;KAUY,oBAAA,iBACM,gBAAA,GAAmB,gBAAA,IACjC,OAAA;AAAA,KACQ,yBAAA,GAA4B,
|
|
1
|
+
{"version":3,"file":"ipc.d.mts","names":[],"sources":["../src/ipc.ts"],"mappings":";;;KAUY,oBAAA,iBACM,gBAAA,GAAmB,gBAAA,IACjC,OAAA;AAAA,KACQ,yBAAA,GAA4B,4BAA4B;AAAA,KACxD,kBAAA,WAA6B,gBAAgB,CAAC,MAAA;AAAA,cAE7C,qBAAA,SAA8B,mBAAA;EACjC,KAAA,EAAO,mBAAA;EACP,QAAA,EAAU,mBAAA;EACV,MAAA,EAAQ,mBAAA;EACR,UAAA,EAAY,mBAAA;AAAA;AAAA,KAGV,2BAAA,iBACM,oBAAA,GAAuB,oBAAA,IACrC,0BAAA,CAA2B,OAAA;AAAA,KACnB,sBAAA,GAAyB,qBAAqB;AAAA,UAEzC,qBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,UAGN,mBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,UAGN,wBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;;;;iBAMP,0BAAA,CAA2B,OAE1C;EADC,UAAA;AAAA;AAjC8D;AAEhE;;AAFgE,iBAyChD,2BAAA,CACd,QAAA,EAAU,yBAAA,GACT,qBAAqB;;;;iBAOR,iCAAA,CACd,MAAA,EAAQ,qBAAA,GACP,qBAAqB;;;;iBA4BR,iCAAA,CACd,YAAA,EAAc,MAAA,UAAgB,UAAA,EAC9B,UAAA,YACC,qBAAA;AAAA,iBAqBa,4BAAA,CACd,MAAA,EAAQ,wBAAA,GACP,yBAAyB;EAAK,OAAA;AAAA;AAAA,iBA+BjB,oBAAA,CACd,OAAA,EAAS,2BAAA,GACR,sBAAsB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ipc.mjs","names":[],"sources":["../src/ipc.ts"],"sourcesContent":["import {\n attachRuntimeBridge,\n type AttachRuntimeBridgeOptions,\n type AttachedRuntimeBridge,\n type BridgeQueryWatch,\n SyncoreBridgeClient,\n type SyncoreBridgeMessageEndpoint,\n type SyncoreDataModel\n} from \"@syncore/core\";\n\nexport type NodeIpcSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\nexport type SyncoreIpcMessageEndpoint = SyncoreBridgeMessageEndpoint;\nexport type RendererQueryWatch<TValue> = BridgeQueryWatch<TValue>;\n\nexport class SyncoreRendererClient extends SyncoreBridgeClient {\n declare query: SyncoreBridgeClient[\"query\"];\n declare mutation: SyncoreBridgeClient[\"mutation\"];\n declare action: SyncoreBridgeClient[\"action\"];\n declare watchQuery: SyncoreBridgeClient[\"watchQuery\"];\n}\n\nexport type AttachNodeIpcRuntimeOptions<\n TSchema extends NodeIpcSyncoreSchema = NodeIpcSyncoreSchema\n> = AttachRuntimeBridgeOptions<TSchema>;\nexport type AttachedNodeIpcRuntime = AttachedRuntimeBridge;\n\nexport interface SyncoreRendererBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreWindowBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreMainProcessBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\n/**\n * Install the default Electron preload bridge used by Syncore renderer helpers.\n */\nexport function installSyncoreWindowBridge(options?: {\n bridgeName?: string;\n}): string {\n return `(function(){const bridgeName=${JSON.stringify(options?.bridgeName ?? \"syncoreBridge\")};const {contextBridge,ipcRenderer}=require(\"electron\");const channel=\"syncore:message\";const listeners=new Map();contextBridge.exposeInMainWorld(bridgeName,{postMessage(message){ipcRenderer.send(channel,message);},onMessage(listener){const wrapped=(_event,payload)=>{listener(payload);};listeners.set(listener,wrapped);ipcRenderer.on(channel,wrapped);return()=>{ipcRenderer.off(channel,wrapped);listeners.delete(listener);};}});})();`;\n}\n\n/**\n * Create a renderer client from a low-level IPC message endpoint.\n */\nexport function createRendererSyncoreClient(\n endpoint: SyncoreIpcMessageEndpoint\n): SyncoreRendererClient {\n return new SyncoreRendererClient(endpoint);\n}\n\n/**\n * Create a renderer client from a bridge object exposed by preload code.\n */\nexport function createRendererSyncoreBridgeClient(\n bridge: SyncoreRendererBridge\n): SyncoreRendererClient {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return createRendererSyncoreClient({\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n }\n });\n}\n\n/**\n * Create a renderer client from `window.syncoreBridge` or another named bridge.\n */\nexport function createRendererSyncoreWindowClient(\n windowObject: Window & typeof globalThis,\n bridgeName = \"syncoreBridge\"\n): SyncoreRendererClient {\n const bridge = (\n windowObject as typeof windowObject & Record<string, unknown>\n )[bridgeName];\n if (!bridge || typeof bridge !== \"object\") {\n throw new Error(`Missing window.${bridgeName} bridge.`);\n }\n\n const candidate = bridge as SyncoreWindowBridge;\n if (\n typeof candidate.postMessage !== \"function\" ||\n typeof candidate.onMessage !== \"function\"\n ) {\n throw new Error(\n `window.${bridgeName} must expose postMessage() and onMessage().`\n );\n }\n\n return createRendererSyncoreBridgeClient(candidate);\n}\n\nexport function createNodeIpcMessageEndpoint(\n bridge: SyncoreMainProcessBridge\n): SyncoreIpcMessageEndpoint & { dispose(): void } {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return {\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n },\n dispose() {\n for (const dispose of listeners.values()) {\n dispose();\n }\n listeners.clear();\n }\n };\n}\n\nexport function attachNodeIpcRuntime(\n options: AttachNodeIpcRuntimeOptions\n): AttachedNodeIpcRuntime {\n return attachRuntimeBridge(options);\n}\n"],"mappings":";;AAgBA,IAAa,wBAAb,cAA2C,oBAAoB
|
|
1
|
+
{"version":3,"file":"ipc.mjs","names":[],"sources":["../src/ipc.ts"],"sourcesContent":["import {\n attachRuntimeBridge,\n type AttachRuntimeBridgeOptions,\n type AttachedRuntimeBridge,\n type BridgeQueryWatch,\n SyncoreBridgeClient,\n type SyncoreBridgeMessageEndpoint,\n type SyncoreDataModel\n} from \"@syncore/core\";\n\nexport type NodeIpcSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\nexport type SyncoreIpcMessageEndpoint = SyncoreBridgeMessageEndpoint;\nexport type RendererQueryWatch<TValue> = BridgeQueryWatch<TValue>;\n\nexport class SyncoreRendererClient extends SyncoreBridgeClient {\n declare query: SyncoreBridgeClient[\"query\"];\n declare mutation: SyncoreBridgeClient[\"mutation\"];\n declare action: SyncoreBridgeClient[\"action\"];\n declare watchQuery: SyncoreBridgeClient[\"watchQuery\"];\n}\n\nexport type AttachNodeIpcRuntimeOptions<\n TSchema extends NodeIpcSyncoreSchema = NodeIpcSyncoreSchema\n> = AttachRuntimeBridgeOptions<TSchema>;\nexport type AttachedNodeIpcRuntime = AttachedRuntimeBridge;\n\nexport interface SyncoreRendererBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreWindowBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreMainProcessBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\n/**\n * Install the default Electron preload bridge used by Syncore renderer helpers.\n */\nexport function installSyncoreWindowBridge(options?: {\n bridgeName?: string;\n}): string {\n return `(function(){const bridgeName=${JSON.stringify(options?.bridgeName ?? \"syncoreBridge\")};const {contextBridge,ipcRenderer}=require(\"electron\");const channel=\"syncore:message\";const listeners=new Map();contextBridge.exposeInMainWorld(bridgeName,{postMessage(message){ipcRenderer.send(channel,message);},onMessage(listener){const wrapped=(_event,payload)=>{listener(payload);};listeners.set(listener,wrapped);ipcRenderer.on(channel,wrapped);return()=>{ipcRenderer.off(channel,wrapped);listeners.delete(listener);};}});})();`;\n}\n\n/**\n * Create a renderer client from a low-level IPC message endpoint.\n */\nexport function createRendererSyncoreClient(\n endpoint: SyncoreIpcMessageEndpoint\n): SyncoreRendererClient {\n return new SyncoreRendererClient(endpoint);\n}\n\n/**\n * Create a renderer client from a bridge object exposed by preload code.\n */\nexport function createRendererSyncoreBridgeClient(\n bridge: SyncoreRendererBridge\n): SyncoreRendererClient {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return createRendererSyncoreClient({\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n }\n });\n}\n\n/**\n * Create a renderer client from `window.syncoreBridge` or another named bridge.\n */\nexport function createRendererSyncoreWindowClient(\n windowObject: Window & typeof globalThis,\n bridgeName = \"syncoreBridge\"\n): SyncoreRendererClient {\n const bridge = (\n windowObject as typeof windowObject & Record<string, unknown>\n )[bridgeName];\n if (!bridge || typeof bridge !== \"object\") {\n throw new Error(`Missing window.${bridgeName} bridge.`);\n }\n\n const candidate = bridge as SyncoreWindowBridge;\n if (\n typeof candidate.postMessage !== \"function\" ||\n typeof candidate.onMessage !== \"function\"\n ) {\n throw new Error(\n `window.${bridgeName} must expose postMessage() and onMessage().`\n );\n }\n\n return createRendererSyncoreBridgeClient(candidate);\n}\n\nexport function createNodeIpcMessageEndpoint(\n bridge: SyncoreMainProcessBridge\n): SyncoreIpcMessageEndpoint & { dispose(): void } {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return {\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n },\n dispose() {\n for (const dispose of listeners.values()) {\n dispose();\n }\n listeners.clear();\n }\n };\n}\n\nexport function attachNodeIpcRuntime(\n options: AttachNodeIpcRuntimeOptions\n): AttachedNodeIpcRuntime {\n return attachRuntimeBridge(options);\n}\n"],"mappings":";;AAgBA,IAAa,wBAAb,cAA2C,oBAAoB,CAK/D;;;;AAyBA,SAAgB,2BAA2B,SAEhC;CACT,OAAO,gCAAgC,KAAK,UAAU,SAAS,cAAc,eAAe,EAAE;AAChG;;;;AAKA,SAAgB,4BACd,UACuB;CACvB,OAAO,IAAI,sBAAsB,QAAQ;AAC3C;;;;AAKA,SAAgB,kCACd,QACuB;CACvB,MAAM,4BAAY,IAAI,IAGpB;CAEF,OAAO,4BAA4B;EACjC,YAAY,SAAS;GACnB,OAAO,YAAY,OAAO;EAC5B;EACA,iBAAiB,OAAO,UAAU;GAChC,UAAU,IACR,UACA,OAAO,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,QAAQ,CAA0B;GACrD,CAAC,CACH;EACF;EACA,oBAAoB,OAAO,UAAU;GACnC,UAAU,IAAI,QAAQ,IAAI;GAC1B,UAAU,OAAO,QAAQ;EAC3B;CACF,CAAC;AACH;;;;AAKA,SAAgB,kCACd,cACA,aAAa,iBACU;CACvB,MAAM,SACJ,aACA;CACF,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B,MAAM,IAAI,MAAM,kBAAkB,WAAW,SAAS;CAGxD,MAAM,YAAY;CAClB,IACE,OAAO,UAAU,gBAAgB,cACjC,OAAO,UAAU,cAAc,YAE/B,MAAM,IAAI,MACR,UAAU,WAAW,4CACvB;CAGF,OAAO,kCAAkC,SAAS;AACpD;AAEA,SAAgB,6BACd,QACiD;CACjD,MAAM,4BAAY,IAAI,IAGpB;CAEF,OAAO;EACL,YAAY,SAAS;GACnB,OAAO,YAAY,OAAO;EAC5B;EACA,iBAAiB,OAAO,UAAU;GAChC,UAAU,IACR,UACA,OAAO,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,QAAQ,CAA0B;GACrD,CAAC,CACH;EACF;EACA,oBAAoB,OAAO,UAAU;GACnC,UAAU,IAAI,QAAQ,IAAI;GAC1B,UAAU,OAAO,QAAQ;EAC3B;EACA,UAAU;GACR,KAAK,MAAM,WAAW,UAAU,OAAO,GACrC,QAAQ;GAEV,UAAU,MAAM;EAClB;CACF;AACF;AAEA,SAAgB,qBACd,SACwB;CACxB,OAAO,oBAAoB,OAAO;AACpC"}
|
|
@@ -4,9 +4,23 @@ import initSqlJs from "sql.js";
|
|
|
4
4
|
|
|
5
5
|
//#region src/external-change.d.ts
|
|
6
6
|
type SqlJsDatabase = initSqlJs.Database;
|
|
7
|
+
/** Options for constructing a {@link BroadcastChannelExternalChangeSignal}. */
|
|
7
8
|
interface BroadcastChannelExternalChangeSignalOptions {
|
|
9
|
+
/** Name of the `BroadcastChannel`, shared by all tabs with the same database. */
|
|
8
10
|
channelName: string;
|
|
9
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* A `BroadcastChannel`-based SyncoreExternalChangeSignal that
|
|
14
|
+
* propagates database-mutation events across all browser tabs sharing the same
|
|
15
|
+
* Syncore database.
|
|
16
|
+
*
|
|
17
|
+
* When a Syncore mutation commits, the runtime publishes a change event on this
|
|
18
|
+
* channel. Other tabs subscribed to the same channel reload their queries
|
|
19
|
+
* automatically, keeping all open tabs in sync without a server round-trip.
|
|
20
|
+
*
|
|
21
|
+
* Constructed automatically by `createWebSyncoreRuntime`. Exposed for
|
|
22
|
+
* advanced setups that build the persistence layer independently.
|
|
23
|
+
*/
|
|
10
24
|
declare class BroadcastChannelExternalChangeSignal implements SyncoreExternalChangeSignal {
|
|
11
25
|
private readonly channel;
|
|
12
26
|
private readonly listeners;
|
|
@@ -16,12 +30,32 @@ declare class BroadcastChannelExternalChangeSignal implements SyncoreExternalCha
|
|
|
16
30
|
close(): void;
|
|
17
31
|
private readonly messageListener;
|
|
18
32
|
}
|
|
33
|
+
/** Options for constructing a {@link SqlJsExternalChangeApplier}. */
|
|
19
34
|
interface SqlJsExternalChangeApplierOptions {
|
|
35
|
+
/** Logical name of the Syncore database, used to load the latest snapshot from persistence. */
|
|
20
36
|
databaseName: string;
|
|
37
|
+
/** The web persistence layer to read the updated database bytes from. */
|
|
21
38
|
persistence: SyncoreWebPersistence;
|
|
39
|
+
/**
|
|
40
|
+
* Factory that creates a new sql.js `Database` instance from optional
|
|
41
|
+
* initial bytes. Called whenever the database needs to be swapped after an
|
|
42
|
+
* external change.
|
|
43
|
+
*/
|
|
22
44
|
createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;
|
|
45
|
+
/** Callback invoked with the newly created database so the runtime can swap its reference. */
|
|
23
46
|
replaceDatabase(database: SqlJsDatabase): void;
|
|
24
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* A SyncoreExternalChangeApplier for sql.js (in-memory) databases.
|
|
50
|
+
*
|
|
51
|
+
* When another tab commits a mutation and broadcasts the change event, this
|
|
52
|
+
* applier loads the latest database snapshot from web persistence (OPFS or
|
|
53
|
+
* IndexedDB) and swaps the in-memory `sql.js` database instance so the current
|
|
54
|
+
* tab reflects the new state.
|
|
55
|
+
*
|
|
56
|
+
* Constructed automatically by `createWebSyncoreRuntime` when using sql.js
|
|
57
|
+
* persistence. Exposed for advanced setups.
|
|
58
|
+
*/
|
|
25
59
|
declare class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier {
|
|
26
60
|
private readonly databaseName;
|
|
27
61
|
private readonly persistence;
|
|
@@ -34,6 +68,13 @@ declare class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier
|
|
|
34
68
|
changedScopes: ImpactScope[];
|
|
35
69
|
}>;
|
|
36
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Derive the canonical `BroadcastChannel` name for cross-tab sync from a
|
|
73
|
+
* logical database name.
|
|
74
|
+
*
|
|
75
|
+
* All Syncore runtimes sharing the same `databaseName` will use the same
|
|
76
|
+
* channel, ensuring mutations in one tab are visible to all others.
|
|
77
|
+
*/
|
|
37
78
|
declare function createDefaultSyncChannelName(databaseName: string): string;
|
|
38
79
|
//#endregion
|
|
39
80
|
export { BroadcastChannelExternalChangeSignal, BroadcastChannelExternalChangeSignalOptions, SqlJsExternalChangeApplier, SqlJsExternalChangeApplierOptions, createDefaultSyncChannelName };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-change.d.ts","names":[],"sources":["../src/external-change.ts"],"mappings":";;;;;KASK,aAAA,GAAgB,SAAA,CAAU,
|
|
1
|
+
{"version":3,"file":"external-change.d.ts","names":[],"sources":["../src/external-change.ts"],"mappings":";;;;;KASK,aAAA,GAAgB,SAAA,CAAU,QAAQ;;UAGtB,2CAAA;EAHC;EAKhB,WAAW;AAAA;AAL0B;AAGvC;;;;AAEa;AAeb;;;;;;AApBuC,cAoB1B,oCAAA,YAAgD,2BAAA;EAAA,iBAC1C,OAAA;EAAA,iBACA,SAAA;cAIL,OAAA,EAAS,2CAAA;EAOrB,SAAA,CAAU,QAAA,GAAW,KAAA,EAAO,0BAAA;EAO5B,OAAA,CAAQ,KAAA,EAAO,0BAAA;EAIf,KAAA,CAAA;EAAA,iBAKiB,eAAA;AAAA;;UAWF,iCAAA;EA3BM;EA6BrB,YAAA;EAtBA;EAwBA,WAAA,EAAa,qBAAA;EAxBL;;;;AASwB;EAqBhC,cAAA,GAAiB,KAAA,GAAQ,UAAA,KAAe,aAAA;EAVQ;EAYhD,eAAA,CAAgB,QAAA,EAAU,aAAA;AAAA;;;;;;;;;;;;cAcf,0BAAA,YAAsC,4BAAA;EAAA,iBAChC,YAAA;EAAA,iBACA,WAAA;EAAA,iBACA,cAAA;EAAA,iBACA,eAAA;cAEL,OAAA,EAAS,iCAAA;EAOf,mBAAA,CAAoB,KAAA,EAAO,0BAAA,GAA0B,OAAA;;;;;;;;;;;;;iBA4B7C,4BAAA,CAA6B,YAAoB"}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
//#region src/external-change.ts
|
|
2
|
+
/**
|
|
3
|
+
* A `BroadcastChannel`-based SyncoreExternalChangeSignal that
|
|
4
|
+
* propagates database-mutation events across all browser tabs sharing the same
|
|
5
|
+
* Syncore database.
|
|
6
|
+
*
|
|
7
|
+
* When a Syncore mutation commits, the runtime publishes a change event on this
|
|
8
|
+
* channel. Other tabs subscribed to the same channel reload their queries
|
|
9
|
+
* automatically, keeping all open tabs in sync without a server round-trip.
|
|
10
|
+
*
|
|
11
|
+
* Constructed automatically by `createWebSyncoreRuntime`. Exposed for
|
|
12
|
+
* advanced setups that build the persistence layer independently.
|
|
13
|
+
*/
|
|
2
14
|
var BroadcastChannelExternalChangeSignal = class {
|
|
3
15
|
channel;
|
|
4
16
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -26,6 +38,17 @@ var BroadcastChannelExternalChangeSignal = class {
|
|
|
26
38
|
for (const listener of this.listeners) listener(event.data);
|
|
27
39
|
};
|
|
28
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* A SyncoreExternalChangeApplier for sql.js (in-memory) databases.
|
|
43
|
+
*
|
|
44
|
+
* When another tab commits a mutation and broadcasts the change event, this
|
|
45
|
+
* applier loads the latest database snapshot from web persistence (OPFS or
|
|
46
|
+
* IndexedDB) and swaps the in-memory `sql.js` database instance so the current
|
|
47
|
+
* tab reflects the new state.
|
|
48
|
+
*
|
|
49
|
+
* Constructed automatically by `createWebSyncoreRuntime` when using sql.js
|
|
50
|
+
* persistence. Exposed for advanced setups.
|
|
51
|
+
*/
|
|
29
52
|
var SqlJsExternalChangeApplier = class {
|
|
30
53
|
databaseName;
|
|
31
54
|
persistence;
|
|
@@ -50,6 +73,13 @@ var SqlJsExternalChangeApplier = class {
|
|
|
50
73
|
};
|
|
51
74
|
}
|
|
52
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* Derive the canonical `BroadcastChannel` name for cross-tab sync from a
|
|
78
|
+
* logical database name.
|
|
79
|
+
*
|
|
80
|
+
* All Syncore runtimes sharing the same `databaseName` will use the same
|
|
81
|
+
* channel, ensuring mutations in one tab are visible to all others.
|
|
82
|
+
*/
|
|
53
83
|
function createDefaultSyncChannelName(databaseName) {
|
|
54
84
|
return `syncore:external:${databaseName}`;
|
|
55
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-change.js","names":[],"sources":["../src/external-change.ts"],"sourcesContent":["import type {\n ImpactScope,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal\n} from \"@syncore/core\";\nimport type initSqlJs from \"sql.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\n\nexport interface BroadcastChannelExternalChangeSignalOptions {\n channelName: string;\n}\n\nexport class BroadcastChannelExternalChangeSignal implements SyncoreExternalChangeSignal {\n private readonly channel: BroadcastChannel | undefined;\n private readonly listeners = new Set<\n (event: SyncoreExternalChangeEvent) => void\n >();\n\n constructor(options: BroadcastChannelExternalChangeSignalOptions) {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(options.channelName);\n this.channel.addEventListener(\"message\", this.messageListener);\n }\n }\n\n subscribe(listener: (event: SyncoreExternalChangeEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n publish(event: SyncoreExternalChangeEvent): void {\n this.channel?.postMessage(event);\n }\n\n close(): void {\n this.channel?.removeEventListener(\"message\", this.messageListener);\n this.channel?.close();\n }\n\n private readonly messageListener = (event: MessageEvent<unknown>) => {\n if (!isExternalChangeEvent(event.data)) {\n return;\n }\n for (const listener of this.listeners) {\n listener(event.data);\n }\n };\n}\n\nexport interface SqlJsExternalChangeApplierOptions {\n databaseName: string;\n persistence: SyncoreWebPersistence;\n createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n replaceDatabase(database: SqlJsDatabase): void;\n}\n\nexport class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier {\n private readonly databaseName: string;\n private readonly persistence: SyncoreWebPersistence;\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n private readonly replaceDatabase: (database: SqlJsDatabase) => void;\n\n constructor(options: SqlJsExternalChangeApplierOptions) {\n this.databaseName = options.databaseName;\n this.persistence = options.persistence;\n this.createDatabase = (bytes) => options.createDatabase(bytes);\n this.replaceDatabase = (database) => options.replaceDatabase(database);\n }\n\n async applyExternalChange(event: SyncoreExternalChangeEvent) {\n const databaseChanged = event.scope === \"database\" || event.scope === \"all\";\n if (databaseChanged) {\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (bytes) {\n this.replaceDatabase(this.createDatabase(bytes));\n }\n }\n return {\n databaseChanged,\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes:\n event.changedScopes ??\n ([\n ...(event.changedTables ?? []).map((tableName) => `table:${tableName}`),\n ...(event.storageIds ?? []).map((storageId) => `storage:${storageId}`)\n ] as ImpactScope[])\n };\n }\n}\n\nexport function createDefaultSyncChannelName(databaseName: string): string {\n return `syncore:external:${databaseName}`;\n}\n\nfunction isExternalChangeEvent(\n value: unknown\n): value is SyncoreExternalChangeEvent {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"sourceId\" in value &&\n \"scope\" in value &&\n \"reason\" in value &&\n \"timestamp\" in value\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"external-change.js","names":[],"sources":["../src/external-change.ts"],"sourcesContent":["import type {\n ImpactScope,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal\n} from \"@syncore/core\";\nimport type initSqlJs from \"sql.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\n\n/** Options for constructing a {@link BroadcastChannelExternalChangeSignal}. */\nexport interface BroadcastChannelExternalChangeSignalOptions {\n /** Name of the `BroadcastChannel`, shared by all tabs with the same database. */\n channelName: string;\n}\n\n/**\n * A `BroadcastChannel`-based SyncoreExternalChangeSignal that\n * propagates database-mutation events across all browser tabs sharing the same\n * Syncore database.\n *\n * When a Syncore mutation commits, the runtime publishes a change event on this\n * channel. Other tabs subscribed to the same channel reload their queries\n * automatically, keeping all open tabs in sync without a server round-trip.\n *\n * Constructed automatically by `createWebSyncoreRuntime`. Exposed for\n * advanced setups that build the persistence layer independently.\n */\nexport class BroadcastChannelExternalChangeSignal implements SyncoreExternalChangeSignal {\n private readonly channel: BroadcastChannel | undefined;\n private readonly listeners = new Set<\n (event: SyncoreExternalChangeEvent) => void\n >();\n\n constructor(options: BroadcastChannelExternalChangeSignalOptions) {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(options.channelName);\n this.channel.addEventListener(\"message\", this.messageListener);\n }\n }\n\n subscribe(listener: (event: SyncoreExternalChangeEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n publish(event: SyncoreExternalChangeEvent): void {\n this.channel?.postMessage(event);\n }\n\n close(): void {\n this.channel?.removeEventListener(\"message\", this.messageListener);\n this.channel?.close();\n }\n\n private readonly messageListener = (event: MessageEvent<unknown>) => {\n if (!isExternalChangeEvent(event.data)) {\n return;\n }\n for (const listener of this.listeners) {\n listener(event.data);\n }\n };\n}\n\n/** Options for constructing a {@link SqlJsExternalChangeApplier}. */\nexport interface SqlJsExternalChangeApplierOptions {\n /** Logical name of the Syncore database, used to load the latest snapshot from persistence. */\n databaseName: string;\n /** The web persistence layer to read the updated database bytes from. */\n persistence: SyncoreWebPersistence;\n /**\n * Factory that creates a new sql.js `Database` instance from optional\n * initial bytes. Called whenever the database needs to be swapped after an\n * external change.\n */\n createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n /** Callback invoked with the newly created database so the runtime can swap its reference. */\n replaceDatabase(database: SqlJsDatabase): void;\n}\n\n/**\n * A SyncoreExternalChangeApplier for sql.js (in-memory) databases.\n *\n * When another tab commits a mutation and broadcasts the change event, this\n * applier loads the latest database snapshot from web persistence (OPFS or\n * IndexedDB) and swaps the in-memory `sql.js` database instance so the current\n * tab reflects the new state.\n *\n * Constructed automatically by `createWebSyncoreRuntime` when using sql.js\n * persistence. Exposed for advanced setups.\n */\nexport class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier {\n private readonly databaseName: string;\n private readonly persistence: SyncoreWebPersistence;\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n private readonly replaceDatabase: (database: SqlJsDatabase) => void;\n\n constructor(options: SqlJsExternalChangeApplierOptions) {\n this.databaseName = options.databaseName;\n this.persistence = options.persistence;\n this.createDatabase = (bytes) => options.createDatabase(bytes);\n this.replaceDatabase = (database) => options.replaceDatabase(database);\n }\n\n async applyExternalChange(event: SyncoreExternalChangeEvent) {\n const databaseChanged = event.scope === \"database\" || event.scope === \"all\";\n if (databaseChanged) {\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (bytes) {\n this.replaceDatabase(this.createDatabase(bytes));\n }\n }\n return {\n databaseChanged,\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes:\n event.changedScopes ??\n ([\n ...(event.changedTables ?? []).map((tableName) => `table:${tableName}`),\n ...(event.storageIds ?? []).map((storageId) => `storage:${storageId}`)\n ] as ImpactScope[])\n };\n }\n}\n\n/**\n * Derive the canonical `BroadcastChannel` name for cross-tab sync from a\n * logical database name.\n *\n * All Syncore runtimes sharing the same `databaseName` will use the same\n * channel, ensuring mutations in one tab are visible to all others.\n */\nexport function createDefaultSyncChannelName(databaseName: string): string {\n return `syncore:external:${databaseName}`;\n}\n\nfunction isExternalChangeEvent(\n value: unknown\n): value is SyncoreExternalChangeEvent {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"sourceId\" in value &&\n \"scope\" in value &&\n \"reason\" in value &&\n \"timestamp\" in value\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,IAAa,uCAAb,MAAyF;CACvF;CACA,4BAA6B,IAAI,IAE/B;CAEF,YAAY,SAAsD;EAChE,IAAI,OAAO,qBAAqB,aAAa;GAC3C,KAAK,UAAU,IAAI,iBAAiB,QAAQ,WAAW;GACvD,KAAK,QAAQ,iBAAiB,WAAW,KAAK,eAAe;EAC/D;CACF;CAEA,UAAU,UAAmE;EAC3E,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa;GACX,KAAK,UAAU,OAAO,QAAQ;EAChC;CACF;CAEA,QAAQ,OAAyC;EAC/C,KAAK,SAAS,YAAY,KAAK;CACjC;CAEA,QAAc;EACZ,KAAK,SAAS,oBAAoB,WAAW,KAAK,eAAe;EACjE,KAAK,SAAS,MAAM;CACtB;CAEA,mBAAoC,UAAiC;EACnE,IAAI,CAAC,sBAAsB,MAAM,IAAI,GACnC;EAEF,KAAK,MAAM,YAAY,KAAK,WAC1B,SAAS,MAAM,IAAI;CAEvB;AACF;;;;;;;;;;;;AA6BA,IAAa,6BAAb,MAAgF;CAC9E;CACA;CACA;CACA;CAEA,YAAY,SAA4C;EACtD,KAAK,eAAe,QAAQ;EAC5B,KAAK,cAAc,QAAQ;EAC3B,KAAK,kBAAkB,UAAU,QAAQ,eAAe,KAAK;EAC7D,KAAK,mBAAmB,aAAa,QAAQ,gBAAgB,QAAQ;CACvE;CAEA,MAAM,oBAAoB,OAAmC;EAC3D,MAAM,kBAAkB,MAAM,UAAU,cAAc,MAAM,UAAU;EACtE,IAAI,iBAAiB;GACnB,MAAM,QAAQ,MAAM,KAAK,YAAY,aAAa,KAAK,YAAY;GACnE,IAAI,OACF,KAAK,gBAAgB,KAAK,eAAe,KAAK,CAAC;EAEnD;EACA,OAAO;GACL;GACA,gBAAgB,MAAM,UAAU,aAAa,MAAM,UAAU;GAC7D,eACE,MAAM,iBACL,CACC,IAAI,MAAM,iBAAiB,CAAC,GAAG,KAAK,cAAc,SAAS,WAAW,GACtE,IAAI,MAAM,cAAc,CAAC,GAAG,KAAK,cAAc,WAAW,WAAW,CACvE;EACJ;CACF;AACF;;;;;;;;AASA,SAAgB,6BAA6B,cAA8B;CACzE,OAAO,oBAAoB;AAC7B;AAEA,SAAS,sBACP,OACqC;CACrC,OACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,WAAW,SACX,YAAY,SACZ,eAAe;AAEnB"}
|