@flurryx/rx 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ import { ResourceErrors } from '@flurryx/core';
2
+
3
+ /**
4
+ * Function type that converts an unknown error into the normalized `ResourceErrors` shape.
5
+ * Plug a custom normalizer into `syncToStore` / `syncToKeyedStore` via the `errorNormalizer` option.
6
+ */
7
+ type ErrorNormalizer = (error: unknown) => ResourceErrors;
8
+ /**
9
+ * Default error normalizer used by `syncToStore` and `syncToKeyedStore`.
10
+ *
11
+ * Handles these shapes (checked in order):
12
+ * 1. `{ error: { errors: [...] } }` — extracts the nested array directly.
13
+ * 2. `{ status: number, message: string }` — wraps into `[{ code, message }]`.
14
+ * 3. `Error` instances — wraps `error.message` with code `'UNKNOWN'`.
15
+ * 4. Anything else — `[{ code: 'UNKNOWN', message: String(error) }]`.
16
+ *
17
+ * @param error - The raw error from an Observable.
18
+ * @returns Normalized `Array<{ code: string; message: string }>`.
19
+ */
20
+ declare function defaultErrorNormalizer(error: unknown): ResourceErrors;
21
+
22
+ export { type ErrorNormalizer as E, defaultErrorNormalizer as d };
@@ -0,0 +1,22 @@
1
+ import { ResourceErrors } from '@flurryx/core';
2
+
3
+ /**
4
+ * Function type that converts an unknown error into the normalized `ResourceErrors` shape.
5
+ * Plug a custom normalizer into `syncToStore` / `syncToKeyedStore` via the `errorNormalizer` option.
6
+ */
7
+ type ErrorNormalizer = (error: unknown) => ResourceErrors;
8
+ /**
9
+ * Default error normalizer used by `syncToStore` and `syncToKeyedStore`.
10
+ *
11
+ * Handles these shapes (checked in order):
12
+ * 1. `{ error: { errors: [...] } }` — extracts the nested array directly.
13
+ * 2. `{ status: number, message: string }` — wraps into `[{ code, message }]`.
14
+ * 3. `Error` instances — wraps `error.message` with code `'UNKNOWN'`.
15
+ * 4. Anything else — `[{ code: 'UNKNOWN', message: String(error) }]`.
16
+ *
17
+ * @param error - The raw error from an Observable.
18
+ * @returns Normalized `Array<{ code: string; message: string }>`.
19
+ */
20
+ declare function defaultErrorNormalizer(error: unknown): ResourceErrors;
21
+
22
+ export { type ErrorNormalizer as E, defaultErrorNormalizer as d };
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\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;AAI3B,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":[]}
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`\n * 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/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
@@ -1,6 +1,25 @@
1
- import { E as ErrorNormalizer } from './error-normalizer-DZz8HS0o.cjs';
1
+ import { E as ErrorNormalizer } from './error-normalizer-DAu9ybBG.cjs';
2
2
  import '@flurryx/core';
3
3
 
4
+ /**
5
+ * Error normalizer specialized for Angular's `HttpErrorResponse`.
6
+ *
7
+ * Import from the `flurryx/http` entry point to keep `@angular/common/http`
8
+ * out of your bundle unless you actually need it.
9
+ *
10
+ * - If the response contains `error.errors` (array), returns it as-is.
11
+ * - Otherwise, wraps `{ status, message }` into a single-element `ResourceErrors`.
12
+ * - Falls back to `{ code: 'UNKNOWN', message: String(error) }` for non-HTTP errors.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { httpErrorNormalizer } from 'flurryx/http';
17
+ *
18
+ * this.http.get('/api/data')
19
+ * .pipe(syncToStore(this.store, 'DATA', { errorNormalizer: httpErrorNormalizer }))
20
+ * .subscribe();
21
+ * ```
22
+ */
4
23
  declare const httpErrorNormalizer: ErrorNormalizer;
5
24
 
6
25
  export { httpErrorNormalizer };
package/dist/http.d.ts CHANGED
@@ -1,6 +1,25 @@
1
- import { E as ErrorNormalizer } from './error-normalizer-DZz8HS0o.js';
1
+ import { E as ErrorNormalizer } from './error-normalizer-DAu9ybBG.js';
2
2
  import '@flurryx/core';
3
3
 
4
+ /**
5
+ * Error normalizer specialized for Angular's `HttpErrorResponse`.
6
+ *
7
+ * Import from the `flurryx/http` entry point to keep `@angular/common/http`
8
+ * out of your bundle unless you actually need it.
9
+ *
10
+ * - If the response contains `error.errors` (array), returns it as-is.
11
+ * - Otherwise, wraps `{ status, message }` into a single-element `ResourceErrors`.
12
+ * - Falls back to `{ code: 'UNKNOWN', message: String(error) }` for non-HTTP errors.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { httpErrorNormalizer } from 'flurryx/http';
17
+ *
18
+ * this.http.get('/api/data')
19
+ * .pipe(syncToStore(this.store, 'DATA', { errorNormalizer: httpErrorNormalizer }))
20
+ * .subscribe();
21
+ * ```
22
+ */
4
23
  declare const httpErrorNormalizer: ErrorNormalizer;
