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

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

19 May 2026 05:40AM UTC coverage: 69.413% (-5.6%) from 74.971%
26078594527

push

github

cr15p1
Remove remaining duplicated Sonar blocks

1990 of 3317 branches covered (59.99%)

Branch coverage included in aggregate %.

9 of 23 new or added lines in 3 files covered. (39.13%)

304 existing lines in 15 files now uncovered.

4321 of 5775 relevant lines covered (74.82%)

25.6 hits per line

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

69.88
/packages/sync/src/source/source.ts
1
import {
2
  type Entity,
3
  type EntityId,
4
} from '../entity.js';
5
import {
6
  DEFAULT_RUN_CONTEXT_CACHE_LIMIT,
7
  DEFAULT_UNIT_DESTROY_DELAY,
8
  applyEntityRunResult,
9
  clearEntityMembership,
10
  createEntityValueReader,
11
  createEntityRunContextMethods,
12
  createFunctionKeyResolver,
13
  createRunContextEntryCache,
14
  createSerializedKeyCache,
15
  createUnitSnapshot,
16
  invokeCleanup,
17
  isCleanup,
18
  isUnitSnapshotEqual,
19
  isNonEmptyString,
20
  isUnitSetAction,
21
  notifyEffectListeners,
22
  resolveEntityFunctionIdentityKey,
23
  resolveEntityFunctionKey,
24
  resolveDefaultUnitValue,
25
  resolveInput,
26
  resolveUnitRunAsVoid,
27
  resolveUnitMode,
28
  resolveValue,
29
  type EntityValueOfStore,
30
  serializeKey,
31
  setManyEntityMembership,
32
  setOneEntityMembership,
33
  type EffectListener,
34
  type UnitDataByEntityMode,
35
  type UnitEntityMode,
36
  type UnitRunPrevious,
37
  type UnitSnapshot,
38
  type ValueUpdater,
39
} from '../utils/index.js';
40

41
import {
42
  getModeValue,
43
  hasStringKeysArray,
44
  isCacheRecordExpired,
45
  readSourceCacheRecord,
46
  resolveCacheLruMaxEntries,
47
  resolveCacheKey,
48
  resolveCacheStorage,
49
  resolveCacheTtl,
50
  shouldUseCache,
51
} from './helpers.js';
52
import type {
53
  ReadEntityValueById,
54
  Source,
55
  SourceBuilderInput,
56
  SourceByEntityModeBuilder,
57
  SourceBuilder,
58
  SourceConfig,
59
  SourceContext,
60
  SourceRunConfig,
61
  SourceRun,
62
  SourceRunInput,
63
  SourceFetchInput,
64
  SourceRunContext,
65
  SourceRunContextEntry,
66
  SourceRunGate,
67
  SourceSnapshot,
68
  SourceUnit,
69
  SourceUnitByKeyMap,
70
  SourceUnitInternal,
71
} from './types.js';
72

73
const SOURCE_CACHE_LRU_STORAGE_KEY_SUFFIX = '__lru__';
10✔
74
const SOURCE_CACHE_ENTITY_KEY_ERROR = 'entity.key is required when source cache is enabled.';
10✔
75
const SOURCE_CACHE_SOURCE_KEY_ERROR = 'source.key is required when source cache is enabled.';
10✔
76
const SOURCE_RUN_RESULT_ERROR = 'source.run() must return void or a cleanup function.';
10✔
77
const resolveSourceFunctionKey = createFunctionKeyResolver({
10✔
78
  prefix: 'source-fallback',
79
});
80

81
interface HydrateFromUnitCacheKeyInput {
82
  unitCacheKey: string;
83
  cacheStateOnHit: 'hit';
84
}
85

86
interface CreateSourceFromConfigInput<
87
  TEntity extends object,
88
  TMode extends UnitEntityMode,
89
> {
90
  entity: Entity<TEntity, EntityId>;
91
  mode: TMode;
92
  readEntityValueByIdOverride?: ReadEntityValueById<EntityId, TEntity>;
93
}
94

95
const createInitialSourceContext = (
10✔
96
  hasEnabledCache: boolean,
97
): SourceContext => {
98
  return {
33✔
99
    cacheState: hasEnabledCache ? 'miss' : 'disabled',
33✔
100
    error: null,
101
  };
102
};
103

104
export const createSourceFromConfig = <
10✔
105
  TIdentity extends object | undefined,
106
  TPayload,
107
  TMeta,
108
  TEntity extends object,
109
  TMode extends UnitEntityMode,
