kaafil-js 0.1.0-beta.7 → 0.2.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.
@@ -1478,6 +1478,10 @@ var CredentialResolver = class {
1478
1478
  };
1479
1479
 
1480
1480
  // src/config.ts
1481
+ var Environment = {
1482
+ Live: "live",
1483
+ Test: "test"
1484
+ };
1481
1485
  var ENVIRONMENT_BASE_URLS = {
1482
1486
  live: "https://engine.kaafil.in",
1483
1487
  test: "https://engine.kaafil.in"
@@ -1488,6 +1492,16 @@ var KaafilConfigError = class extends Error {
1488
1492
  this.name = "KaafilConfigError";
1489
1493
  }
1490
1494
  };
1495
+ function describeConfigValue(value) {
1496
+ return typeof value === "string" ? `"${value}"` : String(value);
1497
+ }
1498
+ function assertValidEnvironment(environment) {
1499
+ if (environment !== Environment.Live && environment !== Environment.Test) {
1500
+ throw new KaafilConfigError(
1501
+ `"environment" is required and must be "${Environment.Live}" or "${Environment.Test}"; received ${describeConfigValue(environment)}.`
1502
+ );
1503
+ }
1504
+ }
1491
1505
  function isValidAbsoluteUrl(value) {
1492
1506
  try {
1493
1507
  const url = new URL(value);
@@ -1497,6 +1511,7 @@ function isValidAbsoluteUrl(value) {
1497
1511
  }
1498
1512
  }
1499
1513
  function resolveBaseUrl(environment, baseUrl) {
1514
+ assertValidEnvironment(environment);
1500
1515
  if (baseUrl === void 0) {
1501
1516
  return ENVIRONMENT_BASE_URLS[environment];
1502
1517
  }
@@ -2949,7 +2964,35 @@ var SYNC_PULL_SECTIONS = [
2949
2964
  "itinerary",
2950
2965
  "closeout"
2951
2966
  ];
2952
- function extractSectionRows(data) {
2967
+ var SECTION_ROW_KEY = {
2968
+ // Delta-shaped `rooms[]`; `windows[]`/`unassigned[]` are derived
2969
+ // projections — the long note above is still exactly right about this one.
2970
+ rooming: "rooms",
2971
+ // The same decision in the same shape: `vehicles[]` is the feed,
2972
+ // `unassignedPool[]` the projection that must never be cached as rows.
2973
+ seating: "vehicles",
2974
+ expenses: "items",
2975
+ float: "data",
2976
+ // The one that was silently dead. `sections[]` is a grouping projection and
2977
+ // `availableTemplates[]` is agency-level catalogue; `items[]` is the trip's
2978
+ // own entity list and the only one of the three carrying tombstones.
2979
+ checklist: "items",
2980
+ // `days[]` is the day-by-day projection the engine recomputes per read;
2981
+ // `items[]` is the entity list.
2982
+ itinerary: "items",
2983
+ closeout: null
2984
+ // `stayWindows`, `pickups`, `bookings` and `collections` send a top-level
2985
+ // array and never reach this table.
2986
+ };
2987
+ function extractSectionRows(data, section) {
2988
+ if (section !== void 0 && section in SECTION_ROW_KEY) {
2989
+ const key = SECTION_ROW_KEY[section];
2990
+ if (key === null || key === void 0 || typeof data !== "object" || data === null) {
2991
+ return void 0;
2992
+ }
2993
+ const value = data[key];
2994
+ return Array.isArray(value) && isRowArray(value) ? value : void 0;
2995
+ }
2953
2996
  if (Array.isArray(data)) {
2954
2997
  return isRowArray(data) ? data : void 0;
2955
2998
  }
@@ -3019,7 +3062,7 @@ function createPullCoordinator(options) {
3019
3062
  absent.push(name);
3020
3063
  continue;
3021
3064
  }
3022
- const rows = extractSectionRows(section.data);
3065
+ const rows = extractSectionRows(section.data, name);
3023
3066
  if (rows === void 0) {
3024
3067
  unmapped.push(name);
3025
3068
  continue;
@@ -3056,6 +3099,49 @@ function createPullCoordinator(options) {
3056
3099
  };
3057
3100
  }
3058
3101
 
3102
+ // src/offline/reads.ts
3103
+ function createReadCacheStore(options) {
3104
+ const key = `${options.keyPrefix}:reads`;
3105
+ let reads = /* @__PURE__ */ new Map();
3106
+ async function persist() {
3107
+ const shape = { reads: Object.fromEntries(reads) };
3108
+ await options.storage.set(key, JSON.stringify(shape));
3109
+ }
3110
+ return {
3111
+ get(readKey) {
3112
+ return reads.get(readKey);
3113
+ },
3114
+ async set(readKey, value, serverTime) {
3115
+ reads.set(readKey, { value, syncedAt: serverTime });
3116
+ await persist();
3117
+ },
3118
+ async purge(readKey) {
3119
+ if (!reads.delete(readKey)) {
3120
+ return;
3121
+ }
3122
+ await persist();
3123
+ },
3124
+ async load() {
3125
+ reads = /* @__PURE__ */ new Map();
3126
+ const raw = await options.storage.get(key);
3127
+ if (raw === void 0) {
3128
+ return;
3129
+ }
3130
+ let parsed;
3131
+ try {
3132
+ parsed = JSON.parse(raw);
3133
+ } catch {
3134
+ return;
3135
+ }
3136
+ for (const [readKey, entry] of Object.entries(parsed.reads ?? {})) {
3137
+ if (entry !== null && typeof entry === "object" && typeof entry.syncedAt === "string") {
3138
+ reads.set(readKey, entry);
3139
+ }
3140
+ }
3141
+ }
3142
+ };
3143
+ }
3144
+
3059
3145
  // src/offline/share.ts
3060
3146
  var SHARE_WRITE_GRACE_MS = 7 * 24 * 60 * 60 * 1e3;
3061
3147
  var KaafilShareLinkExpiredError = class _KaafilShareLinkExpiredError extends Error {
@@ -3430,6 +3516,7 @@ function createOfflineEngine(options) {
3430
3516
  ...options.onEventHandlerError !== void 0 ? { onHandlerError: (error) => options.onEventHandlerError?.(error) } : {}
3431
3517
  });
3432
3518
  const snapshot = createSnapshotStore({ storage: options.storage, keyPrefix });
3519
+ const reads = createReadCacheStore({ storage: options.storage, keyPrefix });
3433
3520
  const outbox = createOutbox({
3434
3521
  storage: options.storage,
3435
3522
  keyPrefix,
@@ -3502,14 +3589,21 @@ function createOfflineEngine(options) {
3502
3589
  return {
3503
3590
  events,
3504
3591
  snapshot,
3592
+ reads,
3505
3593
  outbox,
3506
3594
  drainer,
3507
3595
  pull,
3508
3596
  blobs,
3509
3597
  share,
3510
3598
  async open() {
3511
- await Promise.all([snapshot.load(), outbox.load(), blobs?.load() ?? Promise.resolve()]);
3599
+ await Promise.all([
3600
+ snapshot.load(),
3601
+ reads.load(),
3602
+ outbox.load(),
3603
+ blobs?.load() ?? Promise.resolve()
3604
+ ]);
3512
3605
  unbind = drainer.bindOpportunisticTriggers();
3606
+ drainer.kick();
3513
3607
  },
3514
3608
  enqueue,
3515
3609
  async enqueueWithBlob(input) {
@@ -7495,6 +7589,7 @@ exports.createOfflineEngine = createOfflineEngine;
7495
7589
  exports.createOfflineEvents = createOfflineEvents;
7496
7590
  exports.createOutbox = createOutbox;
7497
7591
  exports.createPullCoordinator = createPullCoordinator;
7592
+ exports.createReadCacheStore = createReadCacheStore;
7498
7593
  exports.createShareGuard = createShareGuard;
7499
7594
  exports.createSnapshotStore = createSnapshotStore;
7500
7595
  exports.createSyncResource = createSyncResource;