kaafil-js 0.6.2 → 0.8.0
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/client-entry.cjs +65 -15
- package/dist/client-entry.cjs.map +1 -1
- package/dist/client-entry.d.cts +25 -0
- package/dist/client-entry.d.ts +25 -0
- package/dist/client-entry.js +65 -15
- package/dist/client-entry.js.map +1 -1
- package/dist/index.cjs +25 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/client-entry.d.cts
CHANGED
|
@@ -1017,6 +1017,31 @@ interface CreateDrainerOptions {
|
|
|
1017
1017
|
/** Fed every observed `meta.serverTime` so the share layer can age a token
|
|
1018
1018
|
* without a device clock (`06 §8`). */
|
|
1019
1019
|
readonly onServerTime?: (serverTime: string) => void;
|
|
1020
|
+
/**
|
|
1021
|
+
* Called with the lanes a pass actually APPLIED something on, so the engine
|
|
1022
|
+
* can pull them straight afterwards.
|
|
1023
|
+
*
|
|
1024
|
+
* ── WHY A DRAIN HAS TO BE FOLLOWED BY A PULL ──────────────────────────────
|
|
1025
|
+
*
|
|
1026
|
+
* A write leaves the snapshot alone unless it was enqueued with an
|
|
1027
|
+
* `entity`/`optimistic` pairing, and several deliberately are not: a write
|
|
1028
|
+
* whose response carries server-assigned fields no client can compute
|
|
1029
|
+
* (`floatMovementId`, a resolved `receiptRequired` threshold, whether a
|
|
1030
|
+
* payment cleared a negative-float guard) is better left unguessed than
|
|
1031
|
+
* painted wrong and silently corrected later. Those writes reach the server
|
|
1032
|
+
* correctly and the snapshot simply does not know yet.
|
|
1033
|
+
*
|
|
1034
|
+
* Until now nothing closed that gap: `sync.drained` was emitted and the pass
|
|
1035
|
+
* ended. A host reading a snapshot-backed list saw its own successful write
|
|
1036
|
+
* missing from it — indefinitely, since nothing else schedules a pull — and
|
|
1037
|
+
* the fix was to reopen the screen. Observed in the field: an expense logged
|
|
1038
|
+
* against a trip's float moved the balance and stayed absent from the list
|
|
1039
|
+
* beside it.
|
|
1040
|
+
*
|
|
1041
|
+
* A callback rather than a direct dependency on `./pull.ts`, so this module
|
|
1042
|
+
* keeps knowing nothing about pulling and stays testable without one.
|
|
1043
|
+
*/
|
|
1044
|
+
readonly onLanesApplied?: (lanes: readonly string[]) => void;
|
|
1020
1045
|
}
|
|
1021
1046
|
declare function createDrainer(options: CreateDrainerOptions): Drainer;
|
|
1022
1047
|
|
package/dist/client-entry.d.ts
CHANGED
|
@@ -1017,6 +1017,31 @@ interface CreateDrainerOptions {
|
|
|
1017
1017
|
/** Fed every observed `meta.serverTime` so the share layer can age a token
|
|
1018
1018
|
* without a device clock (`06 §8`). */
|
|
1019
1019
|
readonly onServerTime?: (serverTime: string) => void;
|
|
1020
|
+
/**
|
|
1021
|
+
* Called with the lanes a pass actually APPLIED something on, so the engine
|
|
1022
|
+
* can pull them straight afterwards.
|
|
1023
|
+
*
|
|
1024
|
+
* ── WHY A DRAIN HAS TO BE FOLLOWED BY A PULL ──────────────────────────────
|
|
1025
|
+
*
|
|
1026
|
+
* A write leaves the snapshot alone unless it was enqueued with an
|
|
1027
|
+
* `entity`/`optimistic` pairing, and several deliberately are not: a write
|
|
1028
|
+
* whose response carries server-assigned fields no client can compute
|
|
1029
|
+
* (`floatMovementId`, a resolved `receiptRequired` threshold, whether a
|
|
1030
|
+
* payment cleared a negative-float guard) is better left unguessed than
|
|
1031
|
+
* painted wrong and silently corrected later. Those writes reach the server
|
|
1032
|
+
* correctly and the snapshot simply does not know yet.
|
|
1033
|
+
*
|
|
1034
|
+
* Until now nothing closed that gap: `sync.drained` was emitted and the pass
|
|
1035
|
+
* ended. A host reading a snapshot-backed list saw its own successful write
|
|
1036
|
+
* missing from it — indefinitely, since nothing else schedules a pull — and
|
|
1037
|
+
* the fix was to reopen the screen. Observed in the field: an expense logged
|
|
1038
|
+
* against a trip's float moved the balance and stayed absent from the list
|
|
1039
|
+
* beside it.
|
|
1040
|
+
*
|
|
1041
|
+
* A callback rather than a direct dependency on `./pull.ts`, so this module
|
|
1042
|
+
* keeps knowing nothing about pulling and stays testable without one.
|
|
1043
|
+
*/
|
|
1044
|
+
readonly onLanesApplied?: (lanes: readonly string[]) => void;
|
|
1020
1045
|
}
|
|
1021
1046
|
declare function createDrainer(options: CreateDrainerOptions): Drainer;
|
|
1022
1047
|
|
package/dist/client-entry.js
CHANGED
|
@@ -1979,11 +1979,35 @@ async function runWithLadder(ctx, request, send) {
|
|
|
1979
1979
|
throw error;
|
|
1980
1980
|
}
|
|
1981
1981
|
}
|
|
1982
|
+
var inFlightReads = /* @__PURE__ */ new WeakMap();
|
|
1983
|
+
function readKey(request) {
|
|
1984
|
+
return [
|
|
1985
|
+
request.operationId,
|
|
1986
|
+
request.path,
|
|
1987
|
+
JSON.stringify(request.pathParams ?? {}),
|
|
1988
|
+
JSON.stringify(request.query ?? {}),
|
|
1989
|
+
JSON.stringify(request.headers ?? {})
|
|
1990
|
+
].join("|");
|
|
1991
|
+
}
|
|
1982
1992
|
function execute(ctx, request) {
|
|
1983
|
-
|
|
1993
|
+
const run = () => runWithLadder(ctx, request, async (idempotencyKey) => {
|
|
1984
1994
|
const { data, meta, facets } = await sendOnce(ctx, request, idempotencyKey);
|
|
1985
1995
|
return attachMeta(data, meta, facets);
|
|
1986
1996
|
});
|
|
1997
|
+
if (request.method !== "GET" || request.signal !== void 0) return run();
|
|
1998
|
+
let perClient = inFlightReads.get(ctx);
|
|
1999
|
+
if (perClient === void 0) {
|
|
2000
|
+
perClient = /* @__PURE__ */ new Map();
|
|
2001
|
+
inFlightReads.set(ctx, perClient);
|
|
2002
|
+
}
|
|
2003
|
+
const key = readKey(request);
|
|
2004
|
+
const existing = perClient.get(key);
|
|
2005
|
+
if (existing !== void 0) return existing;
|
|
2006
|
+
const started = run().finally(() => {
|
|
2007
|
+
perClient.delete(key);
|
|
2008
|
+
});
|
|
2009
|
+
perClient.set(key, started);
|
|
2010
|
+
return started;
|
|
1987
2011
|
}
|
|
1988
2012
|
function executeBinary(ctx, request) {
|
|
1989
2013
|
return runWithLadder(
|
|
@@ -2851,6 +2875,9 @@ function createDrainer(options) {
|
|
|
2851
2875
|
remaining: report.remaining,
|
|
2852
2876
|
usedBatchTransport: report.usedBatchTransport
|
|
2853
2877
|
});
|
|
2878
|
+
if (report.applied > 0) {
|
|
2879
|
+
options.onLanesApplied?.(report.lanes);
|
|
2880
|
+
}
|
|
2854
2881
|
return report;
|
|
2855
2882
|
}
|
|
2856
2883
|
let kickTimer;
|
|
@@ -3144,15 +3171,15 @@ function createReadCacheStore(options) {
|
|
|
3144
3171
|
await options.storage.set(key, JSON.stringify(shape));
|
|
3145
3172
|
}
|
|
3146
3173
|
return {
|
|
3147
|
-
get(
|
|
3148
|
-
return reads.get(
|
|
3174
|
+
get(readKey2) {
|
|
3175
|
+
return reads.get(readKey2);
|
|
3149
3176
|
},
|
|
3150
|
-
async set(
|
|
3151
|
-
reads.set(
|
|
3177
|
+
async set(readKey2, value, serverTime) {
|
|
3178
|
+
reads.set(readKey2, { value, syncedAt: serverTime });
|
|
3152
3179
|
await persist();
|
|
3153
3180
|
},
|
|
3154
|
-
async purge(
|
|
3155
|
-
if (!reads.delete(
|
|
3181
|
+
async purge(readKey2) {
|
|
3182
|
+
if (!reads.delete(readKey2)) {
|
|
3156
3183
|
return;
|
|
3157
3184
|
}
|
|
3158
3185
|
await persist();
|
|
@@ -3169,9 +3196,9 @@ function createReadCacheStore(options) {
|
|
|
3169
3196
|
} catch {
|
|
3170
3197
|
return;
|
|
3171
3198
|
}
|
|
3172
|
-
for (const [
|
|
3199
|
+
for (const [readKey2, entry] of Object.entries(parsed.reads ?? {})) {
|
|
3173
3200
|
if (entry !== null && typeof entry === "object" && typeof entry.syncedAt === "string") {
|
|
3174
|
-
reads.set(
|
|
3201
|
+
reads.set(readKey2, entry);
|
|
3175
3202
|
}
|
|
3176
3203
|
}
|
|
3177
3204
|
}
|
|
@@ -3574,22 +3601,45 @@ function createOfflineEngine(options) {
|
|
|
3574
3601
|
...options.share.monotonic !== void 0 ? { monotonic: options.share.monotonic } : {}
|
|
3575
3602
|
});
|
|
3576
3603
|
}
|
|
3604
|
+
const pull = createPullCoordinator({
|
|
3605
|
+
snapshot,
|
|
3606
|
+
transport: options.transport,
|
|
3607
|
+
events,
|
|
3608
|
+
onServerTime
|
|
3609
|
+
});
|
|
3577
3610
|
const drainer = createDrainer({
|
|
3578
3611
|
outbox,
|
|
3579
3612
|
snapshot,
|
|
3580
3613
|
transport: options.transport,
|
|
3581
3614
|
events,
|
|
3582
3615
|
onServerTime,
|
|
3616
|
+
/*
|
|
3617
|
+
* A DRAIN IS FOLLOWED BY A PULL, for the lanes it actually changed.
|
|
3618
|
+
*
|
|
3619
|
+
* Not every write rebases the snapshot: one enqueued without an
|
|
3620
|
+
* `entity`/`optimistic` pairing — deliberately, wherever the response
|
|
3621
|
+
* carries server-assigned fields a client cannot compute — reaches the
|
|
3622
|
+
* server and leaves the local snapshot untouched. Nothing used to close
|
|
3623
|
+
* that gap, so a host reading a snapshot-backed list saw its own
|
|
3624
|
+
* successful write missing from it until something reopened the screen.
|
|
3625
|
+
*
|
|
3626
|
+
* Failures are swallowed on purpose. This is a background reconcile behind
|
|
3627
|
+
* a write that has already succeeded; a pull that cannot reach the server
|
|
3628
|
+
* changes nothing the caller needs to hear about, and `pullTrip` has
|
|
3629
|
+
* already emitted whatever the host subscribes to. Rethrowing here would
|
|
3630
|
+
* surface a sync failure as if the WRITE had failed, which is the one
|
|
3631
|
+
* reading of it that is untrue.
|
|
3632
|
+
*/
|
|
3633
|
+
onLanesApplied: (lanes) => {
|
|
3634
|
+
for (const tripRef of lanes) {
|
|
3635
|
+
void pull.pullTrip({ tripRef }).catch(() => {
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
},
|
|
3583
3639
|
...options.conflictResolver !== void 0 ? { conflictResolver: options.conflictResolver } : {},
|
|
3584
3640
|
...options.now !== void 0 ? { now: options.now } : {},
|
|
3585
3641
|
...options.random !== void 0 ? { random: options.random } : {}
|
|
3586
3642
|
});
|
|
3587
|
-
const pull = createPullCoordinator({
|
|
3588
|
-
snapshot,
|
|
3589
|
-
transport: options.transport,
|
|
3590
|
-
events,
|
|
3591
|
-
onServerTime
|
|
3592
|
-
});
|
|
3593
3643
|
const blobs = options.blob === void 0 ? void 0 : createBlobLane({
|
|
3594
3644
|
storage: options.storage,
|
|
3595
3645
|
keyPrefix,
|