syncorejs 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +323 -42
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/doctor.mjs +513 -46
  8. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  9. package/dist/_vendor/cli/messages.mjs +5 -4
  10. package/dist/_vendor/cli/messages.mjs.map +1 -1
  11. package/dist/_vendor/cli/project.mjs +110 -12
  12. package/dist/_vendor/cli/project.mjs.map +1 -1
  13. package/dist/_vendor/cli/render.mjs +57 -9
  14. package/dist/_vendor/cli/render.mjs.map +1 -1
  15. package/dist/_vendor/cli/targets.mjs +4 -3
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +13 -3
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +242 -91
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  22. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  23. package/dist/_vendor/core/index.d.mts +5 -3
  24. package/dist/_vendor/core/index.mjs +22 -2
  25. package/dist/_vendor/core/index.mjs.map +1 -1
  26. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  27. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  28. package/dist/_vendor/core/runtime/components.mjs +186 -0
  29. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  30. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  31. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/devtools.mjs +52 -41
  33. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  34. package/dist/_vendor/core/runtime/functions.d.mts +10 -10
  35. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  36. package/dist/_vendor/core/runtime/functions.mjs +2 -2
  37. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  38. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +77 -0
  39. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  40. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +617 -0
  41. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  42. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +186 -0
  43. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  44. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +220 -0
  45. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +203 -0
  47. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +177 -0
  49. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +144 -0
  51. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +220 -0
  53. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  55. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  57. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +37 -0
  59. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/runtime.d.mts +159 -205
  61. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  62. package/dist/_vendor/core/runtime/runtime.mjs +16 -1371
  63. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  64. package/dist/_vendor/core/transport.d.mts +111 -0
  65. package/dist/_vendor/core/transport.d.mts.map +1 -0
  66. package/dist/_vendor/core/transport.mjs +419 -0
  67. package/dist/_vendor/core/transport.mjs.map +1 -0
  68. package/dist/_vendor/devtools-protocol/index.d.ts +39 -1
  69. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  70. package/dist/_vendor/devtools-protocol/index.js +25 -9
  71. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  72. package/dist/_vendor/next/index.d.ts +1 -1
  73. package/dist/_vendor/next/index.d.ts.map +1 -1
  74. package/dist/_vendor/next/index.js +31 -13
  75. package/dist/_vendor/next/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/index.d.ts +12 -12
  77. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  78. package/dist/_vendor/platform-expo/index.js +4 -2
  79. package/dist/_vendor/platform-expo/index.js.map +1 -1
  80. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  81. package/dist/_vendor/platform-expo/react.js +11 -10
  82. package/dist/_vendor/platform-expo/react.js.map +1 -1
  83. package/dist/_vendor/platform-node/index.d.mts +23 -19
  84. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/index.mjs +13 -5
  86. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  87. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  88. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  89. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  90. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  91. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  92. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  93. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  94. package/dist/_vendor/platform-web/external-change.d.ts +2 -1
  95. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/external-change.js +2 -1
  97. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  98. package/dist/_vendor/platform-web/index.d.ts +21 -21
  99. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/index.js +44 -7
  101. package/dist/_vendor/platform-web/index.js.map +1 -1
  102. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  103. package/dist/_vendor/platform-web/react.js +29 -13
  104. package/dist/_vendor/platform-web/react.js.map +1 -1
  105. package/dist/_vendor/platform-web/worker.d.ts +11 -35
  106. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  107. package/dist/_vendor/platform-web/worker.js +3 -267
  108. package/dist/_vendor/platform-web/worker.js.map +1 -1
  109. package/dist/_vendor/react/index.d.ts +36 -20
  110. package/dist/_vendor/react/index.d.ts.map +1 -1
  111. package/dist/_vendor/react/index.js +279 -57
  112. package/dist/_vendor/react/index.js.map +1 -1
  113. package/dist/_vendor/schema/definition.d.ts +48 -63
  114. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  115. package/dist/_vendor/schema/definition.js +22 -39
  116. package/dist/_vendor/schema/definition.js.map +1 -1
  117. package/dist/_vendor/schema/index.d.ts +4 -4
  118. package/dist/_vendor/schema/index.js +2 -2
  119. package/dist/_vendor/schema/planner.d.ts +19 -2
  120. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  121. package/dist/_vendor/schema/planner.js +79 -3
  122. package/dist/_vendor/schema/planner.js.map +1 -1
  123. package/dist/_vendor/schema/validators.d.ts +141 -121
  124. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  125. package/dist/_vendor/schema/validators.js +300 -42
  126. package/dist/_vendor/schema/validators.js.map +1 -1
  127. package/dist/_vendor/svelte/index.d.ts +47 -19
  128. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  129. package/dist/_vendor/svelte/index.js +250 -20
  130. package/dist/_vendor/svelte/index.js.map +1 -1
  131. package/dist/components.d.ts +2 -0
  132. package/dist/components.js +2 -0
  133. package/dist/index.d.ts +3 -2
  134. package/dist/index.js +2 -1
  135. package/package.json +8 -3
@@ -2,16 +2,17 @@ import { createContext, useContext, useEffect, useMemo, useState } from "react";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  //#region src/index.tsx
4
4
  /**
5
- * Pass `"skip"` as the args argument to `useQuery` to suppress the subscription
6
- * entirely and return `undefined` without contacting the runtime.
5
+ * Pass `"skip"` as the args argument to `useQuery`, `useQueryState`,
6
+ * `useQueries`, or `usePaginatedQuery` to suppress the subscription entirely.
7
7
  */
8
8
  const skip = "skip";
9
+ const defaultRuntimeStatus = {
10
+ kind: "starting",
11
+ reason: "booting"
12
+ };
9
13
  const SyncoreContext = createContext(null);
10
14
  /**
11
15
  * 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
  */
