@spooky-sync/client-solid2 0.0.1-alpha.1 → 0.0.1-alpha.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.
- package/AGENTS.md +1 -1
- package/QUICK_START.md +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +28 -19
- 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 +28 -19
- 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/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
|
@@ -391,10 +391,10 @@ function useSyncStatus() {
|
|
|
391
391
|
* The two directions of sync traffic, for an indicator in the app chrome.
|
|
392
392
|
*
|
|
393
393
|
* `fetchingQueries` is one subscription on the engine's aggregate fetch count,
|
|
394
|
-
* not one per query; `pendingMutations` is the outbox depth.
|
|
395
|
-
*
|
|
396
|
-
* local-first page that answers from cache and confirms with the
|
|
397
|
-
* 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
|
|
398
398
|
* pass the `SyncedDb` explicitly.
|
|
399
399
|
*/
|
|
400
400
|
function useSyncActivity(dbOrOptions, maybeOptions) {
|
|
@@ -402,38 +402,47 @@ function useSyncActivity(dbOrOptions, maybeOptions) {
|
|
|
402
402
|
const options = (explicitDb ? maybeOptions : dbOrOptions) ?? {};
|
|
403
403
|
const db = explicitDb ?? useDb();
|
|
404
404
|
const downloadDelayMs = options.downloadDelayMs ?? 200;
|
|
405
|
-
const uploadThreshold = options.uploadThreshold ??
|
|
405
|
+
const uploadThreshold = options.uploadThreshold ?? 0;
|
|
406
|
+
const uploadDelayMs = options.uploadDelayMs ?? 150;
|
|
406
407
|
const fetchingQueries = fromSubscription((cb) => db.subscribeToFetchActivity(cb), db.fetchingQueryCount);
|
|
407
408
|
const pendingMutations = fromSubscription((cb) => db.subscribeToPendingMutations(cb), db.pendingMutationCount);
|
|
408
|
-
|
|
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 });
|
|
409
423
|
let timer;
|
|
410
|
-
(0, solid_js.createEffect)(
|
|
424
|
+
(0, solid_js.createEffect)(busy, (isBusy) => {
|
|
411
425
|
if (timer !== void 0) {
|
|
412
426
|
clearTimeout(timer);
|
|
413
427
|
timer = void 0;
|
|
414
428
|
}
|
|
415
|
-
if (!
|
|
416
|
-
|
|
429
|
+
if (!isBusy) {
|
|
430
|
+
setOn(false);
|
|
417
431
|
return;
|
|
418
432
|
}
|
|
419
|
-
if (
|
|
420
|
-
|
|
433
|
+
if (delayMs <= 0) {
|
|
434
|
+
setOn(true);
|
|
421
435
|
return;
|
|
422
436
|
}
|
|
423
437
|
timer = setTimeout(() => {
|
|
424
438
|
timer = void 0;
|
|
425
|
-
|
|
426
|
-
},
|
|
439
|
+
setOn(true);
|
|
440
|
+
}, delayMs);
|
|
427
441
|
});
|
|
428
442
|
(0, solid_js.onCleanup)(() => {
|
|
429
443
|
if (timer !== void 0) clearTimeout(timer);
|
|
430
444
|
});
|
|
431
|
-
return
|
|
432
|
-
fetchingQueries,
|
|
433
|
-
pendingMutations,
|
|
434
|
-
isDownloading,
|
|
435
|
-
isUploading: () => pendingMutations() > uploadThreshold
|
|
436
|
-
};
|
|
445
|
+
return on;
|
|
437
446
|
}
|
|
438
447
|
|
|
439
448
|
//#endregion
|