syncorejs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{core/src/cli.d.ts → _vendor/cli/app.d.mts} +4 -2
- package/dist/_vendor/cli/app.d.mts.map +1 -0
- package/dist/_vendor/cli/app.mjs +997 -0
- package/dist/_vendor/cli/app.mjs.map +1 -0
- package/dist/_vendor/cli/context.mjs +180 -0
- package/dist/_vendor/cli/context.mjs.map +1 -0
- package/dist/_vendor/cli/dev-session.mjs +49 -0
- package/dist/_vendor/cli/dev-session.mjs.map +1 -0
- package/dist/_vendor/cli/doctor.mjs +80 -0
- package/dist/_vendor/cli/doctor.mjs.map +1 -0
- package/dist/_vendor/cli/errors.mjs +22 -0
- package/dist/_vendor/cli/errors.mjs.map +1 -0
- package/dist/_vendor/cli/help.mjs +26 -0
- package/dist/_vendor/cli/help.mjs.map +1 -0
- package/dist/_vendor/cli/index.d.mts +2 -0
- package/dist/_vendor/cli/index.mjs +23 -0
- package/dist/_vendor/cli/index.mjs.map +1 -0
- package/dist/_vendor/cli/messages.mjs +32 -0
- package/dist/_vendor/cli/messages.mjs.map +1 -0
- package/dist/_vendor/cli/preflight.mjs +35 -0
- package/dist/_vendor/cli/preflight.mjs.map +1 -0
- package/dist/_vendor/cli/project.mjs +583 -0
- package/dist/_vendor/cli/project.mjs.map +1 -0
- package/dist/_vendor/cli/render.mjs +133 -0
- package/dist/_vendor/cli/render.mjs.map +1 -0
- package/dist/_vendor/cli/targets.mjs +87 -0
- package/dist/_vendor/cli/targets.mjs.map +1 -0
- package/dist/_vendor/core/cli.d.mts +59 -1
- package/dist/_vendor/core/cli.d.mts.map +1 -1
- package/dist/_vendor/core/cli.mjs +528 -75
- package/dist/_vendor/core/cli.mjs.map +1 -1
- package/dist/_vendor/core/index.d.mts +12 -4
- package/dist/_vendor/core/index.d.mts.map +1 -0
- package/dist/_vendor/core/index.mjs +4 -3
- package/dist/_vendor/core/index.mjs.map +1 -1
- package/dist/_vendor/core/runtime/devtools.d.mts +32 -6
- package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/devtools.mjs +397 -182
- package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
- package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
- package/dist/_vendor/core/runtime/runtime.d.mts +89 -7
- package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/runtime.mjs +303 -32
- package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
- package/dist/_vendor/devtools-protocol/index.d.ts +189 -82
- package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
- package/dist/_vendor/devtools-protocol/index.js +39 -0
- package/dist/_vendor/devtools-protocol/index.js.map +1 -0
- package/dist/_vendor/next/config.d.ts.map +1 -1
- package/dist/_vendor/next/config.js +2 -5
- package/dist/_vendor/next/config.js.map +1 -1
- package/dist/_vendor/platform-expo/index.d.ts +15 -5
- package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
- package/dist/_vendor/platform-expo/index.js +33 -3
- package/dist/_vendor/platform-expo/index.js.map +1 -1
- package/dist/_vendor/platform-expo/react.js.map +1 -1
- package/dist/_vendor/platform-node/index.d.mts +10 -5
- package/dist/_vendor/platform-node/index.d.mts.map +1 -1
- package/dist/_vendor/platform-node/index.mjs +145 -35
- package/dist/_vendor/platform-node/index.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
- package/dist/_vendor/platform-web/external-change.d.ts +39 -0
- package/dist/_vendor/platform-web/external-change.d.ts.map +1 -0
- package/dist/_vendor/platform-web/external-change.js +61 -0
- package/dist/_vendor/platform-web/external-change.js.map +1 -0
- package/dist/_vendor/platform-web/index.d.ts +27 -5
- package/dist/_vendor/platform-web/index.d.ts.map +1 -1
- package/dist/_vendor/platform-web/index.js +310 -44
- package/dist/_vendor/platform-web/index.js.map +1 -1
- package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
- package/dist/_vendor/platform-web/opfs.js.map +1 -1
- package/dist/_vendor/platform-web/persistence.js.map +1 -1
- package/dist/_vendor/platform-web/react.js.map +1 -1
- package/dist/_vendor/platform-web/sqljs.js +22 -2
- package/dist/_vendor/platform-web/sqljs.js.map +1 -1
- package/dist/_vendor/schema/definition.js.map +1 -1
- package/dist/_vendor/schema/planner.js.map +1 -1
- package/dist/_vendor/schema/validators.js.map +1 -1
- package/dist/browser-react.d.ts +1 -1
- package/dist/browser-react.js +1 -1
- package/dist/browser.d.ts +6 -7
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -5
- package/dist/browser.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +12 -3
- package/dist/cli.js.map +1 -1
- package/dist/expo-react.d.ts +1 -1
- package/dist/expo-react.js +1 -1
- package/dist/expo.d.ts +1 -2
- package/dist/expo.js +1 -2
- package/dist/index.d.ts +3 -7
- package/dist/index.js +3 -8
- package/dist/next-config.d.ts +1 -2
- package/dist/next-config.js +1 -2
- package/dist/next.d.ts +1 -3
- package/dist/next.js +1 -3
- package/dist/node-ipc-react.d.ts +1 -1
- package/dist/node-ipc-react.js +1 -1
- package/dist/node-ipc.d.ts +1 -2
- package/dist/node-ipc.js +1 -2
- package/dist/node.d.ts +1 -4
- package/dist/node.js +1 -3
- package/dist/react.d.ts +1 -2
- package/dist/react.js +1 -2
- package/dist/svelte.d.ts +1 -2
- package/dist/svelte.js +1 -2
- package/package.json +6 -3
- package/dist/core/src/cli.d.ts.map +0 -1
- package/dist/core/src/cli.js +0 -1196
- package/dist/core/src/cli.js.map +0 -1
- package/dist/core/src/index.js +0 -7
- package/dist/core/src/runtime/devtools.d.ts +0 -7
- package/dist/core/src/runtime/devtools.d.ts.map +0 -1
- package/dist/core/src/runtime/devtools.js +0 -300
- package/dist/core/src/runtime/devtools.js.map +0 -1
- package/dist/core/src/runtime/functions.d.ts +0 -123
- package/dist/core/src/runtime/functions.d.ts.map +0 -1
- package/dist/core/src/runtime/functions.js +0 -71
- package/dist/core/src/runtime/functions.js.map +0 -1
- package/dist/core/src/runtime/id.d.ts +0 -13
- package/dist/core/src/runtime/id.d.ts.map +0 -1
- package/dist/core/src/runtime/id.js +0 -28
- package/dist/core/src/runtime/id.js.map +0 -1
- package/dist/core/src/runtime/runtime.d.ts +0 -371
- package/dist/core/src/runtime/runtime.d.ts.map +0 -1
- package/dist/core/src/runtime/runtime.js +0 -1143
- package/dist/core/src/runtime/runtime.js.map +0 -1
- package/dist/devtools-protocol/src/index.d.ts +0 -201
- package/dist/devtools-protocol/src/index.d.ts.map +0 -1
- package/dist/next/src/config.d.ts +0 -17
- package/dist/next/src/config.d.ts.map +0 -1
- package/dist/next/src/config.js +0 -73
- package/dist/next/src/config.js.map +0 -1
- package/dist/next/src/index.d.ts +0 -80
- package/dist/next/src/index.d.ts.map +0 -1
- package/dist/next/src/index.js +0 -82
- package/dist/next/src/index.js.map +0 -1
- package/dist/platform-expo/src/index.d.ts +0 -96
- package/dist/platform-expo/src/index.d.ts.map +0 -1
- package/dist/platform-expo/src/index.js +0 -198
- package/dist/platform-expo/src/index.js.map +0 -1
- package/dist/platform-expo/src/react.d.ts +0 -26
- package/dist/platform-expo/src/react.d.ts.map +0 -1
- package/dist/platform-expo/src/react.js +0 -30
- package/dist/platform-expo/src/react.js.map +0 -1
- package/dist/platform-node/src/index.d.ts +0 -145
- package/dist/platform-node/src/index.d.ts.map +0 -1
- package/dist/platform-node/src/index.js +0 -407
- package/dist/platform-node/src/index.js.map +0 -1
- package/dist/platform-node/src/ipc-react.d.ts +0 -25
- package/dist/platform-node/src/ipc-react.d.ts.map +0 -1
- package/dist/platform-node/src/ipc-react.js +0 -21
- package/dist/platform-node/src/ipc-react.js.map +0 -1
- package/dist/platform-node/src/ipc.d.ts +0 -76
- package/dist/platform-node/src/ipc.d.ts.map +0 -1
- package/dist/platform-node/src/ipc.js +0 -344
- package/dist/platform-node/src/ipc.js.map +0 -1
- package/dist/platform-web/src/index.d.ts +0 -106
- package/dist/platform-web/src/index.d.ts.map +0 -1
- package/dist/platform-web/src/index.js +0 -311
- package/dist/platform-web/src/index.js.map +0 -1
- package/dist/platform-web/src/indexeddb.js +0 -125
- package/dist/platform-web/src/indexeddb.js.map +0 -1
- package/dist/platform-web/src/opfs.js +0 -146
- package/dist/platform-web/src/opfs.js.map +0 -1
- package/dist/platform-web/src/persistence.d.ts +0 -20
- package/dist/platform-web/src/persistence.d.ts.map +0 -1
- package/dist/platform-web/src/persistence.js +0 -23
- package/dist/platform-web/src/persistence.js.map +0 -1
- package/dist/platform-web/src/react.d.ts +0 -35
- package/dist/platform-web/src/react.d.ts.map +0 -1
- package/dist/platform-web/src/react.js +0 -42
- package/dist/platform-web/src/react.js.map +0 -1
- package/dist/platform-web/src/sqljs.js +0 -133
- package/dist/platform-web/src/sqljs.js.map +0 -1
- package/dist/platform-web/src/worker.d.ts +0 -79
- package/dist/platform-web/src/worker.d.ts.map +0 -1
- package/dist/platform-web/src/worker.js +0 -308
- package/dist/platform-web/src/worker.js.map +0 -1
- package/dist/react/src/index.d.ts +0 -59
- package/dist/react/src/index.d.ts.map +0 -1
- package/dist/react/src/index.js +0 -151
- package/dist/react/src/index.js.map +0 -1
- package/dist/schema/src/definition.d.ts +0 -98
- package/dist/schema/src/definition.d.ts.map +0 -1
- package/dist/schema/src/definition.js +0 -84
- package/dist/schema/src/definition.js.map +0 -1
- package/dist/schema/src/planner.d.ts +0 -42
- package/dist/schema/src/planner.d.ts.map +0 -1
- package/dist/schema/src/planner.js +0 -131
- package/dist/schema/src/planner.js.map +0 -1
- package/dist/schema/src/validators.d.ts +0 -194
- package/dist/schema/src/validators.d.ts.map +0 -1
- package/dist/schema/src/validators.js +0 -158
- package/dist/schema/src/validators.js.map +0 -1
- package/dist/svelte/src/index.d.ts +0 -44
- package/dist/svelte/src/index.d.ts.map +0 -1
- package/dist/svelte/src/index.js +0 -75
- package/dist/svelte/src/index.js.map +0 -1
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { Directory, File, Paths } from "expo-file-system";
|
|
2
2
|
import { defaultDatabaseDirectory, openDatabaseSync } from "expo-sqlite";
|
|
3
3
|
import { SyncoreRuntime } from "../core/index.mjs";
|
|
4
|
+
import { BroadcastChannelExternalChangeSignal, createDefaultSyncChannelName } from "../platform-web/index.js";
|
|
4
5
|
//#region src/index.ts
|
|
5
6
|
/**
|
|
6
7
|
* Create an Expo Syncore runtime backed by `expo-sqlite` and local file storage.
|
|
7
8
|
*/
|
|
8
9
|
function createExpoSyncoreRuntime(options) {
|
|
9
10
|
const databaseDirectory = options.databaseDirectory ?? (typeof defaultDatabaseDirectory === "string" ? defaultDatabaseDirectory : void 0);
|
|
10
|
-
const driver = options.driver ?? new ExpoSqliteDriver(openDatabaseSync(options.databaseName ?? "syncore.db", void 0, databaseDirectory)
|
|
11
|
+
const driver = options.driver ?? new ExpoSqliteDriver(openDatabaseSync(options.databaseName ?? "syncore.db", void 0, databaseDirectory), {
|
|
12
|
+
databaseName: options.databaseName ?? "syncore.db",
|
|
13
|
+
...databaseDirectory ? { databaseDirectory } : {}
|
|
14
|
+
});
|
|
11
15
|
const storage = options.storage ?? new ExpoFileStorageAdapter(options.storageDirectoryName ?? "syncore-storage");
|
|
16
|
+
const isWebEnvironment = typeof window !== "undefined" && typeof document !== "undefined";
|
|
17
|
+
const webExternalChangeSignal = isWebEnvironment && !options.externalChangeSignal ? new BroadcastChannelExternalChangeSignal({ channelName: createDefaultSyncChannelName(options.databaseName ?? "syncore.db") }) : void 0;
|
|
18
|
+
const webExternalChangeApplier = isWebEnvironment && !options.externalChangeApplier && driver instanceof ExpoSqliteDriver ? new ExpoWebExternalChangeApplier(driver) : void 0;
|
|
12
19
|
return new SyncoreRuntime({
|
|
13
20
|
schema: options.schema,
|
|
14
21
|
functions: options.functions,
|
|
15
22
|
driver,
|
|
16
23
|
storage,
|
|
24
|
+
...isWebEnvironment && options.externalChangeSignal ? { externalChangeSignal: options.externalChangeSignal } : isWebEnvironment && webExternalChangeSignal ? { externalChangeSignal: webExternalChangeSignal } : {},
|
|
25
|
+
...isWebEnvironment && options.externalChangeApplier ? { externalChangeApplier: options.externalChangeApplier } : isWebEnvironment && webExternalChangeApplier ? { externalChangeApplier: webExternalChangeApplier } : {},
|
|
17
26
|
platform: options.platform ?? "expo",
|
|
18
27
|
...options.capabilities ? { capabilities: options.capabilities } : {},
|
|
19
28
|
...options.devtools ? { devtools: options.devtools } : {},
|
|
@@ -39,7 +48,7 @@ function createExpoSyncoreBootstrap(options) {
|
|
|
39
48
|
};
|
|
40
49
|
return {
|
|
41
50
|
getRuntime() {
|
|
42
|
-
|
|
51
|
+
throw new Error("createExpoSyncoreBootstrap().getRuntime() is not available synchronously. Use getClient() instead.");
|
|
43
52
|
},
|
|
44
53
|
async getClient() {
|
|
45
54
|
if (!started) {
|
|
@@ -66,8 +75,12 @@ function createExpoSyncoreBootstrap(options) {
|
|
|
66
75
|
var ExpoSqliteDriver = class {
|
|
67
76
|
transactionDepth = 0;
|
|
68
77
|
closed = false;
|
|
69
|
-
|
|
78
|
+
databaseName;
|
|
79
|
+
databaseDirectory;
|
|
80
|
+
constructor(database, options) {
|
|
70
81
|
this.database = database;
|
|
82
|
+
this.databaseName = options?.databaseName ?? "syncore.db";
|
|
83
|
+
this.databaseDirectory = options?.databaseDirectory;
|
|
71
84
|
}
|
|
72
85
|
async exec(sql) {
|
|
73
86
|
this.ensureOpen();
|
|
@@ -130,6 +143,23 @@ var ExpoSqliteDriver = class {
|
|
|
130
143
|
ensureOpen() {
|
|
131
144
|
if (this.closed) throw new Error("The Expo SQLite driver is already closed.");
|
|
132
145
|
}
|
|
146
|
+
async reopen() {
|
|
147
|
+
this.ensureOpen();
|
|
148
|
+
await this.database.closeAsync();
|
|
149
|
+
this.database = openDatabaseSync(this.databaseName, void 0, this.databaseDirectory);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var ExpoWebExternalChangeApplier = class {
|
|
153
|
+
constructor(driver) {
|
|
154
|
+
this.driver = driver;
|
|
155
|
+
}
|
|
156
|
+
async applyExternalChange(event) {
|
|
157
|
+
if (event.scope === "database" || event.scope === "all") await this.driver.reopen();
|
|
158
|
+
return {
|
|
159
|
+
databaseChanged: event.scope === "database" || event.scope === "all",
|
|
160
|
+
storageChanged: event.scope === "storage" || event.scope === "all"
|
|
161
|
+
};
|
|
162
|
+
}
|
|
133
163
|
};
|
|
134
164
|
/**
|
|
135
165
|
* Syncore file/blob storage backed by the Expo file system.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\r\nimport {\r\n defaultDatabaseDirectory,\r\n openDatabaseSync,\r\n type SQLiteDatabase\r\n} from \"expo-sqlite\";\r\nimport {\r\n type AnySyncoreSchema,\r\n type DevtoolsSink,\r\n SyncoreRuntime,\r\n type SchedulerOptions,\r\n type SyncoreCapabilities,\r\n type SyncoreExperimentalPlugin,\r\n type SyncoreRuntimeOptions,\r\n type SyncoreSqlDriver,\r\n type SyncoreStorageAdapter,\r\n type StorageObject,\r\n type StorageWriteInput\r\n} from \"@syncore/core\";\r\n\r\nexport type ExpoSyncoreSchema = AnySyncoreSchema;\r\n\r\n/**\r\n * Options for constructing an Expo Syncore runtime.\r\n *\r\n * Use this when you want Syncore to persist locally with `expo-sqlite` and the\r\n * Expo file system.\r\n */\r\nexport interface CreateExpoRuntimeOptions {\r\n /** The schema for the local Syncore app. */\r\n schema: ExpoSyncoreSchema;\r\n\r\n /** The generated function registry for the local Syncore app. */\r\n functions: SyncoreRuntimeOptions<ExpoSyncoreSchema>[\"functions\"];\r\n\r\n /** Optional platform capabilities exposed to function handlers. */\r\n capabilities?: SyncoreCapabilities;\r\n\r\n /** Optional custom SQL driver. Defaults to `expo-sqlite`. */\r\n driver?: SyncoreSqlDriver;\r\n\r\n /** Optional experimental plugins for runtime hooks. */\r\n experimentalPlugins?: Array<SyncoreExperimentalPlugin<ExpoSyncoreSchema>>;\r\n\r\n /** Optional custom file/blob storage adapter. */\r\n storage?: SyncoreStorageAdapter;\r\n\r\n /** The SQLite database filename to open locally. */\r\n databaseName?: string;\r\n\r\n /** Optional directory for the SQLite database file. */\r\n databaseDirectory?: string;\r\n\r\n /** Directory name used for local file/blob storage. */\r\n storageDirectoryName?: string;\r\n\r\n /** Optional runtime platform label shown in devtools snapshots. */\r\n platform?: string;\r\n\r\n /** Optional devtools sink used during development. */\r\n devtools?: DevtoolsSink;\r\n\r\n /** Optional scheduler configuration for jobs and recurring work. */\r\n scheduler?: SchedulerOptions;\r\n}\r\n\r\n/**\r\n * A reusable bootstrap that lazily creates, starts, and stops an Expo Syncore runtime.\r\n */\r\nexport interface ExpoSyncoreBootstrap {\r\n /** Return the local runtime instance, creating it if needed. */\r\n getRuntime(): SyncoreRuntime<ExpoSyncoreSchema>;\r\n\r\n /** Start the runtime if needed and return a ready client. */\r\n getClient(): Promise<\r\n ReturnType<SyncoreRuntime<ExpoSyncoreSchema>[\"createClient\"]>\r\n >;\r\n\r\n /** Stop the current runtime instance if one exists. */\r\n stop(): Promise<void>;\r\n\r\n /** Fully discard the runtime so the next call recreates it. */\r\n reset(): Promise<void>;\r\n}\r\n\r\n/**\r\n * Create an Expo Syncore runtime backed by `expo-sqlite` and local file storage.\r\n */\r\nexport function createExpoSyncoreRuntime(\r\n options: CreateExpoRuntimeOptions\r\n): SyncoreRuntime<ExpoSyncoreSchema> {\r\n const databaseDirectory =\r\n options.databaseDirectory ??\r\n (typeof defaultDatabaseDirectory === \"string\"\r\n ? defaultDatabaseDirectory\r\n : undefined);\r\n const driver =\r\n options.driver ??\r\n new ExpoSqliteDriver(\r\n openDatabaseSync(\r\n options.databaseName ?? \"syncore.db\",\r\n undefined,\r\n databaseDirectory\r\n )\r\n );\r\n const storage =\r\n options.storage ??\r\n new ExpoFileStorageAdapter(\r\n options.storageDirectoryName ?? \"syncore-storage\"\r\n );\r\n\r\n return new SyncoreRuntime({\r\n schema: options.schema,\r\n functions: options.functions,\r\n driver,\r\n storage,\r\n platform: options.platform ?? \"expo\",\r\n ...(options.capabilities ? { capabilities: options.capabilities } : {}),\r\n ...(options.devtools ? { devtools: options.devtools } : {}),\r\n ...(options.experimentalPlugins\r\n ? { experimentalPlugins: options.experimentalPlugins }\r\n : {}),\r\n ...(options.scheduler ? { scheduler: options.scheduler } : {})\r\n });\r\n}\r\n\r\n/**\r\n * Create a same-process Syncore client from a started Expo runtime.\r\n */\r\nexport function createExpoSyncoreClient(\r\n runtime: SyncoreRuntime<ExpoSyncoreSchema>\r\n) {\r\n return runtime.createClient();\r\n}\r\n\r\n/**\r\n * Create a reusable Expo bootstrap that lazily starts the local runtime.\r\n */\r\nexport function createExpoSyncoreBootstrap(\r\n options: CreateExpoRuntimeOptions\r\n): ExpoSyncoreBootstrap {\r\n let runtime: SyncoreRuntime<ExpoSyncoreSchema> | null = null;\r\n let started: Promise<\r\n ReturnType<SyncoreRuntime<ExpoSyncoreSchema>[\"createClient\"]>\r\n > | null = null;\r\n\r\n const ensureRuntime = () => {\r\n runtime ??= createExpoSyncoreRuntime(options);\r\n return runtime;\r\n };\r\n\r\n return {\r\n getRuntime() {\r\n return ensureRuntime();\r\n },\r\n async getClient() {\r\n if (!started) {\r\n const activeRuntime = ensureRuntime();\r\n started = activeRuntime\r\n .start()\r\n .then(() => activeRuntime.createClient());\r\n }\r\n return started;\r\n },\r\n async stop() {\r\n if (!runtime) {\r\n return;\r\n }\r\n await runtime.stop();\r\n started = null;\r\n },\r\n async reset() {\r\n if (runtime) {\r\n await runtime.stop();\r\n }\r\n runtime = null;\r\n started = null;\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Syncore SQL driver implementation backed by `expo-sqlite`.\r\n */\r\nexport class ExpoSqliteDriver implements SyncoreSqlDriver {\r\n private transactionDepth = 0;\r\n private closed = false;\r\n\r\n constructor(private readonly database: SQLiteDatabase) {}\r\n\r\n async exec(sql: string): Promise<void> {\r\n this.ensureOpen();\r\n await this.database.execAsync(sql);\r\n }\r\n\r\n async run(\r\n sql: string,\r\n params: unknown[] = []\r\n ): Promise<{ changes: number; lastInsertRowid?: number | string }> {\r\n this.ensureOpen();\r\n const result = await this.database.runAsync(sql, normalizeParams(params));\r\n return {\r\n changes: result.changes,\r\n lastInsertRowid: result.lastInsertRowId\r\n };\r\n }\r\n\r\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\r\n this.ensureOpen();\r\n const row = await this.database.getFirstAsync<T>(\r\n sql,\r\n normalizeParams(params)\r\n );\r\n return row ?? undefined;\r\n }\r\n\r\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\r\n this.ensureOpen();\r\n return this.database.getAllAsync<T>(sql, normalizeParams(params));\r\n }\r\n\r\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\r\n this.ensureOpen();\r\n if (this.transactionDepth > 0) {\r\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\r\n }\r\n\r\n this.transactionDepth += 1;\r\n await this.database.execAsync(\"BEGIN IMMEDIATE\");\r\n try {\r\n const result = await callback();\r\n await this.database.execAsync(\"COMMIT\");\r\n return result;\r\n } catch (error) {\r\n await this.database.execAsync(\"ROLLBACK\");\r\n throw error;\r\n } finally {\r\n this.transactionDepth -= 1;\r\n }\r\n }\r\n\r\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\r\n this.ensureOpen();\r\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\r\n this.transactionDepth += 1;\r\n await this.database.execAsync(`SAVEPOINT ${safeName}`);\r\n try {\r\n const result = await callback();\r\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\r\n return result;\r\n } catch (error) {\r\n await this.database.execAsync(`ROLLBACK TO SAVEPOINT ${safeName}`);\r\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\r\n throw error;\r\n } finally {\r\n this.transactionDepth -= 1;\r\n }\r\n }\r\n\r\n async close(): Promise<void> {\r\n if (this.closed) {\r\n return;\r\n }\r\n await this.database.closeAsync();\r\n this.closed = true;\r\n }\r\n\r\n private ensureOpen(): void {\r\n if (this.closed) {\r\n throw new Error(\"The Expo SQLite driver is already closed.\");\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Syncore file/blob storage backed by the Expo file system.\r\n */\r\nexport class ExpoFileStorageAdapter implements SyncoreStorageAdapter {\r\n private readonly rootDirectory: Directory;\r\n\r\n constructor(storageDirectoryName: string) {\r\n this.rootDirectory = new Directory(Paths.document, storageDirectoryName);\r\n if (!this.rootDirectory.exists) {\r\n this.rootDirectory.create({ idempotent: true, intermediates: true });\r\n }\r\n }\r\n\r\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\r\n const file = new File(this.rootDirectory, id);\r\n if (!file.exists) {\r\n file.create({ intermediates: true, overwrite: true });\r\n }\r\n const bytes = normalizeBinary(input.data);\r\n file.write(bytes);\r\n return {\r\n id,\r\n path: file.uri,\r\n size: bytes.byteLength,\r\n contentType: input.contentType ?? null\r\n };\r\n }\r\n\r\n async get(id: string): Promise<StorageObject | null> {\r\n const file = new File(this.rootDirectory, id);\r\n if (!file.exists) {\r\n return null;\r\n }\r\n return {\r\n id,\r\n path: file.uri,\r\n size: file.size,\r\n contentType: file.type || null\r\n };\r\n }\r\n\r\n async read(id: string): Promise<Uint8Array | null> {\r\n const file = new File(this.rootDirectory, id);\r\n if (!file.exists) {\r\n return null;\r\n }\r\n return file.bytes();\r\n }\r\n\r\n async delete(id: string): Promise<void> {\r\n const file = new File(this.rootDirectory, id);\r\n if (file.exists) {\r\n file.delete();\r\n }\r\n }\r\n}\r\n\r\nfunction normalizeParams(\r\n values: unknown[]\r\n): Array<string | number | Uint8Array | null> {\r\n return values.map((value) => {\r\n if (typeof value === \"boolean\") {\r\n return value ? 1 : 0;\r\n }\r\n if (\r\n value === null ||\r\n typeof value === \"string\" ||\r\n typeof value === \"number\" ||\r\n value instanceof Uint8Array\r\n ) {\r\n return value;\r\n }\r\n if (value instanceof ArrayBuffer) {\r\n return new Uint8Array(value);\r\n }\r\n return JSON.stringify(value);\r\n });\r\n}\r\n\r\nfunction normalizeBinary(data: StorageWriteInput[\"data\"]): Uint8Array {\r\n if (typeof data === \"string\") {\r\n return new TextEncoder().encode(data);\r\n }\r\n if (data instanceof Uint8Array) {\r\n return data;\r\n }\r\n return new Uint8Array(data);\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\nimport {\n defaultDatabaseDirectory,\n openDatabaseSync,\n type SQLiteDatabase\n} from \"expo-sqlite\";\nimport {\n type AnySyncoreSchema,\n type DevtoolsSink,\n type SyncoreExternalChangeApplier,\n type SyncoreExternalChangeSignal,\n SyncoreRuntime,\n type SchedulerOptions,\n type SyncoreCapabilities,\n type SyncoreExperimentalPlugin,\n type SyncoreRuntimeOptions,\n type SyncoreSqlDriver,\n type SyncoreStorageAdapter,\n type StorageObject,\n type StorageWriteInput\n} from \"@syncore/core\";\nimport {\n BroadcastChannelExternalChangeSignal,\n createDefaultSyncChannelName\n} from \"@syncore/platform-web\";\n\nexport type ExpoSyncoreSchema = AnySyncoreSchema;\n\n/**\n * Options for constructing an Expo Syncore runtime.\n *\n * Use this when you want Syncore to persist locally with `expo-sqlite` and the\n * Expo file system.\n */\nexport interface CreateExpoRuntimeOptions {\n /** The schema for the local Syncore app. */\n schema: ExpoSyncoreSchema;\n\n /** The generated function registry for the local Syncore app. */\n functions: SyncoreRuntimeOptions<ExpoSyncoreSchema>[\"functions\"];\n\n /** Optional platform capabilities exposed to function handlers. */\n capabilities?: SyncoreCapabilities;\n\n /** Optional custom SQL driver. Defaults to `expo-sqlite`. */\n driver?: SyncoreSqlDriver;\n\n /** Optional experimental plugins for runtime hooks. */\n experimentalPlugins?: Array<SyncoreExperimentalPlugin<ExpoSyncoreSchema>>;\n\n /** Optional custom file/blob storage adapter. */\n storage?: SyncoreStorageAdapter;\n\n /** The SQLite database filename to open locally. */\n databaseName?: string;\n\n /** Optional directory for the SQLite database file. */\n databaseDirectory?: string;\n\n /** Directory name used for local file/blob storage. */\n storageDirectoryName?: string;\n\n /** Optional runtime platform label shown in devtools snapshots. */\n platform?: string;\n\n /** Optional devtools sink used during development. */\n devtools?: DevtoolsSink;\n\n /** Optional scheduler configuration for jobs and recurring work. */\n scheduler?: SchedulerOptions;\n\n /** Optional shared signal used to synchronize browser instances. */\n externalChangeSignal?: SyncoreExternalChangeSignal;\n\n /** Optional applier used to reconcile browser-side external changes. */\n externalChangeApplier?: SyncoreExternalChangeApplier;\n}\n\n/**\n * A reusable bootstrap that lazily creates, starts, and stops an Expo Syncore runtime.\n */\nexport interface ExpoSyncoreBootstrap {\n /** Synchronous access is unavailable; use `getClient()` instead. */\n getRuntime(): never;\n\n /** Start the runtime if needed and return a ready client. */\n getClient(): Promise<\n ReturnType<SyncoreRuntime<ExpoSyncoreSchema>[\"createClient\"]>\n >;\n\n /** Stop the current runtime instance if one exists. */\n stop(): Promise<void>;\n\n /** Fully discard the runtime so the next call recreates it. */\n reset(): Promise<void>;\n}\n\n/**\n * Create an Expo Syncore runtime backed by `expo-sqlite` and local file storage.\n */\nexport function createExpoSyncoreRuntime(\n options: CreateExpoRuntimeOptions\n): SyncoreRuntime<ExpoSyncoreSchema> {\n const databaseDirectory =\n options.databaseDirectory ??\n (typeof defaultDatabaseDirectory === \"string\"\n ? defaultDatabaseDirectory\n : undefined);\n const driver =\n options.driver ??\n new ExpoSqliteDriver(\n openDatabaseSync(\n options.databaseName ?? \"syncore.db\",\n undefined,\n databaseDirectory\n ),\n {\n databaseName: options.databaseName ?? \"syncore.db\",\n ...(databaseDirectory ? { databaseDirectory } : {})\n }\n );\n const storage =\n options.storage ??\n new ExpoFileStorageAdapter(\n options.storageDirectoryName ?? \"syncore-storage\"\n );\n const isWebEnvironment =\n typeof window !== \"undefined\" && typeof document !== \"undefined\";\n const webExternalChangeSignal =\n isWebEnvironment && !options.externalChangeSignal\n ? new BroadcastChannelExternalChangeSignal({\n channelName: createDefaultSyncChannelName(\n options.databaseName ?? \"syncore.db\"\n )\n })\n : undefined;\n const webExternalChangeApplier =\n isWebEnvironment &&\n !options.externalChangeApplier &&\n driver instanceof ExpoSqliteDriver\n ? new ExpoWebExternalChangeApplier(driver)\n : undefined;\n\n return new SyncoreRuntime({\n schema: options.schema,\n functions: options.functions,\n driver,\n storage,\n ...(isWebEnvironment && options.externalChangeSignal\n ? { externalChangeSignal: options.externalChangeSignal }\n : isWebEnvironment && webExternalChangeSignal\n ? { externalChangeSignal: webExternalChangeSignal }\n : {}),\n ...(isWebEnvironment && options.externalChangeApplier\n ? { externalChangeApplier: options.externalChangeApplier }\n : isWebEnvironment && webExternalChangeApplier\n ? { externalChangeApplier: webExternalChangeApplier }\n : {}),\n platform: options.platform ?? \"expo\",\n ...(options.capabilities ? { capabilities: options.capabilities } : {}),\n ...(options.devtools ? { devtools: options.devtools } : {}),\n ...(options.experimentalPlugins\n ? { experimentalPlugins: options.experimentalPlugins }\n : {}),\n ...(options.scheduler ? { scheduler: options.scheduler } : {})\n });\n}\n\n/**\n * Create a same-process Syncore client from a started Expo runtime.\n */\nexport function createExpoSyncoreClient(\n runtime: SyncoreRuntime<ExpoSyncoreSchema>\n) {\n return runtime.createClient();\n}\n\n/**\n * Create a reusable Expo bootstrap that lazily starts the local runtime.\n */\nexport function createExpoSyncoreBootstrap(\n options: CreateExpoRuntimeOptions\n): ExpoSyncoreBootstrap {\n let runtime: SyncoreRuntime<ExpoSyncoreSchema> | null = null;\n let started: Promise<\n ReturnType<SyncoreRuntime<ExpoSyncoreSchema>[\"createClient\"]>\n > | null = null;\n\n const ensureRuntime = () => {\n runtime ??= createExpoSyncoreRuntime(options);\n return runtime;\n };\n\n return {\n getRuntime() {\n throw new Error(\n \"createExpoSyncoreBootstrap().getRuntime() is not available synchronously. Use getClient() instead.\"\n );\n },\n async getClient() {\n if (!started) {\n const activeRuntime = ensureRuntime();\n started = activeRuntime\n .start()\n .then(() => activeRuntime.createClient());\n }\n return started;\n },\n async stop() {\n if (!runtime) {\n return;\n }\n await runtime.stop();\n started = null;\n },\n async reset() {\n if (runtime) {\n await runtime.stop();\n }\n runtime = null;\n started = null;\n }\n };\n}\n\n/**\n * Syncore SQL driver implementation backed by `expo-sqlite`.\n */\nexport class ExpoSqliteDriver implements SyncoreSqlDriver {\n private transactionDepth = 0;\n private closed = false;\n private readonly databaseName: string;\n private readonly databaseDirectory: string | undefined;\n\n constructor(\n private database: SQLiteDatabase,\n options?: {\n databaseName?: string;\n databaseDirectory?: string;\n }\n ) {\n this.databaseName = options?.databaseName ?? \"syncore.db\";\n this.databaseDirectory = options?.databaseDirectory;\n }\n\n async exec(sql: string): Promise<void> {\n this.ensureOpen();\n await this.database.execAsync(sql);\n }\n\n async run(\n sql: string,\n params: unknown[] = []\n ): Promise<{ changes: number; lastInsertRowid?: number | string }> {\n this.ensureOpen();\n const result = await this.database.runAsync(sql, normalizeParams(params));\n return {\n changes: result.changes,\n lastInsertRowid: result.lastInsertRowId\n };\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n this.ensureOpen();\n const row = await this.database.getFirstAsync<T>(\n sql,\n normalizeParams(params)\n );\n return row ?? undefined;\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n this.ensureOpen();\n return this.database.getAllAsync<T>(sql, normalizeParams(params));\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n await this.database.execAsync(\"BEGIN IMMEDIATE\");\n try {\n const result = await callback();\n await this.database.execAsync(\"COMMIT\");\n return result;\n } catch (error) {\n await this.database.execAsync(\"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 this.ensureOpen();\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.transactionDepth += 1;\n await this.database.execAsync(`SAVEPOINT ${safeName}`);\n try {\n const result = await callback();\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n await this.database.execAsync(`ROLLBACK TO SAVEPOINT ${safeName}`);\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n await this.database.closeAsync();\n this.closed = true;\n }\n\n private ensureOpen(): void {\n if (this.closed) {\n throw new Error(\"The Expo SQLite driver is already closed.\");\n }\n }\n\n async reopen(): Promise<void> {\n this.ensureOpen();\n await this.database.closeAsync();\n this.database = openDatabaseSync(\n this.databaseName,\n undefined,\n this.databaseDirectory\n );\n }\n}\n\nclass ExpoWebExternalChangeApplier implements SyncoreExternalChangeApplier {\n constructor(private readonly driver: ExpoSqliteDriver) {}\n\n async applyExternalChange(event: { scope: \"database\" | \"storage\" | \"all\" }) {\n if (event.scope === \"database\" || event.scope === \"all\") {\n await this.driver.reopen();\n }\n return {\n databaseChanged: event.scope === \"database\" || event.scope === \"all\",\n storageChanged: event.scope === \"storage\" || event.scope === \"all\"\n };\n }\n}\n\n/**\n * Syncore file/blob storage backed by the Expo file system.\n */\nexport class ExpoFileStorageAdapter implements SyncoreStorageAdapter {\n private readonly rootDirectory: Directory;\n\n constructor(storageDirectoryName: string) {\n this.rootDirectory = new Directory(Paths.document, storageDirectoryName);\n if (!this.rootDirectory.exists) {\n this.rootDirectory.create({ idempotent: true, intermediates: true });\n }\n }\n\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n file.create({ intermediates: true, overwrite: true });\n }\n const bytes = normalizeBinary(input.data);\n file.write(bytes);\n return {\n id,\n path: file.uri,\n size: bytes.byteLength,\n contentType: input.contentType ?? null\n };\n }\n\n async get(id: string): Promise<StorageObject | null> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n return null;\n }\n return {\n id,\n path: file.uri,\n size: file.size,\n contentType: file.type || null\n };\n }\n\n async read(id: string): Promise<Uint8Array | null> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n return null;\n }\n return file.bytes();\n }\n\n async delete(id: string): Promise<void> {\n const file = new File(this.rootDirectory, id);\n if (file.exists) {\n file.delete();\n }\n }\n}\n\nfunction normalizeParams(\n values: unknown[]\n): Array<string | number | Uint8Array | null> {\n return values.map((value) => {\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n value instanceof Uint8Array\n ) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n return JSON.stringify(value);\n });\n}\n\nfunction normalizeBinary(data: StorageWriteInput[\"data\"]): Uint8Array {\n if (typeof data === \"string\") {\n return new TextEncoder().encode(data);\n }\n if (data instanceof Uint8Array) {\n return data;\n }\n return new Uint8Array(data);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\
|
|
1
|
+
{"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport type { SyncoreClient } from \"@syncore/core\";\nimport type { ExpoSyncoreBootstrap } from \"./index.js\";\n\ntype ExpoSyncoreClient = SyncoreClient;\n\n/**\n * Props for {@link SyncoreExpoProvider}.\n */\nexport interface SyncoreExpoProviderProps {\n /** The bootstrap created with `createExpoSyncoreBootstrap`. */\n bootstrap: ExpoSyncoreBootstrap;\n\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /** Optional fallback content rendered while the local runtime starts. */\n fallback?: ReactNode;\n}\n\n/**\n * Start an Expo Syncore bootstrap and provide its client to React descendants.\n */\nexport function SyncoreExpoProvider({\n bootstrap,\n children,\n fallback = null\n}: SyncoreExpoProviderProps): ReactNode {\n const [client, setClient] = useState<ExpoSyncoreClient | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n void bootstrap.getClient().then((nextClient: ExpoSyncoreClient) => {\n if (!cancelled) {\n setClient(nextClient);\n }\n });\n\n return () => {\n cancelled = true;\n setClient(null);\n void bootstrap.stop();\n };\n }, [bootstrap]);\n\n if (!client) {\n return fallback;\n }\n\n return <SyncoreProvider client={client}>{children}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;AAyBA,SAAgB,oBAAoB,EAClC,WACA,UACA,WAAW,QAC2B;CACtC,MAAM,CAAC,QAAQ,aAAa,SAAmC,KAAK;AAEpE,iBAAgB;EACd,IAAI,YAAY;AACX,YAAU,WAAW,CAAC,MAAM,eAAkC;AACjE,OAAI,CAAC,UACH,WAAU,WAAW;IAEvB;AAEF,eAAa;AACX,eAAY;AACZ,aAAU,KAAK;AACV,aAAU,MAAM;;IAEtB,CAAC,UAAU,CAAC;AAEf,KAAI,CAAC,OACH,QAAO;AAGT,QAAO,oBAAC,iBAAD;EAAyB;EAAS;EAA2B,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AttachNodeIpcRuntimeOptions, AttachedNodeIpcRuntime, NodeIpcSyncoreSchema, RendererQueryWatch, SyncoreIpcMessageEndpoint, SyncoreMainProcessBridge, SyncoreRendererBridge, SyncoreRendererClient, SyncoreWindowBridge, attachNodeIpcRuntime, createNodeIpcMessageEndpoint, createRendererSyncoreBridgeClient, createRendererSyncoreClient, createRendererSyncoreWindowClient, installSyncoreWindowBridge } from "./ipc.mjs";
|
|
2
2
|
import * as _syncore_core0 from "../core/index.d.mts";
|
|
3
|
-
import { AnySyncoreSchema,
|
|
4
|
-
import {
|
|
3
|
+
import { AnySyncoreSchema, DevtoolsCommandHandler, DevtoolsSink, DevtoolsSubscriptionHost, SchedulerOptions, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreExperimentalPlugin, SyncoreRuntime, SyncoreRuntimeOptions, SyncoreSqlDriver, SyncoreStorageAdapter } from "../core/index.d.mts";
|
|
4
|
+
import { SyncoreActiveQueryInfo, SyncoreDevtoolsEvent, SyncoreRuntimeSummary } from "../devtools-protocol/index.d.ts";
|
|
5
5
|
|
|
6
6
|
//#region src/index.d.ts
|
|
7
7
|
type NodeSyncoreSchema = AnySyncoreSchema;
|
|
@@ -133,13 +133,18 @@ interface NodeWebSocketDevtoolsSinkOptions {
|
|
|
133
133
|
appName?: string;
|
|
134
134
|
origin?: string;
|
|
135
135
|
sessionLabel?: string;
|
|
136
|
+
targetKind?: "client" | "project";
|
|
137
|
+
storageProtocol?: string;
|
|
138
|
+
databaseLabel?: string;
|
|
139
|
+
storageIdentity?: string;
|
|
136
140
|
}
|
|
137
141
|
interface NodeWebSocketDevtoolsSink extends DevtoolsSink {
|
|
138
|
-
attachRuntime(
|
|
139
|
-
|
|
142
|
+
attachRuntime(runtime: SyncoreRuntime<AnySyncoreSchema>): void;
|
|
143
|
+
attachCommandHandler(handler: DevtoolsCommandHandler): void;
|
|
144
|
+
attachSubscriptionHost(host: DevtoolsSubscriptionHost): void;
|
|
140
145
|
dispose(): void;
|
|
141
146
|
}
|
|
142
147
|
declare function createNodeWebSocketDevtoolsSink(options: NodeWebSocketDevtoolsSinkOptions): NodeWebSocketDevtoolsSink;
|
|
143
148
|
//#endregion
|
|
144
|
-
export { AttachNodeIpcRuntimeOptions, AttachedNodeIpcRuntime, CreateElectronSyncoreBridgeOptions, CreateNodeRuntimeOptions, CreateSyncoreRendererWindowClientOptions, ManagedNodeSyncoreClient, NodeFileStorageAdapter, NodeIpcSyncoreSchema, NodeSqliteDriver, NodeSyncoreSchema, NodeWebSocketDevtoolsSink, NodeWebSocketDevtoolsSinkOptions, RendererQueryWatch, type
|
|
149
|
+
export { AttachNodeIpcRuntimeOptions, AttachedNodeIpcRuntime, CreateElectronSyncoreBridgeOptions, CreateNodeRuntimeOptions, CreateSyncoreRendererWindowClientOptions, ManagedNodeSyncoreClient, NodeFileStorageAdapter, NodeIpcSyncoreSchema, NodeSqliteDriver, NodeSyncoreSchema, NodeWebSocketDevtoolsSink, NodeWebSocketDevtoolsSinkOptions, RendererQueryWatch, type SyncoreActiveQueryInfo, type SyncoreDevtoolsEvent, SyncoreElectronBridgeWindow, SyncoreElectronIpcBinding, SyncoreElectronIpcMain, SyncoreIpcMessageEndpoint, SyncoreMainProcessBridge, SyncoreRendererBridge, SyncoreRendererClient, type SyncoreRuntimeSummary, SyncoreWindowBridge, WithNodeSyncoreClientOptions, attachNodeIpcRuntime, bindElectronWindowToSyncoreRuntime, createElectronSyncoreBridge, createManagedNodeSyncoreClient, createNodeIpcMessageEndpoint, createNodeSyncoreClient, createNodeSyncoreRuntime, createNodeWebSocketDevtoolsSink, createRendererSyncoreBridgeClient, createRendererSyncoreClient, createRendererSyncoreWindowClient, installSyncoreWindowBridge, withNodeSyncoreClient };
|
|
145
150
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":"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"}
|
|
@@ -1,15 +1,97 @@
|
|
|
1
1
|
import { SyncoreRendererClient, attachNodeIpcRuntime, createNodeIpcMessageEndpoint, createRendererSyncoreBridgeClient, createRendererSyncoreClient, createRendererSyncoreWindowClient, installSyncoreWindowBridge } from "./ipc.mjs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { DatabaseSync } from "node:sqlite";
|
|
5
6
|
import WebSocket from "ws";
|
|
6
|
-
import { SyncoreRuntime,
|
|
7
|
+
import { SyncoreRuntime, createDevtoolsCommandHandler, createDevtoolsSubscriptionHost } from "../core/index.mjs";
|
|
7
8
|
//#region src/index.ts
|
|
9
|
+
const { Parser: NodeSqlParser } = createRequire(import.meta.url)("node-sql-parser");
|
|
10
|
+
const nodeSqlParser = new NodeSqlParser();
|
|
11
|
+
const nodeDevtoolsSqlSupport = {
|
|
12
|
+
analyzeSqlStatement(query) {
|
|
13
|
+
const ast = nodeSqlParser.astify(query, { database: "sqlite" });
|
|
14
|
+
if (Array.isArray(ast)) throw new Error("Only a single SQL statement is supported.");
|
|
15
|
+
switch (ast.type) {
|
|
16
|
+
case "select": return buildReadAnalysis(ast);
|
|
17
|
+
case "update":
|
|
18
|
+
case "delete":
|
|
19
|
+
case "insert":
|
|
20
|
+
case "replace": return buildWriteAnalysis(extractTables(ast.table), false);
|
|
21
|
+
case "create":
|
|
22
|
+
case "drop":
|
|
23
|
+
case "alter": return buildWriteAnalysis(extractTables(ast.table), true);
|
|
24
|
+
default: throw new Error(`Unsupported SQL statement type: ${String(ast.type)}`);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
ensureSqlMode(analysis, expected) {
|
|
28
|
+
if (expected === "watch") {
|
|
29
|
+
if (analysis.mode !== "read") throw new Error("Live mode supports read-only SQL only.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (analysis.mode !== expected) {
|
|
33
|
+
if (expected === "read") throw new Error("Use SQL Write for mutating statements.");
|
|
34
|
+
throw new Error("Use SQL Read or SQL Live for read-only statements.");
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
runReadonlyQuery(databasePath, query) {
|
|
38
|
+
const analysis = this.analyzeSqlStatement(query);
|
|
39
|
+
this.ensureSqlMode(analysis, "read");
|
|
40
|
+
const database = new DatabaseSync(databasePath, { readOnly: true });
|
|
41
|
+
try {
|
|
42
|
+
const statement = database.prepare(query);
|
|
43
|
+
const rows = statement.all();
|
|
44
|
+
const columnsMeta = statement.columns();
|
|
45
|
+
const columns = columnsMeta.map((column) => column.name);
|
|
46
|
+
const observedTables = Array.from(new Set(columnsMeta.map((column) => column.table).filter((table) => typeof table === "string")));
|
|
47
|
+
return {
|
|
48
|
+
columns,
|
|
49
|
+
rows: rows.map((row) => columns.map((column) => row[column])),
|
|
50
|
+
observedTables: observedTables.length > 0 ? observedTables : analysis.readTables
|
|
51
|
+
};
|
|
52
|
+
} finally {
|
|
53
|
+
database.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
8
57
|
function normalizeData(input) {
|
|
9
58
|
if (typeof input === "string") return Buffer.from(input);
|
|
10
59
|
if (input instanceof Uint8Array) return input;
|
|
11
60
|
return new Uint8Array(input);
|
|
12
61
|
}
|
|
62
|
+
function buildReadAnalysis(ast) {
|
|
63
|
+
const readTables = Array.from(new Set(extractReadTables(ast)));
|
|
64
|
+
return {
|
|
65
|
+
mode: "read",
|
|
66
|
+
readTables,
|
|
67
|
+
writeTables: [],
|
|
68
|
+
schemaChanged: false,
|
|
69
|
+
observedScopes: readTables.length > 0 ? readTables.map((table) => `table:${table}`) : ["all"]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function buildWriteAnalysis(tables, schemaChanged) {
|
|
73
|
+
const uniqueTables = Array.from(new Set(tables));
|
|
74
|
+
return {
|
|
75
|
+
mode: schemaChanged ? "ddl" : "write",
|
|
76
|
+
readTables: [],
|
|
77
|
+
writeTables: uniqueTables,
|
|
78
|
+
schemaChanged,
|
|
79
|
+
observedScopes: schemaChanged ? ["schema.tables", ...uniqueTables.map((table) => `table:${table}`)] : uniqueTables.length > 0 ? uniqueTables.map((table) => `table:${table}`) : ["all"]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function extractReadTables(ast) {
|
|
83
|
+
return (ast.from ?? []).flatMap((entry) => {
|
|
84
|
+
if (entry.table) return [entry.table];
|
|
85
|
+
if (entry.expr?.ast) return extractReadTables(entry.expr.ast);
|
|
86
|
+
return [];
|
|
87
|
+
}).filter((table) => table !== "dual");
|
|
88
|
+
}
|
|
89
|
+
function extractTables(table) {
|
|
90
|
+
if (Array.isArray(table)) return table.map((entry) => entry?.table).filter((value) => typeof value === "string");
|
|
91
|
+
if (table && typeof table === "object") return typeof table.table === "string" ? [table.table] : [];
|
|
92
|
+
if (typeof table === "string") return [table];
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
13
95
|
function toSqlParameters(params) {
|
|
14
96
|
return params;
|
|
15
97
|
}
|
|
@@ -138,7 +220,11 @@ function createNodeSyncoreRuntime(options) {
|
|
|
138
220
|
url: resolvedDevtoolsUrl,
|
|
139
221
|
...options.appName ? { appName: options.appName } : {},
|
|
140
222
|
...options.origin ? { origin: options.origin } : {},
|
|
141
|
-
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
|
|
223
|
+
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
224
|
+
targetKind: "client",
|
|
225
|
+
storageProtocol: "file",
|
|
226
|
+
databaseLabel: path.basename(options.databasePath),
|
|
227
|
+
storageIdentity: `file::${path.resolve(options.databasePath)}`
|
|
142
228
|
}) : void 0;
|
|
143
229
|
const runtimeOptions = {
|
|
144
230
|
schema: options.schema,
|
|
@@ -153,13 +239,21 @@ function createNodeSyncoreRuntime(options) {
|
|
|
153
239
|
if (resolvedDevtools) runtimeOptions.devtools = resolvedDevtools;
|
|
154
240
|
if (options.scheduler) runtimeOptions.scheduler = options.scheduler;
|
|
155
241
|
const runtime = new SyncoreRuntime(runtimeOptions);
|
|
156
|
-
websocketDevtools?.attachRuntime(
|
|
242
|
+
websocketDevtools?.attachRuntime(runtime);
|
|
157
243
|
if (websocketDevtools) {
|
|
158
|
-
websocketDevtools.
|
|
244
|
+
websocketDevtools.attachCommandHandler(createDevtoolsCommandHandler({
|
|
245
|
+
driver: runtimeOptions.driver,
|
|
246
|
+
schema: options.schema,
|
|
247
|
+
functions: options.functions,
|
|
248
|
+
runtime,
|
|
249
|
+
sql: nodeDevtoolsSqlSupport
|
|
250
|
+
}));
|
|
251
|
+
websocketDevtools.attachSubscriptionHost(createDevtoolsSubscriptionHost({
|
|
159
252
|
driver: runtimeOptions.driver,
|
|
160
253
|
schema: options.schema,
|
|
161
254
|
functions: options.functions,
|
|
162
|
-
runtime
|
|
255
|
+
runtime,
|
|
256
|
+
sql: nodeDevtoolsSqlSupport
|
|
163
257
|
}));
|
|
164
258
|
const stop = runtime.stop.bind(runtime);
|
|
165
259
|
runtime.stop = async () => {
|
|
@@ -264,8 +358,9 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
264
358
|
let socket;
|
|
265
359
|
let disposed = false;
|
|
266
360
|
let connectTimer;
|
|
267
|
-
let
|
|
268
|
-
let
|
|
361
|
+
let getSummary;
|
|
362
|
+
let onCommand;
|
|
363
|
+
let subscriptionHost;
|
|
269
364
|
const pendingMessages = [];
|
|
270
365
|
let latestHello;
|
|
271
366
|
const connect = () => {
|
|
@@ -278,11 +373,11 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
278
373
|
platform: latestHello.platform,
|
|
279
374
|
...options.appName ? { appName: options.appName } : {},
|
|
280
375
|
...options.origin ? { origin: options.origin } : {},
|
|
281
|
-
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
376
|
+
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
377
|
+
...options.targetKind ? { targetKind: options.targetKind } : {},
|
|
378
|
+
...options.storageProtocol ? { storageProtocol: options.storageProtocol } : {},
|
|
379
|
+
...options.databaseLabel ? { databaseLabel: options.databaseLabel } : {},
|
|
380
|
+
...options.storageIdentity ? { storageIdentity: options.storageIdentity } : {}
|
|
286
381
|
});
|
|
287
382
|
flushPendingMessages();
|
|
288
383
|
});
|
|
@@ -291,21 +386,21 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
291
386
|
if (rawPayload.length === 0) return;
|
|
292
387
|
const message = JSON.parse(rawPayload);
|
|
293
388
|
if (message.type === "ping") send({ type: "pong" });
|
|
294
|
-
else if (message.type === "
|
|
295
|
-
const runtimeId = latestHello?.runtimeId ??
|
|
389
|
+
else if (message.type === "command" && onCommand) onCommand(message.payload).then((responsePayload) => {
|
|
390
|
+
const runtimeId = latestHello?.runtimeId ?? getSummary?.().runtimeId;
|
|
296
391
|
if (!runtimeId) return;
|
|
297
392
|
send({
|
|
298
|
-
type: "
|
|
299
|
-
|
|
393
|
+
type: "command.result",
|
|
394
|
+
commandId: message.commandId,
|
|
300
395
|
runtimeId,
|
|
301
396
|
payload: responsePayload
|
|
302
397
|
});
|
|
303
398
|
}).catch((err) => {
|
|
304
|
-
const runtimeId = latestHello?.runtimeId ??
|
|
399
|
+
const runtimeId = latestHello?.runtimeId ?? getSummary?.().runtimeId;
|
|
305
400
|
if (!runtimeId) return;
|
|
306
401
|
send({
|
|
307
|
-
type: "
|
|
308
|
-
|
|
402
|
+
type: "command.result",
|
|
403
|
+
commandId: message.commandId,
|
|
309
404
|
runtimeId,
|
|
310
405
|
payload: {
|
|
311
406
|
kind: "error",
|
|
@@ -313,6 +408,17 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
313
408
|
}
|
|
314
409
|
});
|
|
315
410
|
});
|
|
411
|
+
else if (message.type === "subscribe" && subscriptionHost) subscriptionHost.subscribe(message.subscriptionId, message.payload, (payload) => {
|
|
412
|
+
const runtimeId = latestHello?.runtimeId ?? getSummary?.().runtimeId;
|
|
413
|
+
if (!runtimeId) return;
|
|
414
|
+
send({
|
|
415
|
+
type: "subscription.data",
|
|
416
|
+
subscriptionId: message.subscriptionId,
|
|
417
|
+
runtimeId,
|
|
418
|
+
payload
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
else if (message.type === "unsubscribe") subscriptionHost?.unsubscribe(message.subscriptionId);
|
|
316
422
|
});
|
|
317
423
|
socket.on("close", scheduleReconnect);
|
|
318
424
|
socket.on("error", scheduleReconnect);
|
|
@@ -355,41 +461,45 @@ function createNodeWebSocketDevtoolsSink(options) {
|
|
|
355
461
|
platform: event.platform,
|
|
356
462
|
...options.appName ? { appName: options.appName } : {},
|
|
357
463
|
...options.origin ? { origin: options.origin } : {},
|
|
358
|
-
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
|
|
464
|
+
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
465
|
+
...options.targetKind ? { targetKind: options.targetKind } : {},
|
|
466
|
+
...options.storageProtocol ? { storageProtocol: options.storageProtocol } : {},
|
|
467
|
+
...options.databaseLabel ? { databaseLabel: options.databaseLabel } : {},
|
|
468
|
+
...options.storageIdentity ? { storageIdentity: options.storageIdentity } : {}
|
|
359
469
|
});
|
|
360
470
|
}
|
|
361
471
|
send({
|
|
362
472
|
type: "event",
|
|
363
473
|
event
|
|
364
474
|
});
|
|
365
|
-
if (getSnapshot) send({
|
|
366
|
-
type: "snapshot",
|
|
367
|
-
snapshot: withSnapshotMeta(getSnapshot(), options)
|
|
368
|
-
});
|
|
369
475
|
},
|
|
370
|
-
attachRuntime(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
});
|
|
476
|
+
attachRuntime(runtime) {
|
|
477
|
+
getSummary = () => withRuntimeSummaryMeta(runtime.getRuntimeSummary(), options);
|
|
478
|
+
},
|
|
479
|
+
attachCommandHandler(handler) {
|
|
480
|
+
onCommand = handler;
|
|
376
481
|
},
|
|
377
|
-
|
|
378
|
-
|
|
482
|
+
attachSubscriptionHost(host) {
|
|
483
|
+
subscriptionHost = host;
|
|
379
484
|
},
|
|
380
485
|
dispose() {
|
|
381
486
|
disposed = true;
|
|
382
487
|
if (connectTimer) clearTimeout(connectTimer);
|
|
488
|
+
subscriptionHost?.dispose();
|
|
383
489
|
socket?.close();
|
|
384
490
|
}
|
|
385
491
|
};
|
|
386
492
|
}
|
|
387
|
-
function
|
|
493
|
+
function withRuntimeSummaryMeta(summary, options) {
|
|
388
494
|
return {
|
|
389
|
-
...
|
|
495
|
+
...summary,
|
|
390
496
|
...options.appName ? { appName: options.appName } : {},
|
|
391
497
|
...options.origin ? { origin: options.origin } : {},
|
|
392
|
-
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
|
|
498
|
+
...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {},
|
|
499
|
+
...options.targetKind ? { targetKind: options.targetKind } : {},
|
|
500
|
+
...options.storageProtocol ? { storageProtocol: options.storageProtocol } : {},
|
|
501
|
+
...options.databaseLabel ? { databaseLabel: options.databaseLabel } : {},
|
|
502
|
+
...options.storageIdentity ? { storageIdentity: options.storageIdentity } : {}
|
|
393
503
|
};
|
|
394
504
|
}
|
|
395
505
|
function shouldAutoConnectNodeDevtools() {
|