@flurryx/rx 0.8.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/http.cjs.map +1 -1
- package/dist/http.d.cts +3 -3
- package/dist/http.d.ts +3 -3
- package/dist/http.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/http.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http.ts","../src/error/http-error-normalizer.ts"],"sourcesContent":["export { httpErrorNormalizer } from './error/http-error-normalizer';\n","import { HttpErrorResponse } from \"@angular/common/http\";\nimport type { ResourceErrors } from \"@flurryx/core\";\nimport type { ErrorNormalizer } from \"./error-normalizer\";\n\n/**\n * Error normalizer specialized for Angular's `HttpErrorResponse`.\n *\n * Import from the `flurryx/http` entry point to keep `@angular/common/http
|
|
1
|
+
{"version":3,"sources":["../src/http.ts","../src/error/http-error-normalizer.ts"],"sourcesContent":["export { httpErrorNormalizer } from './error/http-error-normalizer';\n","import { HttpErrorResponse } from \"@angular/common/http\";\nimport type { ResourceErrors } from \"@flurryx/core\";\nimport type { ErrorNormalizer } from \"./error-normalizer\";\n\n/**\n * Error normalizer specialized for Angular's `HttpErrorResponse`.\n *\n * Import from `@flurryx/rx/http` or the aggregate `flurryx/http` entry point to\n * keep `@angular/common/http` out of your bundle unless you actually need it.\n *\n * - If the response contains `error.errors` (array), returns it as-is.\n * - Otherwise, wraps `{ status, message }` into a single-element `ResourceErrors`.\n * - Falls back to `{ code: 'UNKNOWN', message: String(error) }` for non-HTTP errors.\n *\n * @example\n * ```ts\n * import { httpErrorNormalizer } from '@flurryx/rx/http';\n *\n * this.http.get('/api/data')\n * .pipe(syncToStore(this.store, 'DATA', { errorNormalizer: httpErrorNormalizer }))\n * .subscribe();\n * ```\n */\nexport const httpErrorNormalizer: ErrorNormalizer = (\n error: unknown\n): ResourceErrors => {\n if (!(error instanceof HttpErrorResponse)) {\n return [\n {\n code: \"UNKNOWN\",\n message: String(error),\n },\n ];\n }\n\n const errors = error.error?.errors as unknown;\n if (Array.isArray(errors)) {\n return errors as ResourceErrors;\n }\n\n return [\n {\n code: error.status.toString(),\n message: error.message,\n },\n ];\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAkC;AAuB3B,IAAM,sBAAuC,CAClD,UACmB;AACnB,MAAI,EAAE,iBAAiB,gCAAoB;AACzC,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,OAAO;AAC5B,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM,MAAM,OAAO,SAAS;AAAA,MAC5B,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AACF;","names":[]}
|
package/dist/http.d.cts
CHANGED
|
@@ -4,8 +4,8 @@ import '@flurryx/core';
|
|
|
4
4
|
/**
|
|
5
5
|
* Error normalizer specialized for Angular's `HttpErrorResponse`.
|
|
6
6
|
*
|
|
7
|
-
* Import from the `flurryx/http` entry point to
|
|
8
|
-
* out of your bundle unless you actually need it.
|
|
7
|
+
* Import from `@flurryx/rx/http` or the aggregate `flurryx/http` entry point to
|
|
8
|
+
* keep `@angular/common/http` out of your bundle unless you actually need it.
|
|
9
9
|
*
|
|
10
10
|
* - If the response contains `error.errors` (array), returns it as-is.
|
|
11
11
|
* - Otherwise, wraps `{ status, message }` into a single-element `ResourceErrors`.
|
|
@@ -13,7 +13,7 @@ import '@flurryx/core';
|
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```ts
|
|
16
|
-
* import { httpErrorNormalizer } from 'flurryx/http';
|
|
16
|
+
* import { httpErrorNormalizer } from '@flurryx/rx/http';
|
|
17
17
|
*
|
|
18
18
|
* this.http.get('/api/data')
|
|
19
19
|
* .pipe(syncToStore(this.store, 'DATA', { errorNormalizer: httpErrorNormalizer }))
|
package/dist/http.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import '@flurryx/core';
|
|
|
4
4
|
/**
|
|
5
5
|
* Error normalizer specialized for Angular's `HttpErrorResponse`.
|
|
6
6
|
*
|
|
7
|
-
* Import from the `flurryx/http` entry point to
|
|
8
|
-
* out of your bundle unless you actually need it.
|
|
7
|
+
* Import from `@flurryx/rx/http` or the aggregate `flurryx/http` entry point to
|
|
8
|
+
* keep `@angular/common/http` out of your bundle unless you actually need it.
|
|
9
9
|
*
|
|
10
10
|
* - If the response contains `error.errors` (array), returns it as-is.
|
|
11
11
|
* - Otherwise, wraps `{ status, message }` into a single-element `ResourceErrors`.
|
|
@@ -13,7 +13,7 @@ import '@flurryx/core';
|
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```ts
|
|
16
|
-
* import { httpErrorNormalizer } from 'flurryx/http';
|
|
16
|
+
* import { httpErrorNormalizer } from '@flurryx/rx/http';
|
|
17
17
|
*
|
|
18
18
|
* this.http.get('/api/data')
|
|
19
19
|
* .pipe(syncToStore(this.store, 'DATA', { errorNormalizer: httpErrorNormalizer }))
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/error/http-error-normalizer.ts"],"sourcesContent":["import { HttpErrorResponse } from \"@angular/common/http\";\nimport type { ResourceErrors } from \"@flurryx/core\";\nimport type { ErrorNormalizer } from \"./error-normalizer\";\n\n/**\n * Error normalizer specialized for Angular's `HttpErrorResponse`.\n *\n * Import from the `flurryx/http` entry point to keep `@angular/common/http
|
|
1
|
+
{"version":3,"sources":["../src/error/http-error-normalizer.ts"],"sourcesContent":["import { HttpErrorResponse } from \"@angular/common/http\";\nimport type { ResourceErrors } from \"@flurryx/core\";\nimport type { ErrorNormalizer } from \"./error-normalizer\";\n\n/**\n * Error normalizer specialized for Angular's `HttpErrorResponse`.\n *\n * Import from `@flurryx/rx/http` or the aggregate `flurryx/http` entry point to\n * keep `@angular/common/http` out of your bundle unless you actually need it.\n *\n * - If the response contains `error.errors` (array), returns it as-is.\n * - Otherwise, wraps `{ status, message }` into a single-element `ResourceErrors`.\n * - Falls back to `{ code: 'UNKNOWN', message: String(error) }` for non-HTTP errors.\n *\n * @example\n * ```ts\n * import { httpErrorNormalizer } from '@flurryx/rx/http';\n *\n * this.http.get('/api/data')\n * .pipe(syncToStore(this.store, 'DATA', { errorNormalizer: httpErrorNormalizer }))\n * .subscribe();\n * ```\n */\nexport const httpErrorNormalizer: ErrorNormalizer = (\n error: unknown\n): ResourceErrors => {\n if (!(error instanceof HttpErrorResponse)) {\n return [\n {\n code: \"UNKNOWN\",\n message: String(error),\n },\n ];\n }\n\n const errors = error.error?.errors as unknown;\n if (Array.isArray(errors)) {\n return errors as ResourceErrors;\n }\n\n return [\n {\n code: error.status.toString(),\n message: error.message,\n },\n ];\n};\n"],"mappings":";AAAA,SAAS,yBAAyB;AAuB3B,IAAM,sBAAuC,CAClD,UACmB;AACnB,MAAI,EAAE,iBAAiB,oBAAoB;AACzC,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,OAAO;AAC5B,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM,MAAM,OAAO,SAAS;AAAA,MAC5B,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/operators/sync-to-store.ts","../src/error/error-normalizer.ts","../src/operators/sync-to-keyed-store.ts","../src/decorators/skip-if-cached.ts","../src/decorators/loading.ts"],"sourcesContent":["export {\n syncToStore,\n type SyncToStoreOptions,\n} from './operators/sync-to-store';\nexport {\n syncToKeyedStore,\n type SyncToKeyedStoreOptions,\n} from './operators/sync-to-keyed-store';\nexport { SkipIfCached } from './decorators/skip-if-cached';\nexport { Loading } from './decorators/loading';\nexport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from './error/error-normalizer';\n","import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport type { ResourceState } from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\n\n/**\n * Options for {@link syncToStore}.\n */\nexport interface SyncToStoreOptions {\n /**\n * Whether to complete the Observable after the first emission (applies `take(1)`).\n * @default true\n */\n completeOnFirstEmission?: boolean;\n /**\n * Callback invoked in `finalize()` after the Observable completes or errors.\n * Useful for side effects like clearing another slot or navigating.\n * @default undefined\n */\n callbackAfterComplete?: () => void;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\ninterface SyncToStoreRuntimeStore {\n update(key: PropertyKey, newState: unknown): void;\n}\n\nexport function syncToStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: BaseStore<TEnum, TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\nexport function syncToStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: IStore<TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\nexport function syncToStore(\n store: unknown,\n key: PropertyKey,\n options: SyncToStoreOptions = { completeOnFirstEmission: true }\n) {\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToStoreRuntimeStore;\n\n return <R>(source: Observable<R>) => {\n let pipeline = source.pipe(\n tap({\n next: (data: R) => {\n syncStore.update(key, {\n data,\n isLoading: false,\n status: \"Success\",\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n syncStore.update(key, {\n data: undefined,\n isLoading: false,\n status: \"Error\",\n errors: normalizeError(error),\n });\n },\n })\n );\n\n if (options.completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (options.callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(options.callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { ResourceErrors } from \"@flurryx/core\";\n\n/**\n * Function type that converts an unknown error into the normalized `ResourceErrors` shape.\n * Plug a custom normalizer into `syncToStore` / `syncToKeyedStore` via the `errorNormalizer` option.\n */\nexport type ErrorNormalizer = (error: unknown) => ResourceErrors;\n\n/**\n * Default error normalizer used by `syncToStore` and `syncToKeyedStore`.\n *\n * Handles these shapes (checked in order):\n * 1. `{ error: { errors: [...] } }` — extracts the nested array directly.\n * 2. `{ status: number, message: string }` — wraps into `[{ code, message }]`.\n * 3. `Error` instances — wraps `error.message` with code `'UNKNOWN'`.\n * 4. Anything else — `[{ code: 'UNKNOWN', message: String(error) }]`.\n *\n * @param error - The raw error from an Observable.\n * @returns Normalized `Array<{ code: string; message: string }>`.\n */\nexport function defaultErrorNormalizer(error: unknown): ResourceErrors {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"error\" in error &&\n typeof (error as Record<string, unknown>).error === \"object\"\n ) {\n const inner = (error as { error: Record<string, unknown> }).error;\n if (inner && Array.isArray(inner.errors)) {\n return inner.errors as ResourceErrors;\n }\n }\n\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"status\" in error &&\n \"message\" in error\n ) {\n const typed = error as { status: number; message: string };\n return [\n {\n code: String(typed.status),\n message: typed.message,\n },\n ];\n }\n\n if (error instanceof Error) {\n return [\n {\n code: \"UNKNOWN\",\n message: error.message,\n },\n ];\n }\n\n return [\n {\n code: \"UNKNOWN\",\n message: String(error),\n },\n ];\n}\n","import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport {\n createKeyedResourceData,\n isAnyKeyLoading,\n type KeyedResourceData,\n type KeyedResourceKey,\n type ResourceErrors,\n type ResourceState,\n type ResourceStatus,\n} from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\nimport type { SyncToStoreOptions } from \"./sync-to-store\";\n\ninterface SyncToKeyedStoreRuntimeStore {\n get(key: PropertyKey): () => ResourceState<unknown>;\n update(key: PropertyKey, newState: unknown): void;\n}\n\n/**\n * Options for {@link syncToKeyedStore}.\n *\n * Extends {@link SyncToStoreOptions} with keyed-specific options.\n *\n * @template R - The raw response type from the Observable.\n * @template TValue - The entity type stored in the keyed slot.\n */\nexport interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {\n /**\n * Transform the raw API response before writing it to the store.\n * Useful when the API envelope differs from the entity type.\n *\n * @default undefined (response is stored as-is)\n *\n * @example\n * ```ts\n * syncToKeyedStore(store, 'ITEMS', id, {\n * mapResponse: (response) => response.data,\n * })\n * ```\n */\n mapResponse?: (response: R) => TValue;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\nfunction withoutKey<TKey extends KeyedResourceKey, TValue>(\n record: Partial<Record<TKey, TValue>>,\n key: TKey\n): Partial<Record<TKey, TValue>> {\n const next: Partial<Record<TKey, TValue>> = {\n ...record,\n };\n delete next[key];\n return next;\n}\n\nexport function syncToKeyedStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: BaseStore<TEnum, TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\nexport function syncToKeyedStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: IStore<TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\nexport function syncToKeyedStore(\n store: unknown,\n storeKey: PropertyKey,\n resourceKey: KeyedResourceKey,\n options: SyncToKeyedStoreOptions<unknown, unknown> = {\n completeOnFirstEmission: true,\n }\n) {\n const { completeOnFirstEmission, callbackAfterComplete, mapResponse } =\n options;\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToKeyedStoreRuntimeStore;\n\n return (source: Observable<unknown>) => {\n let pipeline = source.pipe(\n tap({\n next: (response: unknown) => {\n const value = mapResponse ? mapResponse(response) : response;\n\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Success\" as ResourceStatus,\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n entities: {\n ...data.entities,\n [resourceKey]: value,\n } as Partial<Record<KeyedResourceKey, unknown>>,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: withoutKey(data.errors, resourceKey),\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Error\" as ResourceStatus,\n };\n\n const nextErrors: Partial<Record<KeyedResourceKey, ResourceErrors>> =\n {\n ...data.errors,\n [resourceKey]: normalizeError(error),\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: nextErrors,\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n })\n );\n\n if (completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { Signal } from \"@angular/core\";\nimport { finalize, Observable, of, shareReplay, tap } from \"rxjs\";\nimport {\n isKeyedResourceData,\n CACHE_NO_TIMEOUT,\n DEFAULT_CACHE_TTL_MS,\n} from \"@flurryx/core\";\nimport type { ResourceState, StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithSignal<TKey extends StoreEnum> = {\n get: (key: TKey) => Signal<ResourceState<unknown>> | undefined;\n};\n\ninterface CacheEntry {\n timestamp: number;\n args: string;\n inflight$?: Observable<unknown>;\n}\n\nconst cacheState = new WeakMap<\n object,\n Map<StoreEnum, Map<string, CacheEntry>>\n>();\n\nfunction getStoreKeyMap(\n store: object,\n key: StoreEnum\n): Map<string, CacheEntry> {\n let storeMap = cacheState.get(store);\n if (!storeMap) {\n storeMap = new Map();\n cacheState.set(store, storeMap);\n }\n\n let keyMap = storeMap.get(key);\n if (!keyMap) {\n keyMap = new Map();\n storeMap.set(key, keyMap);\n }\n\n return keyMap;\n}\n\nfunction getCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): CacheEntry | undefined {\n return getStoreKeyMap(store, key).get(cacheKey);\n}\n\nfunction setCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string,\n entry: CacheEntry\n): void {\n getStoreKeyMap(store, key).set(cacheKey, entry);\n}\n\nfunction clearCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): void {\n getStoreKeyMap(store, key).delete(cacheKey);\n}\n\nfunction deriveResourceKey(args: unknown[]): KeyedResourceKey | undefined {\n const key = args[0];\n if (typeof key === \"string\" || typeof key === \"number\") {\n return key;\n }\n return undefined;\n}\n\nfunction isExpired(\n timestamp: number | undefined,\n timeoutMs: number,\n now: number\n): boolean {\n if (timeoutMs === CACHE_NO_TIMEOUT) {\n return false;\n }\n if (timestamp === undefined) {\n return false;\n }\n return now - timestamp >= timeoutMs;\n}\n\ninterface StoreContext {\n store: object;\n storeSignal: Signal<ResourceState<unknown>>;\n currentState: ResourceState<unknown>;\n}\n\nfunction getStoreContext<TTarget, TKey extends StoreEnum>(\n instance: TTarget,\n storeKey: TKey,\n getStore: (i: TTarget) => StoreWithSignal<TKey> | undefined\n): StoreContext | undefined {\n const store = getStore(instance);\n if (!store) {\n return undefined;\n }\n\n const storeSignal = store.get(storeKey);\n if (!storeSignal) {\n return undefined;\n }\n\n const currentState = storeSignal();\n if (currentState === null || currentState === undefined) {\n return undefined;\n }\n\n return { store, storeSignal, currentState };\n}\n\ninterface CacheContext {\n isKeyedCall: boolean;\n resourceKey: KeyedResourceKey | undefined;\n keyedData: ReturnType<typeof isKeyedResourceData> extends true\n ? { entities: object; isLoading: object; status: object; errors: object }\n : undefined;\n runtimeCacheKey: string;\n keyedCacheEntry: CacheEntry | undefined;\n nonKeyedCacheEntry: CacheEntry | undefined;\n}\n\nfunction getCacheContext(\n store: object,\n storeKey: StoreEnum,\n args: unknown[],\n argsString: string,\n currentState: ResourceState<unknown>\n): CacheContext {\n const keyedData = isKeyedResourceData(currentState.data)\n ? currentState.data\n : undefined;\n const resourceKey = keyedData ? deriveResourceKey(args) : undefined;\n const isKeyedCall = keyedData !== undefined && resourceKey !== undefined;\n\n const keyedCacheKey = argsString;\n const nonKeyedCacheKey = \"__single__\";\n const runtimeCacheKey = isKeyedCall ? keyedCacheKey : nonKeyedCacheKey;\n\n const keyedCacheEntry = getCacheEntry(store, storeKey, keyedCacheKey);\n const nonKeyedCacheEntry = getCacheEntry(store, storeKey, nonKeyedCacheKey);\n\n return {\n isKeyedCall,\n resourceKey,\n keyedData: keyedData as CacheContext[\"keyedData\"],\n runtimeCacheKey,\n keyedCacheEntry,\n nonKeyedCacheEntry,\n };\n}\n\nfunction handleCacheErrors(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n currentState: ResourceState<unknown>\n): void {\n if (!context.keyedData && currentState.status === \"Error\") {\n clearCacheEntry(store, storeKey, \"__single__\");\n }\n if (context.keyedData && context.resourceKey !== undefined) {\n const status = (\n context.keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n }\n ).status[context.resourceKey];\n if (status === \"Error\") {\n clearCacheEntry(store, storeKey, context.runtimeCacheKey);\n }\n }\n}\n\ninterface CacheHitResult {\n hit: boolean;\n value?: Observable<unknown>;\n}\n\nfunction handleKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean\n): CacheHitResult {\n const { keyedData, resourceKey, keyedCacheEntry, runtimeCacheKey } = context;\n\n if (!keyedData || resourceKey === undefined) {\n return { hit: false };\n }\n\n const typed = keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n entities: Partial<Record<KeyedResourceKey, unknown>>;\n isLoading: Partial<Record<KeyedResourceKey, boolean>>;\n };\n\n const status = typed.status[resourceKey];\n const entity = typed.entities[resourceKey];\n const loading = typed.isLoading[resourceKey] === true;\n\n const expired = isExpired(keyedCacheEntry?.timestamp, timeoutMs, now);\n if (expired) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n }\n\n if (!expired && status === \"Success\" && entity !== undefined) {\n if (returnObservable) {\n return { hit: true, value: of(entity) };\n }\n return { hit: true };\n }\n\n if (returnObservable) {\n if (keyedCacheEntry?.inflight$) {\n return { hit: true, value: keyedCacheEntry.inflight$ };\n }\n } else if (loading) {\n return { hit: true };\n }\n\n return { hit: false };\n}\n\ninterface NonKeyedCacheExtra {\n readonly currentState: ResourceState<unknown>;\n readonly argsString: string;\n readonly storeSignal: Signal<ResourceState<unknown>>;\n}\n\nfunction handleNonKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean,\n extra: NonKeyedCacheExtra\n): CacheHitResult {\n const { nonKeyedCacheEntry, runtimeCacheKey } = context;\n const { currentState, argsString, storeSignal } = extra;\n\n if (\n returnObservable &&\n nonKeyedCacheEntry?.args === argsString &&\n nonKeyedCacheEntry.inflight$\n ) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n\n const hasValidCacheState =\n currentState?.status === \"Success\" || currentState?.isLoading === true;\n\n if (\n nonKeyedCacheEntry &&\n isExpired(nonKeyedCacheEntry.timestamp, timeoutMs, now)\n ) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n } else if (nonKeyedCacheEntry?.args === argsString && hasValidCacheState) {\n if (returnObservable) {\n if (nonKeyedCacheEntry.inflight$) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n return { hit: true, value: of(storeSignal().data) };\n }\n return { hit: true };\n }\n\n return { hit: false };\n}\n\nfunction createCachedObservable(\n result: Observable<unknown>,\n store: object,\n storeKey: StoreEnum,\n runtimeCacheKey: string,\n argsString: string\n): Observable<unknown> {\n return result.pipe(\n tap({\n next: () => {\n setCacheEntry(store, storeKey, runtimeCacheKey, {\n timestamp: Date.now(),\n args: argsString,\n });\n },\n error: () => {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n },\n }),\n finalize(() => {\n const entry = getCacheEntry(store, storeKey, runtimeCacheKey);\n if (entry?.inflight$) {\n const { inflight$: _inflight$, ...rest } = entry;\n setCacheEntry(store, storeKey, runtimeCacheKey, rest);\n }\n }),\n shareReplay({ bufferSize: 1, refCount: true })\n );\n}\n\n/**\n * Method decorator that skips execution when the store already has valid cached data.\n *\n * **Cache hit** (method skipped) when:\n * - `status === 'Success'` or `isLoading === true`\n * - Timeout has not expired\n * - Method arguments match (compared via `JSON.stringify`)\n *\n * **Cache miss** (method executes) when:\n * - Initial state (no status, not loading)\n * - `status === 'Error'` (errors are never cached)\n * - Timeout expired\n * - Arguments changed\n *\n * **Keyed resources**: When the first argument is a `string | number` and the store\n * data is a `KeyedResourceData`, cache entries are tracked per resource key automatically.\n *\n * @param storeKey - The store slot to check for cached data.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n * @param returnObservable - When `true`, returns `Observable` (with `shareReplay` for deduplication).\n * When `false`, returns `void`.\n * @default false\n * @param timeoutMs - Cache TTL in milliseconds. Use `CACHE_NO_TIMEOUT` for infinite.\n * @default DEFAULT_CACHE_TTL_MS (300 000 ms / 5 minutes)\n *\n * @example\n * ```ts\n * @SkipIfCached('LIST', (i: ProductFacade) => i.store)\n * @Loading('LIST', (i: ProductFacade) => i.store)\n * loadProducts() {\n * this.http.get('/api/products')\n * .pipe(syncToStore(this.store, 'LIST'))\n * .subscribe();\n * }\n * ```\n */\nexport function SkipIfCached<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithSignal<TKey>;\n }) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\n/** @inheritDoc */\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable = false,\n timeoutMs = DEFAULT_CACHE_TTL_MS\n): MethodDecorator {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: TTarget,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const storeContext = getStoreContext(this, storeKey, storeGetter);\n if (!storeContext) {\n return originalMethod.apply(this, args);\n }\n const { store, storeSignal, currentState } = storeContext;\n\n const argsString = JSON.stringify(args);\n const now = Date.now();\n const cacheContext = getCacheContext(\n store,\n storeKey,\n args,\n argsString,\n currentState\n );\n\n handleCacheErrors(store, storeKey, cacheContext, currentState);\n\n let cacheHit: CacheHitResult;\n\n if (cacheContext.isKeyedCall) {\n cacheHit = handleKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable\n );\n } else {\n cacheHit = handleNonKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable,\n { currentState, argsString, storeSignal }\n );\n }\n\n if (cacheHit.hit) {\n return cacheHit.value;\n }\n\n const result = originalMethod.apply(this, args);\n\n if (!returnObservable) {\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n });\n return result;\n }\n\n const inflight$ = createCachedObservable(\n result as Observable<unknown>,\n store,\n storeKey,\n cacheContext.runtimeCacheKey,\n argsString\n );\n\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n inflight$,\n });\n\n return inflight$;\n };\n\n return descriptor;\n };\n}\n","import type { StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithLoading<TKey extends StoreEnum> = {\n startLoading: (key: TKey) => void;\n};\n\n/**\n * Method decorator that calls `store.startLoading(key)` before the original method executes.\n *\n * **Keyed detection**: If the first method argument is a `string | number` and the store\n * has a `startKeyedLoading` method, it calls that instead for per-key loading state.\n *\n * Typically composed with `@SkipIfCached` (which should be the outermost decorator):\n * ```ts\n * @SkipIfCached('LIST', (i) => i.store) // outermost — can short-circuit\n * @Loading('LIST', (i) => i.store) // sets loading before method body\n * loadProducts() { ... }\n * ```\n *\n * @param storeKey - The store slot to mark as loading.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n */\nexport function Loading<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithLoading<TKey>;\n }) => StoreWithLoading<TKey>\n): MethodDecorator;\n/** @inheritDoc */\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n): MethodDecorator;\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n) {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: unknown,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const store = storeGetter(this);\n\n const resourceKey = args[0];\n const canKey =\n typeof resourceKey === \"string\" || typeof resourceKey === \"number\";\n const hasKeyed =\n typeof store === \"object\" &&\n store !== null &&\n \"startKeyedLoading\" in store &&\n typeof (store as { startKeyedLoading?: unknown }).startKeyedLoading ===\n \"function\";\n\n if (canKey && hasKeyed) {\n (\n store as unknown as {\n startKeyedLoading: (\n key: TKey,\n resourceKey: KeyedResourceKey\n ) => void;\n }\n ).startKeyedLoading(storeKey, resourceKey as KeyedResourceKey);\n } else {\n store?.startLoading(storeKey);\n }\n return originalMethod.apply(this, args);\n };\n\n return descriptor;\n };\n}\n"],"mappings":"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","names":["import_rxjs","import_rxjs","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/operators/sync-to-store.ts","../src/error/error-normalizer.ts","../src/operators/sync-to-keyed-store.ts","../src/decorators/skip-if-cached.ts","../src/decorators/loading.ts"],"sourcesContent":["export {\n syncToStore,\n type SyncToStoreOptions,\n} from './operators/sync-to-store';\nexport {\n syncToKeyedStore,\n type SyncToKeyedStoreOptions,\n} from './operators/sync-to-keyed-store';\nexport { SkipIfCached } from './decorators/skip-if-cached';\nexport { Loading } from './decorators/loading';\nexport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from './error/error-normalizer';\n","import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport type { ResourceState } from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\n\n/**\n * Options for {@link syncToStore}.\n */\nexport interface SyncToStoreOptions {\n /**\n * Whether to complete the Observable after the first emission (applies `take(1)`).\n * @default true\n */\n completeOnFirstEmission?: boolean;\n /**\n * Callback invoked in `finalize()` after the Observable completes or errors.\n * Useful for side effects like clearing another slot or navigating.\n * @default undefined\n */\n callbackAfterComplete?: () => void;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\ninterface SyncToStoreRuntimeStore {\n update(key: PropertyKey, newState: unknown): void;\n}\n\n/**\n * Syncs an Observable result into a `ResourceState` slot on a Flurryx store.\n *\n * On success, the target slot is updated with the emitted value, `status: \"Success\"`,\n * and `isLoading: false`. On error, the slot is updated with `status: \"Error\"`\n * and normalized errors.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TEnum - Enum-like store keys used by `BaseStore`.\n * @template TData - Store data shape.\n * @template K - Store key whose value is a `ResourceState`.\n * @param store - The target Flurryx store instance.\n * @param key - The resource slot to update.\n * @param options - Optional behavior for completion, finalization, and error normalization.\n * @returns An RxJS operator function that syncs source emissions into the store.\n *\n * @example\n * ```ts\n * this.api.getUsers().pipe(\n * syncToStore(this.store, \"USERS\")\n * )\n * ```\n */\nexport function syncToStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: BaseStore<TEnum, TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\n/**\n * Syncs an Observable result into a `ResourceState` slot on a Flurryx store.\n *\n * On success, the target slot is updated with the emitted value, `status: \"Success\"`,\n * and `isLoading: false`. On error, the slot is updated with `status: \"Error\"`\n * and normalized errors.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TData - Store data shape.\n * @template K - Store key whose value is a `ResourceState`.\n * @param store - The target Flurryx store instance.\n * @param key - The resource slot to update.\n * @param options - Optional behavior for completion, finalization, and error normalization.\n * @returns An RxJS operator function that syncs source emissions into the store.\n */\nexport function syncToStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: IStore<TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\nexport function syncToStore(\n store: unknown,\n key: PropertyKey,\n options: SyncToStoreOptions = { completeOnFirstEmission: true }\n) {\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToStoreRuntimeStore;\n\n return <R>(source: Observable<R>) => {\n let pipeline = source.pipe(\n tap({\n next: (data: R) => {\n syncStore.update(key, {\n data,\n isLoading: false,\n status: \"Success\",\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n syncStore.update(key, {\n data: undefined,\n isLoading: false,\n status: \"Error\",\n errors: normalizeError(error),\n });\n },\n })\n );\n\n if (options.completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (options.callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(options.callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { ResourceErrors } from \"@flurryx/core\";\n\n/**\n * Function type that converts an unknown error into the normalized `ResourceErrors` shape.\n * Plug a custom normalizer into `syncToStore` / `syncToKeyedStore` via the `errorNormalizer` option.\n */\nexport type ErrorNormalizer = (error: unknown) => ResourceErrors;\n\n/**\n * Default error normalizer used by `syncToStore` and `syncToKeyedStore`.\n *\n * Handles these shapes (checked in order):\n * 1. `{ error: { errors: [...] } }` — extracts the nested array directly.\n * 2. `{ status: number, message: string }` — wraps into `[{ code, message }]`.\n * 3. `Error` instances — wraps `error.message` with code `'UNKNOWN'`.\n * 4. Anything else — `[{ code: 'UNKNOWN', message: String(error) }]`.\n *\n * @param error - The raw error from an Observable.\n * @returns Normalized `Array<{ code: string; message: string }>`.\n */\nexport function defaultErrorNormalizer(error: unknown): ResourceErrors {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"error\" in error &&\n typeof (error as Record<string, unknown>).error === \"object\"\n ) {\n const inner = (error as { error: Record<string, unknown> }).error;\n if (inner && Array.isArray(inner.errors)) {\n return inner.errors as ResourceErrors;\n }\n }\n\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"status\" in error &&\n \"message\" in error\n ) {\n const typed = error as { status: number; message: string };\n return [\n {\n code: String(typed.status),\n message: typed.message,\n },\n ];\n }\n\n if (error instanceof Error) {\n return [\n {\n code: \"UNKNOWN\",\n message: error.message,\n },\n ];\n }\n\n return [\n {\n code: \"UNKNOWN\",\n message: String(error),\n },\n ];\n}\n","import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport {\n createKeyedResourceData,\n isAnyKeyLoading,\n type KeyedResourceData,\n type KeyedResourceKey,\n type ResourceErrors,\n type ResourceState,\n type ResourceStatus,\n} from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\nimport type { SyncToStoreOptions } from \"./sync-to-store\";\n\ninterface SyncToKeyedStoreRuntimeStore {\n get(key: PropertyKey): () => ResourceState<unknown>;\n update(key: PropertyKey, newState: unknown): void;\n}\n\n/**\n * Options for {@link syncToKeyedStore}.\n *\n * Extends {@link SyncToStoreOptions} with keyed-specific options.\n *\n * @template R - The raw response type from the Observable.\n * @template TValue - The entity type stored in the keyed slot.\n */\nexport interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {\n /**\n * Transform the raw API response before writing it to the store.\n * Useful when the API envelope differs from the entity type.\n *\n * @default undefined (response is stored as-is)\n *\n * @example\n * ```ts\n * syncToKeyedStore(store, 'ITEMS', id, {\n * mapResponse: (response) => response.data,\n * })\n * ```\n */\n mapResponse?: (response: R) => TValue;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\nfunction withoutKey<TKey extends KeyedResourceKey, TValue>(\n record: Partial<Record<TKey, TValue>>,\n key: TKey\n): Partial<Record<TKey, TValue>> {\n const next: Partial<Record<TKey, TValue>> = {\n ...record,\n };\n delete next[key];\n return next;\n}\n\n/**\n * Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.\n *\n * Each emission is written under `resourceKey` inside the slot's keyed data map.\n * On success, that key is marked as `\"Success\"` and no longer loading. On error,\n * the key is marked as `\"Error\"` and receives normalized errors.\n *\n * Use `mapResponse` when the Observable emits an API envelope and only part of it\n * should be stored as the keyed entity value.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TEnum - Enum-like store keys used by `BaseStore`.\n * @template TData - Store data shape.\n * @template TStoreKey - Store key whose value is a keyed `ResourceState`.\n * @template TKey - Entity key inside the keyed resource map.\n * @template TValue - Entity type stored in the keyed slot.\n * @template R - Raw Observable emission type.\n * @param store - The target Flurryx store instance.\n * @param storeKey - The keyed resource slot to update.\n * @param resourceKey - The entity identifier inside the keyed slot.\n * @param options - Optional response mapping, completion, finalization, and error normalization behavior.\n * @returns An RxJS operator function that syncs source emissions into the keyed store slot.\n *\n * @example\n * ```ts\n * this.api.getUser(id).pipe(\n * syncToKeyedStore(this.store, \"USERS\", id)\n * )\n * ```\n */\nexport function syncToKeyedStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: BaseStore<TEnum, TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\n/**\n * Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.\n *\n * Each emission is written under `resourceKey` inside the slot's keyed data map.\n * On success, that key is marked as `\"Success\"` and no longer loading. On error,\n * the key is marked as `\"Error\"` and receives normalized errors.\n *\n * Use `mapResponse` when the Observable emits an API envelope and only part of it\n * should be stored as the keyed entity value.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TData - Store data shape.\n * @template TStoreKey - Store key whose value is a keyed `ResourceState`.\n * @template TKey - Entity key inside the keyed resource map.\n * @template TValue - Entity type stored in the keyed slot.\n * @template R - Raw Observable emission type.\n * @param store - The target Flurryx store instance.\n * @param storeKey - The keyed resource slot to update.\n * @param resourceKey - The entity identifier inside the keyed slot.\n * @param options - Optional response mapping, completion, finalization, and error normalization behavior.\n * @returns An RxJS operator function that syncs source emissions into the keyed store slot.\n */\nexport function syncToKeyedStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: IStore<TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\nexport function syncToKeyedStore(\n store: unknown,\n storeKey: PropertyKey,\n resourceKey: KeyedResourceKey,\n options: SyncToKeyedStoreOptions<unknown, unknown> = {\n completeOnFirstEmission: true,\n }\n) {\n const { completeOnFirstEmission, callbackAfterComplete, mapResponse } =\n options;\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToKeyedStoreRuntimeStore;\n\n return (source: Observable<unknown>) => {\n let pipeline = source.pipe(\n tap({\n next: (response: unknown) => {\n const value = mapResponse ? mapResponse(response) : response;\n\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Success\" as ResourceStatus,\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n entities: {\n ...data.entities,\n [resourceKey]: value,\n } as Partial<Record<KeyedResourceKey, unknown>>,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: withoutKey(data.errors, resourceKey),\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Error\" as ResourceStatus,\n };\n\n const nextErrors: Partial<Record<KeyedResourceKey, ResourceErrors>> =\n {\n ...data.errors,\n [resourceKey]: normalizeError(error),\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: nextErrors,\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n })\n );\n\n if (completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { Signal } from \"@angular/core\";\nimport { finalize, Observable, of, shareReplay, tap } from \"rxjs\";\nimport {\n isKeyedResourceData,\n CACHE_NO_TIMEOUT,\n DEFAULT_CACHE_TTL_MS,\n} from \"@flurryx/core\";\nimport type { ResourceState, StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithSignal<TKey extends StoreEnum> = {\n get: (key: TKey) => Signal<ResourceState<unknown>> | undefined;\n};\n\ninterface CacheEntry {\n timestamp: number;\n args: string;\n inflight$?: Observable<unknown>;\n}\n\nconst cacheState = new WeakMap<\n object,\n Map<StoreEnum, Map<string, CacheEntry>>\n>();\n\nfunction getStoreKeyMap(\n store: object,\n key: StoreEnum\n): Map<string, CacheEntry> {\n let storeMap = cacheState.get(store);\n if (!storeMap) {\n storeMap = new Map();\n cacheState.set(store, storeMap);\n }\n\n let keyMap = storeMap.get(key);\n if (!keyMap) {\n keyMap = new Map();\n storeMap.set(key, keyMap);\n }\n\n return keyMap;\n}\n\nfunction getCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): CacheEntry | undefined {\n return getStoreKeyMap(store, key).get(cacheKey);\n}\n\nfunction setCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string,\n entry: CacheEntry\n): void {\n getStoreKeyMap(store, key).set(cacheKey, entry);\n}\n\nfunction clearCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): void {\n getStoreKeyMap(store, key).delete(cacheKey);\n}\n\nfunction deriveResourceKey(args: unknown[]): KeyedResourceKey | undefined {\n const key = args[0];\n if (typeof key === \"string\" || typeof key === \"number\") {\n return key;\n }\n return undefined;\n}\n\nfunction isExpired(\n timestamp: number | undefined,\n timeoutMs: number,\n now: number\n): boolean {\n if (timeoutMs === CACHE_NO_TIMEOUT) {\n return false;\n }\n if (timestamp === undefined) {\n return false;\n }\n return now - timestamp >= timeoutMs;\n}\n\ninterface StoreContext {\n store: object;\n storeSignal: Signal<ResourceState<unknown>>;\n currentState: ResourceState<unknown>;\n}\n\nfunction getStoreContext<TTarget, TKey extends StoreEnum>(\n instance: TTarget,\n storeKey: TKey,\n getStore: (i: TTarget) => StoreWithSignal<TKey> | undefined\n): StoreContext | undefined {\n const store = getStore(instance);\n if (!store) {\n return undefined;\n }\n\n const storeSignal = store.get(storeKey);\n if (!storeSignal) {\n return undefined;\n }\n\n const currentState = storeSignal();\n if (currentState === null || currentState === undefined) {\n return undefined;\n }\n\n return { store, storeSignal, currentState };\n}\n\ninterface CacheContext {\n isKeyedCall: boolean;\n resourceKey: KeyedResourceKey | undefined;\n keyedData: ReturnType<typeof isKeyedResourceData> extends true\n ? { entities: object; isLoading: object; status: object; errors: object }\n : undefined;\n runtimeCacheKey: string;\n keyedCacheEntry: CacheEntry | undefined;\n nonKeyedCacheEntry: CacheEntry | undefined;\n}\n\nfunction getCacheContext(\n store: object,\n storeKey: StoreEnum,\n args: unknown[],\n argsString: string,\n currentState: ResourceState<unknown>\n): CacheContext {\n const keyedData = isKeyedResourceData(currentState.data)\n ? currentState.data\n : undefined;\n const resourceKey = keyedData ? deriveResourceKey(args) : undefined;\n const isKeyedCall = keyedData !== undefined && resourceKey !== undefined;\n\n const keyedCacheKey = argsString;\n const nonKeyedCacheKey = \"__single__\";\n const runtimeCacheKey = isKeyedCall ? keyedCacheKey : nonKeyedCacheKey;\n\n const keyedCacheEntry = getCacheEntry(store, storeKey, keyedCacheKey);\n const nonKeyedCacheEntry = getCacheEntry(store, storeKey, nonKeyedCacheKey);\n\n return {\n isKeyedCall,\n resourceKey,\n keyedData: keyedData as CacheContext[\"keyedData\"],\n runtimeCacheKey,\n keyedCacheEntry,\n nonKeyedCacheEntry,\n };\n}\n\nfunction handleCacheErrors(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n currentState: ResourceState<unknown>\n): void {\n if (!context.keyedData && currentState.status === \"Error\") {\n clearCacheEntry(store, storeKey, \"__single__\");\n }\n if (context.keyedData && context.resourceKey !== undefined) {\n const status = (\n context.keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n }\n ).status[context.resourceKey];\n if (status === \"Error\") {\n clearCacheEntry(store, storeKey, context.runtimeCacheKey);\n }\n }\n}\n\ninterface CacheHitResult {\n hit: boolean;\n value?: Observable<unknown>;\n}\n\nfunction handleKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean\n): CacheHitResult {\n const { keyedData, resourceKey, keyedCacheEntry, runtimeCacheKey } = context;\n\n if (!keyedData || resourceKey === undefined) {\n return { hit: false };\n }\n\n const typed = keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n entities: Partial<Record<KeyedResourceKey, unknown>>;\n isLoading: Partial<Record<KeyedResourceKey, boolean>>;\n };\n\n const status = typed.status[resourceKey];\n const entity = typed.entities[resourceKey];\n const loading = typed.isLoading[resourceKey] === true;\n\n const expired = isExpired(keyedCacheEntry?.timestamp, timeoutMs, now);\n if (expired) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n }\n\n if (!expired && status === \"Success\" && entity !== undefined) {\n if (returnObservable) {\n return { hit: true, value: of(entity) };\n }\n return { hit: true };\n }\n\n if (returnObservable) {\n if (keyedCacheEntry?.inflight$) {\n return { hit: true, value: keyedCacheEntry.inflight$ };\n }\n } else if (loading) {\n return { hit: true };\n }\n\n return { hit: false };\n}\n\ninterface NonKeyedCacheExtra {\n readonly currentState: ResourceState<unknown>;\n readonly argsString: string;\n readonly storeSignal: Signal<ResourceState<unknown>>;\n}\n\nfunction handleNonKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean,\n extra: NonKeyedCacheExtra\n): CacheHitResult {\n const { nonKeyedCacheEntry, runtimeCacheKey } = context;\n const { currentState, argsString, storeSignal } = extra;\n\n if (\n returnObservable &&\n nonKeyedCacheEntry?.args === argsString &&\n nonKeyedCacheEntry.inflight$\n ) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n\n const hasValidCacheState =\n currentState?.status === \"Success\" || currentState?.isLoading === true;\n\n if (\n nonKeyedCacheEntry &&\n isExpired(nonKeyedCacheEntry.timestamp, timeoutMs, now)\n ) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n } else if (nonKeyedCacheEntry?.args === argsString && hasValidCacheState) {\n if (returnObservable) {\n if (nonKeyedCacheEntry.inflight$) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n return { hit: true, value: of(storeSignal().data) };\n }\n return { hit: true };\n }\n\n return { hit: false };\n}\n\nfunction createCachedObservable(\n result: Observable<unknown>,\n store: object,\n storeKey: StoreEnum,\n runtimeCacheKey: string,\n argsString: string\n): Observable<unknown> {\n return result.pipe(\n tap({\n next: () => {\n setCacheEntry(store, storeKey, runtimeCacheKey, {\n timestamp: Date.now(),\n args: argsString,\n });\n },\n error: () => {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n },\n }),\n finalize(() => {\n const entry = getCacheEntry(store, storeKey, runtimeCacheKey);\n if (entry?.inflight$) {\n const { inflight$: _inflight$, ...rest } = entry;\n setCacheEntry(store, storeKey, runtimeCacheKey, rest);\n }\n }),\n shareReplay({ bufferSize: 1, refCount: true })\n );\n}\n\n/**\n * Method decorator that skips execution when the store already has valid cached data.\n *\n * **Cache hit** (method skipped) when:\n * - `status === 'Success'` or `isLoading === true`\n * - Timeout has not expired\n * - Method arguments match (compared via `JSON.stringify`)\n *\n * **Cache miss** (method executes) when:\n * - Initial state (no status, not loading)\n * - `status === 'Error'` (errors are never cached)\n * - Timeout expired\n * - Arguments changed\n *\n * **Keyed resources**: When the first argument is a `string | number` and the store\n * data is a `KeyedResourceData`, cache entries are tracked per resource key automatically.\n *\n * @param storeKey - The store slot to check for cached data.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n * @param returnObservable - When `true`, returns `Observable` (with `shareReplay` for deduplication).\n * When `false`, returns `void`.\n * @default false\n * @param timeoutMs - Cache TTL in milliseconds. Use `CACHE_NO_TIMEOUT` for infinite.\n * @default DEFAULT_CACHE_TTL_MS (300 000 ms / 5 minutes)\n *\n * @example\n * ```ts\n * @SkipIfCached('LIST', (i: ProductFacade) => i.store)\n * @Loading('LIST', (i: ProductFacade) => i.store)\n * loadProducts() {\n * this.http.get('/api/products')\n * .pipe(syncToStore(this.store, 'LIST'))\n * .subscribe();\n * }\n * ```\n */\nexport function SkipIfCached<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithSignal<TKey>;\n }) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\n/** @inheritDoc */\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable = false,\n timeoutMs = DEFAULT_CACHE_TTL_MS\n): MethodDecorator {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: TTarget,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const storeContext = getStoreContext(this, storeKey, storeGetter);\n if (!storeContext) {\n return originalMethod.apply(this, args);\n }\n const { store, storeSignal, currentState } = storeContext;\n\n const argsString = JSON.stringify(args);\n const now = Date.now();\n const cacheContext = getCacheContext(\n store,\n storeKey,\n args,\n argsString,\n currentState\n );\n\n handleCacheErrors(store, storeKey, cacheContext, currentState);\n\n let cacheHit: CacheHitResult;\n\n if (cacheContext.isKeyedCall) {\n cacheHit = handleKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable\n );\n } else {\n cacheHit = handleNonKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable,\n { currentState, argsString, storeSignal }\n );\n }\n\n if (cacheHit.hit) {\n return cacheHit.value;\n }\n\n const result = originalMethod.apply(this, args);\n\n if (!returnObservable) {\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n });\n return result;\n }\n\n const inflight$ = createCachedObservable(\n result as Observable<unknown>,\n store,\n storeKey,\n cacheContext.runtimeCacheKey,\n argsString\n );\n\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n inflight$,\n });\n\n return inflight$;\n };\n\n return descriptor;\n };\n}\n","import type { StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithLoading<TKey extends StoreEnum> = {\n startLoading: (key: TKey) => void;\n};\n\n/**\n * Method decorator that calls `store.startLoading(key)` before the original method executes.\n *\n * **Keyed detection**: If the first method argument is a `string | number` and the store\n * has a `startKeyedLoading` method, it calls that instead for per-key loading state.\n *\n * Typically composed with `@SkipIfCached` (which should be the outermost decorator):\n * ```ts\n * @SkipIfCached('LIST', (i) => i.store) // outermost — can short-circuit\n * @Loading('LIST', (i) => i.store) // sets loading before method body\n * loadProducts() { ... }\n * ```\n *\n * @param storeKey - The store slot to mark as loading.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n */\nexport function Loading<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithLoading<TKey>;\n }) => StoreWithLoading<TKey>\n): MethodDecorator;\n/** @inheritDoc */\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n): MethodDecorator;\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n) {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: unknown,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const store = storeGetter(this);\n\n const resourceKey = args[0];\n const canKey =\n typeof resourceKey === \"string\" || typeof resourceKey === \"number\";\n const hasKeyed =\n typeof store === \"object\" &&\n store !== null &&\n \"startKeyedLoading\" in store &&\n typeof (store as { startKeyedLoading?: unknown }).startKeyedLoading ===\n \"function\";\n\n if (canKey && hasKeyed) {\n (\n store as unknown as {\n startKeyedLoading: (\n key: TKey,\n resourceKey: KeyedResourceKey\n ) => void;\n }\n ).startKeyedLoading(storeKey, resourceKey as KeyedResourceKey);\n } else {\n store?.startLoading(storeKey);\n }\n return originalMethod.apply(this, args);\n };\n\n return descriptor;\n };\n}\n"],"mappings":"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","names":["import_rxjs","import_rxjs","import_core"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -26,9 +26,49 @@ interface SyncToStoreOptions {
|
|
|
26
26
|
*/
|
|
27
27
|
errorNormalizer?: ErrorNormalizer;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Syncs an Observable result into a `ResourceState` slot on a Flurryx store.
|
|
31
|
+
*
|
|
32
|
+
* On success, the target slot is updated with the emitted value, `status: "Success"`,
|
|
33
|
+
* and `isLoading: false`. On error, the slot is updated with `status: "Error"`
|
|
34
|
+
* and normalized errors.
|
|
35
|
+
*
|
|
36
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
37
|
+
*
|
|
38
|
+
* @template TEnum - Enum-like store keys used by `BaseStore`.
|
|
39
|
+
* @template TData - Store data shape.
|
|
40
|
+
* @template K - Store key whose value is a `ResourceState`.
|
|
41
|
+
* @param store - The target Flurryx store instance.
|
|
42
|
+
* @param key - The resource slot to update.
|
|
43
|
+
* @param options - Optional behavior for completion, finalization, and error normalization.
|
|
44
|
+
* @returns An RxJS operator function that syncs source emissions into the store.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* this.api.getUsers().pipe(
|
|
49
|
+
* syncToStore(this.store, "USERS")
|
|
50
|
+
* )
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
29
53
|
declare function syncToStore<TEnum extends Record<string, string | number>, TData extends {
|
|
30
54
|
[K in keyof TEnum]: ResourceState<unknown>;
|
|
31
55
|
}, K extends keyof TData>(store: BaseStore<TEnum, TData>, key: K, options?: SyncToStoreOptions): <R>(source: Observable<R>) => Observable<R>;
|
|
56
|
+
/**
|
|
57
|
+
* Syncs an Observable result into a `ResourceState` slot on a Flurryx store.
|
|
58
|
+
*
|
|
59
|
+
* On success, the target slot is updated with the emitted value, `status: "Success"`,
|
|
60
|
+
* and `isLoading: false`. On error, the slot is updated with `status: "Error"`
|
|
61
|
+
* and normalized errors.
|
|
62
|
+
*
|
|
63
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
64
|
+
*
|
|
65
|
+
* @template TData - Store data shape.
|
|
66
|
+
* @template K - Store key whose value is a `ResourceState`.
|
|
67
|
+
* @param store - The target Flurryx store instance.
|
|
68
|
+
* @param key - The resource slot to update.
|
|
69
|
+
* @param options - Optional behavior for completion, finalization, and error normalization.
|
|
70
|
+
* @returns An RxJS operator function that syncs source emissions into the store.
|
|
71
|
+
*/
|
|
32
72
|
declare function syncToStore<TData extends {
|
|
33
73
|
[K in keyof TData]: ResourceState<unknown>;
|
|
34
74
|
}, K extends keyof TData>(store: IStore<TData>, key: K, options?: SyncToStoreOptions): <R>(source: Observable<R>) => Observable<R>;
|
|
@@ -62,9 +102,63 @@ interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {
|
|
|
62
102
|
*/
|
|
63
103
|
errorNormalizer?: ErrorNormalizer;
|
|
64
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.
|
|
107
|
+
*
|
|
108
|
+
* Each emission is written under `resourceKey` inside the slot's keyed data map.
|
|
109
|
+
* On success, that key is marked as `"Success"` and no longer loading. On error,
|
|
110
|
+
* the key is marked as `"Error"` and receives normalized errors.
|
|
111
|
+
*
|
|
112
|
+
* Use `mapResponse` when the Observable emits an API envelope and only part of it
|
|
113
|
+
* should be stored as the keyed entity value.
|
|
114
|
+
*
|
|
115
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
116
|
+
*
|
|
117
|
+
* @template TEnum - Enum-like store keys used by `BaseStore`.
|
|
118
|
+
* @template TData - Store data shape.
|
|
119
|
+
* @template TStoreKey - Store key whose value is a keyed `ResourceState`.
|
|
120
|
+
* @template TKey - Entity key inside the keyed resource map.
|
|
121
|
+
* @template TValue - Entity type stored in the keyed slot.
|
|
122
|
+
* @template R - Raw Observable emission type.
|
|
123
|
+
* @param store - The target Flurryx store instance.
|
|
124
|
+
* @param storeKey - The keyed resource slot to update.
|
|
125
|
+
* @param resourceKey - The entity identifier inside the keyed slot.
|
|
126
|
+
* @param options - Optional response mapping, completion, finalization, and error normalization behavior.
|
|
127
|
+
* @returns An RxJS operator function that syncs source emissions into the keyed store slot.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* this.api.getUser(id).pipe(
|
|
132
|
+
* syncToKeyedStore(this.store, "USERS", id)
|
|
133
|
+
* )
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
65
136
|
declare function syncToKeyedStore<TEnum extends Record<string, string | number>, TData extends {
|
|
66
137
|
[K in keyof TEnum]: ResourceState<unknown>;
|
|
67
138
|
}, TStoreKey extends keyof TData, TKey extends KeyedResourceKey, TValue, R = TValue>(store: BaseStore<TEnum, TData>, storeKey: TStoreKey, resourceKey: TKey, options?: SyncToKeyedStoreOptions<R, TValue>): (source: Observable<R>) => Observable<R>;
|
|
139
|
+
/**
|
|
140
|
+
* Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.
|
|
141
|
+
*
|
|
142
|
+
* Each emission is written under `resourceKey` inside the slot's keyed data map.
|
|
143
|
+
* On success, that key is marked as `"Success"` and no longer loading. On error,
|
|
144
|
+
* the key is marked as `"Error"` and receives normalized errors.
|
|
145
|
+
*
|
|
146
|
+
* Use `mapResponse` when the Observable emits an API envelope and only part of it
|
|
147
|
+
* should be stored as the keyed entity value.
|
|
148
|
+
*
|
|
149
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
150
|
+
*
|
|
151
|
+
* @template TData - Store data shape.
|
|
152
|
+
* @template TStoreKey - Store key whose value is a keyed `ResourceState`.
|
|
153
|
+
* @template TKey - Entity key inside the keyed resource map.
|
|
154
|
+
* @template TValue - Entity type stored in the keyed slot.
|
|
155
|
+
* @template R - Raw Observable emission type.
|
|
156
|
+
* @param store - The target Flurryx store instance.
|
|
157
|
+
* @param storeKey - The keyed resource slot to update.
|
|
158
|
+
* @param resourceKey - The entity identifier inside the keyed slot.
|
|
159
|
+
* @param options - Optional response mapping, completion, finalization, and error normalization behavior.
|
|
160
|
+
* @returns An RxJS operator function that syncs source emissions into the keyed store slot.
|
|
161
|
+
*/
|
|
68
162
|
declare function syncToKeyedStore<TData extends {
|
|
69
163
|
[K in keyof TData]: ResourceState<unknown>;
|
|
70
164
|
}, TStoreKey extends keyof TData, TKey extends KeyedResourceKey, TValue, R = TValue>(store: IStore<TData>, storeKey: TStoreKey, resourceKey: TKey, options?: SyncToKeyedStoreOptions<R, TValue>): (source: Observable<R>) => Observable<R>;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,9 +26,49 @@ interface SyncToStoreOptions {
|
|
|
26
26
|
*/
|
|
27
27
|
errorNormalizer?: ErrorNormalizer;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Syncs an Observable result into a `ResourceState` slot on a Flurryx store.
|
|
31
|
+
*
|
|
32
|
+
* On success, the target slot is updated with the emitted value, `status: "Success"`,
|
|
33
|
+
* and `isLoading: false`. On error, the slot is updated with `status: "Error"`
|
|
34
|
+
* and normalized errors.
|
|
35
|
+
*
|
|
36
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
37
|
+
*
|
|
38
|
+
* @template TEnum - Enum-like store keys used by `BaseStore`.
|
|
39
|
+
* @template TData - Store data shape.
|
|
40
|
+
* @template K - Store key whose value is a `ResourceState`.
|
|
41
|
+
* @param store - The target Flurryx store instance.
|
|
42
|
+
* @param key - The resource slot to update.
|
|
43
|
+
* @param options - Optional behavior for completion, finalization, and error normalization.
|
|
44
|
+
* @returns An RxJS operator function that syncs source emissions into the store.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* this.api.getUsers().pipe(
|
|
49
|
+
* syncToStore(this.store, "USERS")
|
|
50
|
+
* )
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
29
53
|
declare function syncToStore<TEnum extends Record<string, string | number>, TData extends {
|
|
30
54
|
[K in keyof TEnum]: ResourceState<unknown>;
|
|
31
55
|
}, K extends keyof TData>(store: BaseStore<TEnum, TData>, key: K, options?: SyncToStoreOptions): <R>(source: Observable<R>) => Observable<R>;
|
|
56
|
+
/**
|
|
57
|
+
* Syncs an Observable result into a `ResourceState` slot on a Flurryx store.
|
|
58
|
+
*
|
|
59
|
+
* On success, the target slot is updated with the emitted value, `status: "Success"`,
|
|
60
|
+
* and `isLoading: false`. On error, the slot is updated with `status: "Error"`
|
|
61
|
+
* and normalized errors.
|
|
62
|
+
*
|
|
63
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
64
|
+
*
|
|
65
|
+
* @template TData - Store data shape.
|
|
66
|
+
* @template K - Store key whose value is a `ResourceState`.
|
|
67
|
+
* @param store - The target Flurryx store instance.
|
|
68
|
+
* @param key - The resource slot to update.
|
|
69
|
+
* @param options - Optional behavior for completion, finalization, and error normalization.
|
|
70
|
+
* @returns An RxJS operator function that syncs source emissions into the store.
|
|
71
|
+
*/
|
|
32
72
|
declare function syncToStore<TData extends {
|
|
33
73
|
[K in keyof TData]: ResourceState<unknown>;
|
|
34
74
|
}, K extends keyof TData>(store: IStore<TData>, key: K, options?: SyncToStoreOptions): <R>(source: Observable<R>) => Observable<R>;
|
|
@@ -62,9 +102,63 @@ interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {
|
|
|
62
102
|
*/
|
|
63
103
|
errorNormalizer?: ErrorNormalizer;
|
|
64
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.
|
|
107
|
+
*
|
|
108
|
+
* Each emission is written under `resourceKey` inside the slot's keyed data map.
|
|
109
|
+
* On success, that key is marked as `"Success"` and no longer loading. On error,
|
|
110
|
+
* the key is marked as `"Error"` and receives normalized errors.
|
|
111
|
+
*
|
|
112
|
+
* Use `mapResponse` when the Observable emits an API envelope and only part of it
|
|
113
|
+
* should be stored as the keyed entity value.
|
|
114
|
+
*
|
|
115
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
116
|
+
*
|
|
117
|
+
* @template TEnum - Enum-like store keys used by `BaseStore`.
|
|
118
|
+
* @template TData - Store data shape.
|
|
119
|
+
* @template TStoreKey - Store key whose value is a keyed `ResourceState`.
|
|
120
|
+
* @template TKey - Entity key inside the keyed resource map.
|
|
121
|
+
* @template TValue - Entity type stored in the keyed slot.
|
|
122
|
+
* @template R - Raw Observable emission type.
|
|
123
|
+
* @param store - The target Flurryx store instance.
|
|
124
|
+
* @param storeKey - The keyed resource slot to update.
|
|
125
|
+
* @param resourceKey - The entity identifier inside the keyed slot.
|
|
126
|
+
* @param options - Optional response mapping, completion, finalization, and error normalization behavior.
|
|
127
|
+
* @returns An RxJS operator function that syncs source emissions into the keyed store slot.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* this.api.getUser(id).pipe(
|
|
132
|
+
* syncToKeyedStore(this.store, "USERS", id)
|
|
133
|
+
* )
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
65
136
|
declare function syncToKeyedStore<TEnum extends Record<string, string | number>, TData extends {
|
|
66
137
|
[K in keyof TEnum]: ResourceState<unknown>;
|
|
67
138
|
}, TStoreKey extends keyof TData, TKey extends KeyedResourceKey, TValue, R = TValue>(store: BaseStore<TEnum, TData>, storeKey: TStoreKey, resourceKey: TKey, options?: SyncToKeyedStoreOptions<R, TValue>): (source: Observable<R>) => Observable<R>;
|
|
139
|
+
/**
|
|
140
|
+
* Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.
|
|
141
|
+
*
|
|
142
|
+
* Each emission is written under `resourceKey` inside the slot's keyed data map.
|
|
143
|
+
* On success, that key is marked as `"Success"` and no longer loading. On error,
|
|
144
|
+
* the key is marked as `"Error"` and receives normalized errors.
|
|
145
|
+
*
|
|
146
|
+
* Use `mapResponse` when the Observable emits an API envelope and only part of it
|
|
147
|
+
* should be stored as the keyed entity value.
|
|
148
|
+
*
|
|
149
|
+
* By default, the operator completes after the first emission by applying `take(1)`.
|
|
150
|
+
*
|
|
151
|
+
* @template TData - Store data shape.
|
|
152
|
+
* @template TStoreKey - Store key whose value is a keyed `ResourceState`.
|
|
153
|
+
* @template TKey - Entity key inside the keyed resource map.
|
|
154
|
+
* @template TValue - Entity type stored in the keyed slot.
|
|
155
|
+
* @template R - Raw Observable emission type.
|
|
156
|
+
* @param store - The target Flurryx store instance.
|
|
157
|
+
* @param storeKey - The keyed resource slot to update.
|
|
158
|
+
* @param resourceKey - The entity identifier inside the keyed slot.
|
|
159
|
+
* @param options - Optional response mapping, completion, finalization, and error normalization behavior.
|
|
160
|
+
* @returns An RxJS operator function that syncs source emissions into the keyed store slot.
|
|
161
|
+
*/
|
|
68
162
|
declare function syncToKeyedStore<TData extends {
|
|
69
163
|
[K in keyof TData]: ResourceState<unknown>;
|
|
70
164
|
}, TStoreKey extends keyof TData, TKey extends KeyedResourceKey, TValue, R = TValue>(store: IStore<TData>, storeKey: TStoreKey, resourceKey: TKey, options?: SyncToKeyedStoreOptions<R, TValue>): (source: Observable<R>) => Observable<R>;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/operators/sync-to-store.ts","../src/error/error-normalizer.ts","../src/operators/sync-to-keyed-store.ts","../src/decorators/skip-if-cached.ts","../src/decorators/loading.ts"],"sourcesContent":["import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport type { ResourceState } from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\n\n/**\n * Options for {@link syncToStore}.\n */\nexport interface SyncToStoreOptions {\n /**\n * Whether to complete the Observable after the first emission (applies `take(1)`).\n * @default true\n */\n completeOnFirstEmission?: boolean;\n /**\n * Callback invoked in `finalize()` after the Observable completes or errors.\n * Useful for side effects like clearing another slot or navigating.\n * @default undefined\n */\n callbackAfterComplete?: () => void;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\ninterface SyncToStoreRuntimeStore {\n update(key: PropertyKey, newState: unknown): void;\n}\n\nexport function syncToStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: BaseStore<TEnum, TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\nexport function syncToStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: IStore<TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\nexport function syncToStore(\n store: unknown,\n key: PropertyKey,\n options: SyncToStoreOptions = { completeOnFirstEmission: true }\n) {\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToStoreRuntimeStore;\n\n return <R>(source: Observable<R>) => {\n let pipeline = source.pipe(\n tap({\n next: (data: R) => {\n syncStore.update(key, {\n data,\n isLoading: false,\n status: \"Success\",\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n syncStore.update(key, {\n data: undefined,\n isLoading: false,\n status: \"Error\",\n errors: normalizeError(error),\n });\n },\n })\n );\n\n if (options.completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (options.callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(options.callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { ResourceErrors } from \"@flurryx/core\";\n\n/**\n * Function type that converts an unknown error into the normalized `ResourceErrors` shape.\n * Plug a custom normalizer into `syncToStore` / `syncToKeyedStore` via the `errorNormalizer` option.\n */\nexport type ErrorNormalizer = (error: unknown) => ResourceErrors;\n\n/**\n * Default error normalizer used by `syncToStore` and `syncToKeyedStore`.\n *\n * Handles these shapes (checked in order):\n * 1. `{ error: { errors: [...] } }` — extracts the nested array directly.\n * 2. `{ status: number, message: string }` — wraps into `[{ code, message }]`.\n * 3. `Error` instances — wraps `error.message` with code `'UNKNOWN'`.\n * 4. Anything else — `[{ code: 'UNKNOWN', message: String(error) }]`.\n *\n * @param error - The raw error from an Observable.\n * @returns Normalized `Array<{ code: string; message: string }>`.\n */\nexport function defaultErrorNormalizer(error: unknown): ResourceErrors {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"error\" in error &&\n typeof (error as Record<string, unknown>).error === \"object\"\n ) {\n const inner = (error as { error: Record<string, unknown> }).error;\n if (inner && Array.isArray(inner.errors)) {\n return inner.errors as ResourceErrors;\n }\n }\n\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"status\" in error &&\n \"message\" in error\n ) {\n const typed = error as { status: number; message: string };\n return [\n {\n code: String(typed.status),\n message: typed.message,\n },\n ];\n }\n\n if (error instanceof Error) {\n return [\n {\n code: \"UNKNOWN\",\n message: error.message,\n },\n ];\n }\n\n return [\n {\n code: \"UNKNOWN\",\n message: String(error),\n },\n ];\n}\n","import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport {\n createKeyedResourceData,\n isAnyKeyLoading,\n type KeyedResourceData,\n type KeyedResourceKey,\n type ResourceErrors,\n type ResourceState,\n type ResourceStatus,\n} from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\nimport type { SyncToStoreOptions } from \"./sync-to-store\";\n\ninterface SyncToKeyedStoreRuntimeStore {\n get(key: PropertyKey): () => ResourceState<unknown>;\n update(key: PropertyKey, newState: unknown): void;\n}\n\n/**\n * Options for {@link syncToKeyedStore}.\n *\n * Extends {@link SyncToStoreOptions} with keyed-specific options.\n *\n * @template R - The raw response type from the Observable.\n * @template TValue - The entity type stored in the keyed slot.\n */\nexport interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {\n /**\n * Transform the raw API response before writing it to the store.\n * Useful when the API envelope differs from the entity type.\n *\n * @default undefined (response is stored as-is)\n *\n * @example\n * ```ts\n * syncToKeyedStore(store, 'ITEMS', id, {\n * mapResponse: (response) => response.data,\n * })\n * ```\n */\n mapResponse?: (response: R) => TValue;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\nfunction withoutKey<TKey extends KeyedResourceKey, TValue>(\n record: Partial<Record<TKey, TValue>>,\n key: TKey\n): Partial<Record<TKey, TValue>> {\n const next: Partial<Record<TKey, TValue>> = {\n ...record,\n };\n delete next[key];\n return next;\n}\n\nexport function syncToKeyedStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: BaseStore<TEnum, TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\nexport function syncToKeyedStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: IStore<TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\nexport function syncToKeyedStore(\n store: unknown,\n storeKey: PropertyKey,\n resourceKey: KeyedResourceKey,\n options: SyncToKeyedStoreOptions<unknown, unknown> = {\n completeOnFirstEmission: true,\n }\n) {\n const { completeOnFirstEmission, callbackAfterComplete, mapResponse } =\n options;\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToKeyedStoreRuntimeStore;\n\n return (source: Observable<unknown>) => {\n let pipeline = source.pipe(\n tap({\n next: (response: unknown) => {\n const value = mapResponse ? mapResponse(response) : response;\n\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Success\" as ResourceStatus,\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n entities: {\n ...data.entities,\n [resourceKey]: value,\n } as Partial<Record<KeyedResourceKey, unknown>>,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: withoutKey(data.errors, resourceKey),\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Error\" as ResourceStatus,\n };\n\n const nextErrors: Partial<Record<KeyedResourceKey, ResourceErrors>> =\n {\n ...data.errors,\n [resourceKey]: normalizeError(error),\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: nextErrors,\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n })\n );\n\n if (completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { Signal } from \"@angular/core\";\nimport { finalize, Observable, of, shareReplay, tap } from \"rxjs\";\nimport {\n isKeyedResourceData,\n CACHE_NO_TIMEOUT,\n DEFAULT_CACHE_TTL_MS,\n} from \"@flurryx/core\";\nimport type { ResourceState, StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithSignal<TKey extends StoreEnum> = {\n get: (key: TKey) => Signal<ResourceState<unknown>> | undefined;\n};\n\ninterface CacheEntry {\n timestamp: number;\n args: string;\n inflight$?: Observable<unknown>;\n}\n\nconst cacheState = new WeakMap<\n object,\n Map<StoreEnum, Map<string, CacheEntry>>\n>();\n\nfunction getStoreKeyMap(\n store: object,\n key: StoreEnum\n): Map<string, CacheEntry> {\n let storeMap = cacheState.get(store);\n if (!storeMap) {\n storeMap = new Map();\n cacheState.set(store, storeMap);\n }\n\n let keyMap = storeMap.get(key);\n if (!keyMap) {\n keyMap = new Map();\n storeMap.set(key, keyMap);\n }\n\n return keyMap;\n}\n\nfunction getCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): CacheEntry | undefined {\n return getStoreKeyMap(store, key).get(cacheKey);\n}\n\nfunction setCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string,\n entry: CacheEntry\n): void {\n getStoreKeyMap(store, key).set(cacheKey, entry);\n}\n\nfunction clearCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): void {\n getStoreKeyMap(store, key).delete(cacheKey);\n}\n\nfunction deriveResourceKey(args: unknown[]): KeyedResourceKey | undefined {\n const key = args[0];\n if (typeof key === \"string\" || typeof key === \"number\") {\n return key;\n }\n return undefined;\n}\n\nfunction isExpired(\n timestamp: number | undefined,\n timeoutMs: number,\n now: number\n): boolean {\n if (timeoutMs === CACHE_NO_TIMEOUT) {\n return false;\n }\n if (timestamp === undefined) {\n return false;\n }\n return now - timestamp >= timeoutMs;\n}\n\ninterface StoreContext {\n store: object;\n storeSignal: Signal<ResourceState<unknown>>;\n currentState: ResourceState<unknown>;\n}\n\nfunction getStoreContext<TTarget, TKey extends StoreEnum>(\n instance: TTarget,\n storeKey: TKey,\n getStore: (i: TTarget) => StoreWithSignal<TKey> | undefined\n): StoreContext | undefined {\n const store = getStore(instance);\n if (!store) {\n return undefined;\n }\n\n const storeSignal = store.get(storeKey);\n if (!storeSignal) {\n return undefined;\n }\n\n const currentState = storeSignal();\n if (currentState === null || currentState === undefined) {\n return undefined;\n }\n\n return { store, storeSignal, currentState };\n}\n\ninterface CacheContext {\n isKeyedCall: boolean;\n resourceKey: KeyedResourceKey | undefined;\n keyedData: ReturnType<typeof isKeyedResourceData> extends true\n ? { entities: object; isLoading: object; status: object; errors: object }\n : undefined;\n runtimeCacheKey: string;\n keyedCacheEntry: CacheEntry | undefined;\n nonKeyedCacheEntry: CacheEntry | undefined;\n}\n\nfunction getCacheContext(\n store: object,\n storeKey: StoreEnum,\n args: unknown[],\n argsString: string,\n currentState: ResourceState<unknown>\n): CacheContext {\n const keyedData = isKeyedResourceData(currentState.data)\n ? currentState.data\n : undefined;\n const resourceKey = keyedData ? deriveResourceKey(args) : undefined;\n const isKeyedCall = keyedData !== undefined && resourceKey !== undefined;\n\n const keyedCacheKey = argsString;\n const nonKeyedCacheKey = \"__single__\";\n const runtimeCacheKey = isKeyedCall ? keyedCacheKey : nonKeyedCacheKey;\n\n const keyedCacheEntry = getCacheEntry(store, storeKey, keyedCacheKey);\n const nonKeyedCacheEntry = getCacheEntry(store, storeKey, nonKeyedCacheKey);\n\n return {\n isKeyedCall,\n resourceKey,\n keyedData: keyedData as CacheContext[\"keyedData\"],\n runtimeCacheKey,\n keyedCacheEntry,\n nonKeyedCacheEntry,\n };\n}\n\nfunction handleCacheErrors(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n currentState: ResourceState<unknown>\n): void {\n if (!context.keyedData && currentState.status === \"Error\") {\n clearCacheEntry(store, storeKey, \"__single__\");\n }\n if (context.keyedData && context.resourceKey !== undefined) {\n const status = (\n context.keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n }\n ).status[context.resourceKey];\n if (status === \"Error\") {\n clearCacheEntry(store, storeKey, context.runtimeCacheKey);\n }\n }\n}\n\ninterface CacheHitResult {\n hit: boolean;\n value?: Observable<unknown>;\n}\n\nfunction handleKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean\n): CacheHitResult {\n const { keyedData, resourceKey, keyedCacheEntry, runtimeCacheKey } = context;\n\n if (!keyedData || resourceKey === undefined) {\n return { hit: false };\n }\n\n const typed = keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n entities: Partial<Record<KeyedResourceKey, unknown>>;\n isLoading: Partial<Record<KeyedResourceKey, boolean>>;\n };\n\n const status = typed.status[resourceKey];\n const entity = typed.entities[resourceKey];\n const loading = typed.isLoading[resourceKey] === true;\n\n const expired = isExpired(keyedCacheEntry?.timestamp, timeoutMs, now);\n if (expired) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n }\n\n if (!expired && status === \"Success\" && entity !== undefined) {\n if (returnObservable) {\n return { hit: true, value: of(entity) };\n }\n return { hit: true };\n }\n\n if (returnObservable) {\n if (keyedCacheEntry?.inflight$) {\n return { hit: true, value: keyedCacheEntry.inflight$ };\n }\n } else if (loading) {\n return { hit: true };\n }\n\n return { hit: false };\n}\n\ninterface NonKeyedCacheExtra {\n readonly currentState: ResourceState<unknown>;\n readonly argsString: string;\n readonly storeSignal: Signal<ResourceState<unknown>>;\n}\n\nfunction handleNonKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean,\n extra: NonKeyedCacheExtra\n): CacheHitResult {\n const { nonKeyedCacheEntry, runtimeCacheKey } = context;\n const { currentState, argsString, storeSignal } = extra;\n\n if (\n returnObservable &&\n nonKeyedCacheEntry?.args === argsString &&\n nonKeyedCacheEntry.inflight$\n ) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n\n const hasValidCacheState =\n currentState?.status === \"Success\" || currentState?.isLoading === true;\n\n if (\n nonKeyedCacheEntry &&\n isExpired(nonKeyedCacheEntry.timestamp, timeoutMs, now)\n ) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n } else if (nonKeyedCacheEntry?.args === argsString && hasValidCacheState) {\n if (returnObservable) {\n if (nonKeyedCacheEntry.inflight$) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n return { hit: true, value: of(storeSignal().data) };\n }\n return { hit: true };\n }\n\n return { hit: false };\n}\n\nfunction createCachedObservable(\n result: Observable<unknown>,\n store: object,\n storeKey: StoreEnum,\n runtimeCacheKey: string,\n argsString: string\n): Observable<unknown> {\n return result.pipe(\n tap({\n next: () => {\n setCacheEntry(store, storeKey, runtimeCacheKey, {\n timestamp: Date.now(),\n args: argsString,\n });\n },\n error: () => {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n },\n }),\n finalize(() => {\n const entry = getCacheEntry(store, storeKey, runtimeCacheKey);\n if (entry?.inflight$) {\n const { inflight$: _inflight$, ...rest } = entry;\n setCacheEntry(store, storeKey, runtimeCacheKey, rest);\n }\n }),\n shareReplay({ bufferSize: 1, refCount: true })\n );\n}\n\n/**\n * Method decorator that skips execution when the store already has valid cached data.\n *\n * **Cache hit** (method skipped) when:\n * - `status === 'Success'` or `isLoading === true`\n * - Timeout has not expired\n * - Method arguments match (compared via `JSON.stringify`)\n *\n * **Cache miss** (method executes) when:\n * - Initial state (no status, not loading)\n * - `status === 'Error'` (errors are never cached)\n * - Timeout expired\n * - Arguments changed\n *\n * **Keyed resources**: When the first argument is a `string | number` and the store\n * data is a `KeyedResourceData`, cache entries are tracked per resource key automatically.\n *\n * @param storeKey - The store slot to check for cached data.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n * @param returnObservable - When `true`, returns `Observable` (with `shareReplay` for deduplication).\n * When `false`, returns `void`.\n * @default false\n * @param timeoutMs - Cache TTL in milliseconds. Use `CACHE_NO_TIMEOUT` for infinite.\n * @default DEFAULT_CACHE_TTL_MS (300 000 ms / 5 minutes)\n *\n * @example\n * ```ts\n * @SkipIfCached('LIST', (i: ProductFacade) => i.store)\n * @Loading('LIST', (i: ProductFacade) => i.store)\n * loadProducts() {\n * this.http.get('/api/products')\n * .pipe(syncToStore(this.store, 'LIST'))\n * .subscribe();\n * }\n * ```\n */\nexport function SkipIfCached<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithSignal<TKey>;\n }) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\n/** @inheritDoc */\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable = false,\n timeoutMs = DEFAULT_CACHE_TTL_MS\n): MethodDecorator {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: TTarget,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const storeContext = getStoreContext(this, storeKey, storeGetter);\n if (!storeContext) {\n return originalMethod.apply(this, args);\n }\n const { store, storeSignal, currentState } = storeContext;\n\n const argsString = JSON.stringify(args);\n const now = Date.now();\n const cacheContext = getCacheContext(\n store,\n storeKey,\n args,\n argsString,\n currentState\n );\n\n handleCacheErrors(store, storeKey, cacheContext, currentState);\n\n let cacheHit: CacheHitResult;\n\n if (cacheContext.isKeyedCall) {\n cacheHit = handleKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable\n );\n } else {\n cacheHit = handleNonKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable,\n { currentState, argsString, storeSignal }\n );\n }\n\n if (cacheHit.hit) {\n return cacheHit.value;\n }\n\n const result = originalMethod.apply(this, args);\n\n if (!returnObservable) {\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n });\n return result;\n }\n\n const inflight$ = createCachedObservable(\n result as Observable<unknown>,\n store,\n storeKey,\n cacheContext.runtimeCacheKey,\n argsString\n );\n\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n inflight$,\n });\n\n return inflight$;\n };\n\n return descriptor;\n };\n}\n","import type { StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithLoading<TKey extends StoreEnum> = {\n startLoading: (key: TKey) => void;\n};\n\n/**\n * Method decorator that calls `store.startLoading(key)` before the original method executes.\n *\n * **Keyed detection**: If the first method argument is a `string | number` and the store\n * has a `startKeyedLoading` method, it calls that instead for per-key loading state.\n *\n * Typically composed with `@SkipIfCached` (which should be the outermost decorator):\n * ```ts\n * @SkipIfCached('LIST', (i) => i.store) // outermost — can short-circuit\n * @Loading('LIST', (i) => i.store) // sets loading before method body\n * loadProducts() { ... }\n * ```\n *\n * @param storeKey - The store slot to mark as loading.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n */\nexport function Loading<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithLoading<TKey>;\n }) => StoreWithLoading<TKey>\n): MethodDecorator;\n/** @inheritDoc */\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n): MethodDecorator;\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n) {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: unknown,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const store = storeGetter(this);\n\n const resourceKey = args[0];\n const canKey =\n typeof resourceKey === \"string\" || typeof resourceKey === \"number\";\n const hasKeyed =\n typeof store === \"object\" &&\n store !== null &&\n \"startKeyedLoading\" in store &&\n typeof (store as { startKeyedLoading?: unknown }).startKeyedLoading ===\n \"function\";\n\n if (canKey && hasKeyed) {\n (\n store as unknown as {\n startKeyedLoading: (\n key: TKey,\n resourceKey: KeyedResourceKey\n ) => void;\n }\n ).startKeyedLoading(storeKey, resourceKey as KeyedResourceKey);\n } else {\n store?.startLoading(storeKey);\n }\n return originalMethod.apply(this, args);\n };\n\n return descriptor;\n };\n}\n"],"mappings":"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","names":["finalize","take","tap","tap","take","finalize","finalize","tap"]}
|
|
1
|
+
{"version":3,"sources":["../src/operators/sync-to-store.ts","../src/error/error-normalizer.ts","../src/operators/sync-to-keyed-store.ts","../src/decorators/skip-if-cached.ts","../src/decorators/loading.ts"],"sourcesContent":["import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport type { ResourceState } from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\n\n/**\n * Options for {@link syncToStore}.\n */\nexport interface SyncToStoreOptions {\n /**\n * Whether to complete the Observable after the first emission (applies `take(1)`).\n * @default true\n */\n completeOnFirstEmission?: boolean;\n /**\n * Callback invoked in `finalize()` after the Observable completes or errors.\n * Useful for side effects like clearing another slot or navigating.\n * @default undefined\n */\n callbackAfterComplete?: () => void;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\ninterface SyncToStoreRuntimeStore {\n update(key: PropertyKey, newState: unknown): void;\n}\n\n/**\n * Syncs an Observable result into a `ResourceState` slot on a Flurryx store.\n *\n * On success, the target slot is updated with the emitted value, `status: \"Success\"`,\n * and `isLoading: false`. On error, the slot is updated with `status: \"Error\"`\n * and normalized errors.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TEnum - Enum-like store keys used by `BaseStore`.\n * @template TData - Store data shape.\n * @template K - Store key whose value is a `ResourceState`.\n * @param store - The target Flurryx store instance.\n * @param key - The resource slot to update.\n * @param options - Optional behavior for completion, finalization, and error normalization.\n * @returns An RxJS operator function that syncs source emissions into the store.\n *\n * @example\n * ```ts\n * this.api.getUsers().pipe(\n * syncToStore(this.store, \"USERS\")\n * )\n * ```\n */\nexport function syncToStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: BaseStore<TEnum, TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\n/**\n * Syncs an Observable result into a `ResourceState` slot on a Flurryx store.\n *\n * On success, the target slot is updated with the emitted value, `status: \"Success\"`,\n * and `isLoading: false`. On error, the slot is updated with `status: \"Error\"`\n * and normalized errors.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TData - Store data shape.\n * @template K - Store key whose value is a `ResourceState`.\n * @param store - The target Flurryx store instance.\n * @param key - The resource slot to update.\n * @param options - Optional behavior for completion, finalization, and error normalization.\n * @returns An RxJS operator function that syncs source emissions into the store.\n */\nexport function syncToStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n K extends keyof TData\n>(\n store: IStore<TData>,\n key: K,\n options?: SyncToStoreOptions\n): <R>(source: Observable<R>) => Observable<R>;\n\nexport function syncToStore(\n store: unknown,\n key: PropertyKey,\n options: SyncToStoreOptions = { completeOnFirstEmission: true }\n) {\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToStoreRuntimeStore;\n\n return <R>(source: Observable<R>) => {\n let pipeline = source.pipe(\n tap({\n next: (data: R) => {\n syncStore.update(key, {\n data,\n isLoading: false,\n status: \"Success\",\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n syncStore.update(key, {\n data: undefined,\n isLoading: false,\n status: \"Error\",\n errors: normalizeError(error),\n });\n },\n })\n );\n\n if (options.completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (options.callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(options.callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { ResourceErrors } from \"@flurryx/core\";\n\n/**\n * Function type that converts an unknown error into the normalized `ResourceErrors` shape.\n * Plug a custom normalizer into `syncToStore` / `syncToKeyedStore` via the `errorNormalizer` option.\n */\nexport type ErrorNormalizer = (error: unknown) => ResourceErrors;\n\n/**\n * Default error normalizer used by `syncToStore` and `syncToKeyedStore`.\n *\n * Handles these shapes (checked in order):\n * 1. `{ error: { errors: [...] } }` — extracts the nested array directly.\n * 2. `{ status: number, message: string }` — wraps into `[{ code, message }]`.\n * 3. `Error` instances — wraps `error.message` with code `'UNKNOWN'`.\n * 4. Anything else — `[{ code: 'UNKNOWN', message: String(error) }]`.\n *\n * @param error - The raw error from an Observable.\n * @returns Normalized `Array<{ code: string; message: string }>`.\n */\nexport function defaultErrorNormalizer(error: unknown): ResourceErrors {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"error\" in error &&\n typeof (error as Record<string, unknown>).error === \"object\"\n ) {\n const inner = (error as { error: Record<string, unknown> }).error;\n if (inner && Array.isArray(inner.errors)) {\n return inner.errors as ResourceErrors;\n }\n }\n\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"status\" in error &&\n \"message\" in error\n ) {\n const typed = error as { status: number; message: string };\n return [\n {\n code: String(typed.status),\n message: typed.message,\n },\n ];\n }\n\n if (error instanceof Error) {\n return [\n {\n code: \"UNKNOWN\",\n message: error.message,\n },\n ];\n }\n\n return [\n {\n code: \"UNKNOWN\",\n message: String(error),\n },\n ];\n}\n","import { finalize, Observable, take, tap } from \"rxjs\";\nimport type { BaseStore, IStore } from \"@flurryx/store\";\nimport {\n createKeyedResourceData,\n isAnyKeyLoading,\n type KeyedResourceData,\n type KeyedResourceKey,\n type ResourceErrors,\n type ResourceState,\n type ResourceStatus,\n} from \"@flurryx/core\";\nimport {\n defaultErrorNormalizer,\n type ErrorNormalizer,\n} from \"../error/error-normalizer\";\nimport type { SyncToStoreOptions } from \"./sync-to-store\";\n\ninterface SyncToKeyedStoreRuntimeStore {\n get(key: PropertyKey): () => ResourceState<unknown>;\n update(key: PropertyKey, newState: unknown): void;\n}\n\n/**\n * Options for {@link syncToKeyedStore}.\n *\n * Extends {@link SyncToStoreOptions} with keyed-specific options.\n *\n * @template R - The raw response type from the Observable.\n * @template TValue - The entity type stored in the keyed slot.\n */\nexport interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {\n /**\n * Transform the raw API response before writing it to the store.\n * Useful when the API envelope differs from the entity type.\n *\n * @default undefined (response is stored as-is)\n *\n * @example\n * ```ts\n * syncToKeyedStore(store, 'ITEMS', id, {\n * mapResponse: (response) => response.data,\n * })\n * ```\n */\n mapResponse?: (response: R) => TValue;\n /**\n * Custom function to convert error objects into the normalized `ResourceErrors` shape.\n * @default defaultErrorNormalizer\n */\n errorNormalizer?: ErrorNormalizer;\n}\n\nfunction withoutKey<TKey extends KeyedResourceKey, TValue>(\n record: Partial<Record<TKey, TValue>>,\n key: TKey\n): Partial<Record<TKey, TValue>> {\n const next: Partial<Record<TKey, TValue>> = {\n ...record,\n };\n delete next[key];\n return next;\n}\n\n/**\n * Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.\n *\n * Each emission is written under `resourceKey` inside the slot's keyed data map.\n * On success, that key is marked as `\"Success\"` and no longer loading. On error,\n * the key is marked as `\"Error\"` and receives normalized errors.\n *\n * Use `mapResponse` when the Observable emits an API envelope and only part of it\n * should be stored as the keyed entity value.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TEnum - Enum-like store keys used by `BaseStore`.\n * @template TData - Store data shape.\n * @template TStoreKey - Store key whose value is a keyed `ResourceState`.\n * @template TKey - Entity key inside the keyed resource map.\n * @template TValue - Entity type stored in the keyed slot.\n * @template R - Raw Observable emission type.\n * @param store - The target Flurryx store instance.\n * @param storeKey - The keyed resource slot to update.\n * @param resourceKey - The entity identifier inside the keyed slot.\n * @param options - Optional response mapping, completion, finalization, and error normalization behavior.\n * @returns An RxJS operator function that syncs source emissions into the keyed store slot.\n *\n * @example\n * ```ts\n * this.api.getUser(id).pipe(\n * syncToKeyedStore(this.store, \"USERS\", id)\n * )\n * ```\n */\nexport function syncToKeyedStore<\n TEnum extends Record<string, string | number>,\n TData extends { [K in keyof TEnum]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: BaseStore<TEnum, TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\n/**\n * Syncs an Observable result into a keyed `ResourceState` slot on a Flurryx store.\n *\n * Each emission is written under `resourceKey` inside the slot's keyed data map.\n * On success, that key is marked as `\"Success\"` and no longer loading. On error,\n * the key is marked as `\"Error\"` and receives normalized errors.\n *\n * Use `mapResponse` when the Observable emits an API envelope and only part of it\n * should be stored as the keyed entity value.\n *\n * By default, the operator completes after the first emission by applying `take(1)`.\n *\n * @template TData - Store data shape.\n * @template TStoreKey - Store key whose value is a keyed `ResourceState`.\n * @template TKey - Entity key inside the keyed resource map.\n * @template TValue - Entity type stored in the keyed slot.\n * @template R - Raw Observable emission type.\n * @param store - The target Flurryx store instance.\n * @param storeKey - The keyed resource slot to update.\n * @param resourceKey - The entity identifier inside the keyed slot.\n * @param options - Optional response mapping, completion, finalization, and error normalization behavior.\n * @returns An RxJS operator function that syncs source emissions into the keyed store slot.\n */\nexport function syncToKeyedStore<\n TData extends { [K in keyof TData]: ResourceState<unknown> },\n TStoreKey extends keyof TData,\n TKey extends KeyedResourceKey,\n TValue,\n R = TValue\n>(\n store: IStore<TData>,\n storeKey: TStoreKey,\n resourceKey: TKey,\n options?: SyncToKeyedStoreOptions<R, TValue>\n): (source: Observable<R>) => Observable<R>;\n\nexport function syncToKeyedStore(\n store: unknown,\n storeKey: PropertyKey,\n resourceKey: KeyedResourceKey,\n options: SyncToKeyedStoreOptions<unknown, unknown> = {\n completeOnFirstEmission: true,\n }\n) {\n const { completeOnFirstEmission, callbackAfterComplete, mapResponse } =\n options;\n const normalizeError = options.errorNormalizer ?? defaultErrorNormalizer;\n const syncStore = store as SyncToKeyedStoreRuntimeStore;\n\n return (source: Observable<unknown>) => {\n let pipeline = source.pipe(\n tap({\n next: (response: unknown) => {\n const value = mapResponse ? mapResponse(response) : response;\n\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Success\" as ResourceStatus,\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n entities: {\n ...data.entities,\n [resourceKey]: value,\n } as Partial<Record<KeyedResourceKey, unknown>>,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: withoutKey(data.errors, resourceKey),\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n error: (error: unknown) => {\n const storeSignal = syncStore.get(storeKey);\n const state = storeSignal();\n const data =\n (state.data as\n | KeyedResourceData<KeyedResourceKey, unknown>\n | undefined) ??\n createKeyedResourceData<KeyedResourceKey, unknown>();\n\n const nextIsLoading = {\n ...data.isLoading,\n [resourceKey]: false,\n } as Partial<Record<KeyedResourceKey, boolean>>;\n\n const nextStatus: Partial<Record<KeyedResourceKey, ResourceStatus>> =\n {\n ...data.status,\n [resourceKey]: \"Error\" as ResourceStatus,\n };\n\n const nextErrors: Partial<Record<KeyedResourceKey, ResourceErrors>> =\n {\n ...data.errors,\n [resourceKey]: normalizeError(error),\n };\n\n const nextData: KeyedResourceData<KeyedResourceKey, unknown> = {\n ...data,\n isLoading: nextIsLoading,\n status: nextStatus,\n errors: nextErrors,\n };\n\n syncStore.update(storeKey, {\n data: nextData,\n isLoading: isAnyKeyLoading(nextIsLoading),\n status: undefined,\n errors: undefined,\n });\n },\n })\n );\n\n if (completeOnFirstEmission) {\n pipeline = pipeline.pipe(take(1));\n }\n\n if (callbackAfterComplete) {\n pipeline = pipeline.pipe(finalize(callbackAfterComplete));\n }\n\n return pipeline;\n };\n}\n","import type { Signal } from \"@angular/core\";\nimport { finalize, Observable, of, shareReplay, tap } from \"rxjs\";\nimport {\n isKeyedResourceData,\n CACHE_NO_TIMEOUT,\n DEFAULT_CACHE_TTL_MS,\n} from \"@flurryx/core\";\nimport type { ResourceState, StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithSignal<TKey extends StoreEnum> = {\n get: (key: TKey) => Signal<ResourceState<unknown>> | undefined;\n};\n\ninterface CacheEntry {\n timestamp: number;\n args: string;\n inflight$?: Observable<unknown>;\n}\n\nconst cacheState = new WeakMap<\n object,\n Map<StoreEnum, Map<string, CacheEntry>>\n>();\n\nfunction getStoreKeyMap(\n store: object,\n key: StoreEnum\n): Map<string, CacheEntry> {\n let storeMap = cacheState.get(store);\n if (!storeMap) {\n storeMap = new Map();\n cacheState.set(store, storeMap);\n }\n\n let keyMap = storeMap.get(key);\n if (!keyMap) {\n keyMap = new Map();\n storeMap.set(key, keyMap);\n }\n\n return keyMap;\n}\n\nfunction getCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): CacheEntry | undefined {\n return getStoreKeyMap(store, key).get(cacheKey);\n}\n\nfunction setCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string,\n entry: CacheEntry\n): void {\n getStoreKeyMap(store, key).set(cacheKey, entry);\n}\n\nfunction clearCacheEntry(\n store: object,\n key: StoreEnum,\n cacheKey: string\n): void {\n getStoreKeyMap(store, key).delete(cacheKey);\n}\n\nfunction deriveResourceKey(args: unknown[]): KeyedResourceKey | undefined {\n const key = args[0];\n if (typeof key === \"string\" || typeof key === \"number\") {\n return key;\n }\n return undefined;\n}\n\nfunction isExpired(\n timestamp: number | undefined,\n timeoutMs: number,\n now: number\n): boolean {\n if (timeoutMs === CACHE_NO_TIMEOUT) {\n return false;\n }\n if (timestamp === undefined) {\n return false;\n }\n return now - timestamp >= timeoutMs;\n}\n\ninterface StoreContext {\n store: object;\n storeSignal: Signal<ResourceState<unknown>>;\n currentState: ResourceState<unknown>;\n}\n\nfunction getStoreContext<TTarget, TKey extends StoreEnum>(\n instance: TTarget,\n storeKey: TKey,\n getStore: (i: TTarget) => StoreWithSignal<TKey> | undefined\n): StoreContext | undefined {\n const store = getStore(instance);\n if (!store) {\n return undefined;\n }\n\n const storeSignal = store.get(storeKey);\n if (!storeSignal) {\n return undefined;\n }\n\n const currentState = storeSignal();\n if (currentState === null || currentState === undefined) {\n return undefined;\n }\n\n return { store, storeSignal, currentState };\n}\n\ninterface CacheContext {\n isKeyedCall: boolean;\n resourceKey: KeyedResourceKey | undefined;\n keyedData: ReturnType<typeof isKeyedResourceData> extends true\n ? { entities: object; isLoading: object; status: object; errors: object }\n : undefined;\n runtimeCacheKey: string;\n keyedCacheEntry: CacheEntry | undefined;\n nonKeyedCacheEntry: CacheEntry | undefined;\n}\n\nfunction getCacheContext(\n store: object,\n storeKey: StoreEnum,\n args: unknown[],\n argsString: string,\n currentState: ResourceState<unknown>\n): CacheContext {\n const keyedData = isKeyedResourceData(currentState.data)\n ? currentState.data\n : undefined;\n const resourceKey = keyedData ? deriveResourceKey(args) : undefined;\n const isKeyedCall = keyedData !== undefined && resourceKey !== undefined;\n\n const keyedCacheKey = argsString;\n const nonKeyedCacheKey = \"__single__\";\n const runtimeCacheKey = isKeyedCall ? keyedCacheKey : nonKeyedCacheKey;\n\n const keyedCacheEntry = getCacheEntry(store, storeKey, keyedCacheKey);\n const nonKeyedCacheEntry = getCacheEntry(store, storeKey, nonKeyedCacheKey);\n\n return {\n isKeyedCall,\n resourceKey,\n keyedData: keyedData as CacheContext[\"keyedData\"],\n runtimeCacheKey,\n keyedCacheEntry,\n nonKeyedCacheEntry,\n };\n}\n\nfunction handleCacheErrors(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n currentState: ResourceState<unknown>\n): void {\n if (!context.keyedData && currentState.status === \"Error\") {\n clearCacheEntry(store, storeKey, \"__single__\");\n }\n if (context.keyedData && context.resourceKey !== undefined) {\n const status = (\n context.keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n }\n ).status[context.resourceKey];\n if (status === \"Error\") {\n clearCacheEntry(store, storeKey, context.runtimeCacheKey);\n }\n }\n}\n\ninterface CacheHitResult {\n hit: boolean;\n value?: Observable<unknown>;\n}\n\nfunction handleKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean\n): CacheHitResult {\n const { keyedData, resourceKey, keyedCacheEntry, runtimeCacheKey } = context;\n\n if (!keyedData || resourceKey === undefined) {\n return { hit: false };\n }\n\n const typed = keyedData as {\n status: Partial<Record<KeyedResourceKey, string>>;\n entities: Partial<Record<KeyedResourceKey, unknown>>;\n isLoading: Partial<Record<KeyedResourceKey, boolean>>;\n };\n\n const status = typed.status[resourceKey];\n const entity = typed.entities[resourceKey];\n const loading = typed.isLoading[resourceKey] === true;\n\n const expired = isExpired(keyedCacheEntry?.timestamp, timeoutMs, now);\n if (expired) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n }\n\n if (!expired && status === \"Success\" && entity !== undefined) {\n if (returnObservable) {\n return { hit: true, value: of(entity) };\n }\n return { hit: true };\n }\n\n if (returnObservable) {\n if (keyedCacheEntry?.inflight$) {\n return { hit: true, value: keyedCacheEntry.inflight$ };\n }\n } else if (loading) {\n return { hit: true };\n }\n\n return { hit: false };\n}\n\ninterface NonKeyedCacheExtra {\n readonly currentState: ResourceState<unknown>;\n readonly argsString: string;\n readonly storeSignal: Signal<ResourceState<unknown>>;\n}\n\nfunction handleNonKeyedCache(\n store: object,\n storeKey: StoreEnum,\n context: CacheContext,\n timeoutMs: number,\n now: number,\n returnObservable: boolean,\n extra: NonKeyedCacheExtra\n): CacheHitResult {\n const { nonKeyedCacheEntry, runtimeCacheKey } = context;\n const { currentState, argsString, storeSignal } = extra;\n\n if (\n returnObservable &&\n nonKeyedCacheEntry?.args === argsString &&\n nonKeyedCacheEntry.inflight$\n ) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n\n const hasValidCacheState =\n currentState?.status === \"Success\" || currentState?.isLoading === true;\n\n if (\n nonKeyedCacheEntry &&\n isExpired(nonKeyedCacheEntry.timestamp, timeoutMs, now)\n ) {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n } else if (nonKeyedCacheEntry?.args === argsString && hasValidCacheState) {\n if (returnObservable) {\n if (nonKeyedCacheEntry.inflight$) {\n return { hit: true, value: nonKeyedCacheEntry.inflight$ };\n }\n return { hit: true, value: of(storeSignal().data) };\n }\n return { hit: true };\n }\n\n return { hit: false };\n}\n\nfunction createCachedObservable(\n result: Observable<unknown>,\n store: object,\n storeKey: StoreEnum,\n runtimeCacheKey: string,\n argsString: string\n): Observable<unknown> {\n return result.pipe(\n tap({\n next: () => {\n setCacheEntry(store, storeKey, runtimeCacheKey, {\n timestamp: Date.now(),\n args: argsString,\n });\n },\n error: () => {\n clearCacheEntry(store, storeKey, runtimeCacheKey);\n },\n }),\n finalize(() => {\n const entry = getCacheEntry(store, storeKey, runtimeCacheKey);\n if (entry?.inflight$) {\n const { inflight$: _inflight$, ...rest } = entry;\n setCacheEntry(store, storeKey, runtimeCacheKey, rest);\n }\n }),\n shareReplay({ bufferSize: 1, refCount: true })\n );\n}\n\n/**\n * Method decorator that skips execution when the store already has valid cached data.\n *\n * **Cache hit** (method skipped) when:\n * - `status === 'Success'` or `isLoading === true`\n * - Timeout has not expired\n * - Method arguments match (compared via `JSON.stringify`)\n *\n * **Cache miss** (method executes) when:\n * - Initial state (no status, not loading)\n * - `status === 'Error'` (errors are never cached)\n * - Timeout expired\n * - Arguments changed\n *\n * **Keyed resources**: When the first argument is a `string | number` and the store\n * data is a `KeyedResourceData`, cache entries are tracked per resource key automatically.\n *\n * @param storeKey - The store slot to check for cached data.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n * @param returnObservable - When `true`, returns `Observable` (with `shareReplay` for deduplication).\n * When `false`, returns `void`.\n * @default false\n * @param timeoutMs - Cache TTL in milliseconds. Use `CACHE_NO_TIMEOUT` for infinite.\n * @default DEFAULT_CACHE_TTL_MS (300 000 ms / 5 minutes)\n *\n * @example\n * ```ts\n * @SkipIfCached('LIST', (i: ProductFacade) => i.store)\n * @Loading('LIST', (i: ProductFacade) => i.store)\n * loadProducts() {\n * this.http.get('/api/products')\n * .pipe(syncToStore(this.store, 'LIST'))\n * .subscribe();\n * }\n * ```\n */\nexport function SkipIfCached<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithSignal<TKey>;\n }) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\n/** @inheritDoc */\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable?: boolean,\n timeoutMs?: number\n): MethodDecorator;\nexport function SkipIfCached<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined,\n returnObservable = false,\n timeoutMs = DEFAULT_CACHE_TTL_MS\n): MethodDecorator {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: TTarget,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const storeContext = getStoreContext(this, storeKey, storeGetter);\n if (!storeContext) {\n return originalMethod.apply(this, args);\n }\n const { store, storeSignal, currentState } = storeContext;\n\n const argsString = JSON.stringify(args);\n const now = Date.now();\n const cacheContext = getCacheContext(\n store,\n storeKey,\n args,\n argsString,\n currentState\n );\n\n handleCacheErrors(store, storeKey, cacheContext, currentState);\n\n let cacheHit: CacheHitResult;\n\n if (cacheContext.isKeyedCall) {\n cacheHit = handleKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable\n );\n } else {\n cacheHit = handleNonKeyedCache(\n store,\n storeKey,\n cacheContext,\n timeoutMs,\n now,\n returnObservable,\n { currentState, argsString, storeSignal }\n );\n }\n\n if (cacheHit.hit) {\n return cacheHit.value;\n }\n\n const result = originalMethod.apply(this, args);\n\n if (!returnObservable) {\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n });\n return result;\n }\n\n const inflight$ = createCachedObservable(\n result as Observable<unknown>,\n store,\n storeKey,\n cacheContext.runtimeCacheKey,\n argsString\n );\n\n setCacheEntry(store, storeKey, cacheContext.runtimeCacheKey, {\n timestamp: now,\n args: argsString,\n inflight$,\n });\n\n return inflight$;\n };\n\n return descriptor;\n };\n}\n","import type { StoreEnum, KeyedResourceKey } from \"@flurryx/core\";\n\ntype StoreWithLoading<TKey extends StoreEnum> = {\n startLoading: (key: TKey) => void;\n};\n\n/**\n * Method decorator that calls `store.startLoading(key)` before the original method executes.\n *\n * **Keyed detection**: If the first method argument is a `string | number` and the store\n * has a `startKeyedLoading` method, it calls that instead for per-key loading state.\n *\n * Typically composed with `@SkipIfCached` (which should be the outermost decorator):\n * ```ts\n * @SkipIfCached('LIST', (i) => i.store) // outermost — can short-circuit\n * @Loading('LIST', (i) => i.store) // sets loading before method body\n * loadProducts() { ... }\n * ```\n *\n * @param storeKey - The store slot to mark as loading.\n * @param storeGetter - Function to retrieve the store from the decorated class instance.\n */\nexport function Loading<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithLoading<TKey>;\n }) => StoreWithLoading<TKey>\n): MethodDecorator;\n/** @inheritDoc */\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n): MethodDecorator;\nexport function Loading<TTarget, TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: TTarget) => StoreWithLoading<TKey>\n) {\n return function (\n _target: unknown,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value as (\n this: unknown,\n ...args: unknown[]\n ) => unknown;\n\n descriptor.value = function (this: TTarget, ...args: unknown[]) {\n const store = storeGetter(this);\n\n const resourceKey = args[0];\n const canKey =\n typeof resourceKey === \"string\" || typeof resourceKey === \"number\";\n const hasKeyed =\n typeof store === \"object\" &&\n store !== null &&\n \"startKeyedLoading\" in store &&\n typeof (store as { startKeyedLoading?: unknown }).startKeyedLoading ===\n \"function\";\n\n if (canKey && hasKeyed) {\n (\n store as unknown as {\n startKeyedLoading: (\n key: TKey,\n resourceKey: KeyedResourceKey\n ) => void;\n }\n ).startKeyedLoading(storeKey, resourceKey as KeyedResourceKey);\n } else {\n store?.startLoading(storeKey);\n }\n return originalMethod.apply(this, args);\n };\n\n return descriptor;\n };\n}\n"],"mappings":"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","names":["finalize","take","tap","tap","take","finalize","finalize","tap"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flurryx/rx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "RxJS operators and decorators for flurryx stores",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
"sideEffects": false,
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flurryx/core": "0.
|
|
42
|
-
"@flurryx/store": "0.
|
|
41
|
+
"@flurryx/core": "1.0.0",
|
|
42
|
+
"@flurryx/store": "1.0.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@angular/core": ">=17.0.0",
|