{"version":3,"file":"ngx-campaigns.mjs","sources":["../../../projects/ngx-campaigns/src/lib/injection-tokens/storage-key.ts","../../../projects/ngx-campaigns/src/lib/services/campaign.ts","../../../projects/ngx-campaigns/src/lib/functions/pick-one.ts","../../../projects/ngx-campaigns/src/ngx-campaigns.ts"],"sourcesContent":["import { InjectionToken, isDevMode } from '@angular/core';\r\n\r\n/**\r\n * @description Configures the injector to return a key used to access and write to campaign storage.\r\n * @see {@linkcode InjectionToken}\r\n * @default \"campaigns\"\r\n */\r\nexport const storageKey = new InjectionToken<string>(\r\n\tisDevMode() ? 'storageKey' : '',\r\n\t{ factory: () => 'campaigns' },\r\n);\r\n","import { inject, Injectable } from '@angular/core';\r\nimport { InteropObservable, Subscribable } from 'rxjs';\r\nimport { storageKey } from '../injection-tokens/storage-key';\r\n\r\n/**\r\n * @description An {@linkcode InteropObservable} (see examples) {@linkcode Map|map} of key/value pairs.\r\n * @usage A/B campaign tracking.\r\n * @example\r\n * import { inject } from '@angular/core';\r\n * import { pickOne, CampaignService } from \"ngx-campaigns\";\r\n *\r\n * const campaignService = inject(CampaignService);\r\n * const key = \"test\";\r\n *\r\n * // Role the dice if the key does not exist.\r\n * if(!campaignService.has(key)) campaignService.set(key, pickOne([\"default\", \"A\"]));\r\n * const value = campaignService.get(key)!;\r\n * @example\r\n * import { inject } from '@angular/core';\r\n * import { Observable, from, InteropObservable } from \"rxjs\";\r\n * import { CampaignService } from \"ngx-campaigns\";\r\n *\r\n * const campaignService = inject(CampaignService);\r\n * const campaignService$ = from<InteropObservable<CampaignService>>(campaignService);\r\n * @example\r\n * import { Observable, from, InteropObservable } from \"rxjs\";\r\n * import { CampaignService } from \"ngx-campaigns\";\r\n *\r\n * const campaignService = inject(CampaignService);\r\n * const changeDetected$ = from<InteropObservable<CampaignService>>(campaignService).pipe(skip(1));\r\n * @example\r\n * import { inject } from '@angular/core';\r\n * import { distinctUntilChanged, Observable, from, InteropObservable } from \"rxjs\";\r\n * import { CampaignService } from \"ngx-campaigns\";\r\n *\r\n * const campaignService = inject(CampaignService);\r\n * const key = \"test\";\r\n * const value$ = from<InteropObservable<CampaignService>>(campaignService).pipe(\r\n * \t\tmap((service) => service.get(key)),\r\n * \t\tdistinctUntilChanged(),\r\n * );\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class CampaignService\r\n\timplements Map<string, string>, InteropObservable<CampaignService>\r\n{\r\n\treadonly [Symbol.toStringTag] = CampaignService.name;\r\n\treadonly #storageKey = inject(storageKey);\r\n\treadonly #eventTarget = new EventTarget();\r\n\r\n\tget size(): number {\r\n\t\treturn new Map(this.#stored).size;\r\n\t}\r\n\r\n\tget #stored(): ReadonlyArray<[key: string, value: string]> {\r\n\t\ttry {\r\n\t\t\t// Local storage is not available in some environments or when quotas are exceeded.\r\n\t\t\treturn JSON.parse(\r\n\t\t\t\tglobalThis.localStorage.getItem(this.#storageKey) || '[]',\r\n\t\t\t) as ReadonlyArray<[key: string, value: string]>;\r\n\t\t} catch {\r\n\t\t\treturn [];\r\n\t\t}\r\n\t}\r\n\r\n\tset #stored(iterable: Iterable<[key: string, value: string]>) {\r\n\t\ttry {\r\n\t\t\t// Local storage is not available in some environments or when quotas are exceeded.\r\n\t\t\tglobalThis.localStorage.setItem(\r\n\t\t\t\tthis.#storageKey,\r\n\t\t\t\tJSON.stringify(Array.from(iterable)),\r\n\t\t\t);\r\n\t\t\t// Synthetic events, like the one below, are handled synchronously unlike their DOM-based counterparts.\r\n\t\t\tthis.#eventTarget.dispatchEvent(new Event('changeDetected'));\r\n\t\t} catch {\r\n\t\t\t// Fail silently.\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * @deprecated This requires polly-filling, which we are not doing, and it will not be supported in upcoming versions of RxJS. Use `\"@@observable\"` instead.\r\n\t * @aliases `\"@@observable\"`\r\n\t */\r\n\t// This only exists to satisfy the InteropObservable interface even though internally RxJS allows using \"@@observable\" as per their docs recommendation.\r\n\t// We have unit tests to ensure this is working as expected.\r\n\t[Symbol.observable](): Subscribable<this> {\r\n\t\treturn this['@@observable']();\r\n\t}\r\n\r\n\t/**\r\n\t * @usage Observable interop. See example.\r\n\t * @returns A {@linkcode Subscribable} that emits the {@linkcode CampaignService|service} instance on subscription and when key/value changes are detected.\r\n\t * @alias `Symbol.observable`\r\n\t * @example\r\n\t * import { skip, Observable, from, InteropObservable } from \"rxjs\";\r\n\t *\r\n\t * const campaignService = inject(CampaignService);\r\n\t * const campaignService$ = from<InteropObservable<CampaignService>>(campaignService);\r\n\t * const changeDetected$ = campaignService$.pipe(skip(1));\r\n\t */\r\n\t['@@observable'](): Subscribable<this> {\r\n\t\treturn {\r\n\t\t\tsubscribe: (observer) => {\r\n\t\t\t\tconst controller = new AbortController();\r\n\t\t\t\tconst options = { signal: controller.signal } as const;\r\n\r\n\t\t\t\tthis.#eventTarget.addEventListener(\r\n\t\t\t\t\t'changeDetected',\r\n\t\t\t\t\t() => observer.next?.(this),\r\n\t\t\t\t\toptions,\r\n\t\t\t\t);\r\n\r\n\t\t\t\tglobalThis.addEventListener(\r\n\t\t\t\t\t'storage',\r\n\t\t\t\t\t({ storageArea, key }) => {\r\n\t\t\t\t\t\tif (\r\n\t\t\t\t\t\t\tstorageArea === globalThis.localStorage &&\r\n\t\t\t\t\t\t\tkey === this.#storageKey\r\n\t\t\t\t\t\t) {\r\n\t\t\t\t\t\t\t// In this case, observer.next?.(this) is more efficient than dispatching a 'changeDetected' event.\r\n\t\t\t\t\t\t\tobserver.next?.(this);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t},\r\n\t\t\t\t\toptions,\r\n\t\t\t\t);\r\n\r\n\t\t\t\t// Emit initial value after event listeners are set up.\r\n\t\t\t\tobserver.next?.(this);\r\n\r\n\t\t\t\treturn { unsubscribe: () => controller.abort('unsubscribe') };\r\n\t\t\t},\r\n\t\t};\r\n\t}\r\n\r\n\t[Symbol.iterator](): IterableIterator<[key: string, value: string]> {\r\n\t\treturn new Map(this.#stored)[Symbol.iterator]();\r\n\t}\r\n\r\n\tentries(): IterableIterator<[key: string, value: string]> {\r\n\t\treturn new Map(this.#stored).entries();\r\n\t}\r\n\r\n\tvalues(): IterableIterator<string> {\r\n\t\treturn new Map(this.#stored).values();\r\n\t}\r\n\r\n\tkeys(): IterableIterator<string> {\r\n\t\treturn new Map(this.#stored).keys();\r\n\t}\r\n\r\n\thas(key: string): boolean {\r\n\t\treturn new Map(this.#stored).has(key);\r\n\t}\r\n\r\n\tget(key: string): string | undefined {\r\n\t\treturn new Map(this.#stored).get(key);\r\n\t}\r\n\r\n\tforEach(\r\n\t\tcallbackfn: (value: string, key: string, service: this) => void,\r\n\t\tthisArg?: unknown,\r\n\t): void {\r\n\t\treturn new Map(this.#stored).forEach(\r\n\t\t\t(value, key) => callbackfn(value, key, this),\r\n\t\t\tthisArg,\r\n\t\t);\r\n\t}\r\n\r\n\tset(key: string, value: string): this {\r\n\t\tconst map = new Map(this.#stored);\r\n\t\tconst settable = !map.has(key) || map.get(key) !== value;\r\n\t\tif (settable) this.#stored = map.set(key, value);\r\n\t\treturn this;\r\n\t}\r\n\r\n\tdelete(key: string): boolean {\r\n\t\tconst map = new Map(this.#stored);\r\n\t\tconst deleted = map.delete(key);\r\n\t\tif (deleted) this.#stored = map;\r\n\t\treturn deleted;\r\n\t}\r\n\r\n\tclear(): void {\r\n\t\tif (this.size) this.#stored = new Map();\r\n\t}\r\n}\r\n","import { isDevMode } from '@angular/core';\r\n\r\n/**\r\n * @description Random value picker.\r\n * @usage Picking a random campaign value from a, potentially weighted, list of options.\r\n */\r\nexport function pickOne<Option>(options: ReadonlyArray<Option>): Option;\r\nexport function pickOne<Options extends ReadonlyArray<unknown>>(\r\n\toptions: Options,\r\n\tweights?: Readonly<{ [Key in keyof Options]: 0 }>,\r\n): undefined;\r\nexport function pickOne<Options extends ReadonlyArray<unknown>>(\r\n\toptions: Options,\r\n\tweights?: Readonly<{ [Key in keyof Options]: number }>,\r\n): Options[number];\r\nexport function pickOne(\r\n\toptions: ReadonlyArray<unknown>,\r\n\tweights: ReadonlyArray<number> = options.map(() => 1),\r\n): unknown {\r\n\tif (isDevMode()) assertValidWeights(options, weights);\r\n\tlet thresholds: ReadonlyArray<number> = [];\r\n\tconst weightsSum = weights.reduce((sum, weight) => {\r\n\t\tconst threshold = sum + weight;\r\n\t\tthresholds = [...thresholds, threshold];\r\n\t\treturn threshold;\r\n\t}, 0);\r\n\tconst selection = Math.floor(Math.random() * weightsSum);\r\n\treturn options.find((_, index) => {\r\n\t\tif (weights.at(index) === 0) return false;\r\n\t\treturn selection < thresholds.at(index)!;\r\n\t});\r\n}\r\n\r\nfunction assertValidWeights(\r\n\toptions: ReadonlyArray<unknown>,\r\n\tweights: ReadonlyArray<number>,\r\n): void {\r\n\tassertWeightsLength(options, weights);\r\n\tassertFiniteWeights(weights);\r\n}\r\n\r\nfunction assertFiniteWeights(weights: ReadonlyArray<number>): void {\r\n\tif (weights.every((weight) => Number.isFinite(weight))) return;\r\n\tthrow new RangeError('All weights must be finite numbers.');\r\n}\r\n\r\nfunction assertWeightsLength(\r\n\toptions: ReadonlyArray<unknown>,\r\n\tweights: ReadonlyArray<number>,\r\n): void {\r\n\tif (options.length === weights.length) return;\r\n\tthrow new RangeError(\r\n\t\t`'weights' must have the same length as 'options'. Expected: ${options.length}. Actual: ${weights.length}.`,\r\n\t);\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}