5
24
 
6
25
  export { 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\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;AAI3B,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":[]}
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`\n * 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/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":[]}
@@ -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\nexport interface SyncToStoreOptions {\n completeOnFirstEmission?: boolean;\n callbackAfterComplete?: () => void;\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\nexport type ErrorNormalizer = (error: unknown) => ResourceErrors;\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\nexport interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {\n mapResponse?: (response: R) => TValue;\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\n ? mapResponse(response)\n : 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 ...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 ...data.status,\n [resourceKey]: \"Error\" as ResourceStatus,\n };\n\n const nextErrors: Partial<Record<KeyedResourceKey, ResourceErrors>> = {\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\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;\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\nexport function Loading<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithLoading<TKey>;\n }) => StoreWithLoading<TKey>\n): MethodDecorator;\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\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"]}
package/dist/index.d.cts CHANGED
@@ -1,13 +1,29 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { BaseStore, IStore } from '@flurryx/store';
3
3
  import { ResourceState, KeyedResourceKey, StoreEnum } from '@flurryx/core';
4
- import { E as ErrorNormalizer } from './error-normalizer-DZz8HS0o.cjs';
5
- export { d as defaultErrorNormalizer } from './error-normalizer-DZz8HS0o.cjs';
4
+ import { E as ErrorNormalizer } from './error-normalizer-DAu9ybBG.cjs';
5
+ export { d as defaultErrorNormalizer } from './error-normalizer-DAu9ybBG.cjs';
6
6
  import { Signal } from '@angular/core';
7
7
 
8
+ /**
9
+ * Options for {@link syncToStore}.
10
+ */
8
11
  interface SyncToStoreOptions {
12
+ /**
13
+ * Whether to complete the Observable after the first emission (applies `take(1)`).
14
+ * @default true
15
+ */
9
16
  completeOnFirstEmission?: boolean;
17
+ /**
18
+ * Callback invoked in `finalize()` after the Observable completes or errors.
19
+ * Useful for side effects like clearing another slot or navigating.
20
+ * @default undefined
21
+ */
10
22
  callbackAfterComplete?: () => void;
23
+ /**
24
+ * Custom function to convert error objects into the normalized `ResourceErrors` shape.
25
+ * @default defaultErrorNormalizer
26
+ */
11
27
  errorNormalizer?: ErrorNormalizer;
12
28
  }
13
29
  declare function syncToStore<TEnum extends Record<string, string | number>, TData extends {
@@ -17,8 +33,33 @@ declare function syncToStore<TData extends {
17
33
  [K in keyof TData]: ResourceState<unknown>;
18
34
  }, K extends keyof TData>(store: IStore<TData>, key: K, options?: SyncToStoreOptions): <R>(source: Observable<R>) => Observable<R>;
19
35
 
36
+ /**
37
+ * Options for {@link syncToKeyedStore}.
38
+ *
39
+ * Extends {@link SyncToStoreOptions} with keyed-specific options.
40
+ *
41
+ * @template R - The raw response type from the Observable.
42
+ * @template TValue - The entity type stored in the keyed slot.
43
+ */
20
44
  interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {
45
+ /**
46
+ * Transform the raw API response before writing it to the store.
47
+ * Useful when the API envelope differs from the entity type.
48
+ *
49
+ * @default undefined (response is stored as-is)
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * syncToKeyedStore(store, 'ITEMS', id, {
54
+ * mapResponse: (response) => response.data,
55
+ * })
56
+ * ```
57
+ */
21
58
  mapResponse?: (response: R) => TValue;
59
+ /**
60
+ * Custom function to convert error objects into the normalized `ResourceErrors` shape.
61
+ * @default defaultErrorNormalizer
62
+ */
22
63
  errorNormalizer?: ErrorNormalizer;
23
64
  }
24
65
  declare function syncToKeyedStore<TEnum extends Record<string, string | number>, TData extends {
@@ -31,17 +72,71 @@ declare function syncToKeyedStore<TData extends {
31
72
  type StoreWithSignal<TKey extends StoreEnum> = {
32
73
  get: (key: TKey) => Signal<ResourceState<unknown>> | undefined;
33
74
  };
75
+ /**
76
+ * Method decorator that skips execution when the store already has valid cached data.
77
+ *
78
+ * **Cache hit** (method skipped) when:
79
+ * - `status === 'Success'` or `isLoading === true`
80
+ * - Timeout has not expired
81
+ * - Method arguments match (compared via `JSON.stringify`)
82
+ *
83
+ * **Cache miss** (method executes) when:
84
+ * - Initial state (no status, not loading)
85
+ * - `status === 'Error'` (errors are never cached)
86
+ * - Timeout expired
87
+ * - Arguments changed
88
+ *
89
+ * **Keyed resources**: When the first argument is a `string | number` and the store
90
+ * data is a `KeyedResourceData`, cache entries are tracked per resource key automatically.
91
+ *
92
+ * @param storeKey - The store slot to check for cached data.
93
+ * @param storeGetter - Function to retrieve the store from the decorated class instance.
94
+ * @param returnObservable - When `true`, returns `Observable` (with `shareReplay` for deduplication).
95
+ * When `false`, returns `void`.
96
+ * @default false
97
+ * @param timeoutMs - Cache TTL in milliseconds. Use `CACHE_NO_TIMEOUT` for infinite.
98
+ * @default DEFAULT_CACHE_TTL_MS (300 000 ms / 5 minutes)
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * @SkipIfCached('LIST', (i: ProductFacade) => i.store)
103
+ * @Loading('LIST', (i: ProductFacade) => i.store)
104
+ * loadProducts() {
105
+ * this.http.get('/api/products')
106
+ * .pipe(syncToStore(this.store, 'LIST'))
107
+ * .subscribe();
108
+ * }
109
+ * ```
110
+ */
34
111
  declare function SkipIfCached<TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: {
35
112
  store: StoreWithSignal<TKey>;
36
113
  }) => StoreWithSignal<TKey> | undefined, returnObservable?: boolean, timeoutMs?: number): MethodDecorator;
114
+ /** @inheritDoc */
37
115
  declare function SkipIfCached<TTarget, TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined, returnObservable?: boolean, timeoutMs?: number): MethodDecorator;
38
116
 
39
117
  type StoreWithLoading<TKey extends StoreEnum> = {
40
118
  startLoading: (key: TKey) => void;
41
119
  };
120
+ /**
121
+ * Method decorator that calls `store.startLoading(key)` before the original method executes.
122
+ *
123
+ * **Keyed detection**: If the first method argument is a `string | number` and the store
124
+ * has a `startKeyedLoading` method, it calls that instead for per-key loading state.
125
+ *
126
+ * Typically composed with `@SkipIfCached` (which should be the outermost decorator):
127
+ * ```ts
128
+ * @SkipIfCached('LIST', (i) => i.store) // outermost — can short-circuit
129
+ * @Loading('LIST', (i) => i.store) // sets loading before method body
130
+ * loadProducts() { ... }
131
+ * ```
132
+ *
133
+ * @param storeKey - The store slot to mark as loading.
134
+ * @param storeGetter - Function to retrieve the store from the decorated class instance.
135
+ */
42
136
  declare function Loading<TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: {
43
137
  store: StoreWithLoading<TKey>;
44
138
  }) => StoreWithLoading<TKey>): MethodDecorator;
139
+ /** @inheritDoc */
45
140
  declare function Loading<TTarget, TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: TTarget) => StoreWithLoading<TKey>): MethodDecorator;
46
141
 
47
142
  export { ErrorNormalizer, Loading, SkipIfCached, type SyncToKeyedStoreOptions, type SyncToStoreOptions, syncToKeyedStore, syncToStore };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,29 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { BaseStore, IStore } from '@flurryx/store';
3
3
  import { ResourceState, KeyedResourceKey, StoreEnum } from '@flurryx/core';
4
- import { E as ErrorNormalizer } from './error-normalizer-DZz8HS0o.js';
5
- export { d as defaultErrorNormalizer } from './error-normalizer-DZz8HS0o.js';
4
+ import { E as ErrorNormalizer } from './error-normalizer-DAu9ybBG.js';
5
+ export { d as defaultErrorNormalizer } from './error-normalizer-DAu9ybBG.js';
6
6
  import { Signal } from '@angular/core';
7
7
 
8
+ /**
9
+ * Options for {@link syncToStore}.
10
+ */
8
11
  interface SyncToStoreOptions {
12
+ /**
13
+ * Whether to complete the Observable after the first emission (applies `take(1)`).
14
+ * @default true
15
+ */
9
16
  completeOnFirstEmission?: boolean;
17
+ /**
18
+ * Callback invoked in `finalize()` after the Observable completes or errors.
19
+ * Useful for side effects like clearing another slot or navigating.
20
+ * @default undefined
21
+ */
10
22
  callbackAfterComplete?: () => void;
23
+ /**
24
+ * Custom function to convert error objects into the normalized `ResourceErrors` shape.
25
+ * @default defaultErrorNormalizer
26
+ */
11
27
  errorNormalizer?: ErrorNormalizer;
12
28
  }
13
29
  declare function syncToStore<TEnum extends Record<string, string | number>, TData extends {
@@ -17,8 +33,33 @@ declare function syncToStore<TData extends {
17
33
  [K in keyof TData]: ResourceState<unknown>;
18
34
  }, K extends keyof TData>(store: IStore<TData>, key: K, options?: SyncToStoreOptions): <R>(source: Observable<R>) => Observable<R>;
19
35
 
36
+ /**
37
+ * Options for {@link syncToKeyedStore}.
38
+ *
39
+ * Extends {@link SyncToStoreOptions} with keyed-specific options.
40
+ *
41
+ * @template R - The raw response type from the Observable.
42
+ * @template TValue - The entity type stored in the keyed slot.
43
+ */
20
44
  interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {
45
+ /**
46
+ * Transform the raw API response before writing it to the store.
47
+ * Useful when the API envelope differs from the entity type.
48
+ *
49
+ * @default undefined (response is stored as-is)
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * syncToKeyedStore(store, 'ITEMS', id, {
54
+ * mapResponse: (response) => response.data,
55
+ * })
56
+ * ```
57
+ */
21
58
  mapResponse?: (response: R) => TValue;
59
+ /**
60
+ * Custom function to convert error objects into the normalized `ResourceErrors` shape.
61
+ * @default defaultErrorNormalizer
62
+ */
22
63
  errorNormalizer?: ErrorNormalizer;
23
64
  }
24
65
  declare function syncToKeyedStore<TEnum extends Record<string, string | number>, TData extends {
@@ -31,17 +72,71 @@ declare function syncToKeyedStore<TData extends {
31
72
  type StoreWithSignal<TKey extends StoreEnum> = {
32
73
  get: (key: TKey) => Signal<ResourceState<unknown>> | undefined;
33
74
  };
75
+ /**
76
+ * Method decorator that skips execution when the store already has valid cached data.
77
+ *
78
+ * **Cache hit** (method skipped) when:
79
+ * - `status === 'Success'` or `isLoading === true`
80
+ * - Timeout has not expired
81
+ * - Method arguments match (compared via `JSON.stringify`)
82
+ *
83
+ * **Cache miss** (method executes) when:
84
+ * - Initial state (no status, not loading)
85
+ * - `status === 'Error'` (errors are never cached)
86
+ * - Timeout expired
87
+ * - Arguments changed
88
+ *
89
+ * **Keyed resources**: When the first argument is a `string | number` and the store
90
+ * data is a `KeyedResourceData`, cache entries are tracked per resource key automatically.
91
+ *
92
+ * @param storeKey - The store slot to check for cached data.
93
+ * @param storeGetter - Function to retrieve the store from the decorated class instance.
94
+ * @param returnObservable - When `true`, returns `Observable` (with `shareReplay` for deduplication).
95
+ * When `false`, returns `void`.
96
+ * @default false
97
+ * @param timeoutMs - Cache TTL in milliseconds. Use `CACHE_NO_TIMEOUT` for infinite.
98
+ * @default DEFAULT_CACHE_TTL_MS (300 000 ms / 5 minutes)
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * @SkipIfCached('LIST', (i: ProductFacade) => i.store)
103
+ * @Loading('LIST', (i: ProductFacade) => i.store)
104
+ * loadProducts() {
105
+ * this.http.get('/api/products')
106
+ * .pipe(syncToStore(this.store, 'LIST'))
107
+ * .subscribe();
108
+ * }
109
+ * ```
110
+ */
34
111
  declare function SkipIfCached<TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: {
35
112
  store: StoreWithSignal<TKey>;
36
113
  }) => StoreWithSignal<TKey> | undefined, returnObservable?: boolean, timeoutMs?: number): MethodDecorator;
114
+ /** @inheritDoc */
37
115
  declare function SkipIfCached<TTarget, TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: TTarget) => StoreWithSignal<TKey> | undefined, returnObservable?: boolean, timeoutMs?: number): MethodDecorator;
38
116
 
39
117
  type StoreWithLoading<TKey extends StoreEnum> = {
40
118
  startLoading: (key: TKey) => void;
41
119
  };
120
+ /**
121
+ * Method decorator that calls `store.startLoading(key)` before the original method executes.
122
+ *
123
+ * **Keyed detection**: If the first method argument is a `string | number` and the store
124
+ * has a `startKeyedLoading` method, it calls that instead for per-key loading state.
125
+ *
126
+ * Typically composed with `@SkipIfCached` (which should be the outermost decorator):
127
+ * ```ts
128
+ * @SkipIfCached('LIST', (i) => i.store) // outermost — can short-circuit
129
+ * @Loading('LIST', (i) => i.store) // sets loading before method body
130
+ * loadProducts() { ... }
131
+ * ```
132
+ *
133
+ * @param storeKey - The store slot to mark as loading.
134
+ * @param storeGetter - Function to retrieve the store from the decorated class instance.
135
+ */
42
136
  declare function Loading<TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: {
43
137
  store: StoreWithLoading<TKey>;
44
138
  }) => StoreWithLoading<TKey>): MethodDecorator;
139
+ /** @inheritDoc */
45
140
  declare function Loading<TTarget, TKey extends StoreEnum>(storeKey: TKey, storeGetter: (instance: TTarget) => StoreWithLoading<TKey>): MethodDecorator;
46
141
 
47
142
  export { ErrorNormalizer, Loading, SkipIfCached, type SyncToKeyedStoreOptions, type SyncToStoreOptions, syncToKeyedStore, syncToStore };
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\nexport interface SyncToStoreOptions {\n completeOnFirstEmission?: boolean;\n callbackAfterComplete?: () => void;\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\nexport type ErrorNormalizer = (error: unknown) => ResourceErrors;\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\nexport interface SyncToKeyedStoreOptions<R, TValue> extends SyncToStoreOptions {\n mapResponse?: (response: R) => TValue;\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\n ? mapResponse(response)\n : 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 ...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 ...data.status,\n [resourceKey]: \"Error\" as ResourceStatus,\n };\n\n const nextErrors: Partial<Record<KeyedResourceKey, ResourceErrors>> = {\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\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;\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\nexport function Loading<TKey extends StoreEnum>(\n storeKey: TKey,\n storeGetter: (instance: {\n store: StoreWithLoading<TKey>;\n }) => StoreWithLoading<TKey>\n): MethodDecorator;\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\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":";AAAA,SAAS,UAAsB,MAAM,WAAW;;;ACoBzC,SAAS,uBAAuB,OAAgC;AACrE,MACE,OAAO,UAAU,YACjB,UAAU,QACV,WAAW,SACX,OAAQ,MAAkC,UAAU,UACpD;AACA,UAAM,QAAS,MAA6C;AAC5D,QAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,MACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,aAAa,OACb;AACA,UAAM,QAAQ;AACd,WAAO;AAAA,MACL;AAAA,QACE,MAAM,OAAO,MAAM,MAAM;AAAA,QACzB,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,iBAAiB,OAAO;AAC1B,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS,OAAO,KAAK;AAAA,IACvB;AAAA,EACF;AACF;;;ADVO,SAAS,YACd,OACA,KACA,UAA8B,EAAE,yBAAyB,KAAK,GAC9D;AACA,QAAM,iBAAiB,QAAQ,mBAAmB;AAClD,QAAM,YAAY;AAElB,SAAO,CAAI,WAA0B;AACnC,QAAI,WAAW,OAAO;AAAA,MACpB,IAAI;AAAA,QACF,MAAM,CAAC,SAAY;AACjB,oBAAU,OAAO,KAAK;AAAA,YACpB;AAAA,YACA,WAAW;AAAA,YACX,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,QACA,OAAO,CAAC,UAAmB;AACzB,oBAAU,OAAO,KAAK;AAAA,YACpB,MAAM;AAAA,YACN,WAAW;AAAA,YACX,QAAQ;AAAA,YACR,QAAQ,eAAe,KAAK;AAAA,UAC9B,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,QAAQ,yBAAyB;AACnC,iBAAW,SAAS,KAAK,KAAK,CAAC,CAAC;AAAA,IAClC;AAEA,QAAI,QAAQ,uBAAuB;AACjC,iBAAW,SAAS,KAAK,SAAS,QAAQ,qBAAqB,CAAC;AAAA,IAClE;AAEA,WAAO;AAAA,EACT;AACF;;;AE7FA,SAAS,YAAAA,WAAsB,QAAAC,OAAM,OAAAC,YAAW;AAEhD;AAAA,EACE;AAAA,EACA;AAAA,OAMK;AA0CP,SAAS,WACP,QACA,KAC+B;AAC/B,QAAM,OAAsC;AAAA,IAC1C,GAAG;AAAA,EACL;AACA,SAAO,KAAK,GAAG;AACf,SAAO;AACT;AA6BO,SAAS,iBACd,OACA,UACA,aACA,UAAqD;AAAA,EACnD,yBAAyB;AAC3B,GACA;AACA,QAAM,EAAE,yBAAyB,uBAAuB,YAAY,IAClE;AACF,QAAM,iBAAiB,QAAQ,mBAAmB;AAClD,QAAM,YAAY;AAElB,SAAO,CAAC,WAAgC;AACtC,QAAI,WAAW,OAAO;AAAA,MACpBC,KAAI;AAAA,QACF,MAAM,CAAC,aAAsB;AAC3B,gBAAM,QAAQ,cAAc,YAAY,QAAQ,IAAI;AAEpD,gBAAM,cAAc,UAAU,IAAI,QAAQ;AAC1C,gBAAM,QAAQ,YAAY;AAC1B,gBAAM,OACH,MAAM,QAGP,wBAAmD;AAErD,gBAAM,gBAAgB;AAAA,YACpB,GAAG,KAAK;AAAA,YACR,CAAC,WAAW,GAAG;AAAA,UACjB;AAEA,gBAAM,aACJ;AAAA,YACE,GAAG,KAAK;AAAA,YACR,CAAC,WAAW,GAAG;AAAA,UACjB;AAEF,gBAAM,WAAyD;AAAA,YAC7D,GAAG;AAAA,YACH,UAAU;AAAA,cACR,GAAG,KAAK;AAAA,cACR,CAAC,WAAW,GAAG;AAAA,YACjB;AAAA,YACA,WAAW;AAAA,YACX,QAAQ;AAAA,YACR,QAAQ,WAAW,KAAK,QAAQ,WAAW;AAAA,UAC7C;AAEA,oBAAU,OAAO,UAAU;AAAA,YACzB,MAAM;AAAA,YACN,WAAW,gBAAgB,aAAa;AAAA,YACxC,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,QACA,OAAO,CAAC,UAAmB;AACzB,gBAAM,cAAc,UAAU,IAAI,QAAQ;AAC1C,gBAAM,QAAQ,YAAY;AAC1B,gBAAM,OACH,MAAM,QAGP,wBAAmD;AAErD,gBAAM,gBAAgB;AAAA,YACpB,GAAG,KAAK;AAAA,YACR,CAAC,WAAW,GAAG;AAAA,UACjB;AAEA,gBAAM,aACJ;AAAA,YACE,GAAG,KAAK;AAAA,YACR,CAAC,WAAW,GAAG;AAAA,UACjB;AAEF,gBAAM,aACJ;AAAA,YACE,GAAG,KAAK;AAAA,YACR,CAAC,WAAW,GAAG,eAAe,KAAK;AAAA,UACrC;AAEF,gBAAM,WAAyD;AAAA,YAC7D,GAAG;AAAA,YACH,WAAW;AAAA,YACX,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV;AAEA,oBAAU,OAAO,UAAU;AAAA,YACzB,MAAM;AAAA,YACN,WAAW,gBAAgB,aAAa;AAAA,YACxC,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,yBAAyB;AAC3B,iBAAW,SAAS,KAAKC,MAAK,CAAC,CAAC;AAAA,IAClC;AAEA,QAAI,uBAAuB;AACzB,iBAAW,SAAS,KAAKC,UAAS,qBAAqB,CAAC;AAAA,IAC1D;AAEA,WAAO;AAAA,EACT;AACF;;;ACtMA,SAAS,YAAAC,WAAsB,IAAI,aAAa,OAAAC,YAAW;AAC3D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAaP,IAAM,aAAa,oBAAI,QAGrB;AAEF,SAAS,eACP,OACA,KACyB;AACzB,MAAI,WAAW,WAAW,IAAI,KAAK;AACnC,MAAI,CAAC,UAAU;AACb,eAAW,oBAAI,IAAI;AACnB,eAAW,IAAI,OAAO,QAAQ;AAAA,EAChC;AAEA,MAAI,SAAS,SAAS,IAAI,GAAG;AAC7B,MAAI,CAAC,QAAQ;AACX,aAAS,oBAAI,IAAI;AACjB,aAAS,IAAI,KAAK,MAAM;AAAA,EAC1B;AAEA,SAAO;AACT;AAEA,SAAS,cACP,OACA,KACA,UACwB;AACxB,SAAO,eAAe,OAAO,GAAG,EAAE,IAAI,QAAQ;AAChD;AAEA,SAAS,cACP,OACA,KACA,UACA,OACM;AACN,iBAAe,OAAO,GAAG,EAAE,IAAI,UAAU,KAAK;AAChD;AAEA,SAAS,gBACP,OACA,KACA,UACM;AACN,iBAAe,OAAO,GAAG,EAAE,OAAO,QAAQ;AAC5C;AAEA,SAAS,kBAAkB,MAA+C;AACxE,QAAM,MAAM,KAAK,CAAC;AAClB,MAAI,OAAO,QAAQ,YAAY,OAAO,QAAQ,UAAU;AACtD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,UACP,WACA,WACA,KACS;AACT,MAAI,cAAc,kBAAkB;AAClC,WAAO;AAAA,EACT;AACA,MAAI,cAAc,QAAW;AAC3B,WAAO;AAAA,EACT;AACA,SAAO,MAAM,aAAa;AAC5B;AAQA,SAAS,gBACP,UACA,UACA,UAC0B;AAC1B,QAAM,QAAQ,SAAS,QAAQ;AAC/B,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,IAAI,QAAQ;AACtC,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,YAAY;AACjC,MAAI,iBAAiB,QAAQ,iBAAiB,QAAW;AACvD,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,OAAO,aAAa,aAAa;AAC5C;AAaA,SAAS,gBACP,OACA,UACA,MACA,YACA,cACc;AACd,QAAM,YAAY,oBAAoB,aAAa,IAAI,IACnD,aAAa,OACb;AACJ,QAAM,cAAc,YAAY,kBAAkB,IAAI,IAAI;AAC1D,QAAM,cAAc,cAAc,UAAa,gBAAgB;AAE/D,QAAM,gBAAgB;AACtB,QAAM,mBAAmB;AACzB,QAAM,kBAAkB,cAAc,gBAAgB;AAEtD,QAAM,kBAAkB,cAAc,OAAO,UAAU,aAAa;AACpE,QAAM,qBAAqB,cAAc,OAAO,UAAU,gBAAgB;AAE1E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,kBACP,OACA,UACA,SACA,cACM;AACN,MAAI,CAAC,QAAQ,aAAa,aAAa,WAAW,SAAS;AACzD,oBAAgB,OAAO,UAAU,YAAY;AAAA,EAC/C;AACA,MAAI,QAAQ,aAAa,QAAQ,gBAAgB,QAAW;AAC1D,UAAM,SACJ,QAAQ,UAGR,OAAO,QAAQ,WAAW;AAC5B,QAAI,WAAW,SAAS;AACtB,sBAAgB,OAAO,UAAU,QAAQ,eAAe;AAAA,IAC1D;AAAA,EACF;AACF;AAOA,SAAS,iBACP,OACA,UACA,SACA,WACA,KACA,kBACgB;AAChB,QAAM,EAAE,WAAW,aAAa,iBAAiB,gBAAgB,IAAI;AAErE,MAAI,CAAC,aAAa,gBAAgB,QAAW;AAC3C,WAAO,EAAE,KAAK,MAAM;AAAA,EACtB;AAEA,QAAM,QAAQ;AAMd,QAAM,SAAS,MAAM,OAAO,WAAW;AACvC,QAAM,SAAS,MAAM,SAAS,WAAW;AACzC,QAAM,UAAU,MAAM,UAAU,WAAW,MAAM;AAEjD,QAAM,UAAU,UAAU,iBAAiB,WAAW,WAAW,GAAG;AACpE,MAAI,SAAS;AACX,oBAAgB,OAAO,UAAU,eAAe;AAAA,EAClD;AAEA,MAAI,CAAC,WAAW,WAAW,aAAa,WAAW,QAAW;AAC5D,QAAI,kBAAkB;AACpB,aAAO,EAAE,KAAK,MAAM,OAAO,GAAG,MAAM,EAAE;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAEA,MAAI,kBAAkB;AACpB,QAAI,iBAAiB,WAAW;AAC9B,aAAO,EAAE,KAAK,MAAM,OAAO,gBAAgB,UAAU;AAAA,IACvD;AAAA,EACF,WAAW,SAAS;AAClB,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAEA,SAAO,EAAE,KAAK,MAAM;AACtB;AAQA,SAAS,oBACP,OACA,UACA,SACA,WACA,KACA,kBACA,OACgB;AAChB,QAAM,EAAE,oBAAoB,gBAAgB,IAAI;AAChD,QAAM,EAAE,cAAc,YAAY,YAAY,IAAI;AAElD,MACE,oBACA,oBAAoB,SAAS,cAC7B,mBAAmB,WACnB;AACA,WAAO,EAAE,KAAK,MAAM,OAAO,mBAAmB,UAAU;AAAA,EAC1D;AAEA,QAAM,qBACJ,cAAc,WAAW,aAAa,cAAc,cAAc;AAEpE,MACE,sBACA,UAAU,mBAAmB,WAAW,WAAW,GAAG,GACtD;AACA,oBAAgB,OAAO,UAAU,eAAe;AAAA,EAClD,WAAW,oBAAoB,SAAS,cAAc,oBAAoB;AACxE,QAAI,kBAAkB;AACpB,UAAI,mBAAmB,WAAW;AAChC,eAAO,EAAE,KAAK,MAAM,OAAO,mBAAmB,UAAU;AAAA,MAC1D;AACA,aAAO,EAAE,KAAK,MAAM,OAAO,GAAG,YAAY,EAAE,IAAI,EAAE;AAAA,IACpD;AACA,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAEA,SAAO,EAAE,KAAK,MAAM;AACtB;AAEA,SAAS,uBACP,QACA,OACA,UACA,iBACA,YACqB;AACrB,SAAO,OAAO;AAAA,IACZA,KAAI;AAAA,MACF,MAAM,MAAM;AACV,sBAAc,OAAO,UAAU,iBAAiB;AAAA,UAC9C,WAAW,KAAK,IAAI;AAAA,UACpB,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,OAAO,MAAM;AACX,wBAAgB,OAAO,UAAU,eAAe;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,IACDD,UAAS,MAAM;AACb,YAAM,QAAQ,cAAc,OAAO,UAAU,eAAe;AAC5D,UAAI,OAAO,WAAW;AACpB,cAAM,EAAE,WAAW,YAAY,GAAG,KAAK,IAAI;AAC3C,sBAAc,OAAO,UAAU,iBAAiB,IAAI;AAAA,MACtD;AAAA,IACF,CAAC;AAAA,IACD,YAAY,EAAE,YAAY,GAAG,UAAU,KAAK,CAAC;AAAA,EAC/C;AACF;AAqDO,SAAS,aACd,UACA,aACA,mBAAmB,OACnB,YAAY,sBACK;AACjB,SAAO,SACL,SACA,cACA,YACA;AACA,UAAM,iBAAiB,WAAW;AAKlC,eAAW,QAAQ,YAA4B,MAAiB;AAC9D,YAAM,eAAe,gBAAgB,MAAM,UAAU,WAAW;AAChE,UAAI,CAAC,cAAc;AACjB,eAAO,eAAe,MAAM,MAAM,IAAI;AAAA,MACxC;AACA,YAAM,EAAE,OAAO,aAAa,aAAa,IAAI;AAE7C,YAAM,aAAa,KAAK,UAAU,IAAI;AACtC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,eAAe;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,wBAAkB,OAAO,UAAU,cAAc,YAAY;AAE7D,UAAI;AAEJ,UAAI,aAAa,aAAa;AAC5B,mBAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,EAAE,cAAc,YAAY,YAAY;AAAA,QAC1C;AAAA,MACF;AAEA,UAAI,SAAS,KAAK;AAChB,eAAO,SAAS;AAAA,MAClB;AAEA,YAAM,SAAS,eAAe,MAAM,MAAM,IAAI;AAE9C,UAAI,CAAC,kBAAkB;AACrB,sBAAc,OAAO,UAAU,aAAa,iBAAiB;AAAA,UAC3D,WAAW;AAAA,UACX,MAAM;AAAA,QACR,CAAC;AACD,eAAO;AAAA,MACT;AAEA,YAAM,YAAY;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb;AAAA,MACF;AAEA,oBAAc,OAAO,UAAU,aAAa,iBAAiB;AAAA,QAC3D,WAAW;AAAA,QACX,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;;;ACnaO,SAAS,QACd,UACA,aACA;AACA,SAAO,SACL,SACA,cACA,YACA;AACA,UAAM,iBAAiB,WAAW;AAKlC,eAAW,QAAQ,YAA4B,MAAiB;AAC9D,YAAM,QAAQ,YAAY,IAAI;AAE9B,YAAM,cAAc,KAAK,CAAC;AAC1B,YAAM,SACJ,OAAO,gBAAgB,YAAY,OAAO,gBAAgB;AAC5D,YAAM,WACJ,OAAO,UAAU,YACjB,UAAU,QACV,uBAAuB,SACvB,OAAQ,MAA0C,sBAChD;AAEJ,UAAI,UAAU,UAAU;AACtB,QACE,MAMA,kBAAkB,UAAU,WAA+B;AAAA,MAC/D,OAAO;AACL,eAAO,aAAa,QAAQ;AAAA,MAC9B;AACA,aAAO,eAAe,MAAM,MAAM,IAAI;AAAA,IACxC;AAEA,WAAO;AAAA,EACT;AACF;","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.8.2",
3
+ "version": "0.8.4",
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.8.0",
42
- "@flurryx/store": "0.8.2"
41
+ "@flurryx/core": "0.8.4",
42
+ "@flurryx/store": "0.8.4"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@angular/core": ">=17.0.0",
@@ -1,6 +0,0 @@
1
- import { ResourceErrors } from '@flurryx/core';
2
-
3
- type ErrorNormalizer = (error: unknown) => ResourceErrors;
4
- declare function defaultErrorNormalizer(error: unknown): ResourceErrors;
5
-
6
- export { type ErrorNormalizer as E, defaultErrorNormalizer as d };
@@ -1,6 +0,0 @@
1
- import { ResourceErrors } from '@flurryx/core';
2
-
3
- type ErrorNormalizer = (error: unknown) => ResourceErrors;
4
- declare function defaultErrorNormalizer(error: unknown): ResourceErrors;
5
-
6
- export { type ErrorNormalizer as E, defaultErrorNormalizer as d };