syncorejs 0.1.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/README.md +30 -0
- package/dist/_vendor/core/_virtual/_rolldown/runtime.mjs +27 -0
- package/dist/_vendor/core/cli.d.mts +5 -0
- package/dist/_vendor/core/cli.d.mts.map +1 -0
- package/dist/_vendor/core/cli.mjs +1196 -0
- package/dist/_vendor/core/cli.mjs.map +1 -0
- package/dist/_vendor/core/index.d.mts +7 -0
- package/dist/_vendor/core/index.mjs +25 -0
- package/dist/_vendor/core/index.mjs.map +1 -0
- package/dist/_vendor/core/runtime/devtools.d.mts +15 -0
- package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -0
- package/dist/_vendor/core/runtime/devtools.mjs +300 -0
- package/dist/_vendor/core/runtime/devtools.mjs.map +1 -0
- package/dist/_vendor/core/runtime/functions.d.mts +123 -0
- package/dist/_vendor/core/runtime/functions.d.mts.map +1 -0
- package/dist/_vendor/core/runtime/functions.mjs +71 -0
- package/dist/_vendor/core/runtime/functions.mjs.map +1 -0
- package/dist/_vendor/core/runtime/id.d.mts +13 -0
- package/dist/_vendor/core/runtime/id.d.mts.map +1 -0
- package/dist/_vendor/core/runtime/id.mjs +28 -0
- package/dist/_vendor/core/runtime/id.mjs.map +1 -0
- package/dist/_vendor/core/runtime/runtime.d.mts +370 -0
- package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -0
- package/dist/_vendor/core/runtime/runtime.mjs +1143 -0
- package/dist/_vendor/core/runtime/runtime.mjs.map +1 -0
- package/dist/_vendor/devtools-protocol/index.d.ts +230 -0
- package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -0
- package/dist/_vendor/devtools-protocol/index.js +0 -0
- package/dist/_vendor/next/config.d.ts +17 -0
- package/dist/_vendor/next/config.d.ts.map +1 -0
- package/dist/_vendor/next/config.js +73 -0
- package/dist/_vendor/next/config.js.map +1 -0
- package/dist/_vendor/next/index.d.ts +80 -0
- package/dist/_vendor/next/index.d.ts.map +1 -0
- package/dist/_vendor/next/index.js +81 -0
- package/dist/_vendor/next/index.js.map +1 -0
- package/dist/_vendor/platform-expo/index.d.ts +97 -0
- package/dist/_vendor/platform-expo/index.d.ts.map +1 -0
- package/dist/_vendor/platform-expo/index.js +197 -0
- package/dist/_vendor/platform-expo/index.js.map +1 -0
- package/dist/_vendor/platform-expo/react.d.ts +26 -0
- package/dist/_vendor/platform-expo/react.d.ts.map +1 -0
- package/dist/_vendor/platform-expo/react.js +30 -0
- package/dist/_vendor/platform-expo/react.js.map +1 -0
- package/dist/_vendor/platform-node/index.d.mts +145 -0
- package/dist/_vendor/platform-node/index.d.mts.map +1 -0
- package/dist/_vendor/platform-node/index.mjs +405 -0
- package/dist/_vendor/platform-node/index.mjs.map +1 -0
- package/dist/_vendor/platform-node/ipc-react.d.mts +25 -0
- package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -0
- package/dist/_vendor/platform-node/ipc-react.mjs +21 -0
- package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -0
- package/dist/_vendor/platform-node/ipc.d.mts +75 -0
- package/dist/_vendor/platform-node/ipc.d.mts.map +1 -0
- package/dist/_vendor/platform-node/ipc.mjs +343 -0
- package/dist/_vendor/platform-node/ipc.mjs.map +1 -0
- package/dist/_vendor/platform-web/index.d.ts +123 -0
- package/dist/_vendor/platform-web/index.d.ts.map +1 -0
- package/dist/_vendor/platform-web/index.js +309 -0
- package/dist/_vendor/platform-web/index.js.map +1 -0
- package/dist/_vendor/platform-web/indexeddb.d.ts +25 -0
- package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -0
- package/dist/_vendor/platform-web/indexeddb.js +125 -0
- package/dist/_vendor/platform-web/indexeddb.js.map +1 -0
- package/dist/_vendor/platform-web/opfs.d.ts +27 -0
- package/dist/_vendor/platform-web/opfs.d.ts.map +1 -0
- package/dist/_vendor/platform-web/opfs.js +146 -0
- package/dist/_vendor/platform-web/opfs.js.map +1 -0
- package/dist/_vendor/platform-web/persistence.d.ts +27 -0
- package/dist/_vendor/platform-web/persistence.d.ts.map +1 -0
- package/dist/_vendor/platform-web/persistence.js +23 -0
- package/dist/_vendor/platform-web/persistence.js.map +1 -0
- package/dist/_vendor/platform-web/react.d.ts +35 -0
- package/dist/_vendor/platform-web/react.d.ts.map +1 -0
- package/dist/_vendor/platform-web/react.js +42 -0
- package/dist/_vendor/platform-web/react.js.map +1 -0
- package/dist/_vendor/platform-web/sqljs.js +133 -0
- package/dist/_vendor/platform-web/sqljs.js.map +1 -0
- package/dist/_vendor/platform-web/worker.d.ts +78 -0
- package/dist/_vendor/platform-web/worker.d.ts.map +1 -0
- package/dist/_vendor/platform-web/worker.js +307 -0
- package/dist/_vendor/platform-web/worker.js.map +1 -0
- package/dist/_vendor/react/index.d.ts +58 -0
- package/dist/_vendor/react/index.d.ts.map +1 -0
- package/dist/_vendor/react/index.js +151 -0
- package/dist/_vendor/react/index.js.map +1 -0
- package/dist/_vendor/schema/definition.d.ts +98 -0
- package/dist/_vendor/schema/definition.d.ts.map +1 -0
- package/dist/_vendor/schema/definition.js +84 -0
- package/dist/_vendor/schema/definition.js.map +1 -0
- package/dist/_vendor/schema/index.d.ts +4 -0
- package/dist/_vendor/schema/index.js +4 -0
- package/dist/_vendor/schema/planner.d.ts +42 -0
- package/dist/_vendor/schema/planner.d.ts.map +1 -0
- package/dist/_vendor/schema/planner.js +131 -0
- package/dist/_vendor/schema/planner.js.map +1 -0
- package/dist/_vendor/schema/validators.d.ts +194 -0
- package/dist/_vendor/schema/validators.d.ts.map +1 -0
- package/dist/_vendor/schema/validators.js +158 -0
- package/dist/_vendor/schema/validators.js.map +1 -0
- package/dist/_vendor/svelte/index.d.ts +43 -0
- package/dist/_vendor/svelte/index.d.ts.map +1 -0
- package/dist/_vendor/svelte/index.js +75 -0
- package/dist/_vendor/svelte/index.js.map +1 -0
- package/dist/browser-react.d.ts +2 -0
- package/dist/browser-react.js +2 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +10 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +11 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/src/cli.d.ts +5 -0
- package/dist/core/src/cli.d.ts.map +1 -0
- package/dist/core/src/cli.js +1196 -0
- package/dist/core/src/cli.js.map +1 -0
- package/dist/core/src/index.js +7 -0
- package/dist/core/src/runtime/devtools.d.ts +7 -0
- package/dist/core/src/runtime/devtools.d.ts.map +1 -0
- package/dist/core/src/runtime/devtools.js +300 -0
- package/dist/core/src/runtime/devtools.js.map +1 -0
- package/dist/core/src/runtime/functions.d.ts +123 -0
- package/dist/core/src/runtime/functions.d.ts.map +1 -0
- package/dist/core/src/runtime/functions.js +71 -0
- package/dist/core/src/runtime/functions.js.map +1 -0
- package/dist/core/src/runtime/id.d.ts +13 -0
- package/dist/core/src/runtime/id.d.ts.map +1 -0
- package/dist/core/src/runtime/id.js +28 -0
- package/dist/core/src/runtime/id.js.map +1 -0
- package/dist/core/src/runtime/runtime.d.ts +371 -0
- package/dist/core/src/runtime/runtime.d.ts.map +1 -0
- package/dist/core/src/runtime/runtime.js +1143 -0
- package/dist/core/src/runtime/runtime.js.map +1 -0
- package/dist/devtools-protocol/src/index.d.ts +201 -0
- package/dist/devtools-protocol/src/index.d.ts.map +1 -0
- package/dist/expo-react.d.ts +2 -0
- package/dist/expo-react.js +2 -0
- package/dist/expo.d.ts +2 -0
- package/dist/expo.js +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/dist/next/src/config.d.ts +17 -0
- package/dist/next/src/config.d.ts.map +1 -0
- package/dist/next/src/config.js +73 -0
- package/dist/next/src/config.js.map +1 -0
- package/dist/next/src/index.d.ts +80 -0
- package/dist/next/src/index.d.ts.map +1 -0
- package/dist/next/src/index.js +82 -0
- package/dist/next/src/index.js.map +1 -0
- package/dist/next-config.d.ts +2 -0
- package/dist/next-config.js +2 -0
- package/dist/next.d.ts +3 -0
- package/dist/next.js +3 -0
- package/dist/node-ipc-react.d.ts +2 -0
- package/dist/node-ipc-react.js +2 -0
- package/dist/node-ipc.d.ts +2 -0
- package/dist/node-ipc.js +2 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.js +3 -0
- package/dist/platform-expo/src/index.d.ts +96 -0
- package/dist/platform-expo/src/index.d.ts.map +1 -0
- package/dist/platform-expo/src/index.js +198 -0
- package/dist/platform-expo/src/index.js.map +1 -0
- package/dist/platform-expo/src/react.d.ts +26 -0
- package/dist/platform-expo/src/react.d.ts.map +1 -0
- package/dist/platform-expo/src/react.js +30 -0
- package/dist/platform-expo/src/react.js.map +1 -0
- package/dist/platform-node/src/index.d.ts +145 -0
- package/dist/platform-node/src/index.d.ts.map +1 -0
- package/dist/platform-node/src/index.js +407 -0
- package/dist/platform-node/src/index.js.map +1 -0
- package/dist/platform-node/src/ipc-react.d.ts +25 -0
- package/dist/platform-node/src/ipc-react.d.ts.map +1 -0
- package/dist/platform-node/src/ipc-react.js +21 -0
- package/dist/platform-node/src/ipc-react.js.map +1 -0
- package/dist/platform-node/src/ipc.d.ts +76 -0
- package/dist/platform-node/src/ipc.d.ts.map +1 -0
- package/dist/platform-node/src/ipc.js +344 -0
- package/dist/platform-node/src/ipc.js.map +1 -0
- package/dist/platform-web/src/index.d.ts +106 -0
- package/dist/platform-web/src/index.d.ts.map +1 -0
- package/dist/platform-web/src/index.js +311 -0
- package/dist/platform-web/src/index.js.map +1 -0
- package/dist/platform-web/src/indexeddb.js +125 -0
- package/dist/platform-web/src/indexeddb.js.map +1 -0
- package/dist/platform-web/src/opfs.js +146 -0
- package/dist/platform-web/src/opfs.js.map +1 -0
- package/dist/platform-web/src/persistence.d.ts +20 -0
- package/dist/platform-web/src/persistence.d.ts.map +1 -0
- package/dist/platform-web/src/persistence.js +23 -0
- package/dist/platform-web/src/persistence.js.map +1 -0
- package/dist/platform-web/src/react.d.ts +35 -0
- package/dist/platform-web/src/react.d.ts.map +1 -0
- package/dist/platform-web/src/react.js +42 -0
- package/dist/platform-web/src/react.js.map +1 -0
- package/dist/platform-web/src/sqljs.js +133 -0
- package/dist/platform-web/src/sqljs.js.map +1 -0
- package/dist/platform-web/src/worker.d.ts +79 -0
- package/dist/platform-web/src/worker.d.ts.map +1 -0
- package/dist/platform-web/src/worker.js +308 -0
- package/dist/platform-web/src/worker.js.map +1 -0
- package/dist/react/src/index.d.ts +59 -0
- package/dist/react/src/index.d.ts.map +1 -0
- package/dist/react/src/index.js +151 -0
- package/dist/react/src/index.js.map +1 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +2 -0
- package/dist/schema/src/definition.d.ts +98 -0
- package/dist/schema/src/definition.d.ts.map +1 -0
- package/dist/schema/src/definition.js +84 -0
- package/dist/schema/src/definition.js.map +1 -0
- package/dist/schema/src/planner.d.ts +42 -0
- package/dist/schema/src/planner.d.ts.map +1 -0
- package/dist/schema/src/planner.js +131 -0
- package/dist/schema/src/planner.js.map +1 -0
- package/dist/schema/src/validators.d.ts +194 -0
- package/dist/schema/src/validators.d.ts.map +1 -0
- package/dist/schema/src/validators.js +158 -0
- package/dist/schema/src/validators.js.map +1 -0
- package/dist/svelte/src/index.d.ts +44 -0
- package/dist/svelte/src/index.d.ts.map +1 -0
- package/dist/svelte/src/index.js +75 -0
- package/dist/svelte/src/index.js.map +1 -0
- package/dist/svelte.d.ts +2 -0
- package/dist/svelte.js +2 -0
- package/package.json +152 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { generateId } from "../core/index.mjs";
|
|
2
|
+
//#region src/worker.ts
|
|
3
|
+
var SyncoreWebWorkerClient = class {
|
|
4
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
5
|
+
watchRecordsByKey = /* @__PURE__ */ new Map();
|
|
6
|
+
watchKeyBySubscriptionId = /* @__PURE__ */ new Map();
|
|
7
|
+
disposed = false;
|
|
8
|
+
handleMessage = (event) => {
|
|
9
|
+
const message = event.data;
|
|
10
|
+
if (!message || typeof message !== "object" || !("type" in message)) return;
|
|
11
|
+
switch (message.type) {
|
|
12
|
+
case "runtime.ready": return;
|
|
13
|
+
case "runtime.error":
|
|
14
|
+
this.rejectAllPending(new Error(message.error));
|
|
15
|
+
return;
|
|
16
|
+
case "invoke.result": {
|
|
17
|
+
const pending = this.pendingRequests.get(message.requestId);
|
|
18
|
+
if (!pending) return;
|
|
19
|
+
this.pendingRequests.delete(message.requestId);
|
|
20
|
+
if (message.success) pending.resolve(message.value);
|
|
21
|
+
else pending.reject(new Error(message.error));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
case "watch.update": {
|
|
25
|
+
const watchKey = this.watchKeyBySubscriptionId.get(message.subscriptionId);
|
|
26
|
+
if (!watchKey) return;
|
|
27
|
+
const watchRecord = this.watchRecordsByKey.get(watchKey);
|
|
28
|
+
if (!watchRecord) return;
|
|
29
|
+
if (message.success) {
|
|
30
|
+
watchRecord.result = message.value;
|
|
31
|
+
watchRecord.error = void 0;
|
|
32
|
+
} else watchRecord.error = new Error(message.error);
|
|
33
|
+
for (const listener of watchRecord.listeners) listener();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
constructor(endpoint) {
|
|
38
|
+
this.endpoint = endpoint;
|
|
39
|
+
this.endpoint.addEventListener("message", this.handleMessage);
|
|
40
|
+
}
|
|
41
|
+
query(reference, ...args) {
|
|
42
|
+
return this.invoke("query", reference, normalizeOptionalArgs(args));
|
|
43
|
+
}
|
|
44
|
+
mutation(reference, ...args) {
|
|
45
|
+
return this.invoke("mutation", reference, normalizeOptionalArgs(args));
|
|
46
|
+
}
|
|
47
|
+
action(reference, ...args) {
|
|
48
|
+
return this.invoke("action", reference, normalizeOptionalArgs(args));
|
|
49
|
+
}
|
|
50
|
+
watchQuery(reference, ...args) {
|
|
51
|
+
this.ensureNotDisposed();
|
|
52
|
+
const normalizedArgs = normalizeOptionalArgs(args);
|
|
53
|
+
const watchKey = createWatchKey(reference, normalizedArgs);
|
|
54
|
+
let watchRecord = this.watchRecordsByKey.get(watchKey);
|
|
55
|
+
if (!watchRecord) {
|
|
56
|
+
watchRecord = {
|
|
57
|
+
subscriptionId: generateId(),
|
|
58
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
59
|
+
consumers: 0,
|
|
60
|
+
result: void 0,
|
|
61
|
+
error: void 0
|
|
62
|
+
};
|
|
63
|
+
this.watchRecordsByKey.set(watchKey, watchRecord);
|
|
64
|
+
this.watchKeyBySubscriptionId.set(watchRecord.subscriptionId, watchKey);
|
|
65
|
+
this.endpoint.postMessage({
|
|
66
|
+
type: "watch.subscribe",
|
|
67
|
+
subscriptionId: watchRecord.subscriptionId,
|
|
68
|
+
reference,
|
|
69
|
+
args: normalizedArgs
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
watchRecord.consumers += 1;
|
|
73
|
+
let disposed = false;
|
|
74
|
+
const ownedListeners = /* @__PURE__ */ new Set();
|
|
75
|
+
return {
|
|
76
|
+
onUpdate: (callback) => {
|
|
77
|
+
watchRecord.listeners.add(callback);
|
|
78
|
+
ownedListeners.add(callback);
|
|
79
|
+
queueMicrotask(callback);
|
|
80
|
+
return () => {
|
|
81
|
+
watchRecord.listeners.delete(callback);
|
|
82
|
+
ownedListeners.delete(callback);
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
localQueryResult: () => watchRecord.result,
|
|
86
|
+
localQueryError: () => watchRecord.error,
|
|
87
|
+
dispose: () => {
|
|
88
|
+
if (disposed) return;
|
|
89
|
+
disposed = true;
|
|
90
|
+
for (const callback of ownedListeners) watchRecord.listeners.delete(callback);
|
|
91
|
+
ownedListeners.clear();
|
|
92
|
+
watchRecord.consumers = Math.max(0, watchRecord.consumers - 1);
|
|
93
|
+
if (watchRecord.consumers > 0) return;
|
|
94
|
+
this.endpoint.postMessage({
|
|
95
|
+
type: "watch.unsubscribe",
|
|
96
|
+
subscriptionId: watchRecord.subscriptionId
|
|
97
|
+
});
|
|
98
|
+
this.watchKeyBySubscriptionId.delete(watchRecord.subscriptionId);
|
|
99
|
+
this.watchRecordsByKey.delete(watchKey);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
dispose() {
|
|
104
|
+
if (this.disposed) return;
|
|
105
|
+
this.disposed = true;
|
|
106
|
+
this.endpoint.removeEventListener("message", this.handleMessage);
|
|
107
|
+
for (const watchRecord of this.watchRecordsByKey.values()) this.endpoint.postMessage({
|
|
108
|
+
type: "watch.unsubscribe",
|
|
109
|
+
subscriptionId: watchRecord.subscriptionId
|
|
110
|
+
});
|
|
111
|
+
this.watchKeyBySubscriptionId.clear();
|
|
112
|
+
this.watchRecordsByKey.clear();
|
|
113
|
+
this.rejectAllPending(/* @__PURE__ */ new Error("Syncore worker client was disposed."));
|
|
114
|
+
}
|
|
115
|
+
invoke(kind, reference, args) {
|
|
116
|
+
this.ensureNotDisposed();
|
|
117
|
+
const requestId = generateId();
|
|
118
|
+
const promise = new Promise((resolve, reject) => {
|
|
119
|
+
this.pendingRequests.set(requestId, {
|
|
120
|
+
resolve,
|
|
121
|
+
reject
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
this.endpoint.postMessage(createInvokeRequest(requestId, kind, reference, args));
|
|
125
|
+
return promise;
|
|
126
|
+
}
|
|
127
|
+
rejectAllPending(error) {
|
|
128
|
+
for (const pending of this.pendingRequests.values()) pending.reject(error);
|
|
129
|
+
this.pendingRequests.clear();
|
|
130
|
+
}
|
|
131
|
+
ensureNotDisposed() {
|
|
132
|
+
if (this.disposed) throw new Error("Syncore worker client was disposed.");
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Create a web worker Syncore client from a low-level message endpoint.
|
|
137
|
+
*/
|
|
138
|
+
function createWebWorkerClient(endpoint) {
|
|
139
|
+
return new SyncoreWebWorkerClient(endpoint);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create and manage both a browser Worker and the Syncore client that talks to it.
|
|
143
|
+
*/
|
|
144
|
+
function createManagedWebWorkerClient(options) {
|
|
145
|
+
const worker = options.createWorker();
|
|
146
|
+
const client = createWebWorkerClient(worker);
|
|
147
|
+
return {
|
|
148
|
+
client,
|
|
149
|
+
worker,
|
|
150
|
+
dispose() {
|
|
151
|
+
client.dispose();
|
|
152
|
+
worker.terminate();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create a worker-backed Syncore client using the standard Worker constructor.
|
|
158
|
+
*/
|
|
159
|
+
function createSyncoreWebWorkerClient(options) {
|
|
160
|
+
return createManagedWebWorkerClient({ createWorker: () => new Worker(options.workerUrl, {
|
|
161
|
+
type: options.workerType ?? "module",
|
|
162
|
+
...options.workerName ? { name: options.workerName } : {}
|
|
163
|
+
}) });
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Attach a Syncore runtime implementation to a worker message endpoint.
|
|
167
|
+
*/
|
|
168
|
+
function attachWebWorkerRuntime(options) {
|
|
169
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
170
|
+
const runtimePromise = Promise.resolve(options.createRuntime()).then(async (runtime) => {
|
|
171
|
+
await runtime.start();
|
|
172
|
+
return runtime;
|
|
173
|
+
});
|
|
174
|
+
const clientPromise = runtimePromise.then((runtime) => runtime.createClient());
|
|
175
|
+
const ready = clientPromise.then(() => {
|
|
176
|
+
options.endpoint.postMessage({ type: "runtime.ready" });
|
|
177
|
+
}).catch((error) => {
|
|
178
|
+
options.endpoint.postMessage({
|
|
179
|
+
type: "runtime.error",
|
|
180
|
+
error: error instanceof Error ? error.message : String(error)
|
|
181
|
+
});
|
|
182
|
+
throw error;
|
|
183
|
+
});
|
|
184
|
+
const handleMessage = (event) => {
|
|
185
|
+
(async () => {
|
|
186
|
+
const message = event.data;
|
|
187
|
+
if (!message || typeof message !== "object" || !("type" in message)) return;
|
|
188
|
+
try {
|
|
189
|
+
const client = await clientPromise;
|
|
190
|
+
switch (message.type) {
|
|
191
|
+
case "invoke": {
|
|
192
|
+
const value = message.kind === "query" ? await client.query(message.reference, message.args) : message.kind === "mutation" ? await client.mutation(message.reference, message.args) : await client.action(message.reference, message.args);
|
|
193
|
+
options.endpoint.postMessage({
|
|
194
|
+
type: "invoke.result",
|
|
195
|
+
requestId: message.requestId,
|
|
196
|
+
success: true,
|
|
197
|
+
value
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
case "watch.subscribe": {
|
|
202
|
+
if (subscriptions.has(message.subscriptionId)) return;
|
|
203
|
+
const watch = client.watchQuery(message.reference, message.args);
|
|
204
|
+
const sendCurrentState = () => {
|
|
205
|
+
const error = watch.localQueryError();
|
|
206
|
+
if (error) {
|
|
207
|
+
options.endpoint.postMessage({
|
|
208
|
+
type: "watch.update",
|
|
209
|
+
subscriptionId: message.subscriptionId,
|
|
210
|
+
success: false,
|
|
211
|
+
error: error.message
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
options.endpoint.postMessage({
|
|
216
|
+
type: "watch.update",
|
|
217
|
+
subscriptionId: message.subscriptionId,
|
|
218
|
+
success: true,
|
|
219
|
+
value: watch.localQueryResult()
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
const unsubscribe = watch.onUpdate(sendCurrentState);
|
|
223
|
+
subscriptions.set(message.subscriptionId, {
|
|
224
|
+
watch,
|
|
225
|
+
unsubscribe
|
|
226
|
+
});
|
|
227
|
+
sendCurrentState();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
case "watch.unsubscribe": {
|
|
231
|
+
const subscription = subscriptions.get(message.subscriptionId);
|
|
232
|
+
if (!subscription) return;
|
|
233
|
+
subscription.unsubscribe();
|
|
234
|
+
subscriptions.delete(message.subscriptionId);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
239
|
+
if (message.type === "invoke") options.endpoint.postMessage({
|
|
240
|
+
type: "invoke.result",
|
|
241
|
+
requestId: message.requestId,
|
|
242
|
+
success: false,
|
|
243
|
+
error: errorMessage
|
|
244
|
+
});
|
|
245
|
+
if (message.type === "watch.subscribe") options.endpoint.postMessage({
|
|
246
|
+
type: "watch.update",
|
|
247
|
+
subscriptionId: message.subscriptionId,
|
|
248
|
+
success: false,
|
|
249
|
+
error: errorMessage
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
})();
|
|
253
|
+
};
|
|
254
|
+
options.endpoint.addEventListener("message", handleMessage);
|
|
255
|
+
return {
|
|
256
|
+
ready,
|
|
257
|
+
async dispose() {
|
|
258
|
+
options.endpoint.removeEventListener("message", handleMessage);
|
|
259
|
+
for (const subscription of subscriptions.values()) subscription.unsubscribe();
|
|
260
|
+
subscriptions.clear();
|
|
261
|
+
await (await runtimePromise).stop();
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function createWatchKey(reference, args) {
|
|
266
|
+
return `${reference.name}:${stableStringify(args)}`;
|
|
267
|
+
}
|
|
268
|
+
function createInvokeRequest(requestId, kind, reference, args) {
|
|
269
|
+
switch (kind) {
|
|
270
|
+
case "query": return {
|
|
271
|
+
type: "invoke",
|
|
272
|
+
requestId,
|
|
273
|
+
kind,
|
|
274
|
+
reference,
|
|
275
|
+
args
|
|
276
|
+
};
|
|
277
|
+
case "mutation": return {
|
|
278
|
+
type: "invoke",
|
|
279
|
+
requestId,
|
|
280
|
+
kind,
|
|
281
|
+
reference,
|
|
282
|
+
args
|
|
283
|
+
};
|
|
284
|
+
case "action": return {
|
|
285
|
+
type: "invoke",
|
|
286
|
+
requestId,
|
|
287
|
+
kind,
|
|
288
|
+
reference,
|
|
289
|
+
args
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function stableStringify(value) {
|
|
294
|
+
return JSON.stringify(sortValue(value));
|
|
295
|
+
}
|
|
296
|
+
function sortValue(value) {
|
|
297
|
+
if (Array.isArray(value)) return value.map(sortValue);
|
|
298
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
function normalizeOptionalArgs(args) {
|
|
302
|
+
return args[0] ?? {};
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
305
|
+
export { SyncoreWebWorkerClient, attachWebWorkerRuntime, createManagedWebWorkerClient, createSyncoreWebWorkerClient, createWebWorkerClient };
|
|
306
|
+
|
|
307
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","names":[],"sources":["../src/worker.ts"],"sourcesContent":["import {\n generateId,\n type AnySyncoreSchema,\n type FunctionReference,\n type JsonObject,\n type SyncoreClient,\n type SyncoreRuntime,\n type SyncoreWatch\n} from \"@syncore/core\";\nexport type WebWorkerSyncoreSchema = AnySyncoreSchema;\n\nexport interface SyncoreWorkerMessageEndpoint {\n postMessage(message: unknown): void;\n addEventListener(\n type: \"message\",\n listener: (event: MessageEvent<unknown>) => void\n ): void;\n removeEventListener(\n type: \"message\",\n listener: (event: MessageEvent<unknown>) => void\n ): void;\n}\n\ntype SyncoreWorkerRequest =\n | {\n type: \"invoke\";\n requestId: string;\n kind: \"query\";\n reference: FunctionReference<\"query\", unknown, unknown>;\n args: JsonObject;\n }\n | {\n type: \"invoke\";\n requestId: string;\n kind: \"mutation\";\n reference: FunctionReference<\"mutation\", unknown, unknown>;\n args: JsonObject;\n }\n | {\n type: \"invoke\";\n requestId: string;\n kind: \"action\";\n reference: FunctionReference<\"action\", unknown, unknown>;\n args: JsonObject;\n }\n | {\n type: \"watch.subscribe\";\n subscriptionId: string;\n reference: FunctionReference<\"query\", unknown, unknown>;\n args: JsonObject;\n }\n | {\n type: \"watch.unsubscribe\";\n subscriptionId: string;\n };\n\ntype SyncoreWorkerResponse =\n | {\n type: \"runtime.ready\";\n }\n | {\n type: \"runtime.error\";\n error: string;\n }\n | {\n type: \"invoke.result\";\n requestId: string;\n success: true;\n value: unknown;\n }\n | {\n type: \"invoke.result\";\n requestId: string;\n success: false;\n error: string;\n }\n | {\n type: \"watch.update\";\n subscriptionId: string;\n success: true;\n value: unknown;\n }\n | {\n type: \"watch.update\";\n subscriptionId: string;\n success: false;\n error: string;\n };\n\ninterface PendingRequest {\n resolve(value: unknown): void;\n reject(error: Error): void;\n}\n\ninterface WatchRecord {\n subscriptionId: string;\n listeners: Set<() => void>;\n consumers: number;\n result: unknown;\n error: Error | undefined;\n}\n\nexport type WorkerQueryWatch<TValue> = SyncoreWatch<TValue> & {\n dispose(): void;\n};\n\ntype OptionalArgsTuple<TArgs> =\n Record<never, never> extends TArgs ? [args?: TArgs] : [args: TArgs];\n\nexport class SyncoreWebWorkerClient implements SyncoreClient {\n private readonly pendingRequests = new Map<string, PendingRequest>();\n private readonly watchRecordsByKey = new Map<string, WatchRecord>();\n private readonly watchKeyBySubscriptionId = new Map<string, string>();\n private disposed = false;\n\n private readonly handleMessage = (event: MessageEvent<unknown>) => {\n const message = event.data as SyncoreWorkerResponse;\n if (!message || typeof message !== \"object\" || !(\"type\" in message)) {\n return;\n }\n\n switch (message.type) {\n case \"runtime.ready\":\n return;\n case \"runtime.error\":\n this.rejectAllPending(new Error(message.error));\n return;\n case \"invoke.result\": {\n const pending = this.pendingRequests.get(message.requestId);\n if (!pending) {\n return;\n }\n this.pendingRequests.delete(message.requestId);\n if (message.success) {\n pending.resolve(message.value);\n } else {\n pending.reject(new Error(message.error));\n }\n return;\n }\n case \"watch.update\": {\n const watchKey = this.watchKeyBySubscriptionId.get(\n message.subscriptionId\n );\n if (!watchKey) {\n return;\n }\n const watchRecord = this.watchRecordsByKey.get(watchKey);\n if (!watchRecord) {\n return;\n }\n if (message.success) {\n watchRecord.result = message.value;\n watchRecord.error = undefined;\n } else {\n watchRecord.error = new Error(message.error);\n }\n for (const listener of watchRecord.listeners) {\n listener();\n }\n }\n }\n };\n\n constructor(private readonly endpoint: SyncoreWorkerMessageEndpoint) {\n this.endpoint.addEventListener(\"message\", this.handleMessage);\n }\n\n query<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ): Promise<TResult> {\n return this.invoke(\n \"query\",\n reference,\n normalizeOptionalArgs(args) as JsonObject\n );\n }\n\n mutation<TArgs, TResult>(\n reference: FunctionReference<\"mutation\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ): Promise<TResult> {\n return this.invoke(\n \"mutation\",\n reference,\n normalizeOptionalArgs(args) as JsonObject\n );\n }\n\n action<TArgs, TResult>(\n reference: FunctionReference<\"action\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ): Promise<TResult> {\n return this.invoke(\n \"action\",\n reference,\n normalizeOptionalArgs(args) as JsonObject\n );\n }\n\n watchQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ): WorkerQueryWatch<TResult> {\n this.ensureNotDisposed();\n const normalizedArgs = normalizeOptionalArgs(args) as JsonObject;\n const watchKey = createWatchKey(reference, normalizedArgs);\n let watchRecord = this.watchRecordsByKey.get(watchKey);\n if (!watchRecord) {\n watchRecord = {\n subscriptionId: generateId(),\n listeners: new Set<() => void>(),\n consumers: 0,\n result: undefined,\n error: undefined\n };\n this.watchRecordsByKey.set(watchKey, watchRecord);\n this.watchKeyBySubscriptionId.set(watchRecord.subscriptionId, watchKey);\n this.endpoint.postMessage({\n type: \"watch.subscribe\",\n subscriptionId: watchRecord.subscriptionId,\n reference,\n args: normalizedArgs\n } satisfies SyncoreWorkerRequest);\n }\n\n watchRecord.consumers += 1;\n let disposed = false;\n const ownedListeners = new Set<() => void>();\n\n return {\n onUpdate: (callback) => {\n watchRecord.listeners.add(callback);\n ownedListeners.add(callback);\n queueMicrotask(callback);\n return () => {\n watchRecord.listeners.delete(callback);\n ownedListeners.delete(callback);\n };\n },\n localQueryResult: () => watchRecord.result as TResult | undefined,\n localQueryError: () => watchRecord.error,\n dispose: () => {\n if (disposed) {\n return;\n }\n disposed = true;\n for (const callback of ownedListeners) {\n watchRecord.listeners.delete(callback);\n }\n ownedListeners.clear();\n watchRecord.consumers = Math.max(0, watchRecord.consumers - 1);\n if (watchRecord.consumers > 0) {\n return;\n }\n this.endpoint.postMessage({\n type: \"watch.unsubscribe\",\n subscriptionId: watchRecord.subscriptionId\n } satisfies SyncoreWorkerRequest);\n this.watchKeyBySubscriptionId.delete(watchRecord.subscriptionId);\n this.watchRecordsByKey.delete(watchKey);\n }\n };\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.endpoint.removeEventListener(\"message\", this.handleMessage);\n for (const watchRecord of this.watchRecordsByKey.values()) {\n this.endpoint.postMessage({\n type: \"watch.unsubscribe\",\n subscriptionId: watchRecord.subscriptionId\n } satisfies SyncoreWorkerRequest);\n }\n this.watchKeyBySubscriptionId.clear();\n this.watchRecordsByKey.clear();\n this.rejectAllPending(new Error(\"Syncore worker client was disposed.\"));\n }\n\n private invoke<TArgs, TResult>(\n kind: \"query\",\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args: JsonObject\n ): Promise<TResult>;\n private invoke<TArgs, TResult>(\n kind: \"mutation\",\n reference: FunctionReference<\"mutation\", TArgs, TResult>,\n args: JsonObject\n ): Promise<TResult>;\n private invoke<TArgs, TResult>(\n kind: \"action\",\n reference: FunctionReference<\"action\", TArgs, TResult>,\n args: JsonObject\n ): Promise<TResult>;\n private invoke<TArgs, TResult>(\n kind: \"query\" | \"mutation\" | \"action\",\n reference: FunctionReference<\n \"query\" | \"mutation\" | \"action\",\n TArgs,\n TResult\n >,\n args: JsonObject\n ): Promise<TResult> {\n this.ensureNotDisposed();\n const requestId = generateId();\n const promise = new Promise<TResult>((resolve, reject) => {\n this.pendingRequests.set(requestId, { resolve, reject });\n });\n\n this.endpoint.postMessage(\n createInvokeRequest(requestId, kind, reference, args)\n );\n\n return promise;\n }\n\n private rejectAllPending(error: Error): void {\n for (const pending of this.pendingRequests.values()) {\n pending.reject(error);\n }\n this.pendingRequests.clear();\n }\n\n private ensureNotDisposed(): void {\n if (this.disposed) {\n throw new Error(\"Syncore worker client was disposed.\");\n }\n }\n}\n\nexport interface AttachWebWorkerRuntimeOptions {\n endpoint: SyncoreWorkerMessageEndpoint;\n createRuntime:\n | (() => Promise<SyncoreRuntime<WebWorkerSyncoreSchema>>)\n | (() => SyncoreRuntime<WebWorkerSyncoreSchema>);\n}\n\nexport interface AttachedWebWorkerRuntime {\n ready: Promise<void>;\n dispose(): Promise<void>;\n}\n\n/**\n * A worker-backed browser client plus the Worker instance it owns.\n */\nexport interface ManagedWebWorkerClient {\n client: SyncoreWebWorkerClient;\n worker: Worker;\n dispose(): void;\n}\n\n/**\n * Options for creating a worker-backed Syncore client in the browser.\n */\nexport interface CreateWebWorkerClientProviderOptions {\n /** The worker module URL passed to `new Worker(...)`. */\n workerUrl: URL | string;\n\n /** Optional worker type, defaults to `module`. */\n workerType?: WorkerOptions[\"type\"];\n\n /** Optional name shown in browser devtools. */\n workerName?: string;\n}\n\n/**\n * Create a web worker Syncore client from a low-level message endpoint.\n */\nexport function createWebWorkerClient(\n endpoint: SyncoreWorkerMessageEndpoint\n): SyncoreWebWorkerClient {\n return new SyncoreWebWorkerClient(endpoint);\n}\n\n/**\n * Create and manage both a browser Worker and the Syncore client that talks to it.\n */\nexport function createManagedWebWorkerClient(options: {\n createWorker: () => Worker;\n}): ManagedWebWorkerClient {\n const worker = options.createWorker();\n const client = createWebWorkerClient(worker);\n return {\n client,\n worker,\n dispose() {\n client.dispose();\n worker.terminate();\n }\n };\n}\n\n/**\n * Create a worker-backed Syncore client using the standard Worker constructor.\n */\nexport function createSyncoreWebWorkerClient(\n options: CreateWebWorkerClientProviderOptions\n): ManagedWebWorkerClient {\n return createManagedWebWorkerClient({\n createWorker: () =>\n new Worker(options.workerUrl, {\n type: options.workerType ?? \"module\",\n ...(options.workerName ? { name: options.workerName } : {})\n })\n });\n}\n\n/**\n * Attach a Syncore runtime implementation to a worker message endpoint.\n */\nexport function attachWebWorkerRuntime(\n options: AttachWebWorkerRuntimeOptions\n): AttachedWebWorkerRuntime {\n const subscriptions = new Map<\n string,\n {\n watch: SyncoreWatch<unknown>;\n unsubscribe: () => void;\n }\n >();\n\n const runtimePromise = Promise.resolve(options.createRuntime()).then(\n async (runtime) => {\n await runtime.start();\n return runtime;\n }\n );\n\n const clientPromise = runtimePromise.then((runtime) =>\n runtime.createClient()\n );\n\n const ready = clientPromise\n .then(() => {\n options.endpoint.postMessage({\n type: \"runtime.ready\"\n } satisfies SyncoreWorkerResponse);\n })\n .catch((error) => {\n options.endpoint.postMessage({\n type: \"runtime.error\",\n error: error instanceof Error ? error.message : String(error)\n } satisfies SyncoreWorkerResponse);\n throw error;\n });\n\n const handleMessage = (event: MessageEvent<unknown>) => {\n void (async () => {\n const message = event.data as SyncoreWorkerRequest;\n if (!message || typeof message !== \"object\" || !(\"type\" in message)) {\n return;\n }\n\n try {\n const client = await clientPromise;\n switch (message.type) {\n case \"invoke\": {\n const value =\n message.kind === \"query\"\n ? await client.query(message.reference, message.args)\n : message.kind === \"mutation\"\n ? await client.mutation(message.reference, message.args)\n : await client.action(message.reference, message.args);\n options.endpoint.postMessage({\n type: \"invoke.result\",\n requestId: message.requestId,\n success: true,\n value\n } satisfies SyncoreWorkerResponse);\n return;\n }\n case \"watch.subscribe\": {\n if (subscriptions.has(message.subscriptionId)) {\n return;\n }\n const watch = client.watchQuery(message.reference, message.args);\n const sendCurrentState = () => {\n const error = watch.localQueryError();\n if (error) {\n options.endpoint.postMessage({\n type: \"watch.update\",\n subscriptionId: message.subscriptionId,\n success: false,\n error: error.message\n } satisfies SyncoreWorkerResponse);\n return;\n }\n options.endpoint.postMessage({\n type: \"watch.update\",\n subscriptionId: message.subscriptionId,\n success: true,\n value: watch.localQueryResult()\n } satisfies SyncoreWorkerResponse);\n };\n const unsubscribe = watch.onUpdate(sendCurrentState);\n subscriptions.set(message.subscriptionId, { watch, unsubscribe });\n sendCurrentState();\n return;\n }\n case \"watch.unsubscribe\": {\n const subscription = subscriptions.get(message.subscriptionId);\n if (!subscription) {\n return;\n }\n subscription.unsubscribe();\n subscriptions.delete(message.subscriptionId);\n }\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n if (message.type === \"invoke\") {\n options.endpoint.postMessage({\n type: \"invoke.result\",\n requestId: message.requestId,\n success: false,\n error: errorMessage\n } satisfies SyncoreWorkerResponse);\n }\n if (message.type === \"watch.subscribe\") {\n options.endpoint.postMessage({\n type: \"watch.update\",\n subscriptionId: message.subscriptionId,\n success: false,\n error: errorMessage\n } satisfies SyncoreWorkerResponse);\n }\n }\n })();\n };\n\n options.endpoint.addEventListener(\"message\", handleMessage);\n\n return {\n ready,\n async dispose() {\n options.endpoint.removeEventListener(\"message\", handleMessage);\n for (const subscription of subscriptions.values()) {\n subscription.unsubscribe();\n }\n subscriptions.clear();\n const runtime = await runtimePromise;\n await runtime.stop();\n }\n };\n}\n\nfunction createWatchKey(\n reference: FunctionReference<\"query\", unknown, unknown>,\n args: JsonObject\n): string {\n return `${reference.name}:${stableStringify(args)}`;\n}\n\nfunction createInvokeRequest(\n requestId: string,\n kind: \"query\" | \"mutation\" | \"action\",\n reference:\n | FunctionReference<\"query\", unknown, unknown>\n | FunctionReference<\"mutation\", unknown, unknown>\n | FunctionReference<\"action\", unknown, unknown>,\n args: JsonObject\n): SyncoreWorkerRequest {\n switch (kind) {\n case \"query\":\n return {\n type: \"invoke\",\n requestId,\n kind,\n reference: reference as FunctionReference<\"query\">,\n args\n };\n case \"mutation\":\n return {\n type: \"invoke\",\n requestId,\n kind,\n reference: reference as FunctionReference<\"mutation\">,\n args\n };\n case \"action\":\n return {\n type: \"invoke\",\n requestId,\n kind,\n reference: reference as FunctionReference<\"action\">,\n args\n };\n }\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n\nfunction normalizeOptionalArgs<TArgs>(\n args: [] | [TArgs] | readonly unknown[]\n): TArgs {\n return (args[0] ?? {}) as TArgs;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
import { FunctionReference, SyncoreClient } from "../core/index.d.mts";
|
|
4
|
+
|
|
5
|
+
//#region src/index.d.ts
|
|
6
|
+
type OptionalArgsTuple<TArgs> = Record<never, never> extends TArgs ? [args?: TArgs] : [args: TArgs];
|
|
7
|
+
/**
|
|
8
|
+
* Pass `"skip"` as the args argument to `useQuery` to suppress the subscription
|
|
9
|
+
* entirely and return `undefined` without contacting the runtime.
|
|
10
|
+
*/
|
|
11
|
+
declare const skip: "skip";
|
|
12
|
+
type Skip = typeof skip;
|
|
13
|
+
/**
|
|
14
|
+
* Provide a Syncore client to React descendants.
|
|
15
|
+
*
|
|
16
|
+
* Wrap your app with this component to use Syncore hooks like `useQuery` and
|
|
17
|
+
* `useMutation`.
|
|
18
|
+
*/
|
|
19
|
+
declare function SyncoreProvider({
|
|
20
|
+
client,
|
|
21
|
+
children
|
|
22
|
+
}: {
|
|
23
|
+
client: SyncoreClient;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}): react_jsx_runtime0.JSX.Element;
|
|
26
|
+
/**
|
|
27
|
+
* Read the active Syncore client from React context.
|
|
28
|
+
*
|
|
29
|
+
* Throws if used outside of {@link SyncoreProvider}.
|
|
30
|
+
*/
|
|
31
|
+
declare function useSyncore(): SyncoreClient;
|
|
32
|
+
/**
|
|
33
|
+
* Load a reactive Syncore query within a React component.
|
|
34
|
+
*
|
|
35
|
+
* The hook subscribes automatically and re-renders whenever the local query
|
|
36
|
+
* result changes. Pass `"skip"` as the second argument to suppress the
|
|
37
|
+
* subscription entirely and return `undefined` without contacting the runtime.
|
|
38
|
+
*/
|
|
39
|
+
declare function useQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs> | [Skip]): TResult | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Construct a stable function that executes a Syncore mutation.
|
|
42
|
+
*/
|
|
43
|
+
declare function useMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>): (...args: OptionalArgsTuple<TArgs>) => Promise<TResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Construct a stable function that executes a Syncore action.
|
|
46
|
+
*/
|
|
47
|
+
declare function useAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>): (...args: OptionalArgsTuple<TArgs>) => Promise<TResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Load several Syncore queries at once using explicit keys.
|
|
50
|
+
*/
|
|
51
|
+
declare function useQueries<TResult>(entries: Array<{
|
|
52
|
+
key: string;
|
|
53
|
+
reference: FunctionReference<"query">;
|
|
54
|
+
args?: Record<string, unknown>;
|
|
55
|
+
}>): Record<string, TResult | undefined>;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { SyncoreProvider, skip, useAction, useMutation, useQueries, useQuery, useSyncore };
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;;;;KAkBK,iBAAA,UACH,MAAA,uBAA6B,KAAA,IAAS,IAAA,GAAO,KAAA,KAAU,IAAA,EAAM,KAAA;;;AAPxC;;cAaV,IAAA;AAAA,KACR,IAAA,UAAc,IAAA;;;;;;;iBAUH,eAAA,CAAA;EACd,MAAA;EACA;AAAA;EAEA,MAAA,EAAQ,aAAA;EACR,QAAA,EAAU,SAAA;AAAA,IACX,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;AAjBD;iBA4BgB,UAAA,CAAA,GAAc,aAAA;;;;AA5BM;;;;iBA2CpB,QAAA,gBAAA,CACd,SAAA,EAAW,iBAAA,UAA2B,KAAA,EAAO,OAAA,MAC1C,IAAA,EAAM,iBAAA,CAAkB,KAAA,KAAU,IAAA,IACpC,OAAA;AAnCH;;;AAAA,iBAgFgB,WAAA,gBAAA,CACd,SAAA,EAAW,iBAAA,aAA8B,KAAA,EAAO,OAAA,QAC3C,IAAA,EAAM,iBAAA,CAAkB,KAAA,MAAW,OAAA,CAAQ,OAAA;;;;iBAQlC,SAAA,gBAAA,CACd,SAAA,EAAW,iBAAA,WAA4B,KAAA,EAAO,OAAA,QACzC,IAAA,EAAM,iBAAA,CAAkB,KAAA,MAAW,OAAA,CAAQ,OAAA;;;;iBAQlC,UAAA,SAAA,CACd,OAAA,EAAS,KAAA;EACP,GAAA;EACA,SAAA,EAAW,iBAAA;EACX,IAAA,GAAO,MAAA;AAAA,KAER,MAAA,SAAe,OAAA"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/index.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Pass `"skip"` as the args argument to `useQuery` to suppress the subscription
|
|
6
|
+
* entirely and return `undefined` without contacting the runtime.
|
|
7
|
+
*/
|
|
8
|
+
const skip = "skip";
|
|
9
|
+
const SyncoreContext = createContext(null);
|
|
10
|
+
/**
|
|
11
|
+
* Provide a Syncore client to React descendants.
|
|
12
|
+
*
|
|
13
|
+
* Wrap your app with this component to use Syncore hooks like `useQuery` and
|
|
14
|
+
* `useMutation`.
|
|
15
|
+
*/
|
|
16
|
+
function SyncoreProvider({ client, children }) {
|
|
17
|
+
return /* @__PURE__ */ jsx(SyncoreContext.Provider, {
|
|
18
|
+
value: client,
|
|
19
|
+
children
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the active Syncore client from React context.
|
|
24
|
+
*
|
|
25
|
+
* Throws if used outside of {@link SyncoreProvider}.
|
|
26
|
+
*/
|
|
27
|
+
function useSyncore() {
|
|
28
|
+
const client = useContext(SyncoreContext);
|
|
29
|
+
if (!client) throw new Error("SyncoreProvider is missing from the React tree.");
|
|
30
|
+
return client;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load a reactive Syncore query within a React component.
|
|
34
|
+
*
|
|
35
|
+
* The hook subscribes automatically and re-renders whenever the local query
|
|
36
|
+
* result changes. Pass `"skip"` as the second argument to suppress the
|
|
37
|
+
* subscription entirely and return `undefined` without contacting the runtime.
|
|
38
|
+
*/
|
|
39
|
+
function useQuery(reference, ...args) {
|
|
40
|
+
const isSkipped = args[0] === skip;
|
|
41
|
+
const watch = useManagedQueryWatch(useSyncore(), reference, isSkipped ? void 0 : normalizeOptionalArgs(args), isSkipped);
|
|
42
|
+
const [snapshot, setSnapshot] = useState(() => isSkipped ? noOpSnapshot : readWatchSnapshot(watch));
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (isSkipped) {
|
|
45
|
+
setSnapshot(noOpSnapshot);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const sync = () => {
|
|
49
|
+
setSnapshot(readWatchSnapshot(watch));
|
|
50
|
+
};
|
|
51
|
+
sync();
|
|
52
|
+
return watch.onUpdate(sync);
|
|
53
|
+
}, [watch, isSkipped]);
|
|
54
|
+
if (snapshot.error) throw snapshot.error;
|
|
55
|
+
return snapshot.result;
|
|
56
|
+
}
|
|
57
|
+
const noOpSnapshot = {
|
|
58
|
+
result: void 0,
|
|
59
|
+
error: void 0
|
|
60
|
+
};
|
|
61
|
+
const noOpWatch = {
|
|
62
|
+
onUpdate: () => () => {},
|
|
63
|
+
localQueryResult: () => void 0,
|
|
64
|
+
localQueryError: () => void 0
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Construct a stable function that executes a Syncore mutation.
|
|
68
|
+
*/
|
|
69
|
+
function useMutation(reference) {
|
|
70
|
+
const client = useSyncore();
|
|
71
|
+
return (...args) => client.mutation(reference, normalizeOptionalArgs(args));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Construct a stable function that executes a Syncore action.
|
|
75
|
+
*/
|
|
76
|
+
function useAction(reference) {
|
|
77
|
+
const client = useSyncore();
|
|
78
|
+
return (...args) => client.action(reference, normalizeOptionalArgs(args));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load several Syncore queries at once using explicit keys.
|
|
82
|
+
*/
|
|
83
|
+
function useQueries(entries) {
|
|
84
|
+
const client = useSyncore();
|
|
85
|
+
const entriesKey = stableStringify(entries.map((entry) => ({
|
|
86
|
+
key: entry.key,
|
|
87
|
+
referenceName: entry.reference.name,
|
|
88
|
+
args: entry.args ?? {}
|
|
89
|
+
})));
|
|
90
|
+
const normalizedEntries = useMemo(() => JSON.parse(entriesKey), [entriesKey]);
|
|
91
|
+
const watches = useMemo(() => normalizedEntries.map((entry) => ({
|
|
92
|
+
key: entry.key,
|
|
93
|
+
watch: client.watchQuery({
|
|
94
|
+
kind: "query",
|
|
95
|
+
name: entry.referenceName
|
|
96
|
+
}, entry.args)
|
|
97
|
+
})), [client, normalizedEntries]);
|
|
98
|
+
const [snapshot, setSnapshot] = useState(() => readQueriesSnapshot(watches));
|
|
99
|
+
useEffect(() => () => {
|
|
100
|
+
for (const entry of watches) entry.watch.dispose?.();
|
|
101
|
+
}, [watches]);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const sync = () => {
|
|
104
|
+
setSnapshot(readQueriesSnapshot(watches));
|
|
105
|
+
};
|
|
106
|
+
sync();
|
|
107
|
+
const cleanups = watches.map((entry) => entry.watch.onUpdate(sync));
|
|
108
|
+
return () => {
|
|
109
|
+
for (const cleanup of cleanups) cleanup();
|
|
110
|
+
};
|
|
111
|
+
}, [watches]);
|
|
112
|
+
return snapshot;
|
|
113
|
+
}
|
|
114
|
+
function useManagedQueryWatch(client, reference, args, isSkipped) {
|
|
115
|
+
const argsKey = isSkipped ? "skip" : stableStringify(args ?? {});
|
|
116
|
+
const normalizedArgs = useMemo(() => isSkipped ? void 0 : JSON.parse(argsKey), [argsKey, isSkipped]);
|
|
117
|
+
const watch = useMemo(() => isSkipped ? noOpWatch : client.watchQuery(reference, normalizedArgs), [
|
|
118
|
+
client,
|
|
119
|
+
normalizedArgs,
|
|
120
|
+
reference,
|
|
121
|
+
isSkipped
|
|
122
|
+
]);
|
|
123
|
+
useEffect(() => () => {
|
|
124
|
+
if (!isSkipped) watch.dispose?.();
|
|
125
|
+
}, [watch, isSkipped]);
|
|
126
|
+
return watch;
|
|
127
|
+
}
|
|
128
|
+
function normalizeOptionalArgs(args) {
|
|
129
|
+
return args[0] ?? {};
|
|
130
|
+
}
|
|
131
|
+
function readWatchSnapshot(watch) {
|
|
132
|
+
return {
|
|
133
|
+
result: watch.localQueryResult(),
|
|
134
|
+
error: watch.localQueryError()
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function readQueriesSnapshot(watches) {
|
|
138
|
+
return Object.fromEntries(watches.map((entry) => [entry.key, entry.watch.localQueryResult()]));
|
|
139
|
+
}
|
|
140
|
+
function stableStringify(value) {
|
|
141
|
+
return JSON.stringify(sortValue(value));
|
|
142
|
+
}
|
|
143
|
+
function sortValue(value) {
|
|
144
|
+
if (Array.isArray(value)) return value.map(sortValue);
|
|
145
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { SyncoreProvider, skip, useAction, useMutation, useQueries, useQuery, useSyncore };
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.tsx"],"sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useMemo,\n useState\n} from \"react\";\nimport type {\n FunctionReference,\n SyncoreClient,\n SyncoreWatch\n} from \"@syncore/core\";\n\ntype ManagedSyncoreWatch<TResult> = SyncoreWatch<TResult> & {\n dispose?: () => void;\n};\n\ntype OptionalArgsTuple<TArgs> =\n Record<never, never> extends TArgs ? [args?: TArgs] : [args: TArgs];\n\n/**\n * Pass `\"skip\"` as the args argument to `useQuery` to suppress the subscription\n * entirely and return `undefined` without contacting the runtime.\n */\nexport const skip = \"skip\" as const;\ntype Skip = typeof skip;\n\nconst SyncoreContext = createContext<SyncoreClient | null>(null);\n\n/**\n * Provide a Syncore client to React descendants.\n *\n * Wrap your app with this component to use Syncore hooks like `useQuery` and\n * `useMutation`.\n */\nexport function SyncoreProvider({\n client,\n children\n}: {\n client: SyncoreClient;\n children: ReactNode;\n}) {\n return (\n <SyncoreContext.Provider value={client}>{children}</SyncoreContext.Provider>\n );\n}\n\n/**\n * Read the active Syncore client from React context.\n *\n * Throws if used outside of {@link SyncoreProvider}.\n */\nexport function useSyncore(): SyncoreClient {\n const client = useContext(SyncoreContext);\n if (!client) {\n throw new Error(\"SyncoreProvider is missing from the React tree.\");\n }\n return client;\n}\n\n/**\n * Load a reactive Syncore query within a React component.\n *\n * The hook subscribes automatically and re-renders whenever the local query\n * result changes. Pass `\"skip\"` as the second argument to suppress the\n * subscription entirely and return `undefined` without contacting the runtime.\n */\nexport function useQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs> | [Skip]\n): TResult | undefined {\n const isSkipped = args[0] === skip;\n const client = useSyncore();\n const watch = useManagedQueryWatch(\n client,\n reference,\n isSkipped\n ? undefined\n : normalizeOptionalArgs(args as OptionalArgsTuple<TArgs>),\n isSkipped\n );\n const [snapshot, setSnapshot] = useState(() =>\n isSkipped ? noOpSnapshot : readWatchSnapshot(watch)\n );\n\n useEffect(() => {\n if (isSkipped) {\n setSnapshot(noOpSnapshot);\n return;\n }\n const sync = () => {\n setSnapshot(readWatchSnapshot(watch));\n };\n sync();\n return watch.onUpdate(sync);\n }, [watch, isSkipped]);\n\n if (snapshot.error) {\n throw snapshot.error;\n }\n\n return snapshot.result;\n}\n\nconst noOpSnapshot = { result: undefined, error: undefined };\n\nconst noOpWatch: ManagedSyncoreWatch<never> = {\n onUpdate: () => () => {},\n localQueryResult: () => undefined,\n localQueryError: () => undefined\n};\n\n/**\n * Construct a stable function that executes a Syncore mutation.\n */\nexport function useMutation<TArgs, TResult>(\n reference: FunctionReference<\"mutation\", TArgs, TResult>\n): (...args: OptionalArgsTuple<TArgs>) => Promise<TResult> {\n const client = useSyncore();\n return (...args) => client.mutation(reference, normalizeOptionalArgs(args));\n}\n\n/**\n * Construct a stable function that executes a Syncore action.\n */\nexport function useAction<TArgs, TResult>(\n reference: FunctionReference<\"action\", TArgs, TResult>\n): (...args: OptionalArgsTuple<TArgs>) => Promise<TResult> {\n const client = useSyncore();\n return (...args) => client.action(reference, normalizeOptionalArgs(args));\n}\n\n/**\n * Load several Syncore queries at once using explicit keys.\n */\nexport function useQueries<TResult>(\n entries: Array<{\n key: string;\n reference: FunctionReference<\"query\">;\n args?: Record<string, unknown>;\n }>\n): Record<string, TResult | undefined> {\n const client = useSyncore();\n const entriesKey = stableStringify(\n entries.map((entry) => ({\n key: entry.key,\n referenceName: entry.reference.name,\n args: entry.args ?? {}\n }))\n );\n const normalizedEntries = useMemo(\n () =>\n JSON.parse(entriesKey) as Array<{\n key: string;\n referenceName: string;\n args: Record<string, unknown>;\n }>,\n [entriesKey]\n );\n const watches = useMemo(\n () =>\n normalizedEntries.map((entry) => ({\n key: entry.key,\n watch: client.watchQuery(\n { kind: \"query\", name: entry.referenceName },\n entry.args\n ) as ManagedSyncoreWatch<TResult>\n })),\n [client, normalizedEntries]\n );\n const [snapshot, setSnapshot] = useState(() => readQueriesSnapshot(watches));\n\n useEffect(\n () => () => {\n for (const entry of watches) {\n entry.watch.dispose?.();\n }\n },\n [watches]\n );\n\n useEffect(() => {\n const sync = () => {\n setSnapshot(readQueriesSnapshot(watches));\n };\n sync();\n const cleanups = watches.map((entry) => entry.watch.onUpdate(sync));\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n }, [watches]);\n\n return snapshot;\n}\n\nfunction useManagedQueryWatch<TArgs, TResult>(\n client: SyncoreClient,\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args?: TArgs,\n isSkipped?: boolean\n): ManagedSyncoreWatch<TResult> {\n const argsKey = isSkipped ? \"skip\" : stableStringify(args ?? {});\n const normalizedArgs = useMemo(\n () => (isSkipped ? undefined : (JSON.parse(argsKey) as TArgs)),\n [argsKey, isSkipped]\n );\n const watch = useMemo<ManagedSyncoreWatch<TResult>>(\n () =>\n isSkipped\n ? noOpWatch\n : (client.watchQuery(\n reference,\n normalizedArgs!\n ) as ManagedSyncoreWatch<TResult>),\n [client, normalizedArgs, reference, isSkipped]\n );\n\n useEffect(\n () => () => {\n if (!isSkipped) watch.dispose?.();\n },\n [watch, isSkipped]\n );\n\n return watch;\n}\n\nfunction normalizeOptionalArgs<TArgs>(\n args: [] | [TArgs] | readonly unknown[]\n): TArgs {\n return (args[0] ?? {}) as TArgs;\n}\n\nfunction readWatchSnapshot<TResult>(watch: SyncoreWatch<TResult>): {\n result: TResult | undefined;\n error: Error | undefined;\n} {\n return {\n result: watch.localQueryResult(),\n error: watch.localQueryError()\n };\n}\n\nfunction readQueriesSnapshot<TResult>(\n watches: Array<{\n key: string;\n watch: ManagedSyncoreWatch<TResult>;\n }>\n): Record<string, TResult | undefined> {\n return Object.fromEntries(\n watches.map((entry) => [entry.key, entry.watch.localQueryResult()])\n );\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n"],"mappings":"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"}
|