• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

live-input-vector-output-node / livon-ts / 23551334488

25 Mar 2026 04:08PM UTC coverage: 83.229% (+0.003%) from 83.226%
23551334488

push

github

cr15p1
feat(sync): stabilize data-first unit generics

880 of 1041 branches covered (84.53%)

Branch coverage included in aggregate %.

46 of 47 new or added lines in 12 files covered. (97.87%)

3919 of 4725 relevant lines covered (82.94%)

37.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.84
/packages/sync/src/source/source.ts
1
import { type DraftMode, type EntityId } from '../entity.js';
2
import {
3
  applyEntityRunResult,
4
  clearEntityMembership,
5
  createEntityRunContextMethods,
6
  createRunContextEntryCache,
7
  readOrCreateSharedCacheWriteQueue,
8
  createSerializedKeyCache,
9
  cloneValue,
10
  createUnitSnapshot,
11
  notifyEffectListeners,
12
  resolveInput,
13
  resolveValue,
14
  setManyEntityMembership,
15
  setOneEntityMembership,
16
  serializeKey,
17
  type EffectListener,
18
  type InputUpdater,
19
  type UnitDataEntity,
20
  type UnitDataUpdate,
21
  type ValueUpdater,
22
} from '../utils/index.js';
23

24
import {
25
  areDraftValuesEqual,
26
  getModeValue,
27
  invokeSourceCleanup,
28
  isCacheRecordExpired,
29
  isEntityArray,
30
  isEntityValue,
31
  isRecordValue,
32
  isSourceCleanup,
33
  readSourceCacheRecord,
34
  resolveCacheLruMaxEntries,
35
  resolveCacheKey,
36
  resolveCacheStorage,
37
  resolveCacheTtl,
38
  serializeSourceCacheRecord,
39
  shouldUseCache,
40
} from './helpers.js';
41
import type {
42
  ReadEntityValueById,
43
  Source,
44
  SourceConfig,
45
  SourceContext,
46
  SourceRunContext,
47
  SourceRunContextEntry,
48
  SourceRunGate,
49
  SourceUnit,
50
  SourceUnitByKeyMap,
51
  SourceUnitInternal,
52
} from './types.js';
53

54
const DEFAULT_DESTROY_DELAY = 250;
37✔
55
const DEFAULT_DRAFT_MODE: DraftMode = 'global';
37✔
56
const RUN_CONTEXT_CACHE_LIMIT = 32;
37✔
57
const SOURCE_CACHE_LRU_STORAGE_KEY_SUFFIX = '__lru__';
37✔
58

59
const createInitialSourceContext = (
37✔
60
  hasCacheStorage: boolean,
61
): SourceContext => {
62
  return {
177✔
63
    cacheState: hasCacheStorage ? 'miss' : 'disabled',
64
    error: null,
65
  };
66
};
67

68
export const source = <
37✔
69
  TInput extends object | undefined,
70
  TPayload = unknown,
71
  TData = unknown,
72
  TMeta = unknown,
