@osdk/client 2.2.0-beta.2 → 2.2.0-beta.4

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.
Files changed (94) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/build/browser/Logger.js.map +1 -1
  3. package/build/browser/createClient.js +3 -3
  4. package/build/browser/createClient.js.map +1 -1
  5. package/build/browser/observable/ObservableClient.js.map +1 -1
  6. package/build/browser/observable/internal/ActionApplication.js +102 -0
  7. package/build/browser/observable/internal/ActionApplication.js.map +1 -0
  8. package/build/browser/observable/internal/CacheKey.js +38 -1
  9. package/build/browser/observable/internal/CacheKey.js.map +1 -1
  10. package/build/browser/observable/internal/CacheKeys.js +4 -4
  11. package/build/browser/observable/internal/CacheKeys.js.map +1 -1
  12. package/build/browser/observable/internal/ChangedObjects.js +24 -1
  13. package/build/browser/observable/internal/ChangedObjects.js.map +1 -1
  14. package/build/browser/observable/internal/Layer.js +3 -3
  15. package/build/browser/observable/internal/Layer.js.map +1 -1
  16. package/build/browser/observable/internal/ListQuery.js +188 -66
  17. package/build/browser/observable/internal/ListQuery.js.map +1 -1
  18. package/build/browser/observable/internal/ObjectQuery.js +16 -3
  19. package/build/browser/observable/internal/ObjectQuery.js.map +1 -1
  20. package/build/browser/observable/internal/ObservableClientImpl.js +2 -2
  21. package/build/browser/observable/internal/ObservableClientImpl.js.map +1 -1
  22. package/build/browser/observable/internal/OptimisticJob.js +30 -29
  23. package/build/browser/observable/internal/OptimisticJob.js.map +1 -1
  24. package/build/browser/observable/internal/Query.js +42 -2
  25. package/build/browser/observable/internal/Query.js.map +1 -1
  26. package/build/browser/observable/internal/Store.js +259 -126
  27. package/build/browser/observable/internal/Store.js.map +1 -1
  28. package/build/browser/observable/internal/Store.test.js +416 -76
  29. package/build/browser/observable/internal/Store.test.js.map +1 -1
  30. package/build/browser/observable/internal/testUtils.js +142 -6
  31. package/build/browser/observable/internal/testUtils.js.map +1 -1
  32. package/build/browser/util/UserAgent.js +1 -1
  33. package/build/cjs/index.cjs +4 -4
  34. package/build/cjs/index.cjs.map +1 -1
  35. package/build/cjs/public/unstable-do-not-use.cjs +657 -268
  36. package/build/cjs/public/unstable-do-not-use.cjs.map +1 -1
  37. package/build/cjs/public/unstable-do-not-use.d.cts +13 -4
  38. package/build/esm/Logger.js.map +1 -1
  39. package/build/esm/createClient.js +3 -3
  40. package/build/esm/createClient.js.map +1 -1
  41. package/build/esm/observable/ObservableClient.js.map +1 -1
  42. package/build/esm/observable/internal/ActionApplication.js +102 -0
  43. package/build/esm/observable/internal/ActionApplication.js.map +1 -0
  44. package/build/esm/observable/internal/CacheKey.js +38 -1
  45. package/build/esm/observable/internal/CacheKey.js.map +1 -1
  46. package/build/esm/observable/internal/CacheKeys.js +4 -4
  47. package/build/esm/observable/internal/CacheKeys.js.map +1 -1
  48. package/build/esm/observable/internal/ChangedObjects.js +24 -1
  49. package/build/esm/observable/internal/ChangedObjects.js.map +1 -1
  50. package/build/esm/observable/internal/Layer.js +3 -3
  51. package/build/esm/observable/internal/Layer.js.map +1 -1
  52. package/build/esm/observable/internal/ListQuery.js +188 -66
  53. package/build/esm/observable/internal/ListQuery.js.map +1 -1
  54. package/build/esm/observable/internal/ObjectQuery.js +16 -3
  55. package/build/esm/observable/internal/ObjectQuery.js.map +1 -1
  56. package/build/esm/observable/internal/ObservableClientImpl.js +2 -2
  57. package/build/esm/observable/internal/ObservableClientImpl.js.map +1 -1
  58. package/build/esm/observable/internal/OptimisticJob.js +30 -29
  59. package/build/esm/observable/internal/OptimisticJob.js.map +1 -1
  60. package/build/esm/observable/internal/Query.js +42 -2
  61. package/build/esm/observable/internal/Query.js.map +1 -1
  62. package/build/esm/observable/internal/Store.js +259 -126
  63. package/build/esm/observable/internal/Store.js.map +1 -1
  64. package/build/esm/observable/internal/Store.test.js +416 -76
  65. package/build/esm/observable/internal/Store.test.js.map +1 -1
  66. package/build/esm/observable/internal/testUtils.js +142 -6
  67. package/build/esm/observable/internal/testUtils.js.map +1 -1
  68. package/build/esm/util/UserAgent.js +1 -1
  69. package/build/types/Logger.d.ts +1 -2
  70. package/build/types/Logger.d.ts.map +1 -1
  71. package/build/types/createClient.d.ts.map +1 -1
  72. package/build/types/observable/ObservableClient.d.ts +10 -3
  73. package/build/types/observable/ObservableClient.d.ts.map +1 -1
  74. package/build/types/observable/internal/ActionApplication.d.ts +9 -0
  75. package/build/types/observable/internal/ActionApplication.d.ts.map +1 -0
  76. package/build/types/observable/internal/CacheKeys.d.ts +2 -1
  77. package/build/types/observable/internal/CacheKeys.d.ts.map +1 -1
  78. package/build/types/observable/internal/ChangedObjects.d.ts +6 -2
  79. package/build/types/observable/internal/ChangedObjects.d.ts.map +1 -1
  80. package/build/types/observable/internal/Layer.d.ts +1 -1
  81. package/build/types/observable/internal/Layer.d.ts.map +1 -1
  82. package/build/types/observable/internal/ListQuery.d.ts +18 -17
  83. package/build/types/observable/internal/ListQuery.d.ts.map +1 -1
  84. package/build/types/observable/internal/ObjectQuery.d.ts +3 -3
  85. package/build/types/observable/internal/ObjectQuery.d.ts.map +1 -1
  86. package/build/types/observable/internal/OptimisticJob.d.ts +2 -2
  87. package/build/types/observable/internal/OptimisticJob.d.ts.map +1 -1
  88. package/build/types/observable/internal/Query.d.ts +6 -5
  89. package/build/types/observable/internal/Query.d.ts.map +1 -1
  90. package/build/types/observable/internal/Store.d.ts +47 -19
  91. package/build/types/observable/internal/Store.d.ts.map +1 -1
  92. package/build/types/observable/internal/testUtils.d.ts +13 -3
  93. package/build/types/observable/internal/testUtils.d.ts.map +1 -1
  94. package/package.json +7 -6
@@ -4,8 +4,8 @@ var chunkX7WGNFZ4_cjs = require('../chunk-X7WGNFZ4.cjs');
4
4
  require('../chunk-Q7SFCCGT.cjs');
5
5
  var rxjs = require('rxjs');
6
6
  var invariant2 = require('tiny-invariant');
7
- var trie = require('@wry/trie');
8
7
  var mnemonist = require('mnemonist');
