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.
@@ -1981,11 +1981,35 @@ async function runWithLadder(ctx, request, send) {
1981
1981
  throw error;
1982
1982
  }
1983
1983
  }
1984
+ var inFlightReads = /* @__PURE__ */ new WeakMap();
1985
+ function readKey(request) {
1986
+ return [
1987
+ request.operationId,
1988
+ request.path,
1989
+ JSON.stringify(request.pathParams ?? {}),
1990
+ JSON.stringify(request.query ?? {}),
1991
+ JSON.stringify(request.headers ?? {})
1992
+ ].join("|");
1993
+ }
1984
1994
  function execute(ctx, request) {
1985
- return runWithLadder(ctx, request, async (idempotencyKey) => {
1995
+ const run = () => runWithLadder(ctx, request, async (idempotencyKey) => {
1986
1996
  const { data, meta, facets } = await sendOnce(ctx, request, idempotencyKey);
1987
1997
  return attachMeta(data, meta, facets);
1988
1998
  });
1999
+ if (request.method !== "GET" || request.signal !== void 0) return run();
2000
+ let perClient = inFlightReads.get(ctx);
2001
+ if (perClient === void 0) {
2002
+ perClient = /* @__PURE__ */ new Map();
2003
+ inFlightReads.set(ctx, perClient);
2004
+ }
2005
+ const key = readKey(request);
2006
+ const existing = perClient.get(key);
2007
+ if (existing !== void 0) return existing;
2008
+ const started = run().finally(() => {
2009
+ perClient.delete(key);
2010
+ });
2011
+ perClient.set(key, started);
2012
+ return started;
1989
2013
  }
1990
2014
  function executeBinary(ctx, request) {
1991
2015
  return runWithLadder(
@@ -2853,6 +2877,9 @@ function createDrainer(options) {
2853
2877
  remaining: report.remaining,
2854
2878
  usedBatchTransport: report.usedBatchTransport
2855
2879
  });
2880
+ if (report.applied > 0) {
2881
+ options.onLanesApplied?.(report.lanes);
2882
+ }
2856
2883
  return report;
2857
2884
  }
2858
2885
  let kickTimer;
@@ -3146,15 +3173,15 @@ function createReadCacheStore(options) {
3146
3173
  await options.storage.set(key, JSON.stringify(shape));
3147
3174
  }
3148
3175
  return {
3149
- get(readKey) {
3150
- return reads.get(readKey);
3176
+ get(readKey2) {
3177
+ return reads.get(readKey2);
3151
3178
  },
3152
- async set(readKey, value, serverTime) {
3153
- reads.set(readKey, { value, syncedAt: serverTime });
3179
+ async set(readKey2, value, serverTime) {
3180
+ reads.set(readKey2, { value, syncedAt: serverTime });
3154
3181
  await persist();
3155
3182
  },
3156
- async purge(readKey) {
3157
- if (!reads.delete(readKey)) {
3183
+ async purge(readKey2) {
3184
+ if (!reads.delete(readKey2)) {
3158
3185
  return;
3159
3186
  }
3160
3187
  await persist();
@@ -3171,9 +3198,9 @@ function createReadCacheStore(options) {
3171
3198
  } catch {
3172
3199
  return;
3173
3200
  }
3174
- for (const [readKey, entry] of Object.entries(parsed.reads ?? {})) {
3201
+ for (const [readKey2, entry] of Object.entries(parsed.reads ?? {})) {
3175
3202
  if (entry !== null && typeof entry === "object" && typeof entry.syncedAt === "string") {
3176
- reads.set(readKey, entry);
3203
+ reads.set(readKey2, entry);
3177
3204
  }
3178
3205
  }
3179
3206
  }
@@ -3576,22 +3603,45 @@ function createOfflineEngine(options) {
3576
3603
  ...options.share.monotonic !== void 0 ? { monotonic: options.share.monotonic } : {}
3577
3604
  });
3578
3605
  }
3606
+ const pull = createPullCoordinator({
3607
+ snapshot,
3608
+ transport: options.transport,
3609
+ events,
3610
+ onServerTime
3611
+ });
3579
3612
  const drainer = createDrainer({
3580
3613
  outbox,
3581
3614
  snapshot,
3582
3615
  transport: options.transport,
3583
3616
  events,
3584
3617
  onServerTime,
3618
+ /*
3619
+ * A DRAIN IS FOLLOWED BY A PULL, for the lanes it actually changed.
3620
+ *
3621
+ * Not every write rebases the snapshot: one enqueued without an
3622
+ * `entity`/`optimistic` pairing — deliberately, wherever the response
3623
+ * carries server-assigned fields a client cannot compute — reaches the
3624
+ * server and leaves the local snapshot untouched. Nothing used to close
3625
+ * that gap, so a host reading a snapshot-backed list saw its own
3626
+ * successful write missing from it until something reopened the screen.
3627
+ *
3628
+ * Failures are swallowed on purpose. This is a background reconcile behind
3629
+ * a write that has already succeeded; a pull that cannot reach the server
3630
+ * changes nothing the caller needs to hear about, and `pullTrip` has
3631
+ * already emitted whatever the host subscribes to. Rethrowing here would
3632
+ * surface a sync failure as if the WRITE had failed, which is the one
3633
+ * reading of it that is untrue.
3634
+ */
3635
+ onLanesApplied: (lanes) => {
3636
+ for (const tripRef of lanes) {
3637
+ void pull.pullTrip({ tripRef }).catch(() => {
3638
+ });
3639
+ }
3640
+ },
3585
3641
  ...options.conflictResolver !== void 0 ? { conflictResolver: options.conflictResolver } : {},
3586
3642
  ...options.now !== void 0 ? { now: options.now } : {},
3587
3643
  ...options.random !== void 0 ? { random: options.random } : {}
3588
3644
  });
3589
- const pull = createPullCoordinator({
3590
- snapshot,
3591
- transport: options.transport,
3592
- events,
3593
- onServerTime
3594
- });
3595
3645
  const blobs = options.blob === void 0 ? void 0 : createBlobLane({
3596
3646
  storage: options.storage,
3597
3647
  keyPrefix,