@spooky-sync/core 0.0.1-canary.325 → 0.0.1-canary.326
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +23 -9
- package/package.json +3 -3
- package/src/client/runtime.ts +4 -4
- package/src/state/selectors.test.ts +13 -0
- package/src/state/selectors.ts +13 -1
package/dist/index.js
CHANGED
|
@@ -2188,8 +2188,8 @@ function trimHistory(history, cap) {
|
|
|
2188
2188
|
|
|
2189
2189
|
//#endregion
|
|
2190
2190
|
//#region src/modules/devtools/index.ts
|
|
2191
|
-
const CORE_VERSION = "0.0.1-canary.
|
|
2192
|
-
const WASM_VERSION = "0.0.1-canary.
|
|
2191
|
+
const CORE_VERSION = "0.0.1-canary.326";
|
|
2192
|
+
const WASM_VERSION = "0.0.1-canary.326";
|
|
2193
2193
|
const SURREAL_VERSION = "3.0.3";
|
|
2194
2194
|
var DevToolsService = class DevToolsService {
|
|
2195
2195
|
version = CORE_VERSION;
|
|
@@ -3548,6 +3548,22 @@ const compose = (...reducers) => (s) => reducers.reduce((acc, r) => r(acc), s);
|
|
|
3548
3548
|
//#endregion
|
|
3549
3549
|
//#region src/state/selectors.ts
|
|
3550
3550
|
const activeHashes = (s) => [...s.queries.keys()];
|
|
3551
|
+
/**
|
|
3552
|
+
* The status subscribers see. `fetching` while a fetch cycle is in flight,
|
|
3553
|
+
* and also while a live query is not yet `settled`: the server's membership
|
|
3554
|
+
* is in but its rows are still downloading or not yet shown. Registration
|
|
3555
|
+
* ends its cycle as soon as membership is applied and the body fetch starts
|
|
3556
|
+
* its own a moment later, so the fetch count alone read `idle` in between,
|
|
3557
|
+
* and a binding that settles on "membership known and idle" told the app a
|
|
3558
|
+
* row did not exist while it was on its way (a by-id screen showed "not
|
|
3559
|
+
* found" for a moment, or for as long as the bodies took).
|
|
3560
|
+
*/
|
|
3561
|
+
const queryStatus = (s, hash) => {
|
|
3562
|
+
const e = s.queries.get(hash);
|
|
3563
|
+
if (!e) return void 0;
|
|
3564
|
+
if (deriveStatus(e.lifecycle) === "fetching") return "fetching";
|
|
3565
|
+
return e.lifecycle.phase === "live" && !settled(s, hash) ? "fetching" : "idle";
|
|
3566
|
+
};
|
|
3551
3567
|
/** The optimistic overlay, derived from the outbox (pending AND acked items). */
|
|
3552
3568
|
function overlay(s) {
|
|
3553
3569
|
const writes = /* @__PURE__ */ new Set();
|
|
@@ -7396,9 +7412,8 @@ var Runtime = class {
|
|
|
7396
7412
|
});
|
|
7397
7413
|
}
|
|
7398
7414
|
for (const [hash, subs] of this.statusSubs) {
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
const status = deriveStatus(entry.lifecycle);
|
|
7415
|
+
if (!next.queries.get(hash)) continue;
|
|
7416
|
+
const status = queryStatus(next, hash) ?? "idle";
|
|
7402
7417
|
if (this.lastStatus.get(hash) === status) continue;
|
|
7403
7418
|
this.lastStatus.set(hash, status);
|
|
7404
7419
|
for (const cb of subs) this.safely(() => cb(status));
|
|
@@ -7502,9 +7517,8 @@ var Runtime = class {
|
|
|
7502
7517
|
const set = this.statusSubs.get(hash) ?? /* @__PURE__ */ new Set();
|
|
7503
7518
|
set.add(cb);
|
|
7504
7519
|
this.statusSubs.set(hash, set);
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
const status = deriveStatus(entry.lifecycle);
|
|
7520
|
+
if (this.stateValue.queries.get(hash)) {
|
|
7521
|
+
const status = queryStatus(this.stateValue, hash) ?? "idle";
|
|
7508
7522
|
this.lastStatus.set(hash, status);
|
|
7509
7523
|
if (options.immediate) this.safely(() => cb(status));
|
|
7510
7524
|
}
|
|
@@ -15261,7 +15275,7 @@ function createTabsCoordinator(config, s, host) {
|
|
|
15261
15275
|
return new TabsCoordinator({
|
|
15262
15276
|
tabId: s.tabId,
|
|
15263
15277
|
fingerprint: computeTabsFingerprint({
|
|
15264
|
-
coreVersion: "0.0.1-canary.
|
|
15278
|
+
coreVersion: "0.0.1-canary.326",
|
|
15265
15279
|
schemaHash: hash53(config.schemaSurql),
|
|
15266
15280
|
endpoint: config.database.endpoint ?? "",
|
|
15267
15281
|
namespace: config.database.namespace,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spooky-sync/core",
|
|
3
|
-
"version": "0.0.1-canary.
|
|
3
|
+
"version": "0.0.1-canary.326",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@spooky-sync/query-builder": "0.0.1-canary.
|
|
79
|
-
"@spooky-sync/ssp-wasm": "0.0.1-canary.
|
|
78
|
+
"@spooky-sync/query-builder": "0.0.1-canary.326",
|
|
79
|
+
"@spooky-sync/ssp-wasm": "0.0.1-canary.326",
|
|
80
80
|
"@sqlite.org/sqlite-wasm": "3.53.0-build1",
|
|
81
81
|
"@surrealdb/wasm": "^3.0.3",
|
|
82
82
|
"blurhash": "^2.0.5",
|
package/src/client/runtime.ts
CHANGED
|
@@ -7,9 +7,9 @@ import type { Lane, Saga } from '../kernel/saga';
|
|
|
7
7
|
import { runSaga } from '../kernel/saga';
|
|
8
8
|
import type { ClientState } from '../state/client-state';
|
|
9
9
|
import { emptyState } from '../state/client-state';
|
|
10
|
-
import {
|
|
10
|
+
import { isAuthoritative } from '../state/lifecycle';
|
|
11
11
|
import * as R from '../state/reducers';
|
|
12
|
-
import { fetchingQueryCount, pendingMutationCount } from '../state/selectors';
|
|
12
|
+
import { fetchingQueryCount, pendingMutationCount, queryStatus } from '../state/selectors';
|
|
13
13
|
import type { SagaEnv } from '../query/env';
|
|
14
14
|
import { route } from './router';
|
|
15
15
|
|
|
@@ -152,7 +152,7 @@ export class Runtime {
|
|
|
152
152
|
for (const [hash, subs] of this.statusSubs) {
|
|
153
153
|
const entry = next.queries.get(hash);
|
|
154
154
|
if (!entry) continue;
|
|
155
|
-
const status =
|
|
155
|
+
const status = queryStatus(next, hash) ?? 'idle';
|
|
156
156
|
if (this.lastStatus.get(hash) === status) continue;
|
|
157
157
|
this.lastStatus.set(hash, status);
|
|
158
158
|
for (const cb of subs) this.safely(() => cb(status));
|
|
@@ -255,7 +255,7 @@ export class Runtime {
|
|
|
255
255
|
this.statusSubs.set(hash, set);
|
|
256
256
|
const entry = this.stateValue.queries.get(hash);
|
|
257
257
|
if (entry) {
|
|
258
|
-
const status =
|
|
258
|
+
const status = queryStatus(this.stateValue, hash) ?? 'idle';
|
|
259
259
|
this.lastStatus.set(hash, status);
|
|
260
260
|
if (options.immediate) this.safely(() => cb(status));
|
|
261
261
|
}
|
|
@@ -77,6 +77,19 @@ describe('needed / planFetch / settled', () => {
|
|
|
77
77
|
expect(S.settleFailed(s, 'a')).toBe(false);
|
|
78
78
|
expect(S.settleFailed(R.applyLifecycle('a', { type: 'remote-failed' })(s), 'a')).toBe(true);
|
|
79
79
|
});
|
|
80
|
+
it('queryStatus stays fetching until a live query is settled', () => {
|
|
81
|
+
const base = e('a', { lifecycle: { ...live, notified: true }, remoteArray: [['thing:1', 1]] });
|
|
82
|
+
const s = { ...buildState([base], R.setVersions([['thing:1', 1]])), dirty: new Set<string>() };
|
|
83
|
+
expect(S.queryStatus(s, 'a')).toBe('idle');
|
|
84
|
+
// Membership in, body still downloading (registration already ended its cycle).
|
|
85
|
+
expect(S.queryStatus(R.setVersions([['thing:1', 0]])(s), 'a')).toBe('fetching');
|
|
86
|
+
// Body landed, not yet shown to subscribers.
|
|
87
|
+
expect(S.queryStatus(R.markDirty(['a'])(s), 'a')).toBe('fetching');
|
|
88
|
+
// A cold query with no fetch in flight has nothing to wait for.
|
|
89
|
+
expect(S.queryStatus(buildState([e('c', { lifecycle: { phase: 'cold' } })]), 'c')).toBe('idle');
|
|
90
|
+
expect(S.queryStatus(R.applyLifecycle('a', { type: 'fetch-begin' })(s), 'a')).toBe('fetching');
|
|
91
|
+
expect(S.queryStatus(s, 'missing')).toBeUndefined();
|
|
92
|
+
});
|
|
80
93
|
it('fetchingQueryCount counts entries with fetch depth', () => {
|
|
81
94
|
const s = buildState([e('a', { lifecycle: { fetchDepth: 2 } }), e('b')]);
|
|
82
95
|
expect(S.fetchingQueryCount(s)).toBe(1);
|
package/src/state/selectors.ts
CHANGED
|
@@ -12,9 +12,21 @@ export const activeHashes = (s: ClientState): QueryHash[] => [...s.queries.keys(
|
|
|
12
12
|
export const hashesForTable = (s: ClientState, table: string): QueryHash[] =>
|
|
13
13
|
[...s.queries].filter(([, e]) => e.def.tableName === table).map(([h]) => h);
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The status subscribers see. `fetching` while a fetch cycle is in flight,
|
|
17
|
+
* and also while a live query is not yet `settled`: the server's membership
|
|
18
|
+
* is in but its rows are still downloading or not yet shown. Registration
|
|
19
|
+
* ends its cycle as soon as membership is applied and the body fetch starts
|
|
20
|
+
* its own a moment later, so the fetch count alone read `idle` in between,
|
|
21
|
+
* and a binding that settles on "membership known and idle" told the app a
|
|
22
|
+
* row did not exist while it was on its way (a by-id screen showed "not
|
|
23
|
+
* found" for a moment, or for as long as the bodies took).
|
|
24
|
+
*/
|
|
15
25
|
export const queryStatus = (s: ClientState, hash: QueryHash): QueryStatus | undefined => {
|
|
16
26
|
const e = s.queries.get(hash);
|
|
17
|
-
|
|
27
|
+
if (!e) return undefined;
|
|
28
|
+
if (deriveStatus(e.lifecycle) === 'fetching') return 'fetching';
|
|
29
|
+
return e.lifecycle.phase === 'live' && !settled(s, hash) ? 'fetching' : 'idle';
|
|
18
30
|
};
|
|
19
31
|
|
|
20
32
|
export interface Overlay {
|