8
+ var trie = require('@wry/trie');
9
9
  var deepEqual = require('fast-deep-equal');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -22,8 +22,8 @@ var ObservableClientImpl = class {
22
22
  observeObject(apiName, pk, options, subFn) {
23
23
  return this.#store.observeObject(apiName, pk, options, subFn);
24
24
  }
25
- observeList(apiName, where, options, subFn) {
26
- return this.#store.observeList(apiName, where, options, subFn);
25
+ observeList(options, subFn) {
26
+ return this.#store.observeList(options, subFn);
27
27
  }
28
28
  applyAction(action, args, opts) {
29
29
  return this.#store.applyAction(action, args, opts);
@@ -33,6 +33,10 @@ var ObservableClientImpl = class {
33
33
  }
34
34
  };
35
35
 
36
+ // src/observable/DebugFlags.ts
37
+ process.env.NODE_ENV !== "production" && false;
38
+ var DEBUG_CACHE_KEYS = process.env.NODE_ENV !== "production" && false;
39
+
36
40
  // ../../node_modules/.pnpm/is-node-process@1.2.0/node_modules/is-node-process/lib/index.mjs
37
41
  function isNodeProcess() {
38
42
  if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
@@ -86,9 +90,200 @@ async function delay(durationOrMode) {
86
90
  return new Promise((resolve) => setTimeout(resolve, delayTime));
87
91
  }
88
92
 
89
- // src/observable/DebugFlags.ts
90
- process.env.NODE_ENV !== "production" && false;
91
- var DEBUG_CACHE_KEYS = process.env.NODE_ENV !== "production" && false;
93
+ // src/observable/internal/CacheKey.ts
94
+ function DEBUG_ONLY__cacheKeyToString(x) {
95
+ if (process.env.NODE_ENV !== "production") {
96
+ return `${x.type}CacheKey<${x.otherKeys.map((xx) => JSON.stringify(xx)).join(", ")}>`.replaceAll('"', "'");
97
+ } else {
98
+ throw new Error("not implemented");
99
+ }
100
+ }
101
+ function DEBUG_ONLY__cacheKeysToString(x) {
102
+ if (process.env.NODE_ENV !== "production") {
103
+ return JSON.stringify(x.map(DEBUG_ONLY__cacheKeyToString), null, 2);
104
+ } else {
105
+ throw new Error("not implemented");
106
+ }
107
+ }
108
+
109
+ // src/observable/internal/ChangedObjects.ts
110
+ function createChangedObjects() {
111
+ return {
112
+ modifiedObjects: new mnemonist.MultiMap(),
113
+ addedObjects: new mnemonist.MultiMap(),
114
+ addedLists: /* @__PURE__ */ new Set(),
115
+ modifiedLists: /* @__PURE__ */ new Set()
116
+ };
117
+ }
118
+ function DEBUG_ONLY__changesToString(changes) {
119
+ if (process.env.NODE_ENV !== "production") {
120
+ return JSON.stringify({
121
+ modifiedObjects: multimapHelper(changes.modifiedObjects),
122
+ addedObjects: multimapHelper(changes.addedObjects),
123
+ addedLists: listHelper(changes.addedLists),
124
+ modifiedLists: listHelper(changes.modifiedLists)
125
+ }, null, 2);
126
+ } else {
127
+ throw new Error("not implemented");
128
+ }
129
+ }
130
+ function listHelper(set) {
131
+ return Array.from(set).map(DEBUG_ONLY__cacheKeyToString);
132
+ }
133
+ function multimapHelper(multimap) {
134
+ return Object.fromEntries(Array.from(multimap.associations()).map(([type, objects]) => {
135
+ return [type, objects.map((o) => o.$primaryKey)];
136
+ }));
137
+ }
138
+
139
+ // src/observable/internal/OptimisticId.ts
140
+ function createOptimisticId() {
141
+ if (process.env.NODE_ENV !== "production") {
142
+ if (createOptimisticId.counter === undefined) {
143
+ createOptimisticId.counter = 0;
144
+ }
145
+ return {
146
+ __optimisticId: createOptimisticId.counter++
147
+ };
148
+ }
149
+ return /* @__PURE__ */ Object.create(null);
150
+ }
151
+
152
+ // src/observable/internal/OptimisticJob.ts
153
+ var OptimisticJob = class {
154
+ #result;
155
+ constructor(store, optimisticId) {
156
+ const updatedObjects = [];
157
+ const addedObjectPromises = [];
158
+ this.getResult = () => {
159
+ return this.#result ??= (async () => {
160
+ const addedObjects = await Promise.allSettled(addedObjectPromises);
161
+ const {
162
+ batchResult
163
+ } = store.batch({
164
+ optimisticId
165
+ }, (batch) => {
166
+ for (const obj of addedObjects) {
167
+ if (obj.status === "fulfilled") {
168
+ store.getObjectQuery(obj.value.$objectType, obj.value.$primaryKey).writeToStore(obj.value, "loading", batch);
169
+ } else {
170
+ throw obj;
171
+ }
172
+ }
173
+ for (const obj of updatedObjects) {
174
+ store.getObjectQuery(obj.$objectType, obj.$primaryKey).writeToStore(obj, "loading", batch);
175
+ }
176
+ });
177
+ return batchResult.changes;
178
+ })();
179
+ };
180
+ this.context = {
181
+ updateObject(value) {
182
+ updatedObjects.push(value);
183
+ return this;
184
+ },
185
+ createObject(type, pk, properties) {
186
+ const create = store.client[chunkX7WGNFZ4_cjs.additionalContext].objectFactory2(store.client[chunkX7WGNFZ4_cjs.additionalContext], [{
187
+ $primaryKey: pk,
188
+ $apiName: type.apiName,
189
+ $objectType: type.apiName,
190
+ ...properties
191
+ }], undefined).then((objs) => {
192
+ return objs[0];
193
+ });
194
+ addedObjectPromises.push(create);
195
+ return this;
196
+ }
197
+ };
198
+ }
199
+ };
200
+ function runOptimisticJob(store, optimisticUpdate) {
201
+ if (!optimisticUpdate) {
202
+ return () => Promise.resolve();
203
+ }
204
+ const optimisticId = createOptimisticId();
205
+ const job = new OptimisticJob(store, optimisticId);
206
+ optimisticUpdate(job.context);
207
+ const optimisticApplicationDone = job.getResult();
208
+ return () => {
209
+ return optimisticApplicationDone.then(
210
+ // we don't want to leak the result
211
+ () => undefined
212
+ ).finally(() => {
213
+ store.removeLayer(optimisticId);
214
+ });
215
+ };
216
+ }
217
+
218
+ // src/observable/internal/ActionApplication.ts
219
+ var ACTION_DELAY = process.env.NODE_ENV === "production" ? 0 : 1e3;
220
+ var ActionApplication = class {
221
+ constructor(store) {
222
+ this.store = store;
223
+ }
224
+ applyAction = (action, args, {
225
+ optimisticUpdate
226
+ } = {}) => {
227
+ const removeOptimisticResult = runOptimisticJob(this.store, optimisticUpdate);
228
+ return (async () => {
229
+ try {
230
+ const actionResults = await this.store.client(action).applyAction(args, {
231
+ $returnEdits: true
232
+ });
233
+ if (ACTION_DELAY > 0) {
234
+ console.log("action done, pausing");
235
+ await delay(ACTION_DELAY);
236
+ console.log("action done, pausing done");
237
+ }
238
+ await this.#invalidateActionEditResponse(actionResults);
239
+ return actionResults;
240
+ } finally {
241
+ if (process.env.NODE_ENV !== "production") {
242
+ this.store.logger?.debug("optimistic action complete; remove the results");
243
+ }
244
+ await removeOptimisticResult();
245
+ }
246
+ })();
247
+ };
248
+ #invalidateActionEditResponse = async (value) => {
249
+ const typesToInvalidate = /* @__PURE__ */ new Set();
250
+ let changes;
251
+ if (value.type === "edits") {
252
+ const promisesToWait = [];
253
+ for (const obj of value.modifiedObjects) {
254
+ promisesToWait.push(this.store.invalidateObject(obj.objectType, obj.primaryKey));
255
+ }
256
+ for (const obj of value.addedObjects) {
257
+ promisesToWait.push(this.store.invalidateObject(obj.objectType, obj.primaryKey));
258
+ typesToInvalidate.add(obj.objectType);
259
+ }
260
+ await Promise.all(promisesToWait);
261
+ changes = this.#changesFromActionEditResponse(value);
262
+ await this.store.maybeRevalidateLists(changes);
263
+ } else {
264
+ for (const apiName of value.editedObjectTypes) {
265
+ typesToInvalidate.add(apiName.toString());
266
+ await this.store.invalidateObjectType(apiName, changes);
267
+ }
268
+ }
269
+ return value;
270
+ };
271
+ #changesFromActionEditResponse = (value) => {
272
+ const changes = createChangedObjects();
273
+ for (const changeType of ["addedObjects", "modifiedObjects"]) {
274
+ for (const {
275
+ objectType,
276
+ primaryKey
277
+ } of value[changeType] ?? []) {
278
+ const obj = this.store.getObject(objectType, primaryKey);
279
+ if (obj) {
280
+ changes[changeType].set(objectType, obj);
281
+ }
282
+ }
283
+ }
284
+ return changes;
285
+ };
286
+ };
92
287
  var CacheKeys = class {
93
288
  #cacheKeys = new trie.Trie(false, (keys) => {
94
289
  const ret = {
@@ -100,15 +295,15 @@ var CacheKeys = class {
100
295
  });
101
296
  #cacheKeyFactories = /* @__PURE__ */ new Map();
102
297
  #onCreate;
103
- constructor(whereCanonicalizer, onCreate) {
298
+ constructor(whereCanonicalizer, orderByCanonicalizer, onCreate) {
104
299
  this.#onCreate = onCreate;
105
300
  this.#registerCacheKeyFactory("object", (apiName, pk) => {
106
301
  if (process.env.NODE_ENV !== "production" && DEBUG_CACHE_KEYS) ;
107
302
  return this.#cacheKeys.lookupArray(["object", apiName, pk]);
108
303
  });
109
- this.#registerCacheKeyFactory("list", (apiName, where) => {
304
+ this.#registerCacheKeyFactory("list", (apiName, where, orderBy) => {
110
305
  if (process.env.NODE_ENV !== "production" && DEBUG_CACHE_KEYS) ;
111
- return this.#cacheKeys.lookupArray(["list", apiName, whereCanonicalizer.canonicalize(where)]);
306
+ return this.#cacheKeys.lookupArray(["list", apiName, whereCanonicalizer.canonicalize(where), orderByCanonicalizer.canonicalize(orderBy)]);
112
307
  });
