@spooky-sync/client-solid2 0.0.1-canary.239 → 0.0.1-canary.241
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/AGENTS.md +1 -1
- package/QUICK_START.md +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +32 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +16 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/lib/__tests__/use-sync-activity.test.ts +34 -4
- package/src/lib/create-preload.ts +4 -3
- package/src/lib/use-sync-activity.ts +50 -37
package/AGENTS.md
CHANGED
|
@@ -45,7 +45,7 @@ Provider props are identical to client-solid: `config`, `fallback`, `preload` (a
|
|
|
45
45
|
- `isSettled()` — authoritative AND idle; gate windowed-list end detection on it. Not monotonic; latch it (a `createMemo(prev => prev || q.isSettled())`) for a verdict that must stick.
|
|
46
46
|
- Options: `enabled?: () => boolean`, `deregisterOnCleanup?: boolean`.
|
|
47
47
|
- **`createSubmission(fn)`** — button pending/error state around a mutation: `submit()`, `pending()`, `error()`, `result()`, `clearError()`. There is deliberately no `action()` / `createOptimisticStore` layer (see `src/lib/create-submission.ts`).
|
|
48
|
-
- **`useSyncActivity(options?)`** — `{ fetchingQueries, pendingMutations, isDownloading, isUploading }`. One subscription on the engine's aggregate fetch count plus the outbox depth;
|
|
48
|
+
- **`useSyncActivity(options?)`** — `{ fetchingQueries, pendingMutations, isDownloading, isUploading }`. One subscription on the engine's aggregate fetch count plus the outbox depth; both are debounced ON by their delay (`downloadDelayMs` 200, `uploadDelayMs` 150) and off at once, and `isUploading()` additionally needs `pendingMutations() > uploadThreshold` (0, so any unacknowledged write counts). Drive a logo/traffic indicator off it; pair with `useSyncStatus().connection()` / `.isOffline()` to switch the same mark to a reconnecting or offline state and show the unsaved count.
|
|
49
49
|
- **`createPreload`**, **`usePendingMutations`**, **`useSyncStatus`**, **`useStorageStatus`**, **`useFeatureFlag`**, **`useAppRelease`**, **`useCrdtField`**, **`useFileUpload`**, **`useDownloadFile`** — same shapes as client-solid.
|
|
50
50
|
- **`conflate`**, **`fromSubscription`** — the async-iterable helpers the hooks are built on; useful for wrapping other engine subscriptions.
|
|
51
51
|
|
package/QUICK_START.md
CHANGED
|
@@ -116,7 +116,7 @@ Track a backend job's progress by querying its outbox row: `createQuery(() => db
|
|
|
116
116
|
|
|
117
117
|
## Status hooks
|
|
118
118
|
|
|
119
|
-
Same shapes as client-solid, rebuilt on async-iterable-backed memos: `useSyncStatus`, `useStorageStatus`, `usePendingMutations`, `useSyncActivity` (new: `isDownloading()` after 200 ms of fetching, `isUploading()`
|
|
119
|
+
Same shapes as client-solid, rebuilt on async-iterable-backed memos: `useSyncStatus`, `useStorageStatus`, `usePendingMutations`, `useSyncActivity` (new: `isDownloading()` after 200 ms of fetching, `isUploading()` after 150 ms of a queued write, for a traffic indicator), `useFeatureFlag`, `useAppRelease`, `useCrdtField`, `useFileUpload`, `useDownloadFile`, `createPreload`.
|
|
120
120
|
|
|
121
121
|
## Migrating from client-solid
|
|
122
122
|
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ What "native" means here:
|
|
|
8
8
|
|
|
9
9
|
- Query results are a plain store written from the engine's live subscription and merged keyed by `id`: row identity preserved, coarse `<For>` readers notified, only changed fields written. Not an async-generator projection - see `create-query.ts` for why a never-returning generator breaks navigation transitions.
|
|
10
10
|
- `createQuery` exposes both worlds: non-suspending accessors (`data`, `isLoading`, `isFetching`, `isAuthoritative`, `hasData`, `isEmpty`, `isSettled`, `error`) and a suspending `ready()` for `<Loading>` boundaries. Born committed, so local-first cache paints never suspend. `isLoading()` is true only on a cold first load (nothing cached, server not heard yet); `isEmpty()` is the server saying "no rows".
|
|
11
|
-
- Status hooks (`useSyncStatus`, `useSyncActivity`, `useStorageStatus`, `usePendingMutations`, feature flags, app release) are async-iterable-backed memos with `loadingValue`. `useSyncActivity` is the traffic indicator pair: `isDownloading()` (queries fetching for longer than 200 ms) and `isUploading()` (
|
|
11
|
+
- Status hooks (`useSyncStatus`, `useSyncActivity`, `useStorageStatus`, `usePendingMutations`, feature flags, app release) are async-iterable-backed memos with `loadingValue`. `useSyncActivity` is the traffic indicator pair: `isDownloading()` (queries fetching for longer than 200 ms) and `isUploading()` (writes queued for longer than 150 ms), plus the raw `fetchingQueries()` / `pendingMutations()` counts.
|
|
12
12
|
- Mutations stay plain async calls — the engine is already optimistic local-first end to end (local commit → live re-emit → outbox sync). `createSubmission` adds button pending/error state.
|
|
13
13
|
|
|
14
14
|
See `QUICK_START.md` for usage and the migration table from client-solid, and `src/lib/__tests__/rc-semantics.test.ts` for the probed Solid 2 rc contracts this package depends on (run them after every Solid version bump).
|
package/dist/index.cjs
CHANGED
|
@@ -324,8 +324,9 @@ const useQuery = createQuery;
|
|
|
324
324
|
/**
|
|
325
325
|
* Reactive, fire-and-forget prewarm. Resolves the query (calling it if it's a
|
|
326
326
|
* function so it tracks reactive deps), dedupes on the query's stable identity
|
|
327
|
-
* hash, and
|
|
328
|
-
*
|
|
327
|
+
* hash, and registers it via `db.preload`: a live query nobody subscribes to,
|
|
328
|
+
* evicted a ttl after it was registered unless a view mounts it first. Nothing
|
|
329
|
+
* to clean up here.
|
|
329
330
|
*
|
|
330
331
|
* Typical use: inside a list row, preload the detail query the user is likely
|
|
331
332
|
* to open next, so navigation paints from cache instead of the network.
|
|
@@ -353,10 +354,7 @@ function createPreload(dbOrQuery, queryOrOptions, maybeOptions) {
|
|
|
353
354
|
return query;
|
|
354
355
|
}, (query) => {
|
|
355
356
|
if (!query) return;
|
|
356
|
-
db.getSp00ky().preload(query, {
|
|
357
|
-
refresh: options?.refresh,
|
|
358
|
-
staleTime: options?.staleTime
|
|
359
|
-
});
|
|
357
|
+
db.getSp00ky().preload(query, { signal: options?.signal });
|
|
360
358
|
});
|
|
361
359
|
}
|
|
362
360
|
|
|
@@ -393,10 +391,10 @@ function useSyncStatus() {
|
|
|
393
391
|
* The two directions of sync traffic, for an indicator in the app chrome.
|
|
394
392
|
*
|
|
395
393
|
* `fetchingQueries` is one subscription on the engine's aggregate fetch count,
|
|
396
|
-
* not one per query; `pendingMutations` is the outbox depth.
|
|
397
|
-
*
|
|
398
|
-
* local-first page that answers from cache and confirms with the
|
|
399
|
-
* few milliseconds never flickers. Must be used within a `<Sp00kyProvider>`, or
|
|
394
|
+
* not one per query; `pendingMutations` is the outbox depth. Both
|
|
395
|
+
* `isDownloading` and `isUploading` are debounced ON by their delay (and off at
|
|
396
|
+
* once), so a local-first page that answers from cache and confirms with the
|
|
397
|
+
* server in a few milliseconds never flickers. Must be used within a `<Sp00kyProvider>`, or
|
|
400
398
|
* pass the `SyncedDb` explicitly.
|
|
401
399
|
*/
|
|
402
400
|
function useSyncActivity(dbOrOptions, maybeOptions) {
|
|
@@ -404,38 +402,47 @@ function useSyncActivity(dbOrOptions, maybeOptions) {
|
|
|
404
402
|
const options = (explicitDb ? maybeOptions : dbOrOptions) ?? {};
|
|
405
403
|
const db = explicitDb ?? useDb();
|
|
406
404
|
const downloadDelayMs = options.downloadDelayMs ?? 200;
|
|
407
|
-
const uploadThreshold = options.uploadThreshold ??
|
|
405
|
+
const uploadThreshold = options.uploadThreshold ?? 0;
|
|
406
|
+
const uploadDelayMs = options.uploadDelayMs ?? 150;
|
|
408
407
|
const fetchingQueries = fromSubscription((cb) => db.subscribeToFetchActivity(cb), db.fetchingQueryCount);
|
|
409
408
|
const pendingMutations = fromSubscription((cb) => db.subscribeToPendingMutations(cb), db.pendingMutationCount);
|
|
410
|
-
|
|
409
|
+
return {
|
|
410
|
+
fetchingQueries,
|
|
411
|
+
pendingMutations,
|
|
412
|
+
isDownloading: delayedOn(() => fetchingQueries() > 0, downloadDelayMs),
|
|
413
|
+
isUploading: delayedOn(() => pendingMutations() > uploadThreshold, uploadDelayMs)
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* `busy` held for `delayMs`, dropped the instant it clears. Both directions of
|
|
418
|
+
* traffic use it, so a burst too short to see never flickers the indicator and
|
|
419
|
+
* neither one can latch on after the work is done.
|
|
420
|
+
*/
|
|
421
|
+
function delayedOn(busy, delayMs) {
|
|
422
|
+
const [on, setOn] = (0, solid_js.createSignal)(false, { ownedWrite: true });
|
|
411
423
|
let timer;
|
|
412
|
-
(0, solid_js.createEffect)(
|
|
424
|
+
(0, solid_js.createEffect)(busy, (isBusy) => {
|
|
413
425
|
if (timer !== void 0) {
|
|
414
426
|
clearTimeout(timer);
|
|
415
427
|
timer = void 0;
|
|
416
428
|
}
|
|
417
|
-
if (!
|
|
418
|
-
|
|
429
|
+
if (!isBusy) {
|
|
430
|
+
setOn(false);
|
|
419
431
|
return;
|
|
420
432
|
}
|
|
421
|
-
if (
|
|
422
|
-
|
|
433
|
+
if (delayMs <= 0) {
|
|
434
|
+
setOn(true);
|
|
423
435
|
return;
|
|
424
436
|
}
|
|
425
437
|
timer = setTimeout(() => {
|
|
426
438
|
timer = void 0;
|
|
427
|
-
|
|
428
|
-
},
|
|
439
|
+
setOn(true);
|
|
440
|
+
}, delayMs);
|
|
429
441
|
});
|
|
430
442
|
(0, solid_js.onCleanup)(() => {
|
|
431
443
|
if (timer !== void 0) clearTimeout(timer);
|
|
432
444
|
});
|
|
433
|
-
return
|
|
434
|
-
fetchingQueries,
|
|
435
|
-
pendingMutations,
|
|
436
|
-
isDownloading,
|
|
437
|
-
isUploading: () => pendingMutations() > uploadThreshold
|
|
438
|
-
};
|
|
445
|
+
return on;
|
|
439
446
|
}
|
|
440
447
|
|
|
441
448
|
//#endregion
|