16
17
  function SyncoreProvider({ client, children }) {
17
18
  return /* @__PURE__ */ jsx(SyncoreContext.Provider, {
@@ -21,8 +22,6 @@ function SyncoreProvider({ client, children }) {
21
22
  }
22
23
  /**
23
24
  * Read the active Syncore client from React context.
24
- *
25
- * Throws if used outside of {@link SyncoreProvider}.
26
25
  */
27
26
  function useSyncore() {
28
27
  const client = useContext(SyncoreContext);
@@ -30,15 +29,40 @@ function useSyncore() {
30
29
  return client;
31
30
  }
32
31
  /**
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.
32
+ * Subscribe to the active Syncore client's runtime lifecycle status.
33
+ */
34
+ function useSyncoreStatus() {
35
+ const client = useSyncore();
36
+ const watch = useMemo(() => client.watchRuntimeStatus(), [client]);
37
+ const [status, setStatus] = useState(() => readRuntimeStatusSnapshot(watch));
38
+ useEffect(() => {
39
+ const sync = () => {
40
+ setStatus(readRuntimeStatusSnapshot(watch));
41
+ };
42
+ sync();
43
+ return watch.onUpdate(sync);
44
+ }, [watch]);
45
+ useEffect(() => () => {
46
+ watch.dispose?.();
47
+ }, [watch]);
48
+ return status;
49
+ }
50
+ /**
51
+ * Load a reactive Syncore query and return only the data for the common case.
38
52
  */
39
53
  function useQuery(reference, ...args) {
54
+ const state = useQueryState(reference, ...args);
55
+ if (state.error) throw state.error;
56
+ return state.data;
57
+ }
58
+ /**
59
+ * Load a reactive Syncore query and keep the full local state.
60
+ */
61
+ function useQueryState(reference, ...args) {
40
62
  const isSkipped = args[0] === skip;
41
- const watch = useManagedQueryWatch(useSyncore(), reference, isSkipped ? void 0 : normalizeOptionalArgs(args), isSkipped);
63
+ const client = useSyncore();
64
+ const runtimeStatus = useSyncoreStatus();
65
+ const watch = useManagedQueryWatch(client, reference, isSkipped ? void 0 : normalizeOptionalArgs(args), isSkipped);
42
66
  const [snapshot, setSnapshot] = useState(() => isSkipped ? noOpSnapshot : readWatchSnapshot(watch));
43
67
  useEffect(() => {
44
68
  if (isSkipped) {
@@ -51,18 +75,8 @@ function useQuery(reference, ...args) {
51
75
  sync();
52
76
  return watch.onUpdate(sync);
53
77
  }, [watch, isSkipped]);
54
- if (snapshot.error) throw snapshot.error;
55
- return snapshot.result;
78
+ return toQueryState(snapshot, runtimeStatus, isSkipped);
56
79
  }
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
80
  /**
67
81
  * Construct a stable function that executes a Syncore mutation.
68
82
  */
@@ -78,51 +92,162 @@ function useAction(reference) {
78
92
  return (...args) => client.action(reference, normalizeOptionalArgs(args));
79
93
  }
80
94
  /**
81
- * Load several Syncore queries at once using explicit keys.
95
+ * Load a keyed set of Syncore queries at once with per-entry state.
82
96
  */
83
97
  function useQueries(entries) {
84
98
  const client = useSyncore();
85
- const entriesKey = stableStringify(entries.map((entry) => ({
86
- key: entry.key,
87
- referenceName: entry.reference.name,
88
- args: entry.args ?? {}
99
+ const runtimeStatus = useSyncoreStatus();
100
+ const entriesKey = stableStringify(Object.entries(entries).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => ({
101
+ key,
102
+ referenceName: entry.query.name,
103
+ skipped: entry.args === skip,
104
+ args: entry.args === "skip" ? {} : normalizeOptionalArgs([entry.args ?? {}])
89
105
  })));
90
106
  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]);
107
+ const [observer] = useState(() => new ReactQueriesObserver(client));
108
+ const [, setVersion] = useState(0);
109
+ if (observer.client !== client) observer.replaceClient(client);
110
+ useEffect(() => () => observer.destroy(), [observer]);
102
111
  useEffect(() => {
103
- const sync = () => {
104
- setSnapshot(readQueriesSnapshot(watches));
112
+ observer.setEntries(normalizedEntries);
113
+ setVersion((value) => value + 1);
114
+ return observer.subscribe(() => {
115
+ setVersion((value) => value + 1);
116
+ });
117
+ }, [normalizedEntries, observer]);
118
+ const snapshot = observer.getSnapshot(normalizedEntries);
119
+ return useMemo(() => {
120
+ return Object.fromEntries(normalizedEntries.map((entry) => [entry.key, toQueryState(snapshot[entry.key] ?? noOpSnapshot, runtimeStatus, entry.skipped)]));
121
+ }, [
122
+ normalizedEntries,
123
+ runtimeStatus,
124
+ snapshot
125
+ ]);
126
+ }
127
+ /**
128
+ * Load a paginated Syncore query as a growing reactive list.
129
+ */
130
+ function usePaginatedQuery(reference, args, options) {
131
+ if (typeof options.initialNumItems !== "number" || options.initialNumItems <= 0) throw new Error(`options.initialNumItems must be a positive number. Received ${String(options.initialNumItems)}.`);
132
+ const runtimeStatus = useSyncoreStatus();
133
+ const isSkipped = args === skip;
134
+ const normalizedArgs = isSkipped ? {} : args ?? {};
135
+ const requestKey = stableStringify({
136
+ referenceName: reference.name,
137
+ args: normalizedArgs,
138
+ initialNumItems: options.initialNumItems,
139
+ skipped: isSkipped
140
+ });
141
+ const createInitialState = useMemo(() => () => ({
142
+ requestKey,
143
+ nextPageKey: 1,
144
+ pages: isSkipped ? [] : [{
145
+ key: "0",
146
+ cursor: null,
147
+ numItems: options.initialNumItems
148
+ }]
149
+ }), [
150
+ isSkipped,
151
+ options.initialNumItems,
152
+ requestKey
153
+ ]);
154
+ const [state, setState] = useState(createInitialState);
155
+ let currentState = state;
156
+ if (currentState.requestKey !== requestKey) {
157
+ currentState = createInitialState();
158
+ setState(currentState);
159
+ }
160
+ const pageStates = useQueries(useMemo(() => {
161
+ const requests = {};
162
+ for (const page of currentState.pages) requests[page.key] = {
163
+ query: reference,
164
+ args: {
165
+ ...normalizedArgs,
166
+ paginationOpts: {
167
+ cursor: page.cursor,
168
+ numItems: page.numItems
169
+ }
170
+ }
105
171
  };
106
- sync();
107
- const cleanups = watches.map((entry) => entry.watch.onUpdate(sync));
108
- return () => {
109
- for (const cleanup of cleanups) cleanup();
172
+ return requests;
173
+ }, [
174
+ currentState.pages,
175
+ normalizedArgs,
176
+ reference
177
+ ]));
178
+ const derived = useMemo(() => {
179
+ const pages = [];
180
+ let error;
181
+ for (const page of currentState.pages) {
182
+ const pageState = pageStates[page.key];
183
+ if (!pageState || pageState.status === "loading") break;
184
+ if (pageState.status === "error") {
185
+ error = pageState.error;
186
+ break;
187
+ }
188
+ if (pageState.data) pages.push(pageState.data);
189
+ }
190
+ const results = pages.flatMap((page) => page.page);
191
+ const lastLoadedPage = pages.at(-1);
192
+ const lastRequestedKey = currentState.pages.at(-1)?.key;
193
+ const lastRequestedState = lastRequestedKey ? pageStates[lastRequestedKey] : void 0;
194
+ const isLoading = !isSkipped && pages.length === 0 && !error;
195
+ const isLoadingMore = currentState.pages.length > pages.length || !!lastRequestedState && lastRequestedState.status === "loading" && pages.length > 0;
196
+ const hasMore = !!lastLoadedPage && !lastLoadedPage.isDone;
197
+ const status = error ? "error" : isSkipped ? "ready" : isLoading ? "loading" : isLoadingMore ? "loadingMore" : hasMore ? "ready" : "exhausted";
198
+ return {
199
+ pages,
200
+ results,
201
+ error,
202
+ isLoading,
203
+ isLoadingMore,
204
+ hasMore,
205
+ cursor: lastLoadedPage?.cursor ?? null,
206
+ status
110
207
  };
111
- }, [watches]);
112
- return snapshot;
208
+ }, [
209
+ currentState.pages,
210
+ isSkipped,
211
+ pageStates
212
+ ]);
213
+ return {
214
+ ...derived,
215
+ runtimeStatus,
216
+ loadMore(numItems = options.initialNumItems) {
217
+ if (isSkipped || derived.error || derived.isLoadingMore || !derived.hasMore || !derived.cursor) return;
218
+ setState((previous) => ({
219
+ ...previous,
220
+ nextPageKey: previous.nextPageKey + 1,
221
+ pages: [...previous.pages, {
222
+ key: String(previous.nextPageKey),
223
+ cursor: derived.cursor,
224
+ numItems
225
+ }]
226
+ }));
227
+ }
228
+ };
113
229
  }
114
- function useManagedQueryWatch(client, reference, args, isSkipped) {
115
- const argsKey = isSkipped ? "skip" : stableStringify(args ?? {});
230
+ const noOpSnapshot = {
231
+ data: void 0,
232
+ error: void 0
233
+ };
234
+ const noOpWatch = {
235
+ onUpdate: () => () => void 0,
236
+ localQueryResult: () => void 0,
237
+ localQueryError: () => void 0
238
+ };
239
+ function useManagedQueryWatch(client, reference, args, isSkipped = false) {
240
+ const argsKey = isSkipped ? skip : stableStringify(args ?? {});
116
241
  const normalizedArgs = useMemo(() => isSkipped ? void 0 : JSON.parse(argsKey), [argsKey, isSkipped]);
117
242
  const watch = useMemo(() => isSkipped ? noOpWatch : client.watchQuery(reference, normalizedArgs), [
118
243
  client,
244
+ isSkipped,
119
245
  normalizedArgs,
120
- reference,
121
- isSkipped
246
+ reference
122
247
  ]);
123
248
  useEffect(() => () => {
124
249
  if (!isSkipped) watch.dispose?.();
125
- }, [watch, isSkipped]);
250
+ }, [isSkipped, watch]);
126
251
  return watch;
127
252
  }
128
253
  function normalizeOptionalArgs(args) {
@@ -130,12 +255,36 @@ function normalizeOptionalArgs(args) {
130
255
  }
131
256
  function readWatchSnapshot(watch) {
132
257
  return {
133
- result: watch.localQueryResult(),
258
+ data: watch.localQueryResult(),
134
259
  error: watch.localQueryError()
135
260
  };
136
261
  }
137
- function readQueriesSnapshot(watches) {
138
- return Object.fromEntries(watches.map((entry) => [entry.key, entry.watch.localQueryResult()]));
262
+ function readQueriesSnapshot(records) {
263
+ return Object.fromEntries(records.map((entry) => [entry.key, entry.snapshot]));
264
+ }
265
+ function readRuntimeStatusSnapshot(watch) {
266
+ return watch.localQueryResult() ?? defaultRuntimeStatus;
267
+ }
268
+ function toQueryState(snapshot, runtimeStatus, isSkipped) {
269
+ if (isSkipped) return {
270
+ data: void 0,
271
+ error: void 0,
272
+ status: "skipped",
273
+ runtimeStatus,
274
+ isLoading: false,
275
+ isError: false,
276
+ isReady: false
277
+ };
278
+ const status = snapshot.error !== void 0 ? "error" : snapshot.data === void 0 ? "loading" : "success";
279
+ return {
280
+ data: snapshot.data,
281
+ error: snapshot.error,
282
+ status,
283
+ runtimeStatus,
284
+ isLoading: status === "loading",
285
+ isError: status === "error",
286
+ isReady: status === "success"
287
+ };
139
288
  }
140
289
  function stableStringify(value) {
141
290
  return JSON.stringify(sortValue(value));
@@ -145,7 +294,80 @@ function sortValue(value) {
145
294
  if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
146
295
  return value;
147
296
  }
297
+ var ReactQueriesObserver = class {
298
+ client;
299
+ listeners = /* @__PURE__ */ new Set();
300
+ records = /* @__PURE__ */ new Map();
301
+ constructor(client) {
302
+ this.client = client;
303
+ }
304
+ replaceClient(client) {
305
+ this.destroy();
306
+ this.client = client;
307
+ }
308
+ setEntries(entries) {
309
+ const activeKeys = new Set(entries.map((entry) => entry.key));
310
+ for (const entry of entries) {
311
+ const requestKey = `${entry.referenceName}:${stableStringify(entry.args)}:${String(entry.skipped)}`;
312
+ const current = this.records.get(entry.key);
313
+ if (current?.requestKey === requestKey) continue;
314
+ current?.unsubscribe();
315
+ current?.watch?.dispose?.();
316
+ if (entry.skipped) {
317
+ this.records.set(entry.key, {
318
+ requestKey,
319
+ snapshot: noOpSnapshot,
320
+ unsubscribe: () => void 0
321
+ });
322
+ continue;
323
+ }
324
+ const watch = this.client.watchQuery({
325
+ kind: "query",
326
+ name: entry.referenceName
327
+ }, entry.args);
328
+ const record = {
329
+ requestKey,
330
+ snapshot: readWatchSnapshot(watch),
331
+ unsubscribe: () => void 0,
332
+ watch
333
+ };
334
+ record.unsubscribe = watch.onUpdate(() => {
335
+ record.snapshot = readWatchSnapshot(watch);
336
+ this.notify();
337
+ });
338
+ this.records.set(entry.key, record);
339
+ }
340
+ for (const [key, record] of this.records.entries()) {
341
+ if (activeKeys.has(key)) continue;
342
+ record.unsubscribe();
343
+ record.watch?.dispose?.();
344
+ this.records.delete(key);
345
+ }
346
+ }
347
+ getSnapshot(entries) {
348
+ return readQueriesSnapshot(entries.map((entry) => ({
349
+ key: entry.key,
350
+ snapshot: this.records.get(entry.key)?.snapshot ?? noOpSnapshot
351
+ })));
352
+ }
353
+ subscribe(listener) {
354
+ this.listeners.add(listener);
355
+ return () => {
356
+ this.listeners.delete(listener);
357
+ };
358
+ }
359
+ destroy() {
360
+ for (const record of this.records.values()) {
361
+ record.unsubscribe();
362
+ record.watch?.dispose?.();
363
+ }
364
+ this.records.clear();
365
+ }
366
+ notify() {
367
+ for (const listener of this.listeners) listener();
368
+ }
369
+ };
148
370
  //#endregion
149
- export { SyncoreProvider, skip, useAction, useMutation, useQueries, useQuery, useSyncore };
371
+ export { SyncoreProvider, skip, useAction, useMutation, usePaginatedQuery, useQueries, useQuery, useQueryState, useSyncore, useSyncoreStatus };
150
372
 
151
373
  //# sourceMappingURL=index.js.map
@@ -1 +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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"}
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 FunctionArgs,\n FunctionReference,\n FunctionResult,\n PaginationOptions,\n PaginationResult,\n SyncoreClient,\n SyncorePaginatedQueryStatus,\n SyncoreQueryState,\n SyncoreRuntimeStatus,\n SyncoreWatch,\n UsePaginatedQueryResult\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\ntype QueryRequestInput<\n TReference extends FunctionReference<\"query\"> = FunctionReference<\"query\">\n> = Record<never, never> extends FunctionArgs<TReference>\n ? {\n query: TReference;\n args?: FunctionArgs<TReference> | Skip;\n }\n : {\n query: TReference;\n args: FunctionArgs<TReference> | Skip;\n };\n\ntype QueriesRequestInput = Record<string, QueryRequestInput>;\n\ntype QueryStateForEntry<TEntry> = TEntry extends QueryRequestInput<\n infer TReference\n>\n ? SyncoreQueryState<FunctionResult<TReference>>\n : never;\n\nexport type UseQueriesResult<TEntries extends QueriesRequestInput> = {\n [TKey in keyof TEntries]: QueryStateForEntry<TEntries[TKey]>;\n};\n\ntype PaginatedQueryReference = FunctionReference<\n \"query\",\n Record<string, unknown>,\n PaginationResult<unknown>\n>;\n\ntype PaginatedQueryArgs<TReference extends FunctionReference<\"query\">> =\n FunctionArgs<TReference> extends { paginationOpts: PaginationOptions }\n ? Omit<FunctionArgs<TReference>, \"paginationOpts\">\n : never;\n\ntype PaginatedQueryItem<TReference extends FunctionReference<\"query\">> =\n FunctionResult<TReference> extends PaginationResult<infer TItem>\n ? TItem\n : never;\n\ntype QuerySnapshot<TResult> = {\n data: TResult | undefined;\n error: Error | undefined;\n};\n\ntype NormalizedQueryEntry = {\n key: string;\n referenceName: string;\n args: Record<string, unknown>;\n skipped: boolean;\n};\n\ntype PaginatedQueryInternalState = {\n requestKey: string;\n nextPageKey: number;\n pages: Array<{\n key: string;\n cursor: string | null;\n numItems: number;\n }>;\n};\n\ntype QueryObserverRecord = {\n requestKey: string;\n snapshot: QuerySnapshot<unknown>;\n unsubscribe: () => void;\n watch?: ManagedSyncoreWatch<unknown>;\n};\n\n/**\n * Pass `\"skip\"` as the args argument to `useQuery`, `useQueryState`,\n * `useQueries`, or `usePaginatedQuery` to suppress the subscription entirely.\n */\nexport const skip = \"skip\" as const;\ntype Skip = typeof skip;\n\nconst defaultRuntimeStatus: SyncoreRuntimeStatus = {\n kind: \"starting\",\n reason: \"booting\"\n};\n\nconst SyncoreContext = createContext<SyncoreClient | null>(null);\n\n/**\n * Provide a Syncore client to React descendants.\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 */\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 * Subscribe to the active Syncore client's runtime lifecycle status.\n */\nexport function useSyncoreStatus(): SyncoreRuntimeStatus {\n const client = useSyncore();\n const watch = useMemo(\n () => client.watchRuntimeStatus() as ManagedSyncoreWatch<SyncoreRuntimeStatus>,\n [client]\n );\n const [status, setStatus] = useState<SyncoreRuntimeStatus>(() =>\n readRuntimeStatusSnapshot(watch)\n );\n\n useEffect(() => {\n const sync = () => {\n setStatus(readRuntimeStatusSnapshot(watch));\n };\n sync();\n return watch.onUpdate(sync);\n }, [watch]);\n\n useEffect(\n () => () => {\n watch.dispose?.();\n },\n [watch]\n );\n\n return status;\n}\n\n/**\n * Load a reactive Syncore query and return only the data for the common case.\n */\nexport function useQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs> | [Skip]\n): TResult | undefined {\n const state = useQueryState(reference, ...(args as OptionalArgsTuple<TArgs> | [Skip]));\n if (state.error) {\n throw state.error;\n }\n return state.data;\n}\n\n/**\n * Load a reactive Syncore query and keep the full local state.\n */\nexport function useQueryState<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs> | [Skip]\n): SyncoreQueryState<TResult> {\n const isSkipped = args[0] === skip;\n const client = useSyncore();\n const runtimeStatus = useSyncoreStatus();\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<QuerySnapshot<TResult>>(() =>\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 return toQueryState(snapshot, runtimeStatus, isSkipped);\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 a keyed set of Syncore queries at once with per-entry state.\n */\nexport function useQueries<TEntries extends QueriesRequestInput>(\n entries: TEntries\n): UseQueriesResult<TEntries> {\n const client = useSyncore();\n const runtimeStatus = useSyncoreStatus();\n const entriesKey = stableStringify(\n Object.entries(entries)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, entry]) => ({\n key,\n referenceName: entry.query.name,\n skipped: entry.args === skip,\n args:\n entry.args === skip\n ? {}\n : normalizeOptionalArgs([entry.args ?? {}] as [] | [unknown])\n }))\n );\n const normalizedEntries = useMemo(\n () => JSON.parse(entriesKey) as NormalizedQueryEntry[],\n [entriesKey]\n );\n const [observer] = useState(() => new ReactQueriesObserver(client));\n const [, setVersion] = useState(0);\n\n if (observer.client !== client) {\n observer.replaceClient(client);\n }\n\n useEffect(() => () => observer.destroy(), [observer]);\n\n useEffect(() => {\n observer.setEntries(normalizedEntries);\n setVersion((value) => value + 1);\n return observer.subscribe(() => {\n setVersion((value) => value + 1);\n });\n }, [normalizedEntries, observer]);\n\n const snapshot = observer.getSnapshot(normalizedEntries);\n\n return useMemo(() => {\n return Object.fromEntries(\n normalizedEntries.map((entry) => [\n entry.key,\n toQueryState(\n snapshot[entry.key] ?? noOpSnapshot,\n runtimeStatus,\n entry.skipped\n )\n ])\n ) as UseQueriesResult<TEntries>;\n }, [normalizedEntries, runtimeStatus, snapshot]);\n}\n\n/**\n * Load a paginated Syncore query as a growing reactive list.\n */\nexport function usePaginatedQuery<TReference extends PaginatedQueryReference>(\n reference: TReference,\n args: PaginatedQueryArgs<TReference> | Skip,\n options: {\n initialNumItems: number;\n }\n): UsePaginatedQueryResult<PaginatedQueryItem<TReference>> {\n if (\n typeof options.initialNumItems !== \"number\" ||\n options.initialNumItems <= 0\n ) {\n throw new Error(\n `options.initialNumItems must be a positive number. Received ${String(\n options.initialNumItems\n )}.`\n );\n }\n\n const runtimeStatus = useSyncoreStatus();\n const isSkipped = args === skip;\n const normalizedArgs = isSkipped ? {} : (args ?? {});\n const requestKey = stableStringify({\n referenceName: reference.name,\n args: normalizedArgs,\n initialNumItems: options.initialNumItems,\n skipped: isSkipped\n });\n const createInitialState = useMemo(\n () => () =>\n ({\n requestKey,\n nextPageKey: 1,\n pages: isSkipped\n ? []\n : [\n {\n key: \"0\",\n cursor: null,\n numItems: options.initialNumItems\n }\n ]\n }) satisfies PaginatedQueryInternalState,\n [isSkipped, options.initialNumItems, requestKey]\n );\n const [state, setState] = useState<PaginatedQueryInternalState>(\n createInitialState\n );\n\n let currentState = state;\n if (currentState.requestKey !== requestKey) {\n currentState = createInitialState();\n setState(currentState);\n }\n\n const pageQueries = useMemo(() => {\n const requests: Record<string, QueryRequestInput> = {};\n for (const page of currentState.pages) {\n requests[page.key] = {\n query: reference,\n args: {\n ...(normalizedArgs as Record<string, unknown>),\n paginationOpts: {\n cursor: page.cursor,\n numItems: page.numItems\n }\n }\n };\n }\n return requests;\n }, [currentState.pages, normalizedArgs, reference]);\n const pageStates = useQueries(pageQueries);\n\n const derived = useMemo(() => {\n const pages: Array<PaginationResult<PaginatedQueryItem<TReference>>> = [];\n let error: Error | undefined;\n\n for (const page of currentState.pages) {\n const pageState =\n pageStates[page.key as keyof typeof pageStates] as\n | SyncoreQueryState<PaginationResult<PaginatedQueryItem<TReference>>>\n | undefined;\n if (!pageState || pageState.status === \"loading\") {\n break;\n }\n if (pageState.status === \"error\") {\n error = pageState.error;\n break;\n }\n if (pageState.data) {\n pages.push(pageState.data);\n }\n }\n\n const results = pages.flatMap((page) => page.page);\n const lastLoadedPage = pages.at(-1);\n const lastRequestedKey = currentState.pages.at(-1)?.key;\n const lastRequestedState = lastRequestedKey\n ? (pageStates[lastRequestedKey as keyof typeof pageStates] as\n | SyncoreQueryState<PaginationResult<PaginatedQueryItem<TReference>>>\n | undefined)\n : undefined;\n const isLoading = !isSkipped && pages.length === 0 && !error;\n const isLoadingMore =\n currentState.pages.length > pages.length ||\n (!!lastRequestedState && lastRequestedState.status === \"loading\" && pages.length > 0);\n const hasMore = !!lastLoadedPage && !lastLoadedPage.isDone;\n const status: SyncorePaginatedQueryStatus = error\n ? \"error\"\n : isSkipped\n ? \"ready\"\n : isLoading\n ? \"loading\"\n : isLoadingMore\n ? \"loadingMore\"\n : hasMore\n ? \"ready\"\n : \"exhausted\";\n\n return {\n pages,\n results,\n error,\n isLoading,\n isLoadingMore,\n hasMore,\n cursor: lastLoadedPage?.cursor ?? null,\n status\n };\n }, [currentState.pages, isSkipped, pageStates]);\n\n return {\n ...derived,\n runtimeStatus,\n loadMore(numItems = options.initialNumItems) {\n if (\n isSkipped ||\n derived.error ||\n derived.isLoadingMore ||\n !derived.hasMore ||\n !derived.cursor\n ) {\n return;\n }\n\n setState((previous) => ({\n ...previous,\n nextPageKey: previous.nextPageKey + 1,\n pages: [\n ...previous.pages,\n {\n key: String(previous.nextPageKey),\n cursor: derived.cursor,\n numItems\n }\n ]\n }));\n }\n };\n}\n\nconst noOpSnapshot: QuerySnapshot<never> = {\n data: undefined,\n error: undefined\n};\n\nconst noOpWatch: ManagedSyncoreWatch<never> = {\n onUpdate: () => () => undefined,\n localQueryResult: () => undefined,\n localQueryError: () => undefined\n};\n\nfunction useManagedQueryWatch<TArgs, TResult>(\n client: SyncoreClient,\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args?: TArgs,\n isSkipped = false\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 as TArgs\n ) as ManagedSyncoreWatch<TResult>),\n [client, isSkipped, normalizedArgs, reference]\n );\n\n useEffect(\n () => () => {\n if (!isSkipped) {\n watch.dispose?.();\n }\n },\n [isSkipped, watch]\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>(\n watch: SyncoreWatch<TResult>\n): QuerySnapshot<TResult> {\n return {\n data: watch.localQueryResult(),\n error: watch.localQueryError()\n };\n}\n\nfunction readQueriesSnapshot(\n records: Array<{\n key: string;\n snapshot: QuerySnapshot<unknown>;\n }>\n): Record<string, QuerySnapshot<unknown>> {\n return Object.fromEntries(\n records.map((entry) => [entry.key, entry.snapshot])\n );\n}\n\nfunction readRuntimeStatusSnapshot(\n watch: SyncoreWatch<SyncoreRuntimeStatus>\n): SyncoreRuntimeStatus {\n return watch.localQueryResult() ?? defaultRuntimeStatus;\n}\n\nfunction toQueryState<TResult>(\n snapshot: QuerySnapshot<TResult>,\n runtimeStatus: SyncoreRuntimeStatus,\n isSkipped: boolean\n): SyncoreQueryState<TResult> {\n if (isSkipped) {\n return {\n data: undefined,\n error: undefined,\n status: \"skipped\",\n runtimeStatus,\n isLoading: false,\n isError: false,\n isReady: false\n };\n }\n\n const status =\n snapshot.error !== undefined\n ? \"error\"\n : snapshot.data === undefined\n ? \"loading\"\n : \"success\";\n\n return {\n data: snapshot.data,\n error: snapshot.error,\n status,\n runtimeStatus,\n isLoading: status === \"loading\",\n isError: status === \"error\",\n isReady: status === \"success\"\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\nclass ReactQueriesObserver {\n readonly client: SyncoreClient;\n private readonly listeners = new Set<() => void>();\n private readonly records = new Map<string, QueryObserverRecord>();\n\n constructor(client: SyncoreClient) {\n this.client = client;\n }\n\n replaceClient(client: SyncoreClient): void {\n this.destroy();\n (this as { client: SyncoreClient }).client = client;\n }\n\n setEntries(entries: NormalizedQueryEntry[]): void {\n const activeKeys = new Set(entries.map((entry) => entry.key));\n\n for (const entry of entries) {\n const requestKey = `${entry.referenceName}:${stableStringify(entry.args)}:${String(\n entry.skipped\n )}`;\n const current = this.records.get(entry.key);\n if (current?.requestKey === requestKey) {\n continue;\n }\n\n current?.unsubscribe();\n current?.watch?.dispose?.();\n\n if (entry.skipped) {\n this.records.set(entry.key, {\n requestKey,\n snapshot: noOpSnapshot,\n unsubscribe: () => undefined\n });\n continue;\n }\n\n const watch = this.client.watchQuery(\n { kind: \"query\", name: entry.referenceName },\n entry.args\n ) as ManagedSyncoreWatch<unknown>;\n const record: QueryObserverRecord = {\n requestKey,\n snapshot: readWatchSnapshot(watch),\n unsubscribe: () => undefined,\n watch\n };\n record.unsubscribe = watch.onUpdate(() => {\n record.snapshot = readWatchSnapshot(watch);\n this.notify();\n });\n this.records.set(entry.key, record);\n }\n\n for (const [key, record] of this.records.entries()) {\n if (activeKeys.has(key)) {\n continue;\n }\n record.unsubscribe();\n record.watch?.dispose?.();\n this.records.delete(key);\n }\n }\n\n getSnapshot(entries: NormalizedQueryEntry[]): Record<string, QuerySnapshot<unknown>> {\n return readQueriesSnapshot(\n entries.map((entry) => ({\n key: entry.key,\n snapshot: this.records.get(entry.key)?.snapshot ?? noOpSnapshot\n }))\n );\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n destroy(): void {\n for (const record of this.records.values()) {\n record.unsubscribe();\n record.watch?.dispose?.();\n }\n this.records.clear();\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { Infer, ObjectValidator, ObjectValidatorShape, Validator } from "./validators.js";
1
+ import { FieldPaths, Infer, InferStorage, ObjectValidator, ObjectValidatorShape, Validator, ValidatorDescription } from "./validators.js";
2
2
 
3
3
  //#region src/definition.d.ts
4
+ type Expand<T> = { [TKey in keyof T]: T[TKey] } & {};
4
5
  interface IndexDefinition {
5
6
  name: string;
6
7
  fields: string[];
@@ -12,87 +13,71 @@ interface SearchIndexDefinition {
12
13
  }
13
14
  interface TableDefinitionOptions {
14
15
  tableName?: string;
16
+ componentPath?: string;
17
+ componentName?: string;
15
18
  }
16
19
  interface TableDocumentSystemFields {
17
20
  _id: string;
18
21
  _creationTime: number;
19
22
  }
20
- /**
21
- * Describes a Syncore table and its indexes.
22
- *
23
- * Create tables with {@link defineTable} and then chain index helpers to make
24
- * queries faster and more expressive.
25
- */
26
- declare class TableDefinition<TValidator extends Validator<unknown>> {
23
+ type GenericTableIndexes = Record<string, readonly string[]>;
24
+ type GenericTableSearchIndexes = Record<string, {
25
+ searchField: string;
26
+ filterFields: string;
27
+ }>;
28
+ declare class TableDefinition<TValidator extends Validator<Record<string, unknown>, Record<string, unknown>, string>, TIndexes = Record<never, never>, TSearchIndexes = Record<never, never>> {
27
29
  readonly validator: TValidator;
28
30
  readonly indexes: IndexDefinition[];
29
31
  readonly searchIndexes: SearchIndexDefinition[];
30
32
  readonly options: TableDefinitionOptions;
33
+ readonly document: Infer<TValidator>;
34
+ readonly storageDocument: InferStorage<TValidator>;
35
+ readonly fieldPaths: FieldPaths<TValidator>;
36
+ readonly indexesByName: TIndexes;
37
+ readonly searchIndexesByName: TSearchIndexes;
31
38
  constructor(validator: TValidator, options?: TableDefinitionOptions);
32
- /**
33
- * Add a named index for querying a table by one or more fields.
34
- *
35
- * @param name - The index name used from `ctx.db.query(...).withIndex(...)`.
36
- * @param fields - The fields that participate in the index.
37
- * @returns The same table definition for chaining.
38
- */
39
- index(name: string, fields: string[]): this;
40
- /**
41
- * Add a search index for text search.
42
- *
43
- * @param name - The search index name used from `withSearchIndex(...)`.
44
- * @param config - The indexed search field and optional filter fields.
45
- * @returns The same table definition for chaining.
46
- */
47
- searchIndex(name: string, config: {
48
- searchField: string;
49
- filterFields?: string[];
50
- }): this;
39
+ index<const TIndexName extends string, TFirstField extends FieldPaths<TValidator>, TRestFields extends FieldPaths<TValidator>[]>(name: TIndexName, fields: [TFirstField, ...TRestFields]): TableDefinition<TValidator, Expand<TIndexes & Record<TIndexName, readonly [TFirstField, ...TRestFields]>>, TSearchIndexes>;
40
+ searchIndex<const TIndexName extends string, TSearchField extends FieldPaths<TValidator>, TFilterField extends FieldPaths<TValidator> = never>(name: TIndexName, config: {
41
+ searchField: TSearchField;
42
+ filterFields?: TFilterField[];
43
+ }): TableDefinition<TValidator, TIndexes, Expand<TSearchIndexes & Record<TIndexName, {
44
+ searchField: TSearchField;
45
+ filterFields: TFilterField;
46
+ }>>>;
47
+ parse(value: unknown): Infer<TValidator>;
48
+ serialize(value: Infer<TValidator>): InferStorage<TValidator>;
49
+ deserialize(value: unknown): Infer<TValidator>;
50
+ parseAndSerialize(value: unknown): InferStorage<TValidator>;
51
+ describe(): ValidatorDescription;
51
52
  }
52
- type AnyTableDefinition = TableDefinition<Validator<unknown>>;
53
+ type AnyTableDefinition = TableDefinition<Validator<Record<string, unknown>, Record<string, unknown>, string>, GenericTableIndexes, GenericTableSearchIndexes>;
53
54
  type InferDocument<TTable extends AnyTableDefinition> = Infer<TTable["validator"]> & TableDocumentSystemFields;
54
- type InferTableInput<TTable extends AnyTableDefinition> = Omit<InferDocument<TTable>, keyof TableDocumentSystemFields>;
55
- /**
56
- * Define a table in a Syncore schema.
57
- *
58
- * Pass an object of validators describing the document fields stored in the
59
- * table. Chain `.index(...)` or `.searchIndex(...)` to add query helpers.
60
- *
61
- * @example
62
- * ```ts
63
- * const tasks = defineTable({
64
- * text: v.string(),
65
- * done: v.boolean()
66
- * }).index("by_done", ["done"]);
67
- * ```
68
- */
69
- declare function defineTable<TShape extends ObjectValidatorShape>(validator: TShape): TableDefinition<ObjectValidator<TShape>>;
70
- declare function defineTable<TValidator extends Validator<unknown>>(validator: TValidator): TableDefinition<TValidator>;
55
+ type InferTableInput<TTable extends AnyTableDefinition> = Infer<TTable["validator"]>;
56
+ type TableFieldPaths<TTable> = TTable extends TableDefinition<infer TValidator, unknown, unknown> ? FieldPaths<TValidator> : never;
57
+ type TableIndexes<TTable> = TTable extends TableDefinition<Validator<Record<string, unknown>, Record<string, unknown>, string>, infer TIndexes, unknown> ? TIndexes : never;
58
+ type TableSearchIndexes<TTable> = TTable extends TableDefinition<Validator<Record<string, unknown>, Record<string, unknown>, string>, unknown, infer TSearchIndexes> ? TSearchIndexes : never;
59
+ type TableIndexNames<TTable> = Extract<keyof TableIndexes<TTable>, string>;
60
+ type TableSearchIndexNames<TTable> = Extract<keyof TableSearchIndexes<TTable>, string>;
61
+ type TableIndexFields<TTable, TIndexName extends TableIndexNames<TTable>> = TableIndexes<TTable>[TIndexName];
62
+ type TableSearchIndexConfig<TTable, TIndexName extends TableSearchIndexNames<TTable>> = TableSearchIndexes<TTable>[TIndexName];
63
+ type TableFieldDefinitionSummary = {
64
+ name: string;
65
+ validator: ReturnType<AnyTableDefinition["describe"]>;
66
+ storage: ReturnType<AnyTableDefinition["describe"]>;
67
+ optional: boolean;
68
+ };
69
+ declare function defineTable<const TShape extends ObjectValidatorShape>(validator: TShape): TableDefinition<ObjectValidator<TShape>>;
70
+ declare function defineTable<TValidator extends Validator<Record<string, unknown>, Record<string, unknown>, string>>(validator: TValidator): TableDefinition<TValidator>;
71
71
  interface SyncoreSchemaDefinition {
72
72
  [tableName: string]: AnyTableDefinition;
73
73
  }
74
- declare class SyncoreSchema<TTables extends SyncoreSchemaDefinition> {
74
+ declare class SyncoreSchema<TTables> {
75
75
  readonly tables: TTables;
76
76
  constructor(tables: TTables);
77
77
  getTable<TTableName extends Extract<keyof TTables, string>>(tableName: TTableName): TTables[TTableName];
78
78
  tableNames(): Array<Extract<keyof TTables, string>>;
79
79
  }
80
- /**
81
- * Define the tables that make up your Syncore app.
82
- *
83
- * The returned schema is used by runtimes, code generation, and type inference.
84
- *
85
- * @example
86
- * ```ts
87
- * export default defineSchema({
88
- * tasks: defineTable({
89
- * text: v.string(),
90
- * done: v.boolean()
91
- * })
92
- * });
93
- * ```
94
- */
95
- declare function defineSchema<TTables extends SyncoreSchemaDefinition>(tables: TTables): SyncoreSchema<TTables>;
80
+ declare function defineSchema<const TTables extends SyncoreSchemaDefinition>(tables: TTables): SyncoreSchema<TTables>;
96
81
  //#endregion
97
- export { AnyTableDefinition, IndexDefinition, InferDocument, InferTableInput, SearchIndexDefinition, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, defineSchema, defineTable };
82
+ export { AnyTableDefinition, GenericTableIndexes, GenericTableSearchIndexes, IndexDefinition, InferDocument, InferTableInput, SearchIndexDefinition, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, TableFieldDefinitionSummary, TableFieldPaths, TableIndexFields, TableIndexNames, TableIndexes, TableSearchIndexConfig, TableSearchIndexNames, TableSearchIndexes, defineSchema, defineTable };
98
83
  //# sourceMappingURL=definition.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","names":[],"sources":["../src/definition.ts"],"mappings":";;;UAQiB,eAAA;EACf,IAAA;EACA,MAAA;AAAA;AAAA,UAGe,qBAAA;EACf,IAAA;EACA,WAAA;EACA,YAAA;AAAA;AAAA,UAGe,sBAAA;EACf,SAAA;AAAA;AAAA,UAGe,yBAAA;EACf,GAAA;EACA,aAAA;AAAA;;;;;AAFF;;cAWa,eAAA,oBAAmC,SAAA;EAAA,SAM5B,SAAA,EAAW,UAAA;EAAA,SALpB,OAAA,EAAS,eAAA;EAAA,SACT,aAAA,EAAe,qBAAA;EAAA,SACf,OAAA,EAAS,sBAAA;cAGA,SAAA,EAAW,UAAA,EAC3B,OAAA,GAAU,sBAAA;EAPkC;;;;;;;EAmB9C,KAAA,CAAM,IAAA,UAAc,MAAA;EAZc;;;;;;;EAwBlC,WAAA,CACE,IAAA,UACA,MAAA;IAAU,WAAA;IAAqB,YAAA;EAAA;AAAA;AAAA,KAWvB,kBAAA,GAAqB,eAAA,CAAgB,SAAA;AAAA,KAErC,aAAA,gBAA6B,kBAAA,IAAsB,KAAA,CAC7D,MAAA,iBAEA,yBAAA;AAAA,KAEU,eAAA,gBAA+B,kBAAA,IAAsB,IAAA,CAC/D,aAAA,CAAc,MAAA,SACR,yBAAA;;;;;;;;;;;;AATR;;;iBA0BgB,WAAA,gBAA2B,oBAAA,CAAA,CACzC,SAAA,EAAW,MAAA,GACV,eAAA,CAAgB,eAAA,CAAgB,MAAA;AAAA,iBACnB,WAAA,oBAA+B,SAAA,UAAA,CAC7C,SAAA,EAAW,UAAA,GACV,eAAA,CAAgB,UAAA;AAAA,UAOF,uBAAA;EAAA,CACd,SAAA,WAAoB,kBAAA;AAAA;AAAA,cAGV,aAAA,iBAA8B,uBAAA;EAAA,SACb,MAAA,EAAQ,OAAA;cAAR,MAAA,EAAQ,OAAA;EAEpC,QAAA,oBAA4B,OAAA,OAAc,OAAA,UAAA,CACxC,SAAA,EAAW,UAAA,GACV,OAAA,CAAQ,UAAA;EAQX,UAAA,CAAA,GAAc,KAAA,CAAM,OAAA,OAAc,OAAA;AAAA;;;;;;;;AAhDpC;;;;;;;;iBAoEgB,YAAA,iBAA6B,uBAAA,CAAA,CAC3C,MAAA,EAAQ,OAAA,GACP,aAAA,CAAc,OAAA"}
1
+ {"version":3,"file":"definition.d.ts","names":[],"sources":["../src/definition.ts"],"mappings":"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"}