113
308
  }
114
309
  #registerCacheKeyFactory(type, factory) {
@@ -123,12 +318,6 @@ var CacheKeys = class {
123
318
  this.#cacheKeys.remove(cacheKey.type, ...cacheKey.otherKeys);
124
319
  }
125
320
  };
126
- function createChangedObjects() {
127
- return {
128
- modifiedObjects: new mnemonist.MultiMap(),
129
- addedObjects: new mnemonist.MultiMap()
130
- };
131
- }
132
321
 
133
322
  // src/observable/internal/WeakMapWithEntries.ts
134
323
  var WeakMapWithEntries = class {
@@ -262,7 +451,7 @@ var Layer = class _Layer {
262
451
  this.#parent = this.#parent.removeLayer(layerId);
263
452
  return this;
264
453
  }
265
- return this.#parent ?? this;
454
+ return this.#parent.removeLayer(layerId);
266
455
  }
267
456
  entries() {
268
457
  return this.#cache.entries();
@@ -275,11 +464,11 @@ var Layer = class _Layer {
275
464
  }
276
465
  };
277
466
  var Entry = class {
278
- constructor(cacheKey, value, lastUpdated) {
467
+ constructor(cacheKey, value, lastUpdated, status = "init") {
279
468
  this.cacheKey = cacheKey;
280
469
  this.value = value;
281
470
  this.lastUpdated = lastUpdated;
282
- this.status = "init";
471
+ this.status = status;
283
472
  }
284
473
  };
285
474
  function is$and(whereClause) {
@@ -374,11 +563,15 @@ var Query = class {
374
563
  #connectable;
375
564
  #subscription;
376
565
  #subject;
377
- constructor(store, observable, opts, cacheKey) {
566
+ /** @internal */
567
+ constructor(store, observable, opts, cacheKey, logger) {
378
568
  this.options = opts;
379
569
  this.cacheKey = cacheKey;
380
570
  this.store = store;
381
571
  this.#subject = observable;
572
+ this.logger = logger ?? (process.env.NODE_ENV === "production" ? store.client[chunkX7WGNFZ4_cjs.additionalContext].logger : store.client[chunkX7WGNFZ4_cjs.additionalContext].logger?.child({}, {
573
+ msgPrefix: process.env.NODE_ENV !== "production" ? `Query<${cacheKey.type}, ${cacheKey.otherKeys.map((x) => JSON.stringify(x)).join(", ")}>` : "Query"
574
+ }));
382
575
  }
383
576
  subscribe(observer) {
384
577
  this.#connectable ??= this._createConnectable(this.#subject);
@@ -386,6 +579,11 @@ var Query = class {
386
579
  return this.#connectable.subscribe(observer);
387
580
  }
388
581
  revalidate(force) {
582
+ if (process.env.NODE_ENV !== "production") {
583
+ this.logger?.info({
584
+ methodName: "revalidate"
585
+ });
586
+ }
389
587
  if (force) {
390
588
  this.abortController?.abort();
391
589
  }
@@ -393,6 +591,11 @@ var Query = class {
393
591
  return this.pendingFetch;
394
592
  }
395
593
  if ((this.options.dedupeInterval ?? 0) > 0 && this.lastFetchStarted != null && Date.now() - this.lastFetchStarted < (this.options.dedupeInterval ?? 0)) {
594
+ if (process.env.NODE_ENV !== "production") {
595
+ this.logger?.trace({
596
+ methodName: "revalidate"
597
+ }, "DEDUPE");
598
+ }
396
599
  return Promise.resolve();
397
600
  }
398
601
  this.store.batch({}, (batch) => {
@@ -400,14 +603,37 @@ var Query = class {
400
603
  });
401
604
  this._preFetch();
402
605
  this.lastFetchStarted = Date.now();
403
- this.pendingFetch = this._fetch().finally(() => {
606
+ if (process.env.NODE_ENV !== "production") {
607
+ this.logger?.trace({
608
+ methodName: "revalidate"
609
+ }, "calling _fetch()");
610
+ }
611
+ this.pendingFetch = this._fetch().catch((e) => {
612
+ this.logger?.error({
613
+ methodName: "revalidate"
614
+ }, "_fetch() FAILED", e);
615
+ throw e;
616
+ }).finally(() => {
617
+ this.logger?.info({
618
+ methodName: "revalidate"
619
+ }, "finally _fetch()");
404
620
  this.pendingFetch = undefined;
405
621
  });
622
+ if (process.env.NODE_ENV !== "production") {
623
+ this.logger?.info({
624
+ methodName: "revalidate"
625
+ }, "Returning");
626
+ }
406
627
  return this.pendingFetch;
407
628
  }
408
629
  _preFetch() {
409
630
  }
410
631
  setStatus(status, batch) {
632
+ if (process.env.NODE_ENV !== "production") {
633
+ this.logger?.trace({
634
+ methodName: "setStatus"
635
+ }, status);
636
+ }
411
637
  const existing = batch.read(this.cacheKey);
412
638
  if (existing?.status === status) return;
413
639
  batch.write(this.cacheKey, existing?.value, status);
@@ -424,7 +650,6 @@ var Query = class {
424
650
  };
425
651
 
426
652
  // src/observable/internal/ListQuery.ts
427
- rxjs.auditTime(0);
428
653
  var ListQuery = class extends Query {
429
654
  // pageSize?: number; // this is the internal page size. we need to track this properly
430
655
  #client;
@@ -435,16 +660,23 @@ var ListQuery = class extends Query {
435
660
  #nextPageToken;
436
661
  #pendingPageFetch;
437
662
  #toRelease = /* @__PURE__ */ new Set();
438
- constructor(store, subject, type, whereClause, cacheKey, opts) {
439
- super(store, subject, opts, cacheKey);
663
+ #orderBy;
664
+ constructor(store, subject, objectType, whereClause, orderBy, cacheKey, opts) {
665
+ super(store, subject, opts, cacheKey, process.env.NODE_ENV !== "production" ? store.client[chunkX7WGNFZ4_cjs.additionalContext].logger?.child({}, {
666
+ msgPrefix: `ListQuery<${cacheKey.otherKeys.map((x) => JSON.stringify(x)).join(", ")}>`
667
+ }) : undefined);
440
668
  this.#client = store.client;
441
- this.#type = type;
669
+ this.#type = objectType;
442
670
  this.#whereClause = whereClause;
671
+ this.#orderBy = orderBy;
443
672
  rxjs.observeOn(rxjs.asyncScheduler);
444
673
  }
674
+ get canonicalWhere() {
675
+ return this.#whereClause;
676
+ }
445
677
  _createConnectable(subject) {
446
678
  return rxjs.connectable(subject.pipe(
447
- rxjs.mergeMap((listEntry) => {
679
+ rxjs.switchMap((listEntry) => {
448
680
  return rxjs.combineLatest({
449
681
  resolvedList: listEntry?.value?.data == null ? rxjs.of([]) : rxjs.combineLatest(listEntry.value.data.map((cacheKey) => this.store.getSubject(cacheKey).pipe(rxjs.map((objectEntry) => objectEntry?.value)))),
450
682
  isOptimistic: rxjs.of(listEntry.isOptimistic),
@@ -518,7 +750,12 @@ var ListQuery = class extends Query {
518
750
  nextPageToken
519
751
  } = await objectSet.fetchPage({
520
752
  $nextPageToken: this.#nextPageToken,
521
- $pageSize: this.options.pageSize
753
+ $pageSize: this.options.pageSize,
754
+ // For now this keeps the shared test code from falling apart
755
+ // but shouldn't be needed ideally
756
+ ...Object.keys(this.#orderBy).length > 0 ? {
757
+ $orderBy: this.#orderBy
758
+ } : {}
522
759
  });
523
760
  if (signal?.aborted) {
524
761
  return;
@@ -527,70 +764,121 @@ var ListQuery = class extends Query {
527
764
  const {
528
765
  retVal
529
766
  } = this.store.batch({}, (batch) => {
530
- return this.updateList(data, append, nextPageToken ? status : "loaded", batch);
767
+ return this.updateList(this.store.updateObjects(data, batch), append, nextPageToken ? status : "loaded", batch);
531
768
  });
532
769
  return retVal;
533
770
  }
534
771
  /**
535
- * Caller is responsible for removing the layer
536
- *
537
- * @param changedObjects
772
+ * Note: This method is not async because I want it to return right after it
773
+ * finishes the synchronous updates. The promise that is returned
774
+ * will resolve after the revalidation is complete.
775
+ * @param changes
538
776
  * @param optimisticId
539
- * @returns
777
+ * @returns If revalidation is needed, a promise that resolves after the
778
+ * revalidation is complete. Otherwise, undefined.
540
779
  */
541
- maybeUpdate(changedObjects, optimisticId) {
542
- let needsRevalidation = false;
543
- const objectsToInsert = [];
544
- for (const [type, objects] of changedObjects.addedObjects.associations()) {
545
- if (this.cacheKey.otherKeys[0] !== type) {
546
- continue;
780
+ maybeUpdateAndRevalidate = (changes, optimisticId) => {
781
+ if (process.env.NODE_ENV !== "production") {
782
+ this.logger?.info({
783
+ methodName: "#maybeMaybe"
784
+ }, DEBUG_ONLY__changesToString(changes));
785
+ }
786
+ if (changes.modifiedLists.has(this.cacheKey)) return;
787
+ try {
788
+ const relevantObjects = {
789
+ added: {
790
+ all: changes.addedObjects.get(this.cacheKey.otherKeys[0]) ?? [],
791
+ strictMatches: /* @__PURE__ */ new Set(),
792
+ sortaMatches: /* @__PURE__ */ new Set()
793
+ },
794
+ modified: {
795
+ all: changes.modifiedObjects.get(this.cacheKey.otherKeys[0]) ?? [],
796
+ strictMatches: /* @__PURE__ */ new Set(),
797
+ sortaMatches: /* @__PURE__ */ new Set()
798
+ }
799
+ };
800
+ if (relevantObjects.added.all.length === 0 && relevantObjects.modified.all.length === 0) {
801
+ return;
547
802
  }
548
- for (const obj of objects) {
549
- const strictMatch = objectSortaMatchesWhereClause(obj, this.#whereClause, true);
550
- if (strictMatch) {
551
- objectsToInsert.push(obj);
552
- } else {
553
- const sortaMatch = objectSortaMatchesWhereClause(obj, this.#whereClause, false);
554
- if (sortaMatch) {
555
- needsRevalidation = true;
803
+ for (const group of Object.values(relevantObjects)) {
804
+ for (const obj of group.all ?? []) {
805
+ const strictMatch = objectSortaMatchesWhereClause(obj, this.#whereClause, true);
806
+ if (strictMatch) {
807
+ group.strictMatches.add(obj);
808
+ } else {
809
+ const sortaMatch = objectSortaMatchesWhereClause(obj, this.#whereClause, false);
810
+ if (sortaMatch) {
811
+ group.sortaMatches.add(obj);
812
+ }
556
813
  }
557
814
  }
558
815
  }
559
- }
560
- needsRevalidation ||= objectsToInsert.length > 0;
561
- if (objectsToInsert.length > 0) {
816
+ const status = optimisticId || relevantObjects.added.sortaMatches.size > 0 || relevantObjects.modified.sortaMatches.size > 0 ? "loading" : "loaded";
817
+ changes.modifiedLists.add(this.cacheKey);
818
+ const newList = [];
819
+ let needsRevalidation = false;
562
820
  this.store.batch({
563
- optimisticId
821
+ optimisticId,
822
+ changes
564
823
  }, (batch) => {
565
- this.updateList(objectsToInsert, true, "loading", batch);
824
+ const curValue = batch.read(this.cacheKey);
825
+ const existingList = new Set(curValue?.value?.data);
826
+ const toAdd = new Set(
827
+ // easy case. objects are new to the cache and they match this filter
828
+ relevantObjects.added.strictMatches
829
+ );
830
+ const toRemove = /* @__PURE__ */ new Set();
831
+ for (const obj of relevantObjects.modified.all) {
832
+ if (relevantObjects.modified.strictMatches.has(obj)) {
833
+ const existingObjectCacheKey = this.store.getCacheKey("object", obj.$apiName, obj.$primaryKey);
834
+ if (!existingList.has(existingObjectCacheKey)) {
835
+ toAdd.add(obj);
836
+ }
837
+ continue;
838
+ } else if (batch.optimisticWrite) {
839
+ continue;
840
+ } else {
841
+ const existingObjectCacheKey = this.store.getCacheKey("object", obj.$apiName, obj.$primaryKey);
842
+ toRemove.add(existingObjectCacheKey);
843
+ if (relevantObjects.modified.sortaMatches.has(obj)) {
844
+ needsRevalidation = true;
845
+ }
846
+ }
847
+ }
848
+ for (const key of existingList) {
849
+ if (toRemove.has(key)) continue;
850
+ newList.push(key);
851
+ }
852
+ for (const obj of toAdd) {
853
+ newList.push(this.store.getCacheKey("object", obj.$apiName, obj.$primaryKey));
854
+ }
855
+ this.updateList(
856
+ newList,
857
+ /* append */
858
+ false,
859
+ status,
860
+ batch
861
+ );
566
862
  });
567
- }
568
- return needsRevalidation;
569
- }
570
- maybeRevalidate(changedObjects) {
571
- let needsRevalidation = false;
572
- for (const [type, objects] of changedObjects.addedObjects.associations()) {
573
- if (this.cacheKey.otherKeys[0] !== type) {
574
- continue;
863
+ if (needsRevalidation) {
864
+ changes.modifiedLists.add(this.cacheKey);
865
+ return this.revalidate(true).then(() => void 0);
575
866
  }
576
- for (const obj of objects) {
577
- const sortaMatch = objectSortaMatchesWhereClause(obj, this.#whereClause, false);
578
- if (sortaMatch) {
579
- needsRevalidation = true;
580
- }
867
+ return void 0;
868
+ } finally {
869
+ if (process.env.NODE_ENV !== "production") {
870
+ this.logger?.trace({
871
+ methodName: "#maybeMaybe"
872
+ }, "in finally");
581
873
  }
582
874
  }
583
- if (needsRevalidation) {
584
- return this.revalidate(true);
875
+ };
876
+ updateList(objectCacheKeys, append, status, batch) {
877
+ if (process.env.NODE_ENV !== "production") {
878
+ this.logger?.trace({
879
+ methodName: "updateList"
880
+ }, `{status: ${status}}`, JSON.stringify(objectCacheKeys, null, 2));
585
881
  }
586
- return Promise.resolve();
587
- }
588
- updateList(values, append, status, batch) {
589
- let objectCacheKeys = values.map((v) => {
590
- if (v instanceof Entry) return v.cacheKey;
591
- this.store.getObjectQuery(this.#type, v.$primaryKey).writeToStore(v, "loaded", batch);
592
- return this.store.getCacheKey("object", v.$apiName, v.$primaryKey);
593
- });
594
882
  const existingList = batch.read(this.cacheKey);
595
883
  if (!batch.optimisticWrite) {
596
884
  if (!append) {
@@ -607,17 +895,67 @@ var ListQuery = class extends Query {
607
895
  if (append) {
608
896
  objectCacheKeys = [...existingList?.value?.data ?? [], ...objectCacheKeys];
609
897
  }
898
+ if (Object.keys(this.#orderBy).length > 0) {
899
+ if (process.env.NODE_ENV !== "production") {
900
+ this.logger?.info({
901
+ methodName: "updateList"
902
+ }, "Sorting entries");
903
+ this.logger?.trace({
904
+ methodName: "updateList"
905
+ }, DEBUG_ONLY__cacheKeysToString(objectCacheKeys));
906
+ }
907
+ const sortFns = Object.entries(this.#orderBy).map(([key, order]) => {
908
+ return (a, b) => {
909
+ const aValue = a?.[key];
910
+ const bValue = b?.[key];
911
+ if (aValue == null && bValue == null) {
912
+ return 0;
913
+ }
914
+ if (aValue == null) {
915
+ return 1;
916
+ }
917
+ if (bValue == null) {
918
+ return -1;
919
+ }
920
+ const m = order === "asc" ? -1 : 1;
921
+ return aValue < bValue ? m : aValue > bValue ? -m : 0;
922
+ };
923
+ });
924
+ objectCacheKeys = objectCacheKeys.sort((a, b) => {
925
+ for (const sortFn of sortFns) {
926
+ const ret = sortFn(batch.read(a)?.value, batch.read(b)?.value);
927
+ if (ret !== 0) {
928
+ return ret;
929
+ }
930
+ }
931
+ return 0;
932
+ });
933
+ }
934
+ const visited = /* @__PURE__ */ new Set();
935
+ objectCacheKeys = objectCacheKeys.filter((key) => {
936
+ batch.read(key);
937
+ if (visited.has(key)) {
938
+ return false;
939
+ }
940
+ visited.add(key);
941
+ return true;
942
+ });
610
943
  return this.writeToStore({
611
944
  data: objectCacheKeys
612
945
  }, status, batch);
613
946
  }
614
947
  writeToStore(data, status, batch) {
948
+ if (process.env.NODE_ENV !== "production") {
949
+ this.logger?.trace({
950
+ methodName: "writeToStore"
951
+ }, `{status: ${status}},`, DEBUG_ONLY__cacheKeysToString(data.data));
952
+ }
615
953
  const entry = batch.read(this.cacheKey);
616
954
  if (entry && deepEqual__default.default(data, entry.value)) {
617
955
  return batch.write(this.cacheKey, entry.value, status);
618
956
  }
619
957
  const ret = batch.write(this.cacheKey, data, status);
620
- batch.modifiedLists.add(this.cacheKey);
958
+ batch.changes.modifiedLists.add(this.cacheKey);
621
959
  return ret;
622
960
  }
623
961
  _dispose() {
@@ -639,7 +977,9 @@ var ObjectQuery = class extends Query {
639
977
  #apiName;
640
978
  #pk;
641
979
  constructor(store, subject, type, pk, cacheKey, opts) {
642
- super(store, subject, opts, cacheKey);
980
+ super(store, subject, opts, cacheKey, process.env.NODE_ENV !== "production" ? store.client[chunkX7WGNFZ4_cjs.additionalContext].logger?.child({}, {
981
+ msgPrefix: `ObjectQuery<${cacheKey.otherKeys.map((x) => JSON.stringify(x)).join(", ")}>`
982
+ }) : undefined);
643
983
  this.#apiName = type;
644
984
  this.#pk = pk;
645
985
  }
@@ -661,6 +1001,11 @@ var ObjectQuery = class extends Query {
661
1001
  });
662
1002
  }
663
1003
  async _fetch() {
1004
+ if (process.env.NODE_ENV !== "production") {
1005
+ this.logger?.info({
1006
+ methodName: "_fetch"
1007
+ });
1008
+ }
664
1009
  const objectSet = this.store.client({
665
1010
  type: "object",
666
1011
  apiName: this.#apiName
@@ -671,103 +1016,25 @@ var ObjectQuery = class extends Query {
671
1016
  });
672
1017
  }
673
1018
  writeToStore(data, status, batch) {
1019
+ if (process.env.NODE_ENV !== "production") {
1020
+ this.logger?.trace({
1021
+ methodName: "writeToStore"
1022
+ }, `{status: ${status}},`, data);
1023
+ }
674
1024
  const entry = batch.read(this.cacheKey);
675
1025
  if (entry && deepEqual__default.default(data, entry.value)) {
676
1026
  return batch.write(this.cacheKey, entry.value, status);
677
1027
  }
678
1028
  const ret = batch.write(this.cacheKey, data, status);
679
1029
  if (entry) {
680
- batch.modifiedObjects.add(this.cacheKey);
1030
+ batch.changes.modifiedObjects.set(data.$apiName, data);
681
1031
  } else {
682
- batch.addedObjects.add(this.cacheKey);
1032
+ batch.changes.addedObjects.set(data.$apiName, data);
683
1033
  }
684
1034
  return ret;
685
1035
  }
686
1036
  };
687
1037
 
688
- // src/observable/internal/OptimisticId.ts
689
- function createOptimisticId() {
690
- if (process.env.NODE_ENV !== "production") {
691
- if (createOptimisticId.counter === undefined) {
692
- createOptimisticId.counter = 0;
693
- }
694
- return {
695
- __optimisticId: createOptimisticId.counter++
696
- };
697
- }
698
- return /* @__PURE__ */ Object.create(null);
699
- }
700
-
701
- // src/observable/internal/OptimisticJob.ts
702
- var OptimisticJob = class {
703
- #result;
704
- constructor(store, optimisticId) {
705
- const updatedObjects = [];
706
- const addedObjects = [];
707
- this.getResult = () => {
708
- return this.#result ??= (async () => {
709
- const changes = {
710
- addedObjects: new mnemonist.MultiMap(),
711
- modifiedObjects: new mnemonist.MultiMap()
712
- };
713
- const settled = await Promise.allSettled(addedObjects);
714
- for (const added of settled) {
715
- if (added.status === "fulfilled") {
716
- changes.addedObjects.set(added.value.$objectType, added.value);
717
- } else {
718
- throw added;
719
- }
720
- }
721
- for (const modified of updatedObjects) {
722
- changes.modifiedObjects.set(modified.$apiName, modified);
723
- }
724
- store.batch({
725
- optimisticId
726
- }, (batch) => {
727
- for (const a of ["addedObjects", "modifiedObjects"]) {
728
- for (const b of changes[a].values()) {
729
- store.getObjectQuery(b.$objectType, b.$primaryKey).writeToStore(b, "loading", batch);
730
- }
731
- }
732
- });
733
- return changes;
734
- })();
735
- };
736
- this.context = {
737
- updateObject(value) {
738
- updatedObjects.push(value);
739
- return this;
740
- },
741
- createObject(type, pk, properties) {
742
- const create = store.client[chunkX7WGNFZ4_cjs.additionalContext].objectFactory2(store.client[chunkX7WGNFZ4_cjs.additionalContext], [{
743
- $primaryKey: pk,
744
- $apiName: type.apiName,
745
- $objectType: type.apiName,
746
- ...properties
747
- }], undefined).then((x) => x[0]);
748
- addedObjects.push(create);
749
- return this;
750
- }
751
- };
752
- }
753
- };
754
- function runOptimisticJob(store, optimisticUpdate) {
755
- if (!optimisticUpdate) {
756
- return () => Promise.resolve();
757
- }
758
- const optimisticId = createOptimisticId();
759
- const job = new OptimisticJob(store, optimisticId);
760
- optimisticUpdate(job.context);
761
- const optimisticApplicationDone = job.getResult().then((result) => {
762
- store.maybeUpdateLists(result, optimisticId);
763
- });
764
- return () => {
765
- return optimisticApplicationDone.finally(() => {
766
- store.removeLayer(optimisticId);
767
- });
768
- };
769
- }
770
-
771
1038
  // src/observable/internal/RefCounts.ts
772
1039
  var RefCounts = class {
773
1040
  refCounts = /* @__PURE__ */ new Map();
@@ -871,7 +1138,6 @@ var WhereClauseCanonicalizer = class {
871
1138
  };
872
1139
 
873
1140
  // src/observable/internal/Store.ts
874
- var ACTION_DELAY = process.env.NODE_ENV === "production" ? 0 : 1e3;
875
1141
  function createInitEntry(cacheKey) {
876
1142
  return {
877
1143
  cacheKey,
@@ -880,19 +1146,36 @@ function createInitEntry(cacheKey) {
880
1146
  lastUpdated: 0
881
1147
  };
882
1148
  }
1149
+ var OrderByCanonicalizer = class {
1150
+ // crappy version
1151
+ #map = /* @__PURE__ */ new Map();
1152
+ canonicalize = (orderBy) => {
1153
+ if (this.#map.has(JSON.stringify(orderBy))) {
1154
+ return this.#map.get(JSON.stringify(orderBy));
1155
+ } else {
1156
+ this.#map.set(JSON.stringify(orderBy), orderBy);
1157
+ return orderBy;
1158
+ }
1159
+ };
1160
+ };
883
1161
  var Store = class {
884
1162
  whereCanonicalizer = new WhereClauseCanonicalizer();
1163
+ orderByCanonicalizer = new OrderByCanonicalizer();
885
1164
  #truthLayer = new Layer(undefined, undefined);
886
1165
  #topLayer;
887
- #queries = /* @__PURE__ */ new WeakMap();
1166
+ /** @internal */
1167
+ #queries = new WeakMapWithEntries();
888
1168
  #cacheKeyToSubject = /* @__PURE__ */ new WeakMap();
889
1169
  #cacheKeys;
890
1170
  #refCounts = new RefCounts(6e4, (k) => this.#cleanupCacheKey(k));
891
1171
  #finalizationRegistry;
892
1172
  constructor(client) {
893
1173
  this.client = client;
1174
+ this.logger = client[chunkX7WGNFZ4_cjs.additionalContext].logger?.child({}, {
1175
+ msgPrefix: "Store"
1176
+ });
894
1177
  this.#topLayer = this.#truthLayer;
895
- this.#cacheKeys = new CacheKeys(this.whereCanonicalizer, (k) => {
1178
+ this.#cacheKeys = new CacheKeys(this.whereCanonicalizer, this.orderByCanonicalizer, (k) => {
896
1179
  this.#refCounts.register(k);
897
1180
  });
898
1181
  setInterval(() => {
@@ -977,7 +1260,13 @@ var Store = class {
977
1260
  const query = this.getObjectQuery(apiName, pk);
978
1261
  this.#refCounts.retain(query.cacheKey);
979
1262
  if (options.mode !== "offline") {
980
- void query.revalidate(options.mode === "force");
1263
+ query.revalidate(options.mode === "force").catch((e) => {
1264
+ if (this.logger) {
1265
+ this.logger.error("Unhandled error in observeObject", e);
1266
+ } else {
1267
+ throw e;
1268
+ }
1269
+ });
981
1270
  }
982
1271
  const sub = query.subscribe({
983
1272
  next: subFn
@@ -989,11 +1278,8 @@ var Store = class {
989
1278
  }
990
1279
  };
991
1280
  }
992
- observeList(apiName, where, options, subFn) {
993
- if (typeof apiName !== "string") {
994
- apiName = apiName.apiName;
995
- }
996
- const query = this.getListQuery(apiName, where, options);
1281
+ observeList(options, subFn) {
1282
+ const query = this.getListQuery(options.objectType, options.where ?? {}, options.orderBy ?? {}, options);
997
1283
  this.#refCounts.retain(query.cacheKey);
998
1284
  if (options.mode !== "offline") {
999
1285
  void query.revalidate(options.mode === "force");
@@ -1001,6 +1287,99 @@ var Store = class {
1001
1287
  const sub = query.subscribe({
1002
1288
  next: subFn
1003
1289
  });
1290
+ if (options.streamUpdates) {
1291
+ const miniDef = {
1292
+ type: "object",
1293
+ apiName: typeof options.objectType === "string" ? options.objectType : options.objectType.apiName
1294
+ };
1295
+ let objectSet = this.client(miniDef);
1296
+ if (options.where) {
1297
+ objectSet = objectSet.where(options.where ?? {});
1298
+ }
1299
+ const store = this;
1300
+ const websocketSubscription = objectSet.subscribe({
1301
+ onChange({
1302
+ object,
1303
+ state
1304
+ }) {
1305
+ if (process.env.NODE_ENV !== "production") {
1306
+ store.logger?.debug({
1307
+ methodName: "onError"
1308
+ }, "updates", state, object);
1309
+ }
1310
+ const cacheKey = store.getCacheKey("object", object.$objectType, object.$primaryKey);
1311
+ const type = store.#peekQuery(cacheKey) == null ? "addedObjects" : "modifiedObjects";
1312
+ const changes = createChangedObjects();
1313
+ changes[type].set(object.$objectType, object);
1314
+ if (state === "ADDED_OR_UPDATED") {
1315
+ store.updateObject(object.$objectType, object);
1316
+ store.maybeRevalidateLists(changes).catch((err) => {
1317
+ console.error("Unhandled error in maybeRevalidateLists", err);
1318
+ });
1319
+ } else if (state === "REMOVED") {
1320
+ const changes2 = createChangedObjects();
1321
+ store.batch({
1322
+ changes: changes2
1323
+ }, (batch) => {
1324
+ const existing = batch.read(query.cacheKey);
1325
+ const cacheKeyToRemove = store.getCacheKey("object", object.$objectType, object.$primaryKey);
1326
+ if (existing?.status === "loaded") {
1327
+ const newObjects = existing.value?.data.filter((o) => o !== cacheKeyToRemove);
1328
+ if (newObjects?.length !== existing.value?.data.length) {
1329
+ batch.changes.modifiedLists.add(query.cacheKey);
1330
+ batch.write(query.cacheKey, {
1331
+ data: newObjects ?? []
1332
+ }, "loaded");
1333
+ }
1334
+ } else {
1335
+ if (process.env.NODE_ENV !== "production") {
1336
+ store.logger?.info("Removing an object from an object list that is in the middle of being loaded.", existing);
1337
+ }
1338
+ query.revalidate(
1339
+ /* force */
1340
+ true
1341
+ ).catch((e) => {
1342
+ if (store.logger) {
1343
+ store.logger?.error("Uncaught error while revalidating list", e);
1344
+ } else {
1345
+ console.error("Uncaught error while revalidating list", e);
1346
+ }
1347
+ });
1348
+ }
1349
+ });
1350
+ }
1351
+ },
1352
+ onError(errors) {
1353
+ if (process.env.NODE_ENV !== "production") {
1354
+ store.logger?.info({
1355
+ methodName: "onError"
1356
+ }, "subscription errors", errors);
1357
+ }
1358
+ },
1359
+ onOutOfDate() {
1360
+ if (process.env.NODE_ENV !== "production") {
1361
+ store.logger?.info({
1362
+ methodName: "onOutOfDate"
1363
+ });
1364
+ }
1365
+ },
1366
+ onSuccessfulSubscription() {
1367
+ if (process.env.NODE_ENV !== "production") {
1368
+ store.logger?.info({
1369
+ methodName: "onSuccessfulSubscription"
1370
+ });
1371
+ }
1372
+ }
1373
+ });
1374
+ sub.add(() => {
1375
+ if (process.env.NODE_ENV !== "production") {
1376
+ store.logger?.info({
1377
+ methodName: "observeList"
1378
+ }, "Unsubscribing from websocket");
1379
+ }
1380
+ websocketSubscription.unsubscribe();
1381
+ });
1382
+ }
1004
1383
  return {
1005
1384
  unsubscribe: () => {
1006
1385
  sub.unsubscribe();
@@ -1019,14 +1398,15 @@ var Store = class {
1019
1398
  }
1020
1399
  return query;
1021
1400
  }
1022
- getListQuery(apiName, where, opts, peek = false) {
1401
+ getListQuery(apiName, where, orderBy, opts) {
1023
1402
  if (typeof apiName !== "string") {
1024
1403
  apiName = apiName.apiName;
1025
1404
  }
1026
1405
  const canonWhere = this.whereCanonicalizer.canonicalize(where);
1027
- const listCacheKey = this.getCacheKey("list", apiName, canonWhere);
1406
+ const canonOrderBy = this.orderByCanonicalizer.canonicalize(orderBy);
1407
+ const listCacheKey = this.getCacheKey("list", apiName, canonWhere, canonOrderBy);
1028
1408
  return this.#getQuery(listCacheKey, () => {
1029
- return new ListQuery(this, this.getSubject(listCacheKey), apiName, canonWhere, listCacheKey, opts);
1409
+ return new ListQuery(this, this.getSubject(listCacheKey), apiName, canonWhere, canonOrderBy, listCacheKey, opts);
1030
1410
  });
1031
1411
  }
1032
1412
  getObjectQuery(apiName, pk) {
@@ -1047,14 +1427,13 @@ var Store = class {
1047
1427
  return objEntry?.value;
1048
1428
  }
1049
1429
  batch = ({
1050
- optimisticId
1430
+ optimisticId,
1431
+ changes = createChangedObjects()
1051
1432
  }, batchFn) => {
1052
1433
  !(optimisticId === undefined || !!optimisticId) ? process.env.NODE_ENV !== "production" ? invariant2__default.default(false, "optimistic must be undefined or not falsy") : invariant2__default.default(false) : undefined;
1053
1434
  let needsLayer = optimisticId !== undefined;
1054
1435
  const batchContext = {
1055
- addedObjects: /* @__PURE__ */ new Set(),
1056
- modifiedObjects: /* @__PURE__ */ new Set(),
1057
- modifiedLists: /* @__PURE__ */ new Set(),
1436
+ changes,
1058
1437
  createLayerIfNeeded: () => {
1059
1438
  if (needsLayer) {
1060
1439
  this.#topLayer = this.#topLayer.addLayer(optimisticId);
@@ -1066,16 +1445,12 @@ var Store = class {
1066
1445
  const oldTopValue = this.#topLayer.get(cacheKey);
1067
1446
  if (optimisticId) batchContext.createLayerIfNeeded();
1068
1447
  const writeLayer = optimisticId ? this.#topLayer : this.#truthLayer;
1069
- const newValue = {
1070
- cacheKey,
1071
- value,
1072
- lastUpdated: Date.now(),
1073
- status
1074
- };
1448
+ const newValue = new Entry(cacheKey, value, Date.now(), status);
1075
1449
  writeLayer.set(cacheKey, newValue);
1076
1450
  const newTopValue = this.#topLayer.get(cacheKey);
1077
1451
  if (oldTopValue !== newTopValue) {
1078
1452
  this.#cacheKeyToSubject.get(cacheKey)?.next({
1453
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
1079
1454
  ...newValue,
1080
1455
  isOptimistic: newTopValue?.value !== this.#truthLayer.get(cacheKey)?.value
1081
1456
  });
@@ -1087,6 +1462,7 @@ var Store = class {
1087
1462
  }
1088
1463
  };
1089
1464
  const retVal = batchFn(batchContext);
1465
+ void this.maybeUpdateLists(changes, optimisticId);
1090
1466
  return {
1091
1467
  batchResult: batchContext,
1092
1468
  retVal
@@ -1099,35 +1475,90 @@ var Store = class {
1099
1475
  const query = this.getObjectQuery(apiName, pk);
1100
1476
  return query.revalidate(true);
1101
1477
  }
1102
- maybeRevalidateLists(changes) {
1103
- for (const [cacheKey, v] of this.#truthLayer.entries()) {
1104
- if (isListCacheKey(cacheKey)) {
1105
- void this.#peekQuery(cacheKey)?.maybeRevalidate(changes);
1478
+ async maybeRevalidateLists(changes) {
1479
+ if (process.env.NODE_ENV !== "production") {
1480
+ this.logger?.trace({
1481
+ methodName: "maybeRevalidateList"
1482
+ }, DEBUG_ONLY__changesToString(changes));
1483
+ }
1484
+ try {
1485
+ const promises = [];
1486
+ for (const [cacheKey, v] of this.#truthLayer.entries()) {
1487
+ if (isListCacheKey(cacheKey)) {
1488
+ const promise = this.#peekQuery(cacheKey)?.maybeUpdateAndRevalidate(changes, void 0);
1489
+ if (promise) promises.push(promise);
1490
+ }
1491
+ }
1492
+ await Promise.all(promises);
1493
+ } finally {
1494
+ if (process.env.NODE_ENV !== "production") {
1495
+ this.logger?.trace({
1496
+ methodName: "maybeRevalidateList"
1497
+ }, "in finally", DEBUG_ONLY__changesToString(changes));
1106
1498
  }
1107
1499
  }
1108
1500
  }
1109
1501
  maybeUpdateLists(changes, optimisticId) {
1110
- for (const [cacheKey, v] of this.#truthLayer.entries()) {
1502
+ if (process.env.NODE_ENV !== "production") {
1503
+ this.logger?.trace({
1504
+ methodName: "maybeUpdateLists"
1505
+ }, DEBUG_ONLY__changesToString(changes), {
1506
+ optimisticId
1507
+ });
1508
+ }
1509
+ if (changes.addedObjects.size === 0 && changes.modifiedObjects.size === 0) {
1510
+ return Promise.resolve([]);
1511
+ }
1512
+ const promises = [];
1513
+ for (const cacheKey of this.#queries.keys()) {
1111
1514
  if (isListCacheKey(cacheKey)) {
1112
- void this.#peekQuery(cacheKey)?.maybeUpdate(changes, optimisticId);
1515
+ if (!changes.modifiedLists.has(cacheKey)) {
1516
+ const promise = this.#peekQuery(cacheKey)?.maybeUpdateAndRevalidate(changes, optimisticId);
1517
+ if (promise) promises.push(promise);
1518
+ }
1113
1519
  }
1114
1520
  }
1521
+ return Promise.all(promises);
1115
1522
  }
1116
- invalidateObjectType(apiName) {
1523
+ /**
1524
+ * @param apiName
1525
+ * @param changes The changes we know about / to update
1526
+ * @returns
1527
+ */
1528
+ invalidateObjectType(apiName, changes) {
1117
1529
  if (typeof apiName !== "string") {
1118
1530
  apiName = apiName.apiName;
1119
1531
  }
1532
+ if (process.env.NODE_ENV !== "production") {
1533
+ this.logger?.info({
1534
+ methodName: "invalidateObjectType"
1535
+ }, changes ? DEBUG_ONLY__changesToString(changes) : undefined);
1536
+ }
1537
+ const promises = [];
1120
1538
  for (const [cacheKey, v] of this.#truthLayer.entries()) {
1121
1539
  if (isListCacheKey(cacheKey, apiName)) {
1122
- void this.#peekQuery(cacheKey)?.revalidate(true);
1540
+ if (!changes || !changes.modifiedLists.has(cacheKey)) {
1541
+ const promise = this.#peekQuery(cacheKey)?.revalidate(true);
1542
+ if (promise) {
1543
+ promises.push(promise);
1544
+ changes?.modifiedLists.add(cacheKey);
1545
+ }
1546
+ }
1123
1547
  }
1124
1548
  }
1549
+ return Promise.all(promises);
1125
1550
  }
1126
- invalidateList(apiName, where) {
1127
- if (typeof apiName !== "string") {
1128
- apiName = apiName.apiName;
1551
+ invalidateList({
1552
+ objectType,
1553
+ where,
1554
+ orderBy
1555
+ }) {
1556
+ if (typeof objectType !== "string") {
1557
+ objectType = objectType.apiName;
1129
1558
  }
1130
- const cacheKey = this.getCacheKey("list", apiName, where);
1559
+ where = this.whereCanonicalizer.canonicalize(where ?? {});
1560
+ orderBy = this.orderByCanonicalizer.canonicalize(orderBy ?? {});
1561
+ const cacheKey = this.getCacheKey("list", objectType, where, orderBy);
1131
1562
  void this.#peekQuery(cacheKey)?.revalidate(true);
1132
1563
  }
1133
1564
  updateObject(apiName, value, {
@@ -1143,19 +1574,45 @@ var Store = class {
1143
1574
  return query.writeToStore(value, "loaded", batch);
1144
1575
  }).retVal.value;
1145
1576
  }
1146
- updateList(apiName, where, values, {
1577
+ updateObjects(values, batch) {
1578
+ return values.map((v) => {
1579
+ return this.getObjectQuery(v.$apiName, v.$primaryKey).writeToStore(v, "loaded", batch).cacheKey;
1580
+ });
1581
+ }
1582
+ /**
1583
+ * Updates the internal state of a list and will create a new internal query if needed.
1584
+ *
1585
+ * Helper method only for tests right now. May be removed later.
1586
+ *
1587
+ * @param apiName
1588
+ * @param where
1589
+ * @param orderBy
1590
+ * @param objects
1591
+ * @param param4
1592
+ * @param opts
1593
+ */
1594
+ updateList({
1595
+ objectType: apiName,
1596
+ where,
1597
+ orderBy
1598
+ }, objects, {
1147
1599
  optimisticId
1148
1600
  } = {}, opts = {
1149
1601
  dedupeInterval: 0
1150
1602
  }) {
1151
- if (typeof apiName !== "string") {
1152
- apiName = apiName.apiName;
1603
+ if (process.env.NODE_ENV !== "production") {
1604
+ this.logger?.info({
1605
+ methodName: "updateList"
1606
+ }, "", {
1607
+ optimisticId
1608
+ });
1153
1609
  }
1154
- const query = this.getListQuery(apiName, where, opts);
1610
+ const query = this.getListQuery(apiName, where ?? {}, orderBy ?? {}, opts);
1155
1611
  this.batch({
1156
1612
  optimisticId
1157
- }, (b) => {
1158
- query.updateList(values, false, "loaded", b);
1613
+ }, (batch) => {
1614
+ const objectCacheKeys = this.updateObjects(objects, batch);
1615
+ query.updateList(objectCacheKeys, false, "loaded", batch);
1159
1616
  });
1160
1617
  }
1161
1618
  retain(cacheKey) {
@@ -1165,74 +1622,6 @@ var Store = class {
1165
1622
  this.#refCounts.release(cacheKey);
1166
1623
  }
1167
1624
  };
1168
- var ActionApplication = class {
1169
- constructor(store) {
1170
- this.store = store;
1171
- }
1172
- applyAction = (action, args, {
1173
- optimisticUpdate
1174
- } = {}) => {
1175
- const removeOptimisticResult = runOptimisticJob(this.store, optimisticUpdate);
1176
- return (async () => {
1177
- try {
1178
- const actionResults = await this.store.client(action).applyAction(args, {
1179
- $returnEdits: true
1180
- });
1181
- if (ACTION_DELAY > 0) {
1182
- console.log("action done, pausing");
1183
- await delay(ACTION_DELAY);
1184
- console.log("action done, pausing done");
1185
- }
1186
- await this.#invalidateActionEditResponse(actionResults);
1187
- return actionResults;
1188
- } finally {
1189
- await removeOptimisticResult();
1190
- }
1191
- })();
1192
- };
1193
- #invalidateActionEditResponse = (value) => {
1194
- const typesToInvalidate = /* @__PURE__ */ new Set();
1195
- let promisesToWait = [];
1196
- if (value.type === "edits") {
1197
- for (const obj of value.modifiedObjects) {
1198
- promisesToWait.push(this.store.invalidateObject(obj.objectType, obj.primaryKey));
1199
- }
1200
- for (const obj of value.addedObjects) {
1201
- promisesToWait.push(this.store.invalidateObject(obj.objectType, obj.primaryKey));
1202
- typesToInvalidate.add(obj.objectType);
1203
- }
1204
- promisesToWait = [Promise.allSettled(promisesToWait).then(() => {
1205
- const changes2 = this.#changesFromActionEditResponse(value);
1206
- this.store.maybeRevalidateLists(changes2);
1207
- })];
1208
- } else {
1209
- for (const apiName of value.editedObjectTypes) {
1210
- typesToInvalidate.add(apiName.toString());
1211
- }
1212
- }
1213
- return Promise.allSettled(promisesToWait).then(() => {
1214
- for (const objectType of typesToInvalidate) {
1215
- this.store.invalidateObjectType(objectType);
1216
- }
1217
- return value;
1218
- });
1219
- };
1220
- #changesFromActionEditResponse = (value) => {
1221
- const changes = createChangedObjects();
1222
- for (const changeType of ["addedObjects", "modifiedObjects"]) {
1223
- for (const {
1224
- objectType,
1225
- primaryKey
1226
- } of value[changeType] ?? []) {
1227
- const obj = this.store.getObject(objectType, primaryKey);
1228
- if (obj) {
1229
- changes[changeType].set(objectType, obj);
1230
- }
1231
- }
1232
- }
1233
- return changes;
1234
- };
1235
- };
1236
1625
 
1237
1626
  // src/observable/ObservableClient.ts
1238
1627
  function createObservableClient(client) {