110
>(
111
{
112
  entity,
113
  mode,
114
  readEntityValueByIdOverride,
115
}: CreateSourceFromConfigInput<TEntity, TMode>,
116
{
117
  key: sourceKey,
118
  ttl = entity.ttl ?? 0,
84✔
119
  cache,
120
  destroyDelay = entity.destroyDelay ?? DEFAULT_UNIT_DESTROY_DELAY,
99✔
121
  run,
122
  defaultValue,
123
}: SourceConfig<TIdentity, TPayload, TEntity, TMode, TMeta>,
124
): Source<TIdentity, TPayload, UnitDataByEntityMode<TEntity, TMode>, TMeta> => {
125
  type TData = UnitDataByEntityMode<TEntity, TMode>;
126
  const cacheTtl = resolveCacheTtl({
33✔
127
    sourceCache: cache,
128
    entityCache: entity.cache,
129
  });
130
  const cacheLruMaxEntries = resolveCacheLruMaxEntries({
33✔
131
    sourceCache: cache,
132
    entityCache: entity.cache,
133
  });
134
  const cacheStorage = resolveCacheStorage();
33✔
135
  const normalizedEntityKey = isNonEmptyString(entity.key)
33✔
136
    ? entity.key
137
    : '';
138
  const hasValidSourceFunctionKey = isNonEmptyString(sourceKey);
33✔
139
  const resolvedSourceFunctionKey = resolveSourceFunctionKey(sourceKey);
33✔
140
  const {
141
    mode: resolvedSourceMode,
142
    modeLocked: resolvedModeLocked,
143
  } = resolveUnitMode({
33✔
144
    entityMode: mode,
145
    defaultValue,
146
  });
147
  const hasConfiguredCacheEnabled = Boolean(
33✔
148
    cacheStorage
42!
149
    && (cacheTtl === 'infinity' || cacheTtl > 0),
150
  );
151
  if (hasConfiguredCacheEnabled && normalizedEntityKey.length === 0) {
33✔
152
    throw new Error(SOURCE_CACHE_ENTITY_KEY_ERROR);
1✔
153
  }
154
  if (hasConfiguredCacheEnabled && !hasValidSourceFunctionKey) {
32✔
155
    throw new Error(SOURCE_CACHE_SOURCE_KEY_ERROR);
1✔
156
  }
157
  const sourceEntityFunctionKey = resolveEntityFunctionKey({
31✔
158
    entityKey: normalizedEntityKey,
159
    functionKey: serializeKey({
160
      sourceKey: resolvedSourceFunctionKey,
161
      entityMode: resolvedSourceMode,
162
    }),
163
  });
164
  const cacheKeyPrefix = resolveCacheKey({
31✔
165
    sourceKey: sourceEntityFunctionKey,
166
  });
167

168
  const unitsByKey: SourceUnitByKeyMap<TIdentity, TPayload, TData, TMeta> =
169
    new Map<string, SourceUnitInternal<TIdentity, TPayload, TData, TMeta>>();
31✔
170
  const unitKeyCache = createSerializedKeyCache({
31✔
171
    mode: 'identity-unit',
172
  });
173

174
  const sourceFactory: Source<TIdentity, TPayload, TData, TMeta> = (identity) => {
31✔
175
    const identityKey = unitKeyCache.getOrCreateKey(identity);
37✔
176
    const unitKey = resolveEntityFunctionIdentityKey({
37✔
177
      entityFunctionKey: sourceEntityFunctionKey,
178
      identityKey,
179
    });
180
    const readEntityValueById: ReadEntityValueById<EntityId, TEntity> = readEntityValueByIdOverride
37✔
181
      ?? createEntityValueReader<TEntity, EntityId>({
182
        entity,
183
        identityKey,
184
        localIdentityKey: unitKey,
185
      });
186
    const existingUnit = unitsByKey.get(unitKey);
37✔
187

188
    if (existingUnit) {
37✔
189
      return existingUnit.unit;
4✔
190
    }
191

192
    const initialValue = resolveDefaultUnitValue({
33✔
193
      defaultValue,
194
      mode: resolvedSourceMode,
195
    }) as TData;
196
    const initialPayload = undefined as TPayload;
33✔
197
    const initialMode: 'one' | 'many' = resolvedSourceMode;
33✔
198

199
    const internal: SourceUnitInternal<TIdentity, TPayload, TData, TMeta> = {
33✔
200
      key: unitKey,
201
      ttl,
202
      destroyDelay,
203
      identity,
204
      payload: initialPayload,
205
      state: {
206
        value: initialValue,
207
        status: 'idle',
208
        meta: null,
209
        context: createInitialSourceContext(
210
          hasConfiguredCacheEnabled && cacheStorage?.readStatus() !== 'disabled',
40✔
211
        ),
212
      },
213
      mode: initialMode,
214
      modeLocked: resolvedModeLocked,
215
      hasEntityValue: false,
216
      membershipIds: [],
217
      readWrite: {
218
        subview: entity.readWrite.subview,
219
      },
220
      listeners: new Set<EffectListener<TData, TMeta | null, SourceContext>>(),
221
      inFlightByPayload: new Map<string, Promise<TData>>(),
222
      runSequence: 0,
223
      latestRunSequence: 0,
224
      lastRunAt: null,
225
      cleanup: null,
226
      stopped: false,
227
      destroyed: false,
228
      destroyHandled: false,
229
      unit: {} as SourceUnit<TIdentity, TPayload, TData, TMeta>,
230
    };
231
    const payloadKeyCache = createSerializedKeyCache({
37✔
232
      mode: 'payload-hot-path',
233
      limit: DEFAULT_RUN_CONTEXT_CACHE_LIMIT,
234
    });
235
    let isValueDirty = false;
37✔
236
    const refreshValueFromEntity = (): void => {
37✔
237
      internal.state.value = getModeValue(internal, readEntityValueById);
28✔
238
      isValueDirty = false;
28✔
239
    };
240
    const setRawValue = (value: TData): void => {
37✔
241
      internal.state.value = value;
×
UNCOV
242
      isValueDirty = false;
×
243
    };
244
    const markValueDirty = (): void => {
37✔
245
      isValueDirty = true;
7✔
246
    };
247
    const ensureValueFresh = (): void => {
37✔
248
      if (!isValueDirty) {
251✔
249
        return;
244✔
250
      }
251

252
      refreshValueFromEntity();
7✔
253
    };
254
    let snapshotCache: UnitSnapshot<TData, TMeta | null, SourceContext, TIdentity> = createUnitSnapshot({
37✔
255
      identity: internal.identity,
256
      value: internal.state.value,
257
      status: internal.state.status,
258
      meta: internal.state.meta,
259
      context: internal.state.context,
260
    });
261
    let lastNotifiedSnapshot = snapshotCache;
37✔
262

263
    const readSnapshot = (): UnitSnapshot<TData, TMeta | null, SourceContext, TIdentity> => {
37✔
264
      if (
101!
265
        snapshotCache.status === internal.state.status
135✔
266
        && Object.is(snapshotCache.value, internal.state.value)
267
        && Object.is(snapshotCache.meta, internal.state.meta)
268
        && Object.is(snapshotCache.context, internal.state.context)
269
      ) {
UNCOV
270
        return snapshotCache;
×
271
      }
272

273
      if (isUnitSnapshotEqual({
101✔
274
        left: snapshotCache,
275
        right: internal.state,
276
      })) {
277
        return snapshotCache;
1✔
278
      }
279

280
      snapshotCache = createUnitSnapshot({
100✔
281
        identity: internal.identity,
282
        value: internal.state.value,
283
        status: internal.state.status,
284
        meta: internal.state.meta,
285
        context: internal.state.context,
286
      });
287
      return snapshotCache;
100✔
288
    };
289

290
    const notifyUnit = (): void => {
37✔
291
      ensureValueFresh();
92✔
292
      const nextSnapshot = readSnapshot();
92✔
293

294
      if (internal.listeners.size === 0) {
92✔
295
        return;
63✔
296
      }
297

298
      if (Object.is(lastNotifiedSnapshot, nextSnapshot)) {
29!
UNCOV
299
        return;
×
300
      }
301

302
      lastNotifiedSnapshot = nextSnapshot;
29✔
303
      notifyEffectListeners(
29✔
304
        internal.listeners,
305
        nextSnapshot,
306
      );
307
    };
308

309
    const createRunContextEntry = (
37✔
310
      payload: TPayload,
311
    ): SourceRunContextEntry<TIdentity, TPayload, TData, TMeta, TEntity> => {
312
      const gate: SourceRunGate = {
32✔
UNCOV
313
        isLatestRun: () => false,
×
314
      };
315
      const runContextMethods = createEntityRunContextMethods<
32✔
316
        TEntity,
317
        TData,
318
        EntityId
319
      >({
320
        entity,
321
        state: internal,
322
        isActive: () => gate.isLatestRun(),
15✔
323
        refreshValue: refreshValueFromEntity,
UNCOV
324
        readValue: () => internal.state.value,
×
325
        refreshOnGet: true,
326
        refreshStrategy: 'lazy',
327
      });
328
      const runContextBase: SourceRunContext<
329
        TIdentity,
330
        TPayload,
331
        TData,
332
        TMeta,
333
        TEntity
334
      > = {
32✔
335
        identity: internal.identity,
336
        value: internal.state.value,
337
        status: internal.state.status,
338
        meta: internal.state.meta,
339
        context: internal.state.context,
340
        payload,
341
        setMeta: (metaInput: TMeta | null | ValueUpdater<TMeta | null, TMeta | null>) => {
342
          if (!gate.isLatestRun()) {
3!
UNCOV
343
            return;
×
344
          }
345

346
          const nextMeta = resolveValue(
3✔
347
            internal.state.meta,
348
            metaInput,
349
          );
350
          if (Object.is(nextMeta, internal.state.meta)) {
3!
UNCOV
351
            return;
×
352
          }
353

354
          internal.state.meta = nextMeta;
3✔
355
          runContextBase.meta = nextMeta;
3✔
356
          notifyUnit();
3✔
357
        },
358
        set: (input: TData | ValueUpdater<TData, TData>) => {
359
          if (!gate.isLatestRun()) {
19!
UNCOV
360
            return;
×
361
          }
362

363
          const nextValue = resolveValue(internal.state.value, input);
19✔
364
          applyEntityRunResult({
19✔
365
            entity,
366
            state: internal,
367
            nextValue,
368
            refreshValueFromMembership: refreshValueFromEntity,
369
            setRawValue,
370
            upsertOneOperation: 'runContext.set() object',
371
            upsertManyOperation: 'runContext.set() array',
372
          });
373

374
          if (!gate.isLatestRun()) {
19!
UNCOV
375
            return;
×
376
          }
377

378
          runContextBase.value = internal.state.value;
19✔
379
          notifyUnit();
19✔
380
          requestCacheSync();
19✔
381
        },
382
        reset: () => {
383
          if (!gate.isLatestRun()) {
×
UNCOV
384
            return;
×
385
          }
386

387
          resetState();
×
388
          runContextBase.value = internal.state.value;
×
389
          runContextBase.status = internal.state.status;
×
390
          runContextBase.meta = internal.state.meta;
×
UNCOV
391
          runContextBase.context = internal.state.context;
×
392
        },
393
        ...runContextMethods,
394
      };
395

396
      return {
32✔
397
        gate,
398
        context: runContextBase,
399
      };
400
    };
401
    const runContextEntryCache = createRunContextEntryCache<
37✔
402
      TPayload,
403
      SourceRunContextEntry<TIdentity, TPayload, TData, TMeta, TEntity>
404
    >({
405
      createEntry: createRunContextEntry,
406
      limit: DEFAULT_RUN_CONTEXT_CACHE_LIMIT,
407
    });
408

409
    const setContext = (input: Partial<SourceContext>): void => {
37✔
410
      const hasCacheState = Object.prototype.hasOwnProperty.call(input, 'cacheState');
140✔
411
      const hasError = Object.prototype.hasOwnProperty.call(input, 'error');
140✔
412

413
      if (
140✔
414
        (!hasCacheState || input.cacheState === internal.state.context.cacheState)
415
        && (!hasError || Object.is(input.error, internal.state.context.error))
416
      ) {
417
        return;
137✔
418
      }
419

420
      internal.state.context = {
3✔
421
        ...internal.state.context,
422
        ...input,
423
      };
424
    };
425

426
    const cacheEnabled = hasConfiguredCacheEnabled;
37✔
427
    const isCacheUnavailable = (): boolean => {
37✔
428
      if (!cacheStorage) {
34!
UNCOV
429
        return true;
×
430
      }
431

432
      return cacheStorage.readStatus() === 'disabled';
34✔
433
    };
434

435
    const isCacheLruEnabled = (): boolean => {
37✔
436
      return cacheEnabled
18✔
437
        && cacheLruMaxEntries > 0
438
        && !isCacheUnavailable();
439
    };
440
    const cacheLruStorageKey = `${cacheKeyPrefix}:${SOURCE_CACHE_LRU_STORAGE_KEY_SUFFIX}`;
37✔
441
    let cacheLruLoaded = false;
37✔
442
    let cacheLruKeyOrderMap = new Map<string, true>();
37✔
443
    let cacheLruNewestKey: string | null = null;
37✔
444
    let hasHydratedFromIdentityCache = false;
37✔
445
    let activeRunCount = 0;
37✔
446
    let hasPendingCacheSync = false;
37✔
447

448
    const syncDisabledCacheContext = (): void => {
37✔
449
      if (!cacheStorage || cacheStorage.readStatus() !== 'disabled') {
38✔
450
        return;
36✔
451
      }
452

453
      setContext({
2✔
454
        cacheState: 'disabled',
455
        error: cacheStorage.readFailure(),
456
      });
457
    };
458

459
    const readNewestCacheLruKey = (): string | null => {
37✔
460
      let nextNewestKey: string | null = null;
2✔
461
      cacheLruKeyOrderMap.forEach((_present, currentKey) => {
2✔
462
        nextNewestKey = currentKey;
2✔
463
      });
464

465
      return nextNewestKey;
2✔
466
    };
467

468
    const readPersistedCacheLruKeys = (): string[] => {
37✔
469
      return Array.from(cacheLruKeyOrderMap.keys()).reverse();
4✔
470
    };
471

472
    const loadCacheLruState = (): void => {
37✔
473
      if (!isCacheLruEnabled() || cacheLruLoaded || !cacheStorage) {
7✔
474
        return;
1✔
475
      }
476

477
      cacheLruLoaded = true;
6✔
478
      let rawLruState: unknown | null = null;
6✔
479
      try {
6✔
480
        rawLruState = cacheStorage.getItem(cacheLruStorageKey);
6✔
481
      } catch {
482
        syncDisabledCacheContext();
×
UNCOV
483
        return;
×
484
      }
485

486
      if (!rawLruState) {
6✔
487
        return;
4✔
488
      }
489

490
      if (!hasStringKeysArray(rawLruState)) {
2!
UNCOV
491
        return;
×
492
      }
493

494
      const keysCandidate = rawLruState.keys;
2✔
495
      const nextKeyOrderMap = new Map<string, true>();
2✔
496
      keysCandidate
2✔
497
        .slice()
498
        .reverse()
499
        .forEach((entry) => {
500
          nextKeyOrderMap.set(entry, true);
2✔
501
        });
502

503
      cacheLruKeyOrderMap = nextKeyOrderMap;
2✔
504
      cacheLruNewestKey = readNewestCacheLruKey();
2✔
505
    };
506

507
    const persistCacheLruState = (): void => {
37✔
508
      if (!isCacheLruEnabled() || !cacheStorage) {
4!
UNCOV
509
        return;
×
510
      }
511

512
      try {
4✔
513
        if (cacheLruKeyOrderMap.size === 0) {
4!
514
          cacheStorage.removeItem(cacheLruStorageKey);
×
UNCOV
515
          return;
×
516
        }
517

518
        const persistedKeys = readPersistedCacheLruKeys();
4✔
519
        cacheStorage.setItem(cacheLruStorageKey, {
4✔
520
          keys: persistedKeys,
521
        });
522
      } catch {
523
        syncDisabledCacheContext();
1✔
524
      }
525
    };
526

527
    const touchCacheLruKey = (unitCacheKey: string): void => {
37✔
528
      if (!isCacheLruEnabled() || !cacheStorage) {
7!
UNCOV
529
        return;
×
530
      }
531

532
      loadCacheLruState();
7✔
533
      if (cacheLruNewestKey === unitCacheKey) {
7✔
534
        return;
3✔
535
      }
536

537
      if (cacheLruKeyOrderMap.has(unitCacheKey)) {
4!
538
        cacheLruKeyOrderMap.delete(unitCacheKey);
×
539
        cacheLruKeyOrderMap.set(unitCacheKey, true);
×
540
        cacheLruNewestKey = unitCacheKey;
×
541
        persistCacheLruState();
×
UNCOV
542
        return;
×
543
      }
544

545
      cacheLruKeyOrderMap.set(unitCacheKey, true);
4✔
546
      cacheLruNewestKey = unitCacheKey;
4✔
547
      const evictedEntry = cacheLruKeyOrderMap.size > cacheLruMaxEntries
4!
548
        ? cacheLruKeyOrderMap.keys().next().value
549
        : undefined;
550
      if (evictedEntry && evictedEntry !== unitCacheKey) {
7!
551
        cacheLruKeyOrderMap.delete(evictedEntry);
×
552
        try {
×
UNCOV
553
          cacheStorage.removeItem(evictedEntry);
×
554
        } catch {
555
          syncDisabledCacheContext();
×
UNCOV
556
          return;
×
557
        }
558
      }
559

560
      persistCacheLruState();
4✔
561
    };
562

563
    const removeCacheLruKey = (unitCacheKey: string): void => {
37✔
564
      if (!isCacheLruEnabled()) {
×
UNCOV
565
        return;
×
566
      }
567

568
      loadCacheLruState();
×
569
      const existed = cacheLruKeyOrderMap.delete(unitCacheKey);
×
570
      if (!existed) {
×
UNCOV
571
        return;
×
572
      }
573

574
      if (cacheLruNewestKey === unitCacheKey) {
×
UNCOV
575
        cacheLruNewestKey = readNewestCacheLruKey();
×
576
      }
577

UNCOV
578
      persistCacheLruState();
×
579
    };
580

581
    const resolveUnitCacheKey = (): string => {
37✔
582
      return `${cacheKeyPrefix}:${unitKey}`;
73✔
583
    };
584

585
    const syncCacheRecord = (): void => {
37✔
586
      if (!cacheEnabled || !cacheStorage || isCacheUnavailable()) {
41✔
587
        syncDisabledCacheContext();
36✔
588
        return;
36✔
589
      }
590

591
      const unitCacheKey = resolveUnitCacheKey();
5✔
592
      if (!internal.hasEntityValue) {
5!
593
        removeCacheLruKey(unitCacheKey);
×
594
        try {
×
UNCOV
595
          cacheStorage.removeItem(unitCacheKey);
×
596
        } catch {
UNCOV
597
          syncDisabledCacheContext();
×
598
        }
UNCOV
599
        return;
×
600
      }
601

602
      touchCacheLruKey(unitCacheKey);
5✔
603
      if (!cacheEnabled) {
5!
UNCOV
604
        return;
×
605
      }
606

607
      const entities = internal.membershipIds
5✔
608
        .map((id) => readEntityValueById(id))
5✔
609
        .filter((entry): entry is TEntity => entry !== undefined);
5✔
610
      const record = {
5✔
611
        mode: internal.mode,
612
        entities,
613
        writtenAt: Date.now(),
614
      };
615
      try {
5✔
616
        cacheStorage.setItem(unitCacheKey, record);
5✔
617
      } catch {
618
        syncDisabledCacheContext();
1✔
619
      }
620
    };
621
    const requestCacheSync = (): void => {
37✔
622
      if (activeRunCount > 0) {
61✔
623
        hasPendingCacheSync = true;
55✔
624
        return;
55✔
625
      }
626

627
      syncCacheRecord();
6✔
628
    };
629
    const flushPendingCacheSync = (): void => {
37✔
630
      if (!hasPendingCacheSync || activeRunCount > 0) {
35!
UNCOV
631
        return;
×
632
      }
633

634
      hasPendingCacheSync = false;
35✔
635
      syncCacheRecord();
35✔
636
    };
637

638
    const hydrateFromUnitCacheKey = ({
37✔
639
      unitCacheKey,
640
      cacheStateOnHit,
641
    }: HydrateFromUnitCacheKeyInput): boolean => {
642
      if (!cacheStorage || !cacheEnabled || isCacheUnavailable()) {
68✔
643
        setContext({
57✔
644
          cacheState: 'disabled',
645
          error: cacheStorage?.readFailure() ?? null,
114✔
646
        });
647
        return false;
57✔
648
      }
649

650
      let rawRecord: unknown | null = null;
11✔
651
      try {
11✔
652
        rawRecord = cacheStorage.getItem(unitCacheKey);
11✔
653
      } catch {
654
        syncDisabledCacheContext();
×
UNCOV
655
        return false;
×
656
      }
657

658
      const parsedRecord = readSourceCacheRecord<TEntity>(rawRecord);
11✔
659
      if (!parsedRecord) {
11✔
660
        setContext({
9✔
661
          cacheState: 'miss',
662
        });
663
        return false;
9✔
664
      }
665

666
      if (isCacheRecordExpired({ ttl: cacheTtl, writtenAt: parsedRecord.writtenAt })) {
2!
667
        removeCacheLruKey(unitCacheKey);
×
668
        try {
×
UNCOV
669
          cacheStorage.removeItem(unitCacheKey);
×
670
        } catch {
671
          syncDisabledCacheContext();
×
UNCOV
672
          return false;
×
673
        }
UNCOV
674
        setContext({
×
675
          cacheState: 'stale',
676
        });
UNCOV
677
        return false;
×
678
      }
679

680
      touchCacheLruKey(unitCacheKey);
2✔
681
      if (parsedRecord.mode === 'one') {
2!
682
        const firstEntity = parsedRecord.entities[0];
×
UNCOV
683
        internal.hasEntityValue = true;
×
684

685
        if (firstEntity) {
×
686
          const upsertedEntity = entity.upsertOne(firstEntity);
×
UNCOV
687
          setOneEntityMembership(internal, {
×
688
            entity,
689
            value: upsertedEntity,
690
            operation: 'cache rehydrate one',
691
          });
UNCOV
692
          refreshValueFromEntity();
×
693
        } else {
694
          clearEntityMembership(internal, { clearUnitMembership: entity.clearUnitMembership });
×
UNCOV
695
          refreshValueFromEntity();
×
696
        }
697
      }
698

699
      if (parsedRecord.mode === 'many') {
2!
700
        const upsertedEntities = entity.upsertMany(parsedRecord.entities);
2✔
701
        setManyEntityMembership(internal, {
2✔
702
          entity,
703
          values: upsertedEntities,
704
          operation: 'cache rehydrate many',
705
        });
706
        refreshValueFromEntity();
2✔
707
      }
708

709
      internal.state.status = 'rehydrated';
2✔
710
      setContext({
2✔
711
        cacheState: cacheStateOnHit,
712
        error: null,
713
      });
714
      return true;
2✔
715
    };
716
    const hydrateFromIdentityCache = (): boolean => {
37✔
717
      if (hasHydratedFromIdentityCache) {
69✔
718
        return false;
1✔
719
      }
720

721
      const unitCacheKey = resolveUnitCacheKey();
68✔
722
      const didHydrate = hydrateFromUnitCacheKey({
68✔
723
        unitCacheKey,
724
        cacheStateOnHit: 'hit',
725
      });
726
      hasHydratedFromIdentityCache = didHydrate;
68✔
727
      return didHydrate;
68✔
728
    };
729

730
    entity.registerUnit({
37✔
731
      key: internal.key,
732
      onChange: () => {
733
        if (internal.destroyed) {
7!
UNCOV
734
          return;
×
735
        }
736

737
        markValueDirty();
7✔
738
        if (internal.listeners.size > 0) {
7!
UNCOV
739
          notifyUnit();
×
740
        }
741
        requestCacheSync();
7✔
742
      },
743
    });
744

745
    hydrateFromIdentityCache();
37✔
746

747
    let singleInFlightPromise: Promise<TData> | null = null;
37✔
748
    let hasSingleInFlightPayload = false;
37✔
749
    let singleInFlightPayload: TPayload | undefined;
750
    let singleInFlightPayloadKey: string | null = null;
37✔
751
    let runConfig: SourceRunConfig | undefined;
752

753
    const clearInFlightTracking = (): void => {
37✔
754
      internal.inFlightByPayload.clear();
×
755
      singleInFlightPromise = null;
×
756
      hasSingleInFlightPayload = false;
×
757
      singleInFlightPayload = undefined;
×
UNCOV
758
      singleInFlightPayloadKey = null;
×
759
    };
760

761
    const stopInternal = (): void => {
37✔
762
      internal.runSequence += 1;
×
763
      internal.latestRunSequence = internal.runSequence;
×
764
      internal.stopped = true;
×
765
      invokeCleanup(internal.cleanup);
×
UNCOV
766
      internal.cleanup = null;
×
767
    };
768

769
    const resetState = (): void => {
37✔
770
      if (internal.destroyed) {
×
UNCOV
771
        return;
×
772
      }
773

774
      stopInternal();
×
775
      clearInFlightTracking();
×
776
      runContextEntryCache.clear();
×
777
      payloadKeyCache.clear();
×
778
      hasHydratedFromIdentityCache = false;
×
UNCOV
779
      internal.payload = initialPayload;
×
780

781
      clearEntityMembership(internal, { clearUnitMembership: entity.clearUnitMembership });
×
782
      internal.mode = initialMode;
×
783
      internal.modeLocked = resolvedModeLocked;
×
784
      internal.lastRunAt = null;
×
785
      internal.stopped = false;
×
786
      internal.state.value = initialValue;
×
787
      isValueDirty = false;
×
788
      internal.state.status = 'idle';
×
789
      internal.state.meta = null;
×
UNCOV
790
      internal.state.context = createInitialSourceContext(
×
791
        cacheEnabled && !isCacheUnavailable(),
×
792
      );
793
      notifyUnit();
×
UNCOV
794
      requestCacheSync();
×
795
    };
796

797
    const executeRun = (
37✔
798
      isForce: boolean,
799
      payloadInput?: TPayload,
800
    ): Promise<TData> => {
801
      if (internal.destroyed) {
38!
UNCOV
802
        return Promise.resolve(internal.state.value);
×
803
      }
804

805
      const hasPayloadInput = payloadInput !== undefined;
38✔
806
      const nextPayload = resolveInput(internal.payload, payloadInput);
38✔
807
      if (hasPayloadInput) {
38✔
808
        internal.payload = nextPayload;
17✔
809
      }
810
      let payloadKey: string | null = null;
38✔
811
      if (singleInFlightPromise) {
38✔
812
        if (hasSingleInFlightPayload && Object.is(singleInFlightPayload, internal.payload)) {
2!
NEW
813
          return singleInFlightPromise;
×
814
        }
815

816
        payloadKey = payloadKeyCache.getOrCreateKey(internal.payload);
2✔
817
        if (singleInFlightPayloadKey === null && hasSingleInFlightPayload) {
2!
818
          singleInFlightPayloadKey = payloadKeyCache.getOrCreateKey(singleInFlightPayload);
2✔
819
        }
820

821
        if (singleInFlightPayloadKey === payloadKey) {
2!
822
          return singleInFlightPromise;
2✔
823
        }
824
      }
825

826
      if (internal.inFlightByPayload.size > 0) {
36!
827
        payloadKey ??= payloadKeyCache.getOrCreateKey(internal.payload);
×
828
        const inFlightForPayload = internal.inFlightByPayload.get(payloadKey);
×
829
        if (inFlightForPayload) {
×
830
          return inFlightForPayload;
×
831
        }
832
      }
833

834
      internal.stopped = false;
36✔
835
      internal.runSequence += 1;
36✔
836
      internal.latestRunSequence = internal.runSequence;
36✔
837
      const runSequence = internal.runSequence;
36✔
838
      const isLatestRun = () => {
36✔
839
        return internal.latestRunSequence === runSequence && !internal.destroyed;
126✔
840
      };
841

842
      const didHydrateFromIdentityCache = hydrateFromIdentityCache();
36✔
843
      if (didHydrateFromIdentityCache && isLatestRun()) {
36!
844
        notifyUnit();
×
845
      }
846

847
      if (!isForce && !hasPayloadInput && shouldUseCache(internal)) {
36✔
848
        return Promise.resolve(internal.state.value);
1✔
849
      }
850

851
      const shouldUseRefreshingStatus = internal.state.status === 'rehydrated'
35✔
852
        || internal.state.status === 'success';
853
      if (isLatestRun()) {
38✔
854
        internal.state.status = shouldUseRefreshingStatus ? 'refreshing' : 'loading';
35✔
855
        setContext({
35✔
856
          error: null,
857
        });
858
        notifyUnit();
35✔
859
      }
860

861
      const runContextEntry = runContextEntryCache.getOrCreate(internal.payload);
35✔
862
      runContextEntry.gate.isLatestRun = isLatestRun;
35✔
863
      runContextEntry.context.payload = internal.payload;
35✔
864
      runContextEntry.context.value = internal.state.value;
35✔
865
      runContextEntry.context.status = internal.state.status;
35✔
866
      runContextEntry.context.meta = internal.state.meta;
35✔
867
      runContextEntry.context.context = internal.state.context;
35✔
868

869
      const usesSingleInFlight = singleInFlightPromise === null && internal.inFlightByPayload.size === 0;
35✔
870
      const trackedPayloadKey = usesSingleInFlight
38!
871
        ? null
872
        : (payloadKey ?? payloadKeyCache.getOrCreateKey(internal.payload));
×
873

874
      activeRunCount += 1;
38✔
875
      const runPromise: Promise<TData> = Promise.resolve(run(runContextEntry.context))
38✔
876
        .then((result) => {
877
          if (isCleanup(result)) {
35!
878
            if (!isLatestRun()) {
×
879
              invokeCleanup(result);
×
880
              return internal.state.value;
×
881
            }
882

883
            if (internal.destroyed || internal.stopped) {
×
884
              invokeCleanup(result);
×
885
            } else {
886
              invokeCleanup(internal.cleanup);
×
887
              internal.cleanup = result;
×
888
            }
889

890
            refreshValueFromEntity();
×
891
          } else if (result !== undefined) {
35!
892
            throw new TypeError(SOURCE_RUN_RESULT_ERROR);
×
893
          }
894

895
          if (!isLatestRun()) {
35!
896
            return internal.state.value;
×
897
          }
898

899
          internal.lastRunAt = Date.now();
35✔
900
          internal.state.status = 'success';
35✔
901
          setContext({
35✔
902
            error: null,
903
          });
904
          notifyUnit();
35✔
905
          requestCacheSync();
35✔
906

907
          return internal.state.value;
35✔
908
        })
909
        .catch((error: unknown) => {
910
          if (isLatestRun()) {
×
911
            internal.state.status = 'error';
×
912
            setContext({
×
913
              error,
914
            });
915
            notifyUnit();
×
916
          }
917

918
          throw error;
×
919
        })
920
        .finally(() => {
921
          activeRunCount = Math.max(0, activeRunCount - 1);
35✔
922
          flushPendingCacheSync();
35✔
923

924
          if (usesSingleInFlight) {
35!
925
            if (singleInFlightPromise === runPromise) {
35!
926
              singleInFlightPromise = null;
35✔
927
              hasSingleInFlightPayload = false;
35✔
928
              singleInFlightPayload = undefined;
35✔
929
              singleInFlightPayloadKey = null;
35✔
930
            }
931
            return;
35✔
932
          }
933

934
          if (trackedPayloadKey !== null) {
×
935
            internal.inFlightByPayload.delete(trackedPayloadKey);
×
936
          }
937
        });
938

939
      if (usesSingleInFlight) {
38!
940
        singleInFlightPromise = runPromise;
35✔
941
        hasSingleInFlightPayload = true;
35✔
942
        singleInFlightPayload = internal.payload;
35✔
943
        singleInFlightPayloadKey = null;
35✔
944
      } else {
945
        if (trackedPayloadKey !== null) {
×
946
          internal.inFlightByPayload.set(trackedPayloadKey, runPromise);
×
947
        }
948
      }
949

950
      return runPromise;
35✔
951
    };
952

953
    const subscribe = (listener: EffectListener<TData, TMeta | null, SourceContext, TIdentity>) => {
37✔
954
      if (internal.destroyed) {
10!
955
        return undefined;
×
956
      }
957

958
      internal.listeners.add(listener);
10✔
959

960
      return () => {
10✔
961
        internal.listeners.delete(listener);
2✔
962
      };
963
    };
964
    const fetchUnit: SourceRun<TPayload, TData, TMeta> = (
37✔
965
      input?: SourceRunInput<TPayload, TData, TMeta>,
966
      config?: SourceRunConfig,
967
    ) => {
968
      if (config) {
38✔
969
        runConfig = config;
5✔
970
      }
971

972
      let payloadInput: TPayload | undefined;
973
      if (isUnitSetAction<TPayload, SourceRunConfig, TData, TMeta | null, SourceContext>(input)) {
38✔
974
        const previous: UnitRunPrevious<
975
          TPayload,
976
          SourceRunConfig,
977
          TData,
978
          TMeta | null,
979
          SourceContext
980
        > = {
2✔
981
          snapshot: getSnapshot(),
982
          data: internal.payload,
983
          config: runConfig,
984
        };
985
        payloadInput = input(previous, config);
2✔
986
      } else {
987
        payloadInput = input;
36✔
988
      }
989

990
      if (config?.mode === 'force' || config?.mode === 'refetch') {
38✔
991
        return executeRun(true, payloadInput).then(resolveUnitRunAsVoid);
5✔
992
      }
993
      return executeRun(false, payloadInput).then(resolveUnitRunAsVoid);
33✔
994
    };
995
    const refetch = (
37✔
996
      input?: SourceFetchInput<TPayload, TData, TMeta>,
997
    ): Promise<void> => {
998
      if (input === undefined) {
×
999
        return Promise.resolve(
×
1000
          Reflect.apply(fetchUnit, undefined, [
1001
            undefined,
1002
            { mode: 'refetch' },
1003
          ]),
1004
        );
1005
      }
1006

1007
      return Promise.resolve(
×
1008
        Reflect.apply(fetchUnit, undefined, [
1009
          input,
1010
          { mode: 'refetch' },
1011
        ]),
1012
      );
1013
    };
1014
    const force = (
37✔
1015
      input?: SourceFetchInput<TPayload, TData, TMeta>,
1016
    ): Promise<void> => {
1017
      if (input === undefined) {
×
1018
        return Promise.resolve(
×
1019
          Reflect.apply(fetchUnit, undefined, [
1020
            undefined,
1021
            { mode: 'force' },
1022
          ]),
1023
        );
1024
      }
1025

1026
      return Promise.resolve(
×
1027
        Reflect.apply(fetchUnit, undefined, [
1028
          input,
1029
          { mode: 'force' },
1030
        ]),
1031
      );
1032
    };
1033
    let baseSnapshotCache = snapshotCache;
37✔
1034
    let sourceSnapshotCache: SourceSnapshot<TIdentity, TPayload, TData, TMeta> | null = null;
37✔
1035
    const resolveSnapshot = (): SourceSnapshot<TIdentity, TPayload, TData, TMeta> => {
37✔
1036
      ensureValueFresh();
159✔
1037
      const baseSnapshot = (
1038
        snapshotCache.status === internal.state.status
159✔
1039
        && Object.is(snapshotCache.value, internal.state.value)
1040
        && Object.is(snapshotCache.meta, internal.state.meta)
1041
        && Object.is(snapshotCache.context, internal.state.context)
1042
      )
1043
        ? snapshotCache
1044
        : readSnapshot();
1045

1046
      if (sourceSnapshotCache && Object.is(baseSnapshotCache, baseSnapshot)) {
159✔
1047
        return sourceSnapshotCache;
71✔
1048
      }
1049

1050
      baseSnapshotCache = baseSnapshot;
88✔
1051
      sourceSnapshotCache = {
88✔
1052
        ...baseSnapshot,
1053
        load: fetchUnit,
1054
        refetch,
1055
        force,
1056
      };
1057
      return sourceSnapshotCache;
88✔
1058
    };
1059
    const getSnapshot = (): SourceSnapshot<TIdentity, TPayload, TData, TMeta> => {
37✔
1060
      return resolveSnapshot();
159✔
1061
    };
1062
    const unit: SourceUnit<TIdentity, TPayload, TData, TMeta> = {
37✔
1063
      getSnapshot,
1064
      subscribe,
1065
    };
1066

1067
    internal.unit = unit;
37✔
1068
    unitsByKey.set(unitKey, internal);
37✔
1069

1070
    return unit;
37✔
1071
  };
1072

1073
  return sourceFactory;
31✔
1074
};
1075

1076
export const source: SourceBuilder = <
10✔
1077
  TEntityStore extends Entity<object, EntityId>,
1078
  TMode extends UnitEntityMode,
1079
>({
1080
  entity,
1081
  mode,
1082
}: SourceBuilderInput<TEntityStore, TMode>): SourceByEntityModeBuilder<TEntityStore, TMode> => {
1083
  const sourceByEntityMode: SourceByEntityModeBuilder<TEntityStore, TMode> = <
33✔
1084
    TIdentity extends object | undefined,
1085
    TPayload,
1086
    TMeta,
1087
  >(
1088
    config: SourceConfig<
1089
      TIdentity,
1090
      TPayload,
1091
      EntityValueOfStore<TEntityStore>,
1092
      TMode,
1093
      TMeta
1094
    >,
1095
  ) => {
1096
    return createSourceFromConfig({
33✔
1097
      entity,
1098
      mode,
1099
    }, config);
1100
  };
1101

1102
  return sourceByEntityMode;
33✔
1103
};
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