@drakkar.software/starfish-client 3.0.0-alpha.35 → 3.0.0-alpha.36
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/bindings/zustand.js +38 -16
- package/dist/bindings/zustand.js.map +2 -2
- package/dist/client.d.ts +6 -0
- package/dist/index.js +38 -16
- package/dist/index.js.map +2 -2
- package/dist/sync.d.ts +14 -4
- package/package.json +2 -2
package/dist/bindings/zustand.js
CHANGED
|
@@ -335,6 +335,12 @@ var StarfishClient = class {
|
|
|
335
335
|
cacheFallbackStatuses;
|
|
336
336
|
onRevalidated;
|
|
337
337
|
revalidating = /* @__PURE__ */ new Set();
|
|
338
|
+
/**
|
|
339
|
+
* In-memory mirror of the latest document timestamp written to each cache
|
|
340
|
+
* key via {@link writeCache}. Updated synchronously so {@link revalidateLoop}
|
|
341
|
+
* can guard against stale overwrites without an extra async cache read.
|
|
342
|
+
*/
|
|
343
|
+
latestCacheTimestamp = /* @__PURE__ */ new Map();
|
|
338
344
|
/**
|
|
339
345
|
* Installed client-side plugins. Currently stored as inert data; no
|
|
340
346
|
* hooks fire yet. Extensions can inspect this list if needed.
|
|
@@ -545,6 +551,9 @@ var StarfishClient = class {
|
|
|
545
551
|
*/
|
|
546
552
|
writeCache(cacheKey, result) {
|
|
547
553
|
if (!this.cache) return;
|
|
554
|
+
if (result.timestamp > (this.latestCacheTimestamp.get(cacheKey) ?? -1)) {
|
|
555
|
+
this.latestCacheTimestamp.set(cacheKey, result.timestamp);
|
|
556
|
+
}
|
|
548
557
|
const snapshot = {
|
|
549
558
|
data: result.data,
|
|
550
559
|
hash: result.hash,
|
|
@@ -606,8 +615,11 @@ var StarfishClient = class {
|
|
|
606
615
|
const res = await this.revalidateFetch(pathAndQuery);
|
|
607
616
|
if (res.ok) {
|
|
608
617
|
const result = await res.json();
|
|
609
|
-
this.
|
|
610
|
-
|
|
618
|
+
const latestTs = this.latestCacheTimestamp.get(cacheKey) ?? -1;
|
|
619
|
+
if (result.timestamp >= latestTs) {
|
|
620
|
+
this.writeCache(cacheKey, result);
|
|
621
|
+
this.onRevalidated?.(pathAndQuery, result);
|
|
622
|
+
}
|
|
611
623
|
return;
|
|
612
624
|
}
|
|
613
625
|
if (!fallbackSet?.has(res.status)) {
|
|
@@ -1012,20 +1024,32 @@ var SyncManager = class {
|
|
|
1012
1024
|
* action to absorb a background revalidation result (delivered via
|
|
1013
1025
|
* {@link StarfishClientOptions.onRevalidated}) into the store.
|
|
1014
1026
|
*
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1027
|
+
* Like {@link pull}, `ingest` conflict-merges the snapshot against the
|
|
1028
|
+
* established baseline via `this.onConflict` when a checkpoint exists — so a
|
|
1029
|
+
* union-merge store does not lose array items when a revalidation result
|
|
1030
|
+
* (e.g. a stale cache-fallback on 429/5xx) is a shorter snapshot. The first
|
|
1031
|
+
* ingest (no checkpoint yet) takes the snapshot wholesale. Sets
|
|
1032
|
+
* `lastFromCache = false` (a revalidation is a live response) so the binding
|
|
1033
|
+
* can clear its `stale` flag.
|
|
1034
|
+
*
|
|
1035
|
+
* **Staleness guard**: if a `push()` advanced `lastCheckpoint` between the
|
|
1036
|
+
* time the revalidation request was sent and the time it resolves, the
|
|
1037
|
+
* result is from an older document version. Ingesting it would clobber the
|
|
1038
|
+
* user's just-saved edit and reset `lastHash` to a stale server hash
|
|
1039
|
+
* (causing a spurious 409 on the next push). We silently drop the result in
|
|
1040
|
+
* that case — the store's post-push state is already correct.
|
|
1019
1041
|
*/
|
|
1020
1042
|
async ingest(result) {
|
|
1021
1043
|
if (this.aborted) return;
|
|
1044
|
+
if (result.timestamp < this.lastCheckpoint) return;
|
|
1045
|
+
let incoming;
|
|
1022
1046
|
if (this.encryptor) {
|
|
1023
|
-
|
|
1047
|
+
incoming = await this.encryptor.decrypt(result.data);
|
|
1024
1048
|
if (this.aborted) return;
|
|
1025
|
-
this.localData = decrypted;
|
|
1026
1049
|
} else {
|
|
1027
|
-
|
|
1050
|
+
incoming = result.data;
|
|
1028
1051
|
}
|
|
1052
|
+
this.localData = this.lastCheckpoint > 0 ? this.onConflict(this.localData, incoming) : incoming;
|
|
1029
1053
|
this.lastHash = result.hash;
|
|
1030
1054
|
this.lastCheckpoint = result.timestamp;
|
|
1031
1055
|
this.lastFromCache = false;
|
|
@@ -1038,17 +1062,15 @@ var SyncManager = class {
|
|
|
1038
1062
|
const result = await this.client.pull(this.pullPath, this.lastCheckpoint);
|
|
1039
1063
|
if (this.aborted) throw new AbortError();
|
|
1040
1064
|
this.lastFromCache = pullWasFromCache(result);
|
|
1065
|
+
let incoming;
|
|
1041
1066
|
if (this.encryptor) {
|
|
1042
|
-
|
|
1067
|
+
incoming = await this.encryptor.decrypt(result.data);
|
|
1043
1068
|
if (this.aborted) throw new AbortError();
|
|
1044
|
-
this.localData = decrypted;
|
|
1045
|
-
result.data = decrypted;
|
|
1046
|
-
} else if (this.lastCheckpoint > 0) {
|
|
1047
|
-
this.localData = deepMerge(this.localData, result.data);
|
|
1048
|
-
result.data = this.localData;
|
|
1049
1069
|
} else {
|
|
1050
|
-
|
|
1070
|
+
incoming = result.data;
|
|
1051
1071
|
}
|
|
1072
|
+
this.localData = this.lastCheckpoint > 0 ? this.onConflict(this.localData, incoming) : incoming;
|
|
1073
|
+
result.data = this.localData;
|
|
1052
1074
|
this.lastHash = result.hash;
|
|
1053
1075
|
this.lastCheckpoint = result.timestamp;
|
|
1054
1076
|
this.logger?.pullSuccess(this.loggerName, Math.round(performance.now() - start));
|