73
>({
74
  entity,
75
  ttl = entity.ttl ?? 0,
76
  draft = entity.draft ?? DEFAULT_DRAFT_MODE,
77
  cache,
78
  destroyDelay = entity.destroyDelay ?? DEFAULT_DESTROY_DELAY,
79
  onDestroy,
80
  run,
81
  defaultValue,
82
}: SourceConfig<TInput, TPayload, TData, TMeta>,
83
): Source<TInput, TPayload, TData, TMeta> => {
84
  const cacheTtl = resolveCacheTtl({
205✔
85
    sourceCache: cache,
86
    entityCache: entity.cache,
87
  });
88
  const cacheLruMaxEntries = resolveCacheLruMaxEntries({
205✔
89
    sourceCache: cache,
90
    entityCache: entity.cache,
91
  });
92
  const cacheStorage = resolveCacheStorage({
205✔
93
    sourceCache: cache,
94
    entityCache: entity.cache,
95
  });
96
  const cacheWriteQueue = cacheStorage
205✔
97
    ? readOrCreateSharedCacheWriteQueue({ storage: cacheStorage })
98
    : undefined;
99
  const cacheKeyPrefix = resolveCacheKey({
205✔
100
    sourceCache: cache,
101
    entityCache: entity.cache,
102
    sourceKey: serializeKey({
103
      mode: Array.isArray(defaultValue ?? null) ? 'many' : 'one',
104
    }),
105
  });
106

107
  const unitsByKey: SourceUnitByKeyMap<TInput, TPayload, TData, TMeta> =
108
    new Map<string, SourceUnitInternal<TInput, TPayload, TData, TMeta>>();
205✔
109
  const scopedDraftsById = new Map<EntityId, UnitDataEntity<TData>>();
205✔
110
  const unitKeyCache = createSerializedKeyCache({
205✔
111
    mode: 'scoped-unit',
112
  });
113

114
  const notifyScopedUnitsById = (id: EntityId): void => {
205✔
115
    Array.from(unitsByKey.values()).forEach((unitInternal) => {
2✔
116
      const hasMembership = unitInternal.membershipIds.some((membershipId) => membershipId === id);
2✔
117
      if (!hasMembership) {
2✔
118
        return;
×
119
      }
120

121
      notifyUnit(unitInternal);
2✔
122
    });
123
  };
124

125
  const readEntityValueById: ReadEntityValueById<EntityId, UnitDataEntity<TData>> = (id) => {
205✔
126
    if (draft === 'off') {
365✔
127
      return entity.getById(id);
4✔
128
    }
129

130
    if (draft === 'global') {
361✔
131
      const globalDraft = entity.getDraftById(id);
349✔
132
      if (globalDraft) {
349✔
133
        return globalDraft;
14✔
134
      }
135

136
      return entity.getById(id);
335✔
137
    }
138

139
    const scopedDraft = scopedDraftsById.get(id);
12✔
140
    if (scopedDraft) {
12✔
141
      return scopedDraft;
2✔
142
    }
143

144
    return entity.getById(id);
10✔
145
  };
146

147
  const setDraftById = (id: EntityId, value: UnitDataEntity<TData>): void => {
205✔
148
    if (draft === 'off') {
12✔
149
      return;
×
150
    }
151

152
    if (draft === 'global') {
12✔
153
      entity.setDraftById(id, value);
10✔
154
      return;
10✔
155
    }
156

157
    scopedDraftsById.set(id, value);
2✔
158
    notifyScopedUnitsById(id);
2✔
159
  };
160

161
  const clearDraftById = (id: EntityId): void => {
205✔
162
    if (draft === 'off') {
3✔
163
      return;
×
164
    }
165

166
    if (draft === 'global') {
3✔
167
      entity.clearDraftById(id);
3✔
168
      return;
3✔
169
    }
170

171
    const existed = scopedDraftsById.delete(id);
×
172
    if (!existed) {
×
173
      return;
×
174
    }
175

176
    notifyScopedUnitsById(id);
×
177
  };
178

179
  const notifyUnit = (
205✔
180
    internal: SourceUnitInternal<TInput, TPayload, TData, TMeta>,
181
  ): void => {
182
    internal.state.value = getModeValue(internal, readEntityValueById);
348✔
183

184
    notifyEffectListeners(
348✔
185
      internal.listeners,
186
      createUnitSnapshot({
187
        value: internal.state.value,
188
        status: internal.state.status,
189
        meta: internal.state.meta,
190
        context: internal.state.context,
191
      }),
192
    );
193
  };
194

195
  const sourceFactory: Source<TInput, TPayload, TData, TMeta> = (scope) => {
205✔
196
    const key = unitKeyCache.getOrCreateKey(scope);
213✔
197
    const existingUnit = unitsByKey.get(key);
213✔
198

199
    if (existingUnit) {
213✔
200
      return existingUnit.unit;
42✔
201
    }
202

203
    const initialValue = (defaultValue ?? null) as TData;
171✔
204
    const initialPayload = undefined as TPayload;
213✔
205
    const initialMode: 'one' | 'many' = Array.isArray(initialValue) ? 'many' : 'one';
213✔
206

207
    const internal: SourceUnitInternal<TInput, TPayload, TData, TMeta> = {
213✔
208
      key,
209
      ttl,
210
      destroyDelay,
211
      scope,
212
      payload: initialPayload,
213
      state: {
214
        value: initialValue,
215
        status: 'idle',
216
        meta: null,
217
        context: createInitialSourceContext(Boolean(cacheStorage)),
218
      },
219
      mode: initialMode,
220
      modeLocked: false,
221
      hasEntityValue: false,
222
      membershipIds: [],
223
      readWrite: {
224
        subview: entity.readWrite.subview,
225
      },
226
      listeners: new Set<EffectListener<TData, TMeta | null>>(),
227
      inFlightByPayload: new Map<string, Promise<TData>>(),
228
      runSequence: 0,
229
      latestRunSequence: 0,
230
      lastRunAt: null,
231
      cleanup: null,
232
      stopped: false,
233
      destroyed: false,
234
      destroyHandled: false,
235
      unit: {} as SourceUnit<TInput, TPayload, TData, TMeta>,
236
    };
237
    const payloadKeyCache = createSerializedKeyCache({
213✔
238
      mode: 'payload-hot-path',
239
      limit: RUN_CONTEXT_CACHE_LIMIT,
240
    });
241
    const createRunContextEntry = (
213✔
242
      payload: TPayload,
243
    ): SourceRunContextEntry<TInput, TPayload, TData, TMeta> => {
244
      const gate: SourceRunGate = {
138✔
245
        isLatestRun: () => false,
×
246
      };
247
      const runContextMethods = createEntityRunContextMethods<
138✔
248
        UnitDataEntity<TData>,
249
        TData,
250
        EntityId
251
      >({
252
        entity,
253
        state: internal,
254
        isActive: () => gate.isLatestRun(),
1✔
255
        refreshValue: () => {
256
          internal.state.value = getModeValue(internal, readEntityValueById);
1✔
257
        },
258
        readValue: () => internal.state.value,
1✔
259
      });
260
      const runContextBase: SourceRunContext<
261
        TInput,
262
        TPayload,
263
        TData,
264
        TMeta
265
      > = {
138✔
266
        scope: internal.scope,
267
        payload,
268
        setMeta: (metaInput: TMeta | null | ValueUpdater<TMeta | null, TMeta | null>) => {
269
          if (!gate.isLatestRun()) {
14✔
270
            return;
×
271
          }
272

273
          const nextMeta = resolveValue(
14✔
274
            internal.state.meta,
275
            metaInput,
276
          );
277
          if (Object.is(nextMeta, internal.state.meta)) {
14✔
278
            return;
2✔
279
          }
280

281
          internal.state.meta = nextMeta;
12✔
282
          notifyUnit(internal);
12✔
283
        },
284
        reset: () => {
285
          if (!gate.isLatestRun()) {
1✔
286
            return;
×
287
          }
288

289
          resetState();
1✔
290
        },
291
        ...runContextMethods,
292
      };
293

294
      return {
138✔
295
        gate,
296
        context: runContextBase,
297
      };
298
    };
299
    const runContextEntryCache = createRunContextEntryCache<
213✔
300
      TPayload,
301
      SourceRunContextEntry<TInput, TPayload, TData, TMeta>
302
    >({
303
      createEntry: createRunContextEntry,
304
      limit: RUN_CONTEXT_CACHE_LIMIT,
305
    });
306

307
    const setContext = (input: Partial<SourceContext>): void => {
213✔
308
      const hasCacheState = Object.prototype.hasOwnProperty.call(input, 'cacheState');
532✔
309
      const hasError = Object.prototype.hasOwnProperty.call(input, 'error');
532✔
310

311
      if (
532✔
312
        (!hasCacheState || input.cacheState === internal.state.context.cacheState)
313
        && (!hasError || Object.is(input.error, internal.state.context.error))
314
      ) {
315
        return;
489✔
316
      }
317

318
      internal.state.context = {
43✔
319
        ...internal.state.context,
320
        ...input,
321
      };
322
    };
323

324
    const cacheEnabled = Boolean(
213✔
325
      cacheStorage
326
      && (cacheTtl === 'infinity' || cacheTtl > 0),
327
    );
328
    const cacheLruEnabled = Boolean(
213✔
329
      cacheEnabled
330
      && cacheLruMaxEntries > 0
331
      && cacheStorage
332
      && cacheWriteQueue,
333
    );
334
    const cacheLruStorageKey = `${cacheKeyPrefix}:${SOURCE_CACHE_LRU_STORAGE_KEY_SUFFIX}`;
213✔
335
    let cacheLruLoaded = false;
213✔
336
    let cacheLruKeys: string[] = [];
213✔
337
    let activePayloadCacheKey: string | null = null;
213✔
338

339
    const loadCacheLruState = (): void => {
213✔
340
      if (!cacheLruEnabled || cacheLruLoaded || !cacheStorage) {
3✔
341
        return;
2✔
342
      }
343

344
      cacheLruLoaded = true;
1✔
345
      const rawLruState = cacheStorage.getItem(cacheLruStorageKey);
1✔
346
      if (!rawLruState) {
1✔
347
        return;
1✔
348
      }
349

350
      try {
×
351
        const parsedLruState: unknown = JSON.parse(rawLruState);
×
352
        if (!isRecordValue(parsedLruState)) {
×
353
          return;
×
354
        }
355

356
        const keysCandidate = parsedLruState.keys;
×
357
        if (!Array.isArray(keysCandidate)) {
×
358
          return;
×
359
        }
360

361
        if (!keysCandidate.every((entry) => typeof entry === 'string')) {
×
362
          return;
×
363
        }
364

365
        cacheLruKeys = keysCandidate;
×
366
      } catch {
367
        return;
×
368
      }
369
    };
370

371
    const persistCacheLruState = (): void => {
213✔
372
      if (!cacheLruEnabled || !cacheWriteQueue) {
3✔
373
        return;
×
374
      }
375

376
      if (cacheLruKeys.length === 0) {
3✔
377
        cacheWriteQueue.enqueueRemove(cacheLruStorageKey);
×
378
        return;
×
379
      }
380

381
      cacheWriteQueue.enqueueSet(
3✔
382
        cacheLruStorageKey,
383
        () => {
384
          return JSON.stringify({
3✔
385
            keys: cacheLruKeys,
386
          });
387
        },
388
      );
389
    };
390

391
    const touchCacheLruKey = (unitCacheKey: string): void => {
213✔
392
      if (!cacheLruEnabled || !cacheWriteQueue) {
14✔
393
        return;
11✔
394
      }
395

396
      loadCacheLruState();
3✔
397
      if (cacheLruKeys[0] === unitCacheKey) {
3✔
398
        return;
×
399
      }
400

401
      const existingIndex = cacheLruKeys.findIndex((entry) => entry === unitCacheKey);
3✔
402
      if (existingIndex > 0) {
3✔
403
        const existingEntry = cacheLruKeys[existingIndex];
×
404
        if (existingEntry === undefined) {
×
405
          return;
×
406
        }
407

408
        cacheLruKeys.splice(existingIndex, 1);
×
409
        cacheLruKeys.unshift(existingEntry);
×
410
        persistCacheLruState();
×
411
        return;
×
412
      }
413

414
      cacheLruKeys.unshift(unitCacheKey);
3✔
415
      const evictedEntry = cacheLruKeys.length > cacheLruMaxEntries
3✔
416
        ? cacheLruKeys.pop()
417
        : undefined;
418
      if (evictedEntry && evictedEntry !== unitCacheKey) {
14✔
419
        cacheWriteQueue.enqueueRemove(evictedEntry);
1✔
420
      }
421

422
      persistCacheLruState();
3✔
423
    };
424

425
    const removeCacheLruKey = (unitCacheKey: string): void => {
213✔
426
      if (!cacheLruEnabled) {
4✔
427
        return;
4✔
428
      }
429

430
      loadCacheLruState();
×
431
      const existingIndex = cacheLruKeys.findIndex((entry) => entry === unitCacheKey);
×
432
      if (existingIndex < 0) {
×
433
        return;
×
434
      }
435

436
      cacheLruKeys.splice(existingIndex, 1);
×
437
      persistCacheLruState();
×
438
    };
439

440
    const resolvePayloadCacheKey = (payload: TPayload): string => {
213✔
441
      return payloadKeyCache.getOrCreateKey(payload);
334✔
442
    };
443

444
    const resolveUnitCacheKey = (payloadCacheKey: string): string => {
213✔
445
      return `${cacheKeyPrefix}:${key}:${payloadCacheKey}`;
32✔
446
    };
447

448
    const syncCacheRecord = (): void => {
213✔
449
      if (!cacheEnabled || !cacheWriteQueue) {
180✔
450
        return;
170✔
451
      }
452

453
      const payloadCacheKey = resolvePayloadCacheKey(internal.payload);
10✔
454
      const unitCacheKey = resolveUnitCacheKey(payloadCacheKey);
10✔
455
      if (!internal.hasEntityValue) {
10✔
456
        removeCacheLruKey(unitCacheKey);
1✔
457
        cacheWriteQueue.enqueueRemove(unitCacheKey);
1✔
458
        return;
1✔
459
      }
460

461
      touchCacheLruKey(unitCacheKey);
9✔
462
      cacheWriteQueue.enqueueSet(unitCacheKey, () => {
9✔
463
        const entities = internal.membershipIds
8✔
464
          .map((id) => entity.getById(id))
8✔
465
          .filter((entry): entry is UnitDataEntity<TData> => entry !== undefined);
8✔
466

467
        return serializeSourceCacheRecord({
8✔
468
          mode: internal.mode,
469
          entities,
470
          writtenAt: Date.now(),
471
        });
472
      });
473
    };
474

475
    const hydrateFromCache = (payloadCacheKey: string): boolean => {
213✔
476
      if (!cacheStorage) {
239✔
477
        setContext({
184✔
478
          cacheState: 'disabled',
479
        });
480
        return false;
184✔
481
      }
482

483
      if (!cacheEnabled) {
55✔
484
        setContext({
33✔
485
          cacheState: 'disabled',
486
        });
487
        return false;
33✔
488
      }
489

490
      const unitCacheKey = resolveUnitCacheKey(payloadCacheKey);
22✔
491
      const rawRecord = cacheStorage.getItem(unitCacheKey);
22✔
492
      const parsedRecord = readSourceCacheRecord<UnitDataEntity<TData>>(rawRecord);
22✔
493
      if (!parsedRecord) {
22✔
494
        setContext({
14✔
495
          cacheState: 'miss',
496
        });
497
        return false;
14✔
498
      }
499

500
      if (isCacheRecordExpired({ ttl: cacheTtl, writtenAt: parsedRecord.writtenAt })) {
8✔
501
        removeCacheLruKey(unitCacheKey);
3✔
502
        if (cacheWriteQueue) {
3✔
503
          cacheWriteQueue.enqueueRemove(unitCacheKey);
3✔
504
        } else {
505
          try {
×
506
            cacheStorage.removeItem(unitCacheKey);
×
507
          } catch {
508
            // Ignore storage write errors and continue with stale cache behavior.
509
          }
510
        }
511
        setContext({
3✔
512
          cacheState: 'stale',
513
        });
514
        return false;
3✔
515
      }
516

517
      touchCacheLruKey(unitCacheKey);
5✔
518
      if (parsedRecord.mode === 'one') {
5✔
519
        const firstEntity = parsedRecord.entities[0];
5✔
520
        internal.hasEntityValue = true;
5✔
521

522
        if (firstEntity) {
5✔
523
          const upsertedEntity = entity.upsertOne(firstEntity);
5✔
524
          setOneEntityMembership(internal, {
5✔
525
            entity,
526
            value: upsertedEntity,
527
            operation: 'cache rehydrate one',
528
          });
529
          internal.state.value = getModeValue(internal, readEntityValueById);
5✔
530
        } else {
531
          clearEntityMembership(internal, { clearUnitMembership: entity.clearUnitMembership });
×
532
          internal.state.value = getModeValue(internal, readEntityValueById);
×
533
        }
534
      }
535

536
      if (parsedRecord.mode === 'many') {
5✔
537
        const upsertedEntities = entity.upsertMany(parsedRecord.entities);
×
538
        setManyEntityMembership(internal, {
×
539
          entity,
540
          values: upsertedEntities,
541
          operation: 'cache rehydrate many',
542
        });
543
        internal.state.value = getModeValue(internal, readEntityValueById);
×
544
      }
545

546
      internal.state.status = 'rehydrated';
5✔
547
      setContext({
5✔
548
        cacheState: 'hit',
549
        error: null,
550
      });
551
      return true;
5✔
552
    };
553

554
    const hydrateFromPayloadCache = (payload: TPayload): boolean => {
213✔
555
      const payloadCacheKey = resolvePayloadCacheKey(payload);
324✔
556
      if (activePayloadCacheKey === payloadCacheKey) {
324✔
557
        return false;
85✔
558
      }
559

560
      activePayloadCacheKey = payloadCacheKey;
239✔
561
      return hydrateFromCache(payloadCacheKey);
239✔
562
    };
563

564
    const unregisterFromEntity = entity.registerUnit({
213✔
565
      key: internal.key,
566
      onChange: () => {
567
        if (internal.destroyed) {
34✔
568
          return;
×
569
        }
570

571
        notifyUnit(internal);
34✔
572
        syncCacheRecord();
34✔
573
      },
574
    });
575

576
    hydrateFromPayloadCache(internal.payload);
213✔
577

578
    let singleInFlightPromise: Promise<TData> | null = null;
213✔
579
    let hasSingleInFlightPayload = false;
213✔
580
    let singleInFlightPayload: TPayload | undefined;
581
    let singleInFlightPayloadKey: string | null = null;
213✔
582

583
    const clearInFlightTracking = (): void => {
213✔
584
      internal.inFlightByPayload.clear();
14✔
585
      singleInFlightPromise = null;
14✔
586
      hasSingleInFlightPayload = false;
14✔
587
      singleInFlightPayload = undefined;
14✔
588
      singleInFlightPayloadKey = null;
14✔
589
    };
590

591
    const stopInternal = (): void => {
213✔
592
      internal.runSequence += 1;
24✔
593
      internal.latestRunSequence = internal.runSequence;
24✔
594
      internal.stopped = true;
24✔
595
      invokeSourceCleanup(internal.cleanup);
24✔
596
      internal.cleanup = null;
24✔
597
    };
598

599
    const resetState = (): void => {
213✔
600
      if (internal.destroyed) {
6✔
601
        return;
×
602
      }
603

604
      const previousMembershipIds = internal.membershipIds;
6✔
605
      stopInternal();
6✔
606
      clearInFlightTracking();
6✔
607
      runContextEntryCache.clear();
6✔
608
      payloadKeyCache.clear();
6✔
609
      activePayloadCacheKey = null;
6✔
610
      internal.payload = initialPayload;
6✔
611

612
      if (draft === 'scoped') {
6✔
613
        previousMembershipIds.forEach((id) => {
×
614
          scopedDraftsById.delete(id);
×
615
        });
616
      }
617

618
      clearEntityMembership(internal, { clearUnitMembership: entity.clearUnitMembership });
6✔
619
      internal.mode = initialMode;
6✔
620
      internal.modeLocked = false;
6✔
621
      internal.lastRunAt = null;
6✔
622
      internal.stopped = false;
6✔
623
      internal.state.value = initialValue;
6✔
624
      internal.state.status = 'idle';
6✔
625
      internal.state.meta = null;
6✔
626
      internal.state.context = createInitialSourceContext(Boolean(cacheStorage));
6✔
627
      notifyUnit(internal);
6✔
628
      syncCacheRecord();
6✔
629
    };
630

631
    const destroyInternal = (): void => {
213✔
632
      if (internal.destroyed) {
9✔
633
        return;
1✔
634
      }
635

636
      stopInternal();
8✔
637
      internal.destroyed = true;
8✔
638
      runContextEntryCache.clear();
8✔
639
      payloadKeyCache.clear();
8✔
640
      activePayloadCacheKey = null;
8✔
641
      clearInFlightTracking();
8✔
642

643
      unregisterFromEntity();
8✔
644
      internal.listeners.clear();
8✔
645

646
      if (!internal.destroyHandled) {
8✔
647
        internal.destroyHandled = true;
8✔
648
        onDestroy?.({
8✔
649
          scope: internal.scope,
650
          payload: internal.payload,
651
        });
652
      }
653

654
      unitsByKey.delete(internal.key);
8✔
655
    };
656

657
    const executeRun = (
213✔
658
      isForce: boolean,
659
      payloadInput?: TPayload | InputUpdater<TPayload>,
660
    ): Promise<TData> => {
661
      if (internal.destroyed) {
156✔
662
        return Promise.resolve(internal.state.value);
2✔
663
      }
664

665
      const hasPayloadInput = payloadInput !== undefined;
154✔
666
      const nextPayload = resolveInput(internal.payload, payloadInput);
154✔
667
      if (hasPayloadInput) {
154✔
668
        internal.payload = nextPayload;
70✔
669
      }
670
      let payloadKey: string | null = null;
154✔
671
      if (singleInFlightPromise) {
154✔
672
        if (hasSingleInFlightPayload && Object.is(singleInFlightPayload, internal.payload)) {
5✔
673
          return singleInFlightPromise;
×
674
        }
675

676
        payloadKey = payloadKeyCache.getOrCreateKey(internal.payload);
5✔
677
        if (singleInFlightPayloadKey === null && hasSingleInFlightPayload) {
5✔
678
          singleInFlightPayloadKey = payloadKeyCache.getOrCreateKey(singleInFlightPayload);
4✔
679
        }
680

681
        if (singleInFlightPayloadKey === payloadKey) {
5✔
682
          return singleInFlightPromise;
1✔
683
        }
684
      }
685

686
      if (internal.inFlightByPayload.size > 0) {
153✔
687
        payloadKey ??= payloadKeyCache.getOrCreateKey(internal.payload);
1✔
688
        const inFlightForPayload = internal.inFlightByPayload.get(payloadKey);
1✔
689
        if (inFlightForPayload) {
1✔
690
          return inFlightForPayload;
×
691
        }
692
      }
693

694
      internal.stopped = false;
153✔
695
      internal.runSequence += 1;
153✔
696
      internal.latestRunSequence = internal.runSequence;
153✔
697
      const runSequence = internal.runSequence;
153✔
698
      const isLatestRun = () => {
153✔
699
        return internal.latestRunSequence === runSequence && !internal.destroyed;
604✔
700
      };
701

702
      const didHydrateFromPayloadCache = hydrateFromPayloadCache(internal.payload);
153✔
703
      if (didHydrateFromPayloadCache && isLatestRun()) {
153✔
704
        notifyUnit(internal);
1✔
705
      }
706

707
      if (!isForce && !hasPayloadInput && shouldUseCache(internal)) {
153✔
708
        return Promise.resolve(internal.state.value);
2✔
709
      }
710

711
      const shouldUseRefreshingStatus = internal.state.status === 'rehydrated'
151✔
712
        || internal.state.status === 'success';
713
      if (isLatestRun()) {
156✔
714
        internal.state.status = shouldUseRefreshingStatus ? 'refreshing' : 'loading';
151✔
715
        setContext({
151✔
716
          error: null,
717
        });
718
        notifyUnit(internal);
151✔
719
      }
720

721
      const applyRunValue = (nextValue: TData | void): void => {
151✔
722
        if (!isLatestRun()) {
141✔
723
          return;
×
724
        }
725

726
        applyEntityRunResult({
141✔
727
          entity,
728
          state: internal,
729
          nextValue,
730
          refreshValueFromMembership: () => {
731
            internal.state.value = getModeValue(internal, readEntityValueById);
137✔
732
          },
733
          setRawValue: (value) => {
734
            internal.state.value = value;
3✔
735
          },
736
        });
737
      };
738

739
      const runContextEntry = runContextEntryCache.getOrCreate(internal.payload);
151✔
740
      runContextEntry.gate.isLatestRun = isLatestRun;
151✔
741
      runContextEntry.context.payload = internal.payload;
151✔
742

743
      const usesSingleInFlight = singleInFlightPromise === null && internal.inFlightByPayload.size === 0;
151✔
744
      const trackedPayloadKey = usesSingleInFlight
156✔
745
        ? null
746
        : (payloadKey ?? payloadKeyCache.getOrCreateKey(internal.payload));
747

748
      const runPromise: Promise<TData> = Promise.resolve(run(runContextEntry.context))
156✔
749
        .then((result) => {
750
          if (!isLatestRun() && isSourceCleanup(result)) {
148✔
751
            invokeSourceCleanup(result);
3✔
752
            return internal.state.value;
3✔
753
          }
754

755
          if (!isLatestRun()) {
145✔
756
            return internal.state.value;
4✔
757
          }
758

759
          if (isSourceCleanup(result)) {
141✔
760
            if (internal.destroyed || internal.stopped) {
×
761
              invokeSourceCleanup(result);
×
762
            } else {
763
              invokeSourceCleanup(internal.cleanup);
×
764
              internal.cleanup = result;
×
765
            }
766

767
            internal.state.value = getModeValue(internal, readEntityValueById);
×
768
          } else {
769
            applyRunValue(result);
141✔
770
          }
771

772
          internal.lastRunAt = Date.now();
140✔
773
          internal.state.status = 'success';
140✔
774
          setContext({
140✔
775
            error: null,
776
          });
777
          notifyUnit(internal);
140✔
778
          syncCacheRecord();
140✔
779

780
          return internal.state.value;
140✔
781
        })
782
        .catch((error: unknown) => {
783
          if (isLatestRun()) {
2✔
784
            internal.state.status = 'error';
2✔
785
            setContext({
2✔
786
              error,
787
            });
788
            notifyUnit(internal);
2✔
789
          }
790

791
          throw error;
2✔
792
        })
793
        .finally(() => {
794
          if (usesSingleInFlight) {
149✔
795
            if (singleInFlightPromise === runPromise) {
146✔
796
              singleInFlightPromise = null;
144✔
797
              hasSingleInFlightPayload = false;
144✔
798
              singleInFlightPayload = undefined;
144✔
799
              singleInFlightPayloadKey = null;
144✔
800
            }
801
            return;
146✔
802
          }
803

804
          if (trackedPayloadKey !== null) {
3✔
805
            internal.inFlightByPayload.delete(trackedPayloadKey);
3✔
806
          }
807
        });
808

809
      if (usesSingleInFlight) {
156✔
810
        singleInFlightPromise = runPromise;
147✔
811
        hasSingleInFlightPayload = true;
147✔
812
        singleInFlightPayload = internal.payload;
147✔
813
        singleInFlightPayloadKey = null;
147✔
814
      } else {
815
        if (trackedPayloadKey !== null) {
4✔
816
          internal.inFlightByPayload.set(trackedPayloadKey, runPromise);
4✔
817
        }
818
      }
819

820
      return runPromise;
151✔
821
    };
822

823
    const unit = ((payloadInput?: TPayload | InputUpdater<TPayload>) => {
213✔
824
      internal.payload = resolveInput(internal.payload, payloadInput);
×
825
      return unit;
×
826
    }) as SourceUnit<TInput, TPayload, TData, TMeta>;
827

828
    unit.ttl = ttl;
213✔
829
    unit.cacheTtl = cacheTtl;
213✔
830
    unit.destroyDelay = destroyDelay;
213✔
831
    unit.run = (payloadInput) => executeRun(false, payloadInput);
213✔
832
    unit.get = () => {
213✔
833
      return internal.state.value;
79✔
834
    };
835
    unit.draft = {
213✔
836
      set: (input: UnitDataUpdate<TData> | ValueUpdater<TData, UnitDataUpdate<TData>>) => {
837
        if (internal.destroyed || draft === 'off') {
20✔
838
          return;
2✔
839
        }
840

841
        const previousValue: TData = getModeValue(internal, readEntityValueById);
18✔
842
        const draftSeed: TData = cloneValue(previousValue);
18✔
843
        const nextUpdate = resolveValue(draftSeed, input);
18✔
844

845
        if (Object.is(nextUpdate, previousValue)) {
18✔
846
          return;
3✔
847
        }
848

849
        if (areDraftValuesEqual(previousValue, nextUpdate)) {
15✔
850
          return;
3✔
851
        }
852

853
        if (internal.mode === 'one') {
12✔
854
          const firstId = internal.membershipIds[0];
12✔
855
          if (!firstId || !isEntityValue<UnitDataEntity<TData>>(nextUpdate)) {
12✔
856
            return;
×
857
          }
858

859
          setDraftById(firstId, cloneValue(nextUpdate));
12✔
860
          return;
12✔
861
        }
862

863
        if (internal.mode === 'many') {
×
NEW
864
          if (!isEntityArray<UnitDataEntity<TData>>(nextUpdate)) {
×
865
            return;
×
866
          }
867

868
          nextUpdate.forEach((entry) => {
×
869
            const entryId = entity.idOf(entry);
×
870
            setDraftById(entryId, cloneValue(entry));
×
871
          });
872
        }
873
      },
874
      clean: () => {
875
        if (internal.destroyed || draft === 'off') {
5✔
876
          return;
×
877
        }
878

879
        internal.membershipIds.forEach((id) => {
5✔
880
          clearDraftById(id);
3✔
881
        });
882
      },
883
    };
884
    unit.effect = (listener) => {
213✔
885
      if (internal.destroyed) {
67✔
886
        return undefined;
1✔
887
      }
888

889
      internal.listeners.add(listener);
66✔
890

891
      return () => {
66✔
892
        internal.listeners.delete(listener);
20✔
893
      };
894
    };
895
    unit.refetch = (payloadInput) => {
213✔
896
      const nextPayload = resolveInput(internal.payload, payloadInput);
5✔
897
      return sourceFactory(internal.scope).force(nextPayload);
5✔
898
    };
899
    unit.force = (payloadInput) => executeRun(true, payloadInput);
213✔
900
    unit.reset = () => {
213✔
901
      resetState();
5✔
902
    };
903
    unit.stop = () => {
213✔
904
      if (internal.destroyed) {
10✔
905
        return;
×
906
      }
907

908
      stopInternal();
10✔
909
    };
910
    unit.destroy = () => {
213✔
911
      destroyInternal();
9✔
912
    };
913

914
    Object.defineProperty(unit, 'getSnapshot', {
213✔
915
      configurable: false,
916
      enumerable: false,
917
      writable: false,
918
      value: () => {
919
        return createUnitSnapshot({
23✔
920
          value: internal.state.value,
921
          status: internal.state.status,
922
          meta: internal.state.meta,
923
          context: internal.state.context,
924
        });
925
      },
926
    });
927

928
    internal.unit = unit;
213✔
929
    unitsByKey.set(key, internal);
213✔
930

931
    return unit;
213✔
932
  };
933

934
  return sourceFactory;
205✔
935
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc