{"version":3,"file":"ngxtension-derived-async.mjs","sources":["../../../../libs/ngxtension/derived-async/src/derived-async.ts","../../../../libs/ngxtension/derived-async/src/ngxtension-derived-async.ts"],"sourcesContent":["import {\n\tDestroyRef,\n\tInjector,\n\tcomputed,\n\teffect,\n\tinject,\n\tsignal,\n\tuntracked,\n\ttype CreateComputedOptions,\n\ttype Signal,\n\ttype WritableSignal,\n} from '@angular/core';\nimport { assertInjector } from 'ngxtension/assert-injector';\nimport {\n\tObservable,\n\tSubject,\n\tconcatAll,\n\texhaustAll,\n\tisObservable,\n\tmergeAll,\n\tof,\n\tswitchAll,\n} from 'rxjs';\n\ntype derivedAsyncBehavior = 'switch' | 'merge' | 'concat' | 'exhaust';\n\ninterface derivedAsyncOptions<T> extends CreateComputedOptions<T> {\n\tinjector?: Injector;\n\tbehavior?: derivedAsyncBehavior;\n}\n\ntype OptionsWithInitialValue<T> = { initialValue: T } & derivedAsyncOptions<T>;\ntype OptionsWithOptionalInitialValue<T> = {\n\tinitialValue?: undefined;\n} & derivedAsyncOptions<T>;\ntype OptionsWithRequireSync<T> = {\n\trequireSync: true;\n} & derivedAsyncOptions<T>;\n\ntype ObservableComputation<T> = (\n\tpreviousValue?: T | undefined,\n) => Observable<T> | T;\n\ntype PromiseComputation<T> = (previousValue?: T | undefined) => Promise<T> | T;\n\n/**\n * A computed value that can be async! This is useful for when you need to compute a value based on a Promise or Observable.\n *\n * @example\n * ```ts\n * const value = derivedAsync(() =>\n *   fetch(`https://localhost/api/people/${this.userId()}`).then(r => r.json())\n * );\n * ```\n *\n * The computed value will be `undefined` until the promise resolves.\n * Everytime the userId changes, the fetch will be called again, and the previous fetch will be cancelled (it uses switchMap by default).\n * If the promise rejects, the error will be thrown.\n *\n * It can also be used with Observables:\n *\n * ```ts\n * const value = derivedAsync(() =>\n *  this.http.get(`https://localhost/api/people/${this.userId()}`)\n * );\n * ```\n *\n * You can also pass an `initialValue` option to set the initial value of the computed value.\n *\n * ```ts\n * const userTasks = derivedAsync(() =>\n *   this.http.get(`https://localhost/api/tasks?userId=${this.userId()}`),\n *   { initialValue: [] }\n * );\n * ```\n *\n * If you want to require that the observable emits synchronously when `derivedAsync` subscribes, you can set the `requireSync` option to `true`.\n *\n * ```ts\n * const userTasks = derivedAsync(() =>\n *   this.http.get(`https://localhost/api/tasks?userId=${this.userId()}`).pipe(\n * \t   startWith([]),\n *   ),\n *   { requireSync: true }\n * );\n *\n * You can also pass a `behavior` option to change the behavior of the computed value.\n * - `switch` (default): will cancel the previous computation when a new one is triggered\n * - `merge`: will use `mergeMap` to merge the last observable with the new one\n * - `concat`: will use `concatMap` to concat the last observable with the new one\n * - `exhaust`: will use `exhaustMap` to skip all the new emissions until the last observable completes\n *\n * You can also pass an `injector` option if you want to use it outside of the injection context.\n *\n * @param computation\n * @param options\n */\n\n// Base Case -> Initial Value: undefined | Require Sync: undefined  ->  T | undefined\nexport function derivedAsync<T>(\n\tcomputation: (\n\t\tpreviousValue?: T | undefined,\n\t) => Promise<T> | Observable<T> | T | undefined,\n): Signal<T | undefined>;\n\n/*\n * Promise Types\n */\n\n// Initial Value: undefined  ->  T | undefined\nexport function derivedAsync<T>(\n\tcomputation: (\n\t\tpreviousValue?: T | undefined,\n\t) => Observable<T> | Promise<T> | T | undefined,\n\toptions: OptionsWithOptionalInitialValue<T>,\n): Signal<T | undefined>;\n\n// Initial Value: T | null  ->  T | null\nexport function derivedAsync<T>(\n\tcomputation: PromiseComputation<T>,\n\toptions: { initialValue?: null } & derivedAsyncOptions<T>,\n): Signal<T | null>;\n\n// Initial Value: T  ->  T\nexport function derivedAsync<T>(\n\tcomputation: PromiseComputation<T>,\n\toptions: OptionsWithInitialValue<T>,\n): Signal<T>;\n\n// Require Sync: true  ->  never\nexport function derivedAsync<T>(\n\tcomputation: (previousValue?: T | undefined) => Promise<T>,\n\toptions: OptionsWithOptionalInitialValue<T> & {\n\t\t/**\n\t\t * @throws Because the promise will not resolve synchronously.\n\t\t */\n\t\trequireSync: true;\n\t},\n): never;\n\nexport function derivedAsync<T>(\n\tcomputation: (previousValue?: T | undefined) => Promise<T>,\n\toptions: OptionsWithInitialValue<T> & {\n\t\t/**\n\t\t * @throws Because the promise will not resolve synchronously.\n\t\t */\n\t\trequireSync: true;\n\t},\n): never;\n\n/*\n * Observable Types\n */\n\n// Initial Value: undefined | Require Sync: false  ->  T | undefined\nexport function derivedAsync<T>(\n\tcomputation: (previousValue?: T | undefined) => Observable<T> | T | undefined,\n\toptions: {\n\t\tinitialValue?: undefined;\n\t\trequireSync?: false;\n\t} & derivedAsyncOptions<T>,\n): Signal<T | undefined>;\n\n// Initial Value: null | Require Sync: false  ->  T | null\nexport function derivedAsync<T>(\n\tcomputation: ObservableComputation<T>,\n\toptions: {\n\t\tinitialValue?: null;\n\t\trequireSync?: false;\n\t} & derivedAsyncOptions<T>,\n): Signal<T | null>;\n\n// Initial Value: undefined | Require Sync: true  ->  T\nexport function derivedAsync<T>(\n\tcomputation: ObservableComputation<T>,\n\toptions: OptionsWithRequireSync<T> & { initialValue?: undefined },\n): Signal<T>;\n\n// Initial Value: T | Require Sync: true  ->  T\nexport function derivedAsync<T>(\n\tcomputation: ObservableComputation<T>,\n\toptions: OptionsWithRequireSync<T> & { initialValue: T },\n): Signal<T>;\n\n// Initial Value: T | Require Sync: false | undefined  ->  T\nexport function derivedAsync<T>(\n\tcomputation: ObservableComputation<T>,\n\toptions: OptionsWithInitialValue<T>,\n): Signal<T>;\n\nexport function derivedAsync<T>(\n\tcomputation: (\n\t\tpreviousValue?: T | undefined,\n\t) => Promise<T> | Observable<T> | T | undefined,\n\toptions: any = {},\n): Signal<T | undefined> {\n\treturn assertInjector(derivedAsync, options?.injector, () => {\n\t\tconst destroyRef = inject(DestroyRef);\n\n\t\t// source$ is a Subject that will emit the new source value\n\t\tconst sourceEvent$ = new Subject<Promise<T> | Observable<T>>();\n\n\t\t// enhance the sourceEvent$ with the behavior\n\t\tconst source$: Observable<T> = createFlattenObservable(\n\t\t\tsourceEvent$,\n\t\t\toptions?.behavior ?? 'switch',\n\t\t);\n\n\t\tconst sourceResult = source$.subscribe({\n\t\t\tnext: (value) => sourceValue.set({ kind: StateKind.Value, value }),\n\t\t\t// NOTE: Error should be handled by the user (using catchError or .catch())\n\t\t\terror: (error) => sourceValue.set({ kind: StateKind.Error, error }),\n\t\t});\n\n\t\t// we need to unsubscribe the sourceResult when the context gets destroyed\n\t\tdestroyRef.onDestroy(() => sourceResult.unsubscribe());\n\n\t\t// sourceValue is a signal that will hold the current value and the state of the value\n\t\tlet sourceValue: WritableSignal<State<T>>;\n\n\t\tif (options?.requireSync && options?.initialValue === undefined) {\n\t\t\tconst initialCmp = computation(undefined);\n\n\t\t\t// we don't support promises with requireSync and no initialValue also the typings don't allow this case\n\t\t\tif (isPromise(initialCmp)) {\n\t\t\t\tthrow new Error(REQUIRE_SYNC_PROMISE_MESSAGE);\n\t\t\t}\n\n\t\t\tsourceValue = signal<State<T>>({ kind: StateKind.NoValue });\n\n\t\t\tif (isObservable(initialCmp)) {\n\t\t\t\tsourceEvent$.next(initialCmp);\n\t\t\t} else {\n\t\t\t\tsourceValue.set({ kind: StateKind.Value, value: initialCmp as T });\n\t\t\t}\n\t\t} else {\n\t\t\tsourceValue = signal<State<T>>({\n\t\t\t\tkind: StateKind.Value,\n\t\t\t\tvalue: options?.initialValue,\n\t\t\t});\n\t\t}\n\n\t\tif (options?.requireSync && sourceValue().kind === StateKind.NoValue) {\n\t\t\tthrow new Error(REQUIRE_SYNC_ERROR_MESSAGE);\n\t\t}\n\n\t\tlet skipFirstComputation = options?.requireSync === true;\n\n\t\t// effect runs inside injection context, so it will be cleanup up when context gets destroyed\n\t\teffect(() => {\n\t\t\t// we need to have an untracked() here because we don't want to register the sourceValue as a dependency\n\t\t\t// otherwise, we would have an infinite loop.\n\t\t\t// this is needed for previousValue feature to work\n\t\t\tconst currentValue = untracked(() => {\n\t\t\t\tconst currentSourceValue = sourceValue();\n\t\t\t\treturn currentSourceValue.kind === StateKind.Value\n\t\t\t\t\t? currentSourceValue.value\n\t\t\t\t\t: undefined;\n\t\t\t});\n\n\t\t\tconst newSource = computation(currentValue);\n\n\t\t\t// we need to skip the first computation if requireSync is true\n\t\t\t// because we already computed the value in the previous step\n\t\t\tif (skipFirstComputation) {\n\t\t\t\tskipFirstComputation = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// we untrack the source$.next() so that we don't register other signals as dependencies\n\t\t\tuntracked(() => {\n\t\t\t\tsourceEvent$.next(\n\t\t\t\t\tisObservable(newSource) || isPromise(newSource)\n\t\t\t\t\t\t? newSource\n\t\t\t\t\t\t: of(newSource as T),\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\n\t\t// we return a computed value that will return the current value\n\t\t// in order to support the same API as computed()\n\t\treturn computed(\n\t\t\t() => {\n\t\t\t\tconst state = sourceValue();\n\t\t\t\tswitch (state.kind) {\n\t\t\t\t\tcase StateKind.Value:\n\t\t\t\t\t\treturn state.value;\n\t\t\t\t\tcase StateKind.Error:\n\t\t\t\t\t\tthrow state.error;\n\t\t\t\t\tcase StateKind.NoValue:\n\t\t\t\t\t\t// we already throw an error if requireSync is true and there is no initialValue,\n\t\t\t\t\t\t// so we don't need to throw an error here\n\t\t\t\t\t\treturn;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// we should never reach this case\n\t\t\t\t\t\tthrow new Error('Unknown state');\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ equal: options?.equal },\n\t\t);\n\t});\n}\n\nconst REQUIRE_SYNC_PROMISE_MESSAGE = `Promises cannot be used with requireSync. Pass an initialValue or set requireSync to false.`;\nconst REQUIRE_SYNC_ERROR_MESSAGE = `The observable passed to derivedAsync() did not emit synchronously. Pass an initialValue or set requireSync to false.`;\n\nfunction createFlattenObservable<T>(\n\tsource: Subject<Promise<T> | Observable<T>>,\n\tbehavior: derivedAsyncBehavior,\n): Observable<T> {\n\tconst KEY_OPERATOR_MAP = {\n\t\tmerge: mergeAll,\n\t\tconcat: concatAll,\n\t\texhaust: exhaustAll,\n\t\tswitch: switchAll,\n\t};\n\n\treturn source.pipe(KEY_OPERATOR_MAP[behavior]());\n}\n\nfunction isPromise<T>(value: any): value is Promise<T> {\n\treturn value && typeof value.then === 'function';\n}\n\nconst enum StateKind {\n\tNoValue,\n\tValue,\n\tError,\n}\n\ninterface NoValueState {\n\tkind: StateKind.NoValue;\n}\n\ninterface ValueState<T> {\n\tkind: StateKind.Value;\n\tvalue: T;\n}\n\ninterface ErrorState {\n\tkind: StateKind.Error;\n\terror: unknown;\n}\n\ntype State<T> = NoValueState | ValueState<T> | ErrorState;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}