@bespunky/angular-zen
Version:
The Angular tools you always wished were there.
1 lines • 187 kB
Source Map (JSON)
{"version":3,"file":"bespunky-angular-zen-core.mjs","sources":["../../../../libs/angular-zen/core/src/document-ref/document-ref.service.ts","../../../../libs/angular-zen/core/src/window-ref/window-ref.service.ts","../../../../libs/angular-zen/core/src/rxjs/destroyable/destroyable.ts","../../../../libs/angular-zen/core/src/rxjs/observe/abstraction/observe-base.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/directives/observe.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/abstraction/observe-map-base.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/directives/observe-latest.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/abstraction/observe-array-base.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/directives/observe-concat.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/directives/observe-join.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/directives/observe-merge.directive.ts","../../../../libs/angular-zen/core/src/rxjs/observe/observe.module.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/utils/time-utils.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/abstraction/types/on-observer-context.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/abstraction/types/observer-call.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/abstraction/types/view-render-commitment.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/abstraction/on-observer-base.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer-resolving.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer-next.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer-error.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer-complete.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer-active.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/directives/on-observer-finalized.directive.ts","../../../../libs/angular-zen/core/src/rxjs/on-observer/on-observer.module.ts","../../../../libs/angular-zen/core/src/core.module.ts","../../../../libs/angular-zen/core/src/head/head.service.ts","../../../../libs/angular-zen/core/src/bespunky-angular-zen-core.ts"],"sourcesContent":["import { ExistingProvider, Inject, Injectable, InjectionToken } from '@angular/core';\nimport { DOCUMENT as ANGULAR_DOCUMENT } from '@angular/common';\n\n/** A token used to provide the native document implementation for `DocumentRef`. By default, `CoreModule` will provide angular's DOCUMENT token. */\nexport const DOCUMENT = new InjectionToken<Document>('DocumentToken');\n\n/**\n * Provides an injectable wrapper for the `document` object.\n * Inject this in your services/components and you will be able to easily mock or spy on the native `document` object in your tests.\n * \n * By default, the `nativeDocument` property will point to angular's DOM adapter, thus facilitating DOM access and manipulation\n * on the different platforms.\n * To mock the native document, provide a value for the `DOCUMENT` token from `@bespunky/angular-zen/core`.\n * You will safely mock it without trashing angular's `DOCUMENT` provider.\n * \n * @see document-ref.service.spec.ts for examples.\n */\n@Injectable({ providedIn: 'root' })\nexport class DocumentRef\n{\n // Treating native document as `any` save users typecasting everytime and deducing if the object is of type `Document` or `object`.\n /**\n * Creates an instance of `DocumentRef`.\n * \n * @param {*} nativeDocument The native document provided by the `DOCUMENT` token of `@bespunky/angular-zen/core`. See `DocumentRef` for details. \n */\n constructor(@Inject(DOCUMENT) public readonly nativeDocument: any) { }\n}\n\n/**\n * The default provider for the `DOCUMENT` token. Uses angular's DOM adapters which will be injected according to the platform.\n */\nexport const DocumentProvider: ExistingProvider = {\n provide : DOCUMENT,\n useExisting: ANGULAR_DOCUMENT\n};\n\n/**\n * A bundle of all providers needed for DocumentRef to work.\n */\nexport const DocumentRefProviders = [DocumentProvider];\n","import { InjectionToken, PLATFORM_ID, FactoryProvider, Inject, Injectable } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n/**\n * An injectable token that will allow us to replace the provider for the native window object when necessary (e.g. mocking the `window` object).\n */\nexport const WINDOW = new InjectionToken<Window>('WindowToken');\n\n/**\n * Provides an injectable wrapper for the `window` object.\n *\n * Inject this in your services/components and you will be able to easily mock or spy on the native `window` object in your tests.\n * You can replace the default `WINDOW` token provider, which allows you to mock the `window` object.\n *\n * @see window-ref.service.spec.ts for examples.\n */\n@Injectable({ providedIn: 'root' })\nexport class WindowRef\n{\n // Treating native window as `any` save users typecasting everytime and deducing if the object is of type `Window` or `object`.\n /**\n * Creates an instance of WindowRef.\n * \n * @param {*} nativeWindow The native window provided by the `WINDOW` token of `@bespunky/angular-zen/core`. See `WindowRef` for details. \n */\n constructor(@Inject(WINDOW) public readonly nativeWindow: any) { }\n}\n\n/**\n * Provides a platform dependant implementation for retrieving the `window` object.\n *\n * @returns `window` for browser platforms and a new object for non-browser platforms.\n */\nexport function windowFactory(platformId: any): Window | Object\n{\n return isPlatformBrowser(platformId) ? window : new Object();\n}\n\n/**\n * The default provider for the `WINDOW` token. Provides `window` for browser platforms and a new object for non-browser platforms.\n */\nexport const WindowProvider: FactoryProvider = {\n provide: WINDOW,\n useFactory: windowFactory,\n deps: [PLATFORM_ID]\n};\n\n/**\n * A bundle of all providers needed for WindowRef to work.\n */\nexport const WindowRefProviders = [WindowProvider];\n","import { Observable, PartialObserver, Subject, Subscription } from 'rxjs';\nimport { Directive, OnDestroy } from '@angular/core';\n\n/**\n * Facilitates working with components, directives and services which manually subscribe to observables.\n * Extend this class to easily hook into ngOnDestroy and avoid memory leaks.\n *\n * @see [Wiki](https://bs-angular-zen.web.app/docs/zen/additional-documentation/coremodule/destroyable-(abstract).html) for full guide.\n * \n * @export\n * @abstract\n * @class Destroyable\n * @implements {OnDestroy}\n */\n@Directive()\nexport abstract class Destroyable implements OnDestroy\n{\n /**\n * Emits a value when `ngOnDestroy()` is called.\n * Pipe together with `takeUntil()` to auto unsubscribe from your observables.\n *\n * @example\n * observable.pipe(takeUntil(this.destroyed)).subscribe(...);\n * \n * @protected\n * @type {Subject<void>}\n */\n protected readonly destroyed : Subject<void> = new Subject();\n /**\n * A list of all subscriptions manually added using the `subscribe()` method.\n * These are automatically unsubscribed when `ngOnDestroy()` is called.\n *\n * @protected\n * @type {Subscription}\n */\n protected readonly subscriptions: Subscription = new Subscription();\n\n ngOnDestroy()\n {\n this.destroyed.next();\n this.destroyed.complete();\n \n this.subscriptions.unsubscribe();\n }\n \n /**\n * Subscribes to an observable and stores the subscription for automatic disposal.\n * When `ngOnDestroy()` is called, all subscriptions created with this method will unsubscribe.\n *\n * @protected\n * @template T The type of data the observable will emit.\n * @param {Observable<T>} observable The observable to subscribe to.\n * @param {(value: T) => void} [next] (Optional) A callback function to execute on each emission of the observable.\n * @param {(error: any) => void} [error] (Optional) A callback function to execute when the observable errors.\n * @param {() => void} [complete] (Optional) A callback function to execute when the observable completes.\n * @returns {Subscription} The subscription created for the observable.\n */\n protected subscribe<T>(observable: Observable<T>, next?: (value: T) => void, error?: (error: unknown) => void, complete?: () => void): Subscription;\n /**\n * Subscribes to an observable and stores the subscription for automatic disposal.\n * When `ngOnDestroy()` is called, all subscriptions created with this method will unsubscribe.\n *\n * @protected\n * @template T The type of data the observable will emit.\n * @param {Observable<T>} observable The observable to subscribe to.\n * @param {PartialObserver<T>} [observer] The observer that will handle observable events.\n * @returns {Subscription} The subscription created for the observable.\n */\n protected subscribe<T>(observable: Observable<T>, observer?: PartialObserver<T>): Subscription;\n protected subscribe<T>(observable: Observable<T>, observerOrNext?: PartialObserver<T> | ((value: T) => void), error?: (error: unknown) => void, complete?: () => void): Subscription\n {\n // Cast partial observer object\n const observer = observerOrNext instanceof Function ? {\n next: observerOrNext,\n error,\n complete\n } : observerOrNext;\n\n this.subscriptions.add(observable.subscribe(observer));\n\n return this.subscriptions;\n }\n}","import { EMPTY, Observable, Notification, BehaviorSubject } from 'rxjs';\nimport { map, materialize, share, switchMap, tap } from 'rxjs/operators';\nimport { Directive, EmbeddedViewRef, TemplateRef, ViewContainerRef, EventEmitter, Output, OnInit } from '@angular/core';\n\nimport { Destroyable } from '../../destroyable/destroyable';\nimport { ResolvedObserveContext } from './types/general';\n\n/**\n * The base class for all `*observeXXX` directives.\n * This directive bind an observable or a collection of observables with a template, causing the values in the template to be updated whenever the observables emit.\n * \n * Any template assigned with the directive will render immediately, and its view context will be updated with the emitted value on\n * each emission. The directive will be responsible for subscribing on init and unsubscribing on destroy.\n * \n * ## Features\n * \n * #### Shared observable\n * The watched observable will automatically be multicasted so that any child observables created by the template will use the same\n * stream.\n * \n * The shared observable can be accessed using the `let source = source` microsyntax.\n * \n * #### Observer events\n * Whenever the observable changes state or emits a value, the corresponding event is emitted: \n * `nextCalled` - A value has been emitted. `$event` will be the emitted value. \n * `errorCalled` - An error has occured in the pipeline. `$event` will be the error. \n * `completeCalled` - The observable has completed. `$event` will be void.\n *\n * > Because of limitations to Angular's Structural Directives, in order to bind the events the desugared syntax must be used.\n * This, for example, **will trigger** the event:\n * > ```html\n * ><ng-template [observe]=\"x$\" let-source=\"source\" (nextCalled)=\"onNext($event)\">\n * > ...\n * ></ng-template>\n * > ```\n * >\n * >This **will NOT trigger** the event:\n * >```html\n * > <div *observe=\"x$; let source = source\" (nextCalled)=\"onNext($event)\">...</div>\n * >```\n * \n * ## ⚠️ Extending notes:\n * As the base class cannot deduce the directive selector (e.g. `observeLatest`, `observeMerge`, etc.) the extending class\n * is required to do 4 things:\n * 1. Implement the abstract `selector` member and assign it with the directive's selector.\n * 2. Implement and `@Input() public set <selector>(value: T)` which will pass its value to the `input` member.\n * 3. Implement a static context TypeGuard.\n * \n * These will enable Angular features like template type checking and the microsyntax `as` keyword.\n * \n * @export\n * @abstract\n * @class ObserveBaseDirective\n * @extends {Destroyable}\n * @implements {OnInit}\n * @template TInput The type of value this directive will work with. Depends on the extending class.\n * @template TResolved The type of value emitted by the observable. Depends on the extending class.\n * @template TContext The type of the context object the template will work with.\n */\n@Directive()\nexport abstract class ObserveBaseDirective<TInput, TResolved, TContext extends ResolvedObserveContext<TResolved>>\n extends Destroyable implements OnInit\n{\n /**\n * Triggered whenever the observable emits a value. `$event` will be the emitted value.\n *\n * @type {EventEmitter<TResolved>}\n */\n @Output() public nextCalled : EventEmitter<TResolved> = new EventEmitter();\n /**\n * Triggered when an error occurs in the observable's pipeline. `$event` will be the error.\n *\n * @type {EventEmitter<unknown>}\n */\n @Output() public errorCalled : EventEmitter<unknown> = new EventEmitter();\n /**\n * Triggered when the observable completes. `$event` will be the void.\n *\n * @type {EventEmitter<void>}\n */\n @Output() public completeCalled: EventEmitter<void> = new EventEmitter();\n\n private view!: EmbeddedViewRef<TContext>;\n \n /**\n * The selector defined for the directive extending this class. Will be used to create a corresponding\n * property in the view context in order to make the micro-syntax `as` keyword work.\n *\n * @protected\n * @abstract\n * @type {string}\n */\n protected abstract readonly selector: string;\n /**\n * ### Why BehaviorSubject<{@link TInput s} | null> and not Subject<{@link TInput}>\n * `input` is set from @Input properties. For some reason, Angular passes-in the first value BEFORE\n * ngOnInit, even though other @Input properties (e.g. showAfter, showFor) are passed AFTER ngOnInit.\n * If subscription occurs in the constructor, `input` will emit the first observable too fast, which\n * might lead to pipes breaking or misbehaving if they rely on properties to be instantiated first.\n * \n * This leads to subscribing in ngOnInit, to allow Angular time to initialize those.\n * BUT, if `input` is a Subject, as the first value was already emitted BEFORE ngOnInit, it will not be\n * captured by our subscription to `input`. Hence the BehaviorSubject - To allow capturing that first observable.\n */\n protected readonly input: BehaviorSubject<TInput | null> = new BehaviorSubject(null as TInput | null);\n\n constructor(\n private readonly template : TemplateRef<TContext>,\n private readonly viewContainer: ViewContainerRef\n )\n {\n super();\n \n this.renderView();\n }\n \n ngOnInit()\n {\n // See `this.input` documentation for why subscription is done in ngOnInit.\n this.subscribe(this.contextFeed());\n }\n\n /**\n * Takes the input passed to the directive (which might be an observable, a map or an array of observable for example)\n * and creates an observable that combines and represents all the observables in the value.\n * \n * Intended for applying functions like `combineLatest()`, `contact()`, etc.\n * \n * @protected\n * @abstract\n * @param {TInput} input The input passed to the directive (which might be an observable, a map or an array of observable for example).\n * @return {Observable<TResolved>} An observable which combines and represents all the observables in the input value.\n */\n protected abstract observeInput(input: TInput): Observable<TResolved>;\n\n private contextFeed(): Observable<Notification<TResolved>>\n {\n return this.input.pipe(\n // Whenever a new value is provided into the directive use the extender's implementation to observe it and multicast it.\n map (input => input ? this.observeInput(input).pipe(share()) : EMPTY),\n // Replace the source observable in the context with the newly created observable.\n tap (source => this.updateViewContext({ source })),\n // Switch to the new observable and materialize it to watch for state changes and emit events accordingly\n switchMap(source => source.pipe(materialize())),\n // Whenever a materialized notification is emitted, handle it and emit the relevant event\n tap (meta => this.onStateChange(meta))\n );\n }\n\n private onStateChange(meta: Notification<TResolved>): void\n {\n // Call the appropriate handler according to the received notification\n return meta.observe({\n next: value =>\n {\n this.updateViewContext({ value });\n\n this.nextCalled.emit(value);\n },\n error : error => this.errorCalled.emit(error),\n complete: () => this.completeCalled.emit()\n });\n }\n\n private renderView(): void\n {\n const context = this.createViewContext({});\n\n this.view = this.viewContainer.createEmbeddedView(this.template, context);\n }\n\n private updateViewContext(data: { value?: TResolved | null , source?: Observable<TResolved> }): void\n { \n this.view.context = this.createViewContext(data);\n }\n\n protected createViewContext({ value, source }: { value?: TResolved | null , source?: Observable<TResolved> }): TContext\n {\n value ??= this.view?.context.$implicit || null;\n source ??= this.view?.context.source || EMPTY;\n\n return { $implicit: value, [this.selector]: value, source } as TContext;\n }\n}\n","import { Observable } from 'rxjs';\nimport { Directive, Input } from '@angular/core';\n\nimport { ObserveBaseDirective } from '../abstraction/observe-base.directive';\nimport { EmittedTypeOf, ResolvedObserveContext } from '../abstraction/types/general';\n\ntype ObserveContext<T extends Observable<unknown>> = ResolvedObserveContext<EmittedTypeOf<T>> & {\n observe: EmittedTypeOf<T>\n};\n\n/**\n * Documentation in {@link ObserveDirective.observe} to allow in-template tooltips.\n *\n * @export\n * @class ObserveDirective\n * @extends {ObserveBaseDirective<T, EmittedTypeOf<T>, ObserveContext<T>>}\n * @template T The type of observable received by the directive.\n */\n@Directive({\n selector: '[observe]'\n})\nexport class ObserveDirective<T extends Observable<unknown>>\n extends ObserveBaseDirective<T, EmittedTypeOf<T>, ObserveContext<T>>\n{\n protected selector = 'observe';\n\n /**\n * Tracks an observable and updates the template with its emitted value on each emission.\n *\n * Any template assigned with the directive will render immediately, and its view context will be updated with the emitted value on\n * each emission. The directive will be responsible for subscribing on init and unsubscribing on destroy.\n * \n * ## Features\n * \n * #### Shared observable\n * The watched observable will automatically be multicasted so that any child observables created by the template will use the same\n * stream.\n * \n * The shared observable can be accessed using the `let source = source` microsyntax.\n * \n * #### Observer events\n * Whenever the observable changes state or emits a value, the corresponding event is emitted: \n * `nextCalled` - A value has been emitted. `$event` will be the emitted value. \n * `errorCalled` - An error has occured in the pipeline. `$event` will be the error. \n * `completeCalled` - The observable has completed. `$event` will be void.\n *\n * > Because of limitations to Angular's Structural Directives, in order to bind the events the desugared syntax must be used.\n * This, for example, **will trigger** the event:\n * > ```html\n * ><ng-template [observe]=\"x$\" let-source=\"source\" (nextCalled)=\"onNext($event)\">\n * > ...\n * ></ng-template>\n * > ```\n * >\n * >This **will NOT trigger** the event:\n * >```html\n * > <div *observe=\"x$; let source = source\" (nextCalled)=\"onNext($event)\">...</div>\n * >```\n */\n @Input() public set observe(value: T) { this.input.next(value); }\n \n static ngTemplateContextGuard<T extends Observable<unknown>>(directive: ObserveDirective<T>, context: unknown): context is ObserveContext<T> { return true; }\n \n protected observeInput(input: T): Observable<EmittedTypeOf<T>>\n {\n return input as Observable<EmittedTypeOf<T>>;\n }\n}\n","import { Directive } from '@angular/core';\nimport { Observable } from 'rxjs';\n\nimport { ObserveBaseDirective } from './observe-base.directive';\nimport { ObservableMap, EmittedMapOf, ObserveMapContext } from './types/maps';\n\n/**\n * The base class for `*observe` directives combining observables using a map of observable values (i.e. { x: x$, y: y$ }).\n *\n * Emitted values will be available as the implicit context values but will also be spread into the context by key.\n * Meaning, this would work:\n * ```html\n * <div *observeXXX=\"{ x: x$, y: y$ } as result\">{{result.x}}</div>\n * ```\n * \n * And this also:\n * ```\n * <div *observeXXX=\"{ x: x$, y: y$ }; let x = x\">{{x}}</div>\n * ```\n * \n * @export\n * @abstract\n * @class ObserveMapDirective\n * @extends {ObserveBaseDirective<TInput, EmittedMapOf<TInput>, TContext>}\n * @template TInput The type of observable map.\n * @template TContext The the of context the directive will provide to the view.\n */\n@Directive()\nexport abstract class ObserveMapDirective<TInput extends ObservableMap, TContext extends ObserveMapContext<TInput>>\n extends ObserveBaseDirective<TInput, EmittedMapOf<TInput>, TContext>\n{\n protected override createViewContext(data: { value?: EmittedMapOf<TInput> | null , source?: Observable<EmittedMapOf<TInput>> }): TContext\n {\n // Spread the values emitted from the observable to allow `let` microsyntax and directly accessing them\n return { ...super.createViewContext(data), ...data.value };\n }\n}\n","import { Observable, combineLatest } from 'rxjs';\nimport { Directive, Input } from '@angular/core';\n\nimport { ObserveMapDirective } from '../abstraction/observe-map-base.directive';\nimport { ObserveMapContext, EmittedMapOf, ObservableMap } from '../abstraction/types/maps';\n\ntype ObserveLatestContext<T extends ObservableMap> = ObserveMapContext<T> & {\n observeLatest: EmittedMapOf<T>\n};\n\n/**\n * Documentation in {@link ObserveLatestDirective.observeLatest} to allow in-template tooltips.\n * @export\n * @class ObserveLatestDirective\n * @extends {ObserveMapDirective<T, ObserveLatestContext<T>>}\n * @template T The type of observables map received by the directive.\n */\n@Directive({\n selector: '[observeLatest]'\n})\nexport class ObserveLatestDirective<T extends { [key: string]: Observable<unknown> }>\n extends ObserveMapDirective<T, ObserveLatestContext<T>>\n{\n // Seems like for template typechecking to work with a generic type holding `unknown`, the generic type must be flattened\n // and not represented by a new type. When I tried creating an ObservableMap = { ...key: Observable<unknown> } and use it\n // as T extends ObservableMap, the type system failed to infer the inner types of the observables.\n // T extends { ...key: Observable<unknown> } works fine.\n\n protected selector = 'observeLatest';\n\n /**\n * Combines a map of observables using rxjs {@link https://rxjs.dev/api/index/function/combineLatest combineLatest()} and exposes the emitted values to the template.\n * Values are exposed in a map which keys are the same keys as the original observable map and values are\n * the emitted values corresponding to those keys.\n * \n * Emitted values will be available as the implicit context values but will also be spread into the context by key.\n * Meaning, this would work:\n * ```html\n * <div *observeLatest=\"{ x: x$, y: y$ } as result\">{{result.x}}</div>\n * ```\n * \n * And this also:\n * ```\n * <div *observeLatest=\"{ x: x$, y: y$ }; let x = x\">{{x}}</div>\n * ```\n *\n * Any template assigned with the directive will render immediately, and its view context will be updated with the emitted value on\n * each emission. The directive will be responsible for subscribing on init and unsubscribing on destroy.\n * \n * ## Features\n * \n * #### Shared observable\n * The watched observable will automatically be multicasted so that any child observables created by the template will use the same\n * stream.\n * \n * The shared observable can be accessed using the `let source = source` microsyntax.\n * \n * #### Observer events\n * Whenever the observable changes state or emits a value, the corresponding event is emitted: \n * `nextCalled` - A value has been emitted. `$event` will be the emitted value. \n * `errorCalled` - An error has occured in the pipeline. `$event` will be the error. \n * `completeCalled` - The observable has completed. `$event` will be void.\n *\n * > Because of limitations to Angular's Structural Directives, in order to bind the events the desugared syntax must be used.\n * This, for example, **will trigger** the event:\n * > ```html\n * ><ng-template [observe]=\"x$\" let-source=\"source\" (nextCalled)=\"onNext($event)\">\n * > ...\n * ></ng-template>\n * > ```\n * >\n * >This **will NOT trigger** the event:\n * >```html\n * > <div *observe=\"x$; let source = source\" (nextCalled)=\"onNext($event)\">...</div>\n * >```\n */\n @Input() public set observeLatest(value: T) { this.input.next(value); }\n \n static ngTemplateContextGuard<T extends { [key: string]: Observable<unknown> }>(directive: ObserveLatestDirective<T>, context: unknown): context is ObserveLatestContext<T> { return true; }\n \n protected observeInput(input: T): Observable<EmittedMapOf<T>>\n {\n return combineLatest(input) as Observable<EmittedMapOf<T>>;\n }\n}\n","import { Directive } from '@angular/core';\n\nimport { ObserveBaseDirective } from './observe-base.directive';\nimport { ResolvedObserveContext } from './types/general';\nimport { ObservableArray } from './types/arrays';\n\n/**\n * The base class for `*observe` directives combining observables using an array of observable values (i.e. [x$, y$]).\n *\n * @export\n * @abstract\n * @class ObserveArrayDirective\n * @extends {ObserveBaseDirective<TInput, TResolved, TContext>}\n * @template TInput The type of observable array.\n * @template TResolved The type of resolved array.\n * @template TContext The the of context the directive will provide to the view.\n */\n@Directive()\nexport abstract class ObserveArrayDirective<TInput extends ObservableArray, TResolved, TContext extends ResolvedObserveContext<TResolved> = ResolvedObserveContext<TResolved>>\n extends ObserveBaseDirective<TInput, TResolved, TContext>\n{\n\n}\n","import { Observable, concat } from 'rxjs';\nimport { Directive, Input } from '@angular/core';\n\nimport { ObserveArrayDirective } from '../abstraction/observe-array-base.directive';\nimport { ResolvedObserveContext } from '../abstraction/types/general';\nimport { EmittedArrayTypesOf } from '../abstraction/types/arrays';\n\ntype ObserveConcatContext<TInput extends Observable<unknown>[]> = ResolvedObserveContext<EmittedArrayTypesOf<TInput>> & {\n observeConcat: EmittedArrayTypesOf<TInput>\n};\n\n/**\n * Documentation in {@link ObserveConcatDirective.observeConcat} to allow in-template tooltips.\n *\n * @export\n * @class ObserveConcatDirective\n * @extends {ObserveArrayDirective<T, EmittedArrayTypesOf<T>, ObserveConcatContext<T>>}\n * @template T The type of observables tuple received by the directive.\n */\n@Directive({\n selector: '[observeConcat]'\n})\nexport class ObserveConcatDirective<T extends Observable<unknown>[]>\n extends ObserveArrayDirective<T, EmittedArrayTypesOf<T>, ObserveConcatContext<T>>\n{\n protected selector = 'observeConcat';\n\n /**\n * Concats an array of observables using rxjs {@link https://rxjs.dev/api/index/function/concat concat()} and exposes the emitted values to the template.\n * \n * Any template assigned with the directive will render immediately, and its view context will be updated with the emitted value on\n * each emission. The directive will be responsible for subscribing on init and unsubscribing on destroy.\n * \n * ## Features\n * \n * #### Shared observable\n * The watched observable will automatically be multicasted so that any child observables created by the template will use the same\n * stream.\n * \n * The shared observable can be accessed using the `let source = source` microsyntax.\n * \n * #### Observer events\n * Whenever the observable changes state or emits a value, the corresponding event is emitted: \n * `nextCalled` - A value has been emitted. `$event` will be the emitted value. \n * `errorCalled` - An error has occured in the pipeline. `$event` will be the error. \n * `completeCalled` - The observable has completed. `$event` will be void.\n *\n * > Because of limitations to Angular's Structural Directives, in order to bind the events the desugared syntax must be used.\n * This, for example, **will trigger** the event:\n * > ```html\n * ><ng-template [observe]=\"x$\" let-source=\"source\" (nextCalled)=\"onNext($event)\">\n * > ...\n * ></ng-template>\n * > ```\n * >\n * >This **will NOT trigger** the event:\n * >```html\n * > <div *observe=\"x$; let source = source\" (nextCalled)=\"onNext($event)\">...</div>\n * >```\n */\n @Input() public set observeConcat(value: T) { this.input.next(value); }\n\n static ngTemplateContextGuard<T extends Observable<unknown>[]>(directive: ObserveConcatDirective<T>, context: unknown): context is ObserveConcatContext<T> { return true; }\n\n protected observeInput(input: T): Observable<EmittedArrayTypesOf<T>>\n {\n return concat(...input) as Observable<EmittedArrayTypesOf<T>>;\n }\n}\n","import { Observable, forkJoin } from 'rxjs';\nimport { Directive, Input } from '@angular/core';\n\nimport { ObserveMapDirective } from '../abstraction/observe-map-base.directive';\nimport { EmittedMapOf, ObservableMap, ObserveMapContext } from '../abstraction/types/maps';\n\ntype ObserveJoinContext<T extends ObservableMap> = ObserveMapContext<T> & {\n observeJoin: EmittedMapOf<T>\n};\n\n/**\n * Documentation in {@link ObserveJoinDirective.observeJoin} to allow in-template tooltips.\n * @export\n * @class ObserveJoinDirective\n * @extends {ObserveMapDirective<T, ObserveJoinContext<T>>}\n * @template T The type of observables map received by the directive.\n */\n@Directive({\n selector: '[observeJoin]'\n})\nexport class ObserveJoinDirective<T extends { [key: string]: Observable<unknown> }>\n extends ObserveMapDirective<T, ObserveJoinContext<T>>\n{\n protected selector = 'observeJoin';\n\n /**\n * Joins a map of observables using rxjs {@link https://rxjs.dev/api/index/function/forkJoin forkJoin()} and exposes the emitted values to the template.\n * Values are exposed in a map which keys are the same keys as the original observable map and values are\n * the emitted values corresponding to those keys.\n * \n * Emitted values will be available as the implicit context values but will also be spread into the context by key.\n * Meaning, this would work:\n * ```html\n * <div *observeJoin=\"{ x: x$, y: y$ } as result\">{{result.x}}</div>\n * ```\n * \n * And this also:\n * ```\n * <div *observeJoin=\"{ x: x$, y: y$ }; let x = x\">{{x}}</div>\n * ```\n *\n * Any template assigned with the directive will render immediately, and its view context will be updated with the emitted value on\n * each emission. The directive will be responsible for subscribing on init and unsubscribing on destroy.\n * \n * ## Features\n * \n * #### Shared observable\n * The watched observable will automatically be multicasted so that any child observables created by the template will use the same\n * stream.\n * \n * The shared observable can be accessed using the `let source = source` microsyntax.\n * \n * #### Observer events\n * Whenever the observable changes state or emits a value, the corresponding event is emitted: \n * `nextCalled` - A value has been emitted. `$event` will be the emitted value. \n * `errorCalled` - An error has occured in the pipeline. `$event` will be the error. \n * `completeCalled` - The observable has completed. `$event` will be void.\n *\n * > Because of limitations to Angular's Structural Directives, in order to bind the events the desugared syntax must be used.\n * This, for example, **will trigger** the event:\n * > ```html\n * ><ng-template [observe]=\"x$\" let-source=\"source\" (nextCalled)=\"onNext($event)\">\n * > ...\n * ></ng-template>\n * > ```\n * >\n * >This **will NOT trigger** the event:\n * >```html\n * > <div *observe=\"x$; let source = source\" (nextCalled)=\"onNext($event)\">...</div>\n * >```\n */\n @Input() public set observeJoin(value: T) { this.input.next(value); }\n \n static ngTemplateContextGuard<T extends { [key: string]: Observable<unknown> }>(directive: ObserveJoinDirective<T>, context: unknown): context is ObserveJoinContext<T> { return true; }\n \n protected observeInput(input: T): Observable<EmittedMapOf<T>>\n {\n return forkJoin(input) as Observable<EmittedMapOf<T>>;\n }\n}\n","import { Observable, merge } from 'rxjs';\nimport { Directive, Input } from '@angular/core';\n\nimport { ObserveArrayDirective } from '../abstraction/observe-array-base.directive';\nimport { ResolvedObserveContext } from '../abstraction/types/general';\nimport { ObservableArray, EmittedArrayTypesOf } from '../abstraction/types/arrays';\n\ntype ObserveMergeContext<T extends ObservableArray> = ResolvedObserveContext<EmittedArrayTypesOf<T>> & {\n observeMerge: EmittedArrayTypesOf<T>\n};\n\n/**\n * Documentation in {@link ObserveMergeDirective.observeMerge} to allow in-template tooltips.\n *\n * @export\n * @class ObserveMergeDirective\n * @extends {ObserveArrayDirective<T, EmittedArrayTypesOf<T>, ObserveMergeContext<T>>}\n * @template T The type of observables tuple received by the directive.\n */\n@Directive({\n selector: '[observeMerge]'\n})\nexport class ObserveMergeDirective<T extends Observable<unknown>[]>\n extends ObserveArrayDirective<T, EmittedArrayTypesOf<T>, ObserveMergeContext<T>>\n{\n protected selector = 'observeMerge';\n\n /**\n * Combines an array of observables using rxjs {@link https://rxjs.dev/api/index/function/merge merge()} and exposes the emitted values to the template.\n * \n * Any template assigned with the directive will render immediately, and its view context will be updated with the emitted value on\n * each emission. The directive will be responsible for subscribing on init and unsubscribing on destroy.\n * \n * ## Features\n * \n * #### Shared observable\n * The watched observable will automatically be multicasted so that any child observables created by the template will use the same\n * stream.\n * \n * The shared observable can be accessed using the `let source = source` microsyntax.\n * \n * #### Observer events\n * Whenever the observable changes state or emits a value, the corresponding event is emitted: \n * `nextCalled` - A value has been emitted. `$event` will be the emitted value. \n * `errorCalled` - An error has occured in the pipeline. `$event` will be the error. \n * `completeCalled` - The observable has completed. `$event` will be void.\n *\n * > Because of limitations to Angular's Structural Directives, in order to bind the events the desugared syntax must be used.\n * This, for example, **will trigger** the event:\n * > ```html\n * ><ng-template [observe]=\"x$\" let-source=\"source\" (nextCalled)=\"onNext($event)\">\n * > ...\n * ></ng-template>\n * > ```\n * >\n * >This **will NOT trigger** the event:\n * >```html\n * > <div *observe=\"x$; let source = source\" (nextCalled)=\"onNext($event)\">...</div>\n * >```\n */\n @Input() public set observeMerge(value: T) { this.input.next(value); }\n \n static ngTemplateContextGuard<T extends Observable<unknown>[]>(directive: ObserveMergeDirective<T>, context: unknown): context is ObserveMergeContext<T> { return true; }\n \n protected observeInput(input: T): Observable<EmittedArrayTypesOf<T>>\n {\n return merge(...input) as Observable<EmittedArrayTypesOf<T>>;\n }\n}\n","import { NgModule } from '@angular/core';\n\nimport { ObserveDirective } from './directives/observe.directive';\nimport { ObserveLatestDirective } from './directives/observe-latest.directive';\nimport { ObserveConcatDirective } from './directives/observe-concat.directive';\nimport { ObserveJoinDirective } from './directives/observe-join.directive';\nimport { ObserveMergeDirective } from './directives/observe-merge.directive';\n\n/**\n * Provides directives to facilitate in-template subscription management to observables.\n *\n * @export\n * @class ObserveModule\n */\n@NgModule({\n declarations: [\n ObserveDirective,\n ObserveLatestDirective,\n ObserveJoinDirective,\n ObserveMergeDirective,\n ObserveConcatDirective,\n ],\n exports: [\n ObserveDirective,\n ObserveLatestDirective,\n ObserveJoinDirective,\n ObserveMergeDirective,\n ObserveConcatDirective,\n ]\n})\nexport class ObserveModule { }\n","import { DurationAnnotation, DurationUnit, DurationBreakdown } from '../abstraction/types/general';\n\nconst DurationMultipliers: Record<DurationUnit, number> = { ms: 1, s: 1000, m: 60000 };\n\nexport function durationToMs(duration: DurationAnnotation): number\n{\n if (typeof duration === 'number') return duration;\n\n const regex = /(?<value>\\d+(.\\d+)?)(?<units>\\w+)/;\n \n const { value, units } = duration.match(regex)?.groups as { value: string, units: DurationUnit };\n\n return parseInt(value) * (DurationMultipliers[units] || 1);\n}\n\nexport function breakdownTime(showingForMs: number)\n{\n const dummyDate = new Date(showingForMs);\n\n const showingFor: DurationBreakdown = {\n m: dummyDate.getMinutes(),\n s: dummyDate.getSeconds(),\n ms: dummyDate.getMilliseconds(),\n totalMinutes: showingForMs / DurationMultipliers.m,\n totalSeconds: showingForMs / DurationMultipliers.s,\n totalMilliseconds: showingForMs,\n };\n \n return showingFor;\n}\n","import { ObserverName, DurationBreakdown } from './general';\nimport { ViewRenderCommitment } from './view-render-commitment';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { OnObserverBaseDirective } from '../on-observer-base.directive';\n\n/**\n * Represents the context to be fed into a view rendered by an {@link OnObserverBaseDirective `*onObserver`} directive.\n * The context is immutable.\n *\n * @export\n * @class OnObserverContext\n * @template TResolved The type of value emitted by the observable the directive is observing.\n */\nexport class OnObserverContext<TResolved>\n{\n // Indexer allows `this[selector]`. See `selector` constructor argument for details.\n [key: string]: unknown;\n\n /**\n * The resolved value as emitted by the original observable.\n * This allows assigning the emitted value to a variable using `let varName;`.\n * \n * @type {TResolved}\n */\n public readonly $implicit?: TResolved;\n\n /**\n * Creates an instance of OnObserverContext.\n */\n constructor(\n /**\n * The selector of the directive which is creating this context. This will be used to assign the emitted value to a\n * property matching the selector, thus enabling the use of the microsyntax `as` keyword.\n */\n selector : string,\n /** The index of the view rendered by the directive. If the directive is in `'single'` view mode, this will always be 0. */\n public readonly index : number,\n /** The name of the observer call which triggered this context creation. */\n public readonly call : ObserverName,\n /** (Optional) The value, if any, that was emitted by the original observable. */\n value? : TResolved,\n /**\n * (Optional) The time left for the view to be rendered. Only used when {@link OnObserverBaseDirective.showFor `showFor`}\n * is specified in the directive.\n */\n public readonly remaining?: DurationBreakdown,\n /**\n * @deprecated Use {@link OnObserverContext.remaining `remaining`} instead. This will be removed in v6.0.0.\n */\n public readonly showingFor?: DurationBreakdown,\n /**\n * (Optional) The time elapsed from the moment the view was rendered. Only used when {@link OnObserverBaseDirective.showFor `showFor`}\n * is specified in the directive.\n */\n public readonly elapsed?: DurationBreakdown\n )\n {\n this.$implicit = this[selector] = value;\n }\n\n /**\n * Creates a context object for the specified view render commitment.\n *\n * @static\n * @template T The type of value emitted by the observable.\n * @param {string} onObserverSelector The selector of the directive which is creating this context.\n * @param {number} index The index of the view rendered by the directive.\n * @param {ViewRenderCommitment<T>} commitment The view render commitment from which to create the context.\n * @return {OnObserverContext<T>} A context object for the specified view render commitment.\n */\n static fromCommitment<T>(onObserverSelector: string, index: number, { call: { name, value } }: ViewRenderCommitment<T>): OnObserverContext<T>\n {\n return new OnObserverContext(onObserverSelector, index, name, value);\n }\n};\n","import { Notification } from 'rxjs';\nimport { ObserverName } from './general';\n\n/** Maps RxJS materialized notification states to their observer handler name. */\nconst StateNotificationMap: Record<'N' | 'E' | 'C', ObserverName> = {\n N: 'next',\n E: 'error',\n C: 'complete'\n};\n\n/**\n * Represents an intercepted observer call made by a materialized observable.\n *\n * @export\n * @class ObserverCall\n * @template T The type of value emitted by the materialized observable.\n */\nexport class ObserverCall<T>\n{\n /**\n * Creates an instance of ObserverCall.\n */\n /**\n * Creates an instance of ObserverCall.\n * @param {ObserverName} name \n * @param {T} [value] \n */\n private constructor(\n /**\n * The name of the intercepted observer call.\n * \n * @type {ObserverName}\n **/\n public readonly name: ObserverName,\n /**\n * (Optional) The value, if any, emitted by the observable.\n * \n * @type {T}\n * @template T The type of value emitted by the observable.\n */\n public readonly value?: T\n ) { }\n\n /**\n * Creates an ObserverCall representing the resolving state of an observable.\n *\n * @static\n * @template T The type of value emitted by the observable.\n * @return {ObserverCall<T>} \n */\n public static resolving<T>(): ObserverCall<T>\n {\n return new ObserverCall<T>('resolving');\n }\n\n /**\n * Extracts the data from a materialized observable notification and creates an `ObserverCall` representation for it.\n *\n * @static\n * @template T The type of value emitted by the observable.\n * @param {Notification<T>} notification The notification received from the materialized observable.\n * @return {ObserverCall<T>} An `ObserverCall` representing the notification and its data.\n */\n public static fromNotification<T>({ kind, value, error }: Notification<T>): ObserverCall<T>\n {\n return new ObserverCall(StateNotificationMap[kind], error || value);\n }\n}\n","import { RenderedView } from './general';\nimport { ObserverCall } from './observer-call';\n\n/**\n * Represents a state describing a view to be rendered or a view already rendered. The state holds the parameterization indicating\n * when a view should be rendered and destroyed, and also holds the rendered view if there is any.\n * \n * States are created every time an {@link ObserverCall} is emitted. They are used by {@link OnObserverBaseDirective `*onObserver`} directives\n * to understand how a view should be rendered and initiate a commitment to render flow.\n *\n * The state is immutable.\n * \n * @export\n * @class ViewRenderState\n * @template T The type of value emitted by the observable.\n */\nexport class ViewRenderCommitment<T>\n{\n /**\n * Creates an instance of ViewRenderState.\n */\n private constructor(\n /** The id of the commitment to render. Allows identifying the state within a state map. */\n public readonly commitmentId: string,\n /** The intercepted call which triggered the commitment to render. */\n public readonly call : ObserverCall<T>,\n /** The duration (in milliseconds) specified as the delay before rendering the view. */\n public readonly showAfter : number,\n /** The duration (in milliseconds) specified as the delay before destroying the view. */\n public readonly showFor : number,\n /**\n * The timestamp at which the view should be rendered. This value is manually specified and not calculated automatically using\n * `Date.now()` upon state creation because states might be recreated before the {@link showAfter} delay is finished.\n * When a state is recreated, the time that has already passed should be considered, thus the previous value should be used. \n */\n public readonly renderAt : number,\n /** (Optional) The rendered view. Will be provided only after the recreation of the state once the delay has passed. */\n public readonly view? : RenderedView<T> | null,\n ) { }\n\n /**\n * The timestamp at which the view should be destroyed.\n *\n * @readonly\n * @type {number}\n */\n public get destroyAt (): number | undefined { return this.showFor ? this.renderAt + this.showFor : undefined; }\n /**\n * `true` if the state represents a view that is currently rendered; otherwise `false`.\n *\n * @readonly\n * @type {boolean}\n */\n public get isRendered(): boolean { return !!this.view; }\n /**\n * `true` if the state represents a view that should be auto-destroyed; otherwise `false`.\n *\n * @readonly\n * @type {boolean}\n */\n public get autoDestroys(): boolean { return !!this.destroyAt; }\n\n /**\n * Creates a new state representing a new, fresh, commitment to render.\n * Should be used in multi-view mode, or in single-view mode when there is nothing rendered.\n *\n * @static\n * @template T The type of value emitted by the observable.\n * @param {ObserverCall<T>} call The intercepted call which triggered this state.\n * @param {number} showAfter The duration (in milliseconds) to wait before rendering the view.\n * @param {number} showFor The duration (in milliseconds) to wait before destroying the view.\n * @return {ViewRenderCommitment<T>} A new state representing fresh commitment to render.\n */\n static create<T>(call: ObserverCall<T>, showAfter: number, showFor: number): ViewRenderCommitment<T>\n {\n const now = Date.now();\n\n return new ViewRenderCommitment(now.toString(), call, showAfter, showFor, now + showAfter);\n }\n\n /**\n * Clones the state and replaces the call which triggered it.\n * Should be used in single-view mode when the view is already rendered and a new call is intercepted to make sure the\n * latest emitted value is specified.\n *\n * @static\n * @template T The type of value emitted by the observable.\n * @param {ViewRenderCommitment<T>} state The state to clone.\n * @param {ObserverCall<T>} call The intercepted call which triggered this state.\n