@lenne.tech/ng-base 22.0.4 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CLAUDE.md +5 -0
  2. package/README.md +34 -7
  3. package/fesm2022/lenne.tech-ng-base-ai.mjs +27 -27
  4. package/fesm2022/lenne.tech-ng-base-auth-provide.mjs +15 -14
  5. package/fesm2022/lenne.tech-ng-base-auth-provide.mjs.map +1 -1
  6. package/fesm2022/lenne.tech-ng-base-auth.mjs +17 -13
  7. package/fesm2022/lenne.tech-ng-base-auth.mjs.map +1 -1
  8. package/fesm2022/{lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs → lenne.tech-ng-base-bug-bug-report-dialog-f6VNOWnW.mjs} +11 -11
  9. package/fesm2022/{lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs.map → lenne.tech-ng-base-bug-bug-report-dialog-f6VNOWnW.mjs.map} +1 -1
  10. package/fesm2022/{lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs → lenne.tech-ng-base-bug-bug-report-service-D6slR3GD.mjs} +4 -4
  11. package/fesm2022/{lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs.map → lenne.tech-ng-base-bug-bug-report-service-D6slR3GD.mjs.map} +1 -1
  12. package/fesm2022/lenne.tech-ng-base-bug.mjs +5 -5
  13. package/fesm2022/lenne.tech-ng-base-files-ui.mjs +3 -3
  14. package/fesm2022/lenne.tech-ng-base-files.mjs +8 -4
  15. package/fesm2022/lenne.tech-ng-base-files.mjs.map +1 -1
  16. package/fesm2022/lenne.tech-ng-base-share.mjs +3 -3
  17. package/fesm2022/lenne.tech-ng-base-ui-provide.mjs +3 -3
  18. package/fesm2022/lenne.tech-ng-base-ui.mjs +1206 -1216
  19. package/fesm2022/lenne.tech-ng-base-ui.mjs.map +1 -1
  20. package/fesm2022/lenne.tech-ng-base-utils.mjs +9 -9
  21. package/fesm2022/lenne.tech-ng-base.mjs +27 -24
  22. package/fesm2022/lenne.tech-ng-base.mjs.map +1 -1
  23. package/i18n/auth/de.json +1 -3
  24. package/i18n/auth/en.json +1 -3
  25. package/i18n/de.json +1 -2
  26. package/i18n/en.json +1 -2
  27. package/i18n/ui/de.json +0 -5
  28. package/i18n/ui/en.json +0 -5
  29. package/package.json +5 -1
  30. package/types/lenne.tech-ng-base-auth-provide.d.ts +2 -3
  31. package/types/lenne.tech-ng-base-auth.d.ts +4 -1
  32. package/types/lenne.tech-ng-base-files.d.ts +5 -1
  33. package/types/lenne.tech-ng-base.d.ts +8 -1
@@ -1 +1 @@
1
- {"version":3,"file":"lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs","names":["LT_BUG_INPUT_OPTIONS"],"sources":["../../../projects/ng-base/bug/src/lib/bug-options.ts","../../../projects/ng-base/bug/src/lib/i18n/load.ts","../../../projects/ng-base/bug/src/lib/screenshot.ts","../../../projects/ng-base/bug/src/lib/bug-report-service.ts"],"sourcesContent":["import { inject, InjectionToken } from '@angular/core';\nimport { LT_RUNTIME_CONFIG, mergeLtFeatureOptions } from '@lenne.tech/ng-base';\nimport { LT_BUG_INPUT_OPTIONS, type LtBugOptions, type LtResolvedBugOptions } from '@lenne.tech/ng-base/bug/provide';\n\n/** The runtime decision lives in `@lenne.tech/ng-base/bug/provide` (light entry for `app.config.ts` and the shell). */\nexport { isLtBugActive, LT_BUG_ACTIVE, LT_BUG_DEV_MODE, LT_BUG_INPUT_OPTIONS } from '@lenne.tech/ng-base/bug/provide';\n\n/** Default endpoint, the same as `@lenne.tech/bug.lt` (outside `/api`, so an API proxy does not swallow it). */\nexport const LT_BUG_DEFAULT_ENDPOINT = '/_bug-lt/report';\n\n/** Defaults of {@link LtBugOptions} (the module defaults of `@lenne.tech/bug.lt` 1.6.9, except the button color). */\nexport const LT_BUG_DEFAULTS: LtResolvedBugOptions = {\n autoOpenOnError: false,\n autoShow: true,\n // bug.lt: #ef4444; white text on it has 3.8:1 contrast (WCAG AA needs 4.5:1), red-600 has 4.8:1.\n buttonColor: '#dc2626',\n enableBrowserInfo: true,\n enableConsoleLogs: true,\n enableErrorBoundary: true,\n enableNetworkRequests: true,\n enableScreenshot: true,\n enableUserJourney: true,\n endpoint: LT_BUG_DEFAULT_ENDPOINT,\n maxConsoleLogs: 50,\n maxNetworkRequests: 50,\n position: 'bottom-right',\n theme: 'auto',\n userJourney: {\n captureClicks: true,\n captureErrors: true,\n captureFormInteractions: true,\n captureHover: false,\n captureInputChanges: false,\n captureInputValues: false,\n captureKeyboard: true,\n captureModalEvents: true,\n captureNavigation: true,\n captureScroll: false,\n enabled: true,\n maxEvents: 50,\n throttleRate: 100,\n },\n};\n\n/** Resolved options: defaults ← `provideLtBug(options)` ← runtime `lt.bug` (`LT_RUNTIME_CONFIG`). */\nexport const LT_BUG_OPTIONS = new InjectionToken<LtResolvedBugOptions>('LT_BUG_OPTIONS', {\n factory: () => resolveLtBugOptions(inject(LT_BUG_INPUT_OPTIONS) ?? {}, inject(LT_RUNTIME_CONFIG, { optional: true })?.lt?.['bug']),\n});\n\n/** Merge options onto the defaults; `userJourney` is merged per key, `undefined` never overrides, sizes are clamped. */\nexport function resolveLtBugOptions(...overrides: (LtBugOptions | Record<string, unknown> | null | undefined)[]): LtResolvedBugOptions {\n const merged = mergeLtFeatureOptions<LtResolvedBugOptions>(LT_BUG_DEFAULTS, ...overrides);\n return {\n ...merged,\n endpoint: typeof merged.endpoint === 'string' && merged.endpoint.startsWith('/') ? merged.endpoint : LT_BUG_DEFAULT_ENDPOINT,\n maxConsoleLogs: clampSize(merged.maxConsoleLogs, LT_BUG_DEFAULTS.maxConsoleLogs),\n maxNetworkRequests: clampSize(merged.maxNetworkRequests, LT_BUG_DEFAULTS.maxNetworkRequests),\n userJourney: {\n ...merged.userJourney,\n maxEvents: clampSize(merged.userJourney.maxEvents, LT_BUG_DEFAULTS.userJourney.maxEvents),\n throttleRate: Math.max(0, Number(merged.userJourney.throttleRate) || 0),\n },\n };\n}\n\n/** Ring buffer sizes are whole numbers between 1 and 1000. */\nfunction clampSize(value: unknown, fallback: number): number {\n const number = Math.floor(Number(value));\n return Number.isFinite(number) && number > 0 ? Math.min(number, 1000) : fallback;\n}\n","import type { LtI18nMessages, LtI18nService } from '@lenne.tech/ng-base';\n\n/**\n * The only texts needed before the dialog chunk is loaded: the floating button's label and its busy text. Kept equal to\n * `de.json` / `en.json` (checked by `i18n.spec.ts`).\n */\nexport const LT_BUG_EAGER_MESSAGES: Readonly<Record<'de' | 'en', LtI18nMessages>> = {\n de: { lt: { bug: { button: 'Fehler melden', capturing: 'Screenshot wird erstellt …' } } },\n en: { lt: { bug: { button: 'Report a bug', capturing: 'Taking screenshot …' } } },\n};\n\nconst loaded = new WeakSet<LtI18nService>();\nlet messagesModule: Promise<typeof import('./messages')> | null = null;\n\n/** Register the button texts (synchronously; texts the app configured keep priority). */\nexport function registerLtBugEagerMessages(i18n: LtI18nService, appMessages: Record<string, LtI18nMessages> = {}): void {\n for (const [locale, messages] of Object.entries(LT_BUG_EAGER_MESSAGES)) {\n const app = appMessages[locale];\n i18n.registerMessages(locale, app ? withAppTexts(messages, app) : messages);\n }\n}\n\n/** Load (lazy chunk) and register every `lt.bug.*` text once per `LtI18nService` instance. */\nexport async function loadLtBugMessages(i18n: LtI18nService, appMessages: Record<string, LtI18nMessages> = {}): Promise<void> {\n if (loaded.has(i18n)) {\n return;\n }\n messagesModule ??= import('./messages');\n try {\n (await messagesModule).registerLtBugMessages(i18n, appMessages);\n loaded.add(i18n);\n } catch {\n // Chunk failed to load: keep the keys, retry on the next call.\n messagesModule = null;\n }\n}\n\nfunction withAppTexts(base: LtI18nMessages, app: LtI18nMessages): LtI18nMessages {\n const result: LtI18nMessages = { ...base };\n for (const [key, value] of Object.entries(app)) {\n const current = result[key];\n if (current === undefined) {\n continue;\n }\n result[key] = typeof value === 'object' && typeof current === 'object' ? withAppTexts(current, value) : value;\n }\n return result;\n}\n","import { InjectionToken } from '@angular/core';\n\nimport type { LtBugTheme } from './bug-types';\nimport { LT_BUG_UI_ATTRIBUTE } from './constants';\n\n/** The part of `modern-screenshot` the reporter uses (structural, so the peer stays optional). */\nexport interface LtScreenshotLibrary {\n domToPng(node: Node, options?: Record<string, unknown>): Promise<string>;\n}\n\nlet library: Promise<LtScreenshotLibrary | null> | null = null;\n\n/** Load the optional peer `modern-screenshot` once; resolves `null` when it is not installed. */\nexport function loadLtScreenshotLibrary(): Promise<LtScreenshotLibrary | null> {\n library ??= import('modern-screenshot').then(\n (module) => {\n const candidate = module as unknown as Partial<LtScreenshotLibrary> & { default?: Partial<LtScreenshotLibrary> };\n const resolved = typeof candidate.domToPng === 'function' ? candidate : candidate.default;\n return typeof resolved?.domToPng === 'function' ? (resolved as LtScreenshotLibrary) : null;\n },\n () => null,\n );\n return library;\n}\n\n/**\n * Loader of the screenshot library (default: lazy `import('modern-screenshot')`, `null` when the optional peer is\n * missing: the screenshot feature is then hidden). Replace it in tests.\n */\nexport const LT_BUG_SCREENSHOT_LOADER = new InjectionToken<() => Promise<LtScreenshotLibrary | null>>('LT_BUG_SCREENSHOT_LOADER', {\n factory: () => loadLtScreenshotLibrary,\n});\n\n/**\n * PNG data URL of the visible viewport (current scroll position, device pixel ratio up to 2), without the reporter's\n * own UI. The background follows `theme` (`auto`: `html.dark` / `html.light`, then `color-scheme`, then the system).\n */\nexport function captureLtBugScreenshot(lib: LtScreenshotLibrary, view: Window, theme: LtBugTheme): Promise<string> {\n const doc = view.document;\n const dark = theme === 'dark' || (theme === 'auto' && isLtDarkColorMode(view));\n return lib.domToPng(doc.documentElement, {\n backgroundColor: dark ? '#0a0a0a' : '#ffffff',\n features: { restoreScrollPosition: true },\n fetch: { bypassingCache: true, requestInit: { mode: 'cors' } },\n filter: (node: Node) => !(node.nodeType === 1 && (node as Element).hasAttribute?.(LT_BUG_UI_ATTRIBUTE)),\n height: view.innerHeight,\n scale: Math.min(view.devicePixelRatio || 1, 2),\n style: { transform: 'none', transformOrigin: 'top left' },\n width: view.innerWidth,\n });\n}\n\n/** Whether the page currently renders dark (`html.dark`, `html.light`, computed `color-scheme`, system preference). */\nexport function isLtDarkColorMode(view: Window): boolean {\n const root = view.document?.documentElement;\n if (root?.classList.contains('dark')) {\n return true;\n }\n if (root?.classList.contains('light')) {\n return false;\n }\n const scheme = root && typeof view.getComputedStyle === 'function' ? view.getComputedStyle(root).colorScheme : '';\n if (scheme === 'dark' || scheme === 'light') {\n return scheme === 'dark';\n }\n return !!view.matchMedia?.('(prefers-color-scheme: dark)').matches;\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, ErrorHandler, inject, Injector, PLATFORM_ID, Service, signal } from '@angular/core';\nimport { Router, ROUTES } from '@angular/router';\nimport { LT_I18N_OPTIONS, LT_RUNTIME_CONFIG, LtI18nService, LtToastService } from '@lenne.tech/ng-base';\n\nimport { isLtBugActive, LT_BUG_DEV_MODE, LT_BUG_INPUT_OPTIONS, LT_BUG_OPTIONS } from './bug-options';\nimport type {\n LtBrowserInfo,\n LtBugAttachment,\n LtBugErrorInfo,\n LtBugReportData,\n LtBugReportResponse,\n LtConsoleLogEntry,\n LtNetworkRequestEntry,\n LtUserInteractionEvent,\n LtUserInteractionMetadata,\n LtUserInteractionType,\n} from './bug-types';\nimport type { LtBugCaptureEngine } from './capture-engine';\nimport { loadLtBugMessages } from './i18n/load';\nimport type { LtBugReportFormValue, LtBugReportIncludes } from './report';\nimport { captureLtBugScreenshot, LT_BUG_SCREENSHOT_LOADER } from './screenshot';\n\n/** Error of {@link LtBugReportService.submit}; `message` is the translated text shown to the user. */\nexport class LtBugSubmitError extends Error {\n constructor(\n message: string,\n readonly status: number,\n ) {\n super(message);\n this.name = 'LtBugSubmitError';\n }\n}\n\n/**\n * State and actions of the bug reporter, the counterpart of `useBugReport()` of `@lenne.tech/bug.lt`.\n *\n * `<lt-bug-report>` calls {@link start} when it is created: when the reporter is {@link active} it loads the capture engine\n * (a chunk of its own) and installs the hooks (console, `fetch`/`XMLHttpRequest`, user journey, `ErrorHandler`);\n * otherwise it loads and installs nothing. `<lt-bug-report>` renders the button and opens the dialog for {@link open}\n * requests.\n */\n@Service()\nexport class LtBugReportService {\n /** Resolved options. */\n readonly options = inject(LT_BUG_OPTIONS);\n\n private readonly document = inject(DOCUMENT);\n private readonly injector = inject(Injector);\n private readonly i18n = inject(LtI18nService);\n private readonly i18nOptions = inject(LT_I18N_OPTIONS);\n private readonly toast = inject(LtToastService);\n private readonly screenshotLoader = inject(LT_BUG_SCREENSHOT_LOADER);\n\n /**\n * Whether the reporter runs: `provideLtBug()` is present, the code runs in the browser and the runtime decision says\n * on (`enabled`, else app env not `production`, else dev mode only). Decided once at startup.\n */\n readonly active: boolean =\n inject(LT_BUG_INPUT_OPTIONS) !== null &&\n isPlatformBrowser(inject(PLATFORM_ID)) &&\n isLtBugActive(this.options, inject(LT_RUNTIME_CONFIG, { optional: true })?.appEnv, inject(LT_BUG_DEV_MODE));\n\n /** The dialog is open (or about to open). */\n readonly isOpen = signal(false);\n /** A report is being sent. */\n readonly isSubmitting = signal(false);\n /** Message of the last failed submission (shown in the dialog). */\n readonly error = signal<string | null>(null);\n /** The last captured error (pre-fills the form). */\n readonly lastError = signal<LtBugErrorInfo | null>(null);\n /** Screenshot taken before the dialog opened (data URL). */\n readonly screenshot = signal<string | null>(null);\n /** A screenshot is being taken. */\n readonly capturingScreenshot = signal(false);\n /** Whether `modern-screenshot` could be loaded (`null` until the first attempt). */\n readonly screenshotAvailable = signal<boolean | null>(null);\n /** An {@link open} request waits for `<lt-bug-report>` (read and reset by the component). */\n readonly openRequested = signal(false);\n\n private engine: LtBugCaptureEngine | null = null;\n private engineLoad: Promise<LtBugCaptureEngine | null> = Promise.resolve(null);\n private pendingErrors: { error: unknown; source?: string }[] = [];\n private started = false;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.stop());\n }\n\n /** Whether the capture hooks are installed. */\n get running(): boolean {\n return this.engine !== null;\n }\n\n /**\n * Load the capture engine and install the hooks for the enabled features (idempotent; does nothing while not\n * {@link active}). Errors reported before the engine is ready are handed over once it is.\n */\n start(): void {\n const view = this.document.defaultView;\n if (!this.active || this.started || !view) {\n return;\n }\n this.started = true;\n const router = this.injector.get(ROUTES, null) ? this.injector.get(Router, null) : null;\n const errorHandler = this.injector.get(ErrorHandler, null);\n this.engineLoad = import('./capture-engine').then(\n ({ startLtBugCapture }) => {\n if (!this.started) {\n return null;\n }\n this.engine = startLtBugCapture({ baseUri: this.document.baseURI, errorHandler, onError: (info) => this.onCapturedError(info), options: this.options, router, view });\n for (const { error, source } of this.pendingErrors.splice(0)) {\n this.engine.reportError(error, source);\n }\n return this.engine;\n },\n (error: unknown) => {\n console.warn('[@lenne.tech/ng-base/bug] Could not load the capture engine', error);\n return null;\n },\n );\n }\n\n /** Resolves once {@link start} has installed the hooks (`false` when inactive or the engine failed to load). */\n async whenReady(): Promise<boolean> {\n return (await this.engineLoad) !== null;\n }\n\n /** Remove every capture hook and restore the patched globals. */\n stop(): void {\n this.engine?.stop();\n this.engine = null;\n this.engineLoad = Promise.resolve(null);\n this.pendingErrors = [];\n this.started = false;\n }\n\n /**\n * Ask `<lt-bug-report>` to take a screenshot and open the dialog (also when the floating button is hidden with\n * `autoShow: false`); `error` pre-fills the form. Ignored while the reporter is not active.\n */\n open(error?: LtBugErrorInfo): void {\n if (!this.active) {\n return;\n }\n if (error) {\n this.lastError.set(error);\n }\n this.error.set(null);\n this.openRequested.set(true);\n }\n\n /** Report an error by hand (e.g. from a `catch` block): it becomes {@link lastError} and may auto-open the dialog. */\n reportError(error: unknown, source?: string): void {\n if (!this.active || !this.started) {\n return;\n }\n if (this.engine) {\n this.engine.reportError(error, source);\n } else {\n this.pendingErrors.push({ error, source });\n }\n }\n\n /** Record a custom user journey event (no-op while the journey is off). */\n recordInteraction(type: LtUserInteractionType, target: string, metadata?: LtUserInteractionMetadata): void {\n this.engine?.record(type, target, metadata);\n }\n\n /** The captured console entries (newest `maxConsoleLogs`). */\n getConsoleLogs(): LtConsoleLogEntry[] {\n return this.engine?.consoleLogs() ?? [];\n }\n\n /** The captured network requests (newest `maxNetworkRequests`). */\n getNetworkRequests(): LtNetworkRequestEntry[] {\n return this.engine?.networkRequests() ?? [];\n }\n\n /** The captured user journey (newest `userJourney.maxEvents`). */\n getUserJourney(): LtUserInteractionEvent[] {\n return this.engine?.userJourney() ?? [];\n }\n\n /** Browser details of the current page (`null` while inactive or outside the browser). */\n getBrowserInfo(): LtBrowserInfo | null {\n return this.engine?.browserInfo() ?? null;\n }\n\n /**\n * Take a viewport screenshot into {@link screenshot} (without the reporter's UI; network capture paused meanwhile).\n * Resolves `null` when screenshots are disabled, `modern-screenshot` is missing or the capture failed.\n */\n async captureScreenshot(): Promise<string | null> {\n const view = this.document.defaultView;\n if (!this.active || !this.options.enableScreenshot || !view) {\n return null;\n }\n this.capturingScreenshot.set(true);\n this.engine?.setNetworkPaused(true);\n try {\n const library = await this.screenshotLoader();\n this.screenshotAvailable.set(!!library);\n const dataUrl = library ? await captureLtBugScreenshot(library, view, this.options.theme) : null;\n this.screenshot.set(dataUrl);\n return dataUrl;\n } catch (error) {\n console.warn('[@lenne.tech/ng-base/bug] Screenshot capture failed', error);\n this.screenshot.set(null);\n return null;\n } finally {\n this.capturingScreenshot.set(false);\n this.engine?.setNetworkPaused(false);\n }\n }\n\n /** Assemble the payload from the form, the attachments and the collected data the user kept switched on. */\n async buildReport(form: LtBugReportFormValue, attachments: readonly LtBugAttachment[], includes: LtBugReportIncludes): Promise<LtBugReportData> {\n const engine = await this.engineLoad;\n if (engine) {\n return engine.buildReport(form, attachments, includes);\n }\n const { buildLtBugReport } = await import('./report');\n return buildLtBugReport(form, attachments, includes, {});\n }\n\n /**\n * POST the report to the endpoint. Shows a success or error toast; on failure sets {@link error} and rejects with a\n * {@link LtBugSubmitError} carrying the translated message.\n */\n async submit(report: LtBugReportData): Promise<LtBugReportResponse> {\n this.isSubmitting.set(true);\n this.error.set(null);\n try {\n // The texts of toasts and errors live in the lazy part (also when an app submits without the dialog).\n await loadLtBugMessages(this.i18n, this.i18nOptions.messages);\n const view = this.document.defaultView;\n if (!view?.fetch) {\n throw new LtBugSubmitError(this.i18n.t('lt.bug.errorSubmit'), 0);\n }\n let response: Response;\n try {\n response = await view.fetch(this.options.endpoint, {\n body: JSON.stringify(report),\n credentials: 'same-origin',\n headers: { accept: 'application/json', 'content-type': 'application/json' },\n method: 'POST',\n });\n } catch {\n throw new LtBugSubmitError(this.i18n.t('lt.bug.errorSubmit'), 0);\n }\n let body: LtBugReportResponse | null = null;\n try {\n body = (await response.json()) as LtBugReportResponse;\n } catch {\n body = null;\n }\n if (!response.ok || !body?.success) {\n throw new LtBugSubmitError(this.messageForStatus(response.status), response.status);\n }\n this.toast.show({\n color: 'success',\n description: body.identifier ? this.i18n.t('lt.bug.successDescription', { identifier: body.identifier }) : undefined,\n duration: 5000,\n title: this.i18n.t('lt.bug.successTitle'),\n });\n return body;\n } catch (error) {\n const failure = error instanceof LtBugSubmitError ? error : new LtBugSubmitError(this.i18n.t('lt.bug.errorSubmit'), 0);\n this.error.set(failure.message);\n this.toast.show({ color: 'error', description: failure.message, duration: 7000, title: this.i18n.t('lt.bug.errorTitle') });\n throw failure;\n } finally {\n this.isSubmitting.set(false);\n }\n }\n\n /** Forget screenshot, pre-fill error and submission error (after a sent report). */\n reset(): void {\n this.screenshot.set(null);\n this.lastError.set(null);\n this.error.set(null);\n }\n\n private onCapturedError(info: LtBugErrorInfo): void {\n // Errors may arrive while Angular renders a template: update the state outside of it.\n queueMicrotask(() => {\n this.lastError.set(info);\n if (this.options.autoOpenOnError && !this.isOpen()) {\n this.open(info);\n }\n });\n }\n\n private messageForStatus(status: number): string {\n switch (status) {\n case 400:\n return this.i18n.t('lt.bug.errorInvalid');\n case 404:\n return this.i18n.t('lt.bug.errorUnavailable');\n case 413:\n return this.i18n.t('lt.bug.errorTooLarge');\n case 429:\n return this.i18n.t('lt.bug.errorRateLimited');\n default:\n return this.i18n.t('lt.bug.errorSubmit');\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lenne.tech-ng-base-bug-bug-report-service-D6slR3GD.mjs","names":["LT_BUG_INPUT_OPTIONS"],"sources":["../../../projects/ng-base/bug/src/lib/bug-options.ts","../../../projects/ng-base/bug/src/lib/i18n/load.ts","../../../projects/ng-base/bug/src/lib/screenshot.ts","../../../projects/ng-base/bug/src/lib/bug-report-service.ts"],"sourcesContent":["import { inject, InjectionToken } from '@angular/core';\nimport { LT_RUNTIME_CONFIG, mergeLtFeatureOptions } from '@lenne.tech/ng-base';\nimport { LT_BUG_INPUT_OPTIONS, type LtBugOptions, type LtResolvedBugOptions } from '@lenne.tech/ng-base/bug/provide';\n\n/** The runtime decision lives in `@lenne.tech/ng-base/bug/provide` (light entry for `app.config.ts` and the shell). */\nexport { isLtBugActive, LT_BUG_ACTIVE, LT_BUG_DEV_MODE, LT_BUG_INPUT_OPTIONS } from '@lenne.tech/ng-base/bug/provide';\n\n/** Default endpoint, the same as `@lenne.tech/bug.lt` (outside `/api`, so an API proxy does not swallow it). */\nexport const LT_BUG_DEFAULT_ENDPOINT = '/_bug-lt/report';\n\n/** Defaults of {@link LtBugOptions} (the module defaults of `@lenne.tech/bug.lt` 1.6.9, except the button color). */\nexport const LT_BUG_DEFAULTS: LtResolvedBugOptions = {\n autoOpenOnError: false,\n autoShow: true,\n // bug.lt: #ef4444; white text on it has 3.8:1 contrast (WCAG AA needs 4.5:1), red-600 has 4.8:1.\n buttonColor: '#dc2626',\n enableBrowserInfo: true,\n enableConsoleLogs: true,\n enableErrorBoundary: true,\n enableNetworkRequests: true,\n enableScreenshot: true,\n enableUserJourney: true,\n endpoint: LT_BUG_DEFAULT_ENDPOINT,\n maxConsoleLogs: 50,\n maxNetworkRequests: 50,\n position: 'bottom-right',\n theme: 'auto',\n userJourney: {\n captureClicks: true,\n captureErrors: true,\n captureFormInteractions: true,\n captureHover: false,\n captureInputChanges: false,\n captureInputValues: false,\n captureKeyboard: true,\n captureModalEvents: true,\n captureNavigation: true,\n captureScroll: false,\n enabled: true,\n maxEvents: 50,\n throttleRate: 100,\n },\n};\n\n/** Resolved options: defaults ← `provideLtBug(options)` ← runtime `lt.bug` (`LT_RUNTIME_CONFIG`). */\nexport const LT_BUG_OPTIONS = new InjectionToken<LtResolvedBugOptions>('LT_BUG_OPTIONS', {\n factory: () => resolveLtBugOptions(inject(LT_BUG_INPUT_OPTIONS) ?? {}, inject(LT_RUNTIME_CONFIG, { optional: true })?.lt?.['bug']),\n});\n\n/** Merge options onto the defaults; `userJourney` is merged per key, `undefined` never overrides, sizes are clamped. */\nexport function resolveLtBugOptions(...overrides: (LtBugOptions | Record<string, unknown> | null | undefined)[]): LtResolvedBugOptions {\n const merged = mergeLtFeatureOptions<LtResolvedBugOptions>(LT_BUG_DEFAULTS, ...overrides);\n return {\n ...merged,\n endpoint: typeof merged.endpoint === 'string' && merged.endpoint.startsWith('/') ? merged.endpoint : LT_BUG_DEFAULT_ENDPOINT,\n maxConsoleLogs: clampSize(merged.maxConsoleLogs, LT_BUG_DEFAULTS.maxConsoleLogs),\n maxNetworkRequests: clampSize(merged.maxNetworkRequests, LT_BUG_DEFAULTS.maxNetworkRequests),\n userJourney: {\n ...merged.userJourney,\n maxEvents: clampSize(merged.userJourney.maxEvents, LT_BUG_DEFAULTS.userJourney.maxEvents),\n throttleRate: Math.max(0, Number(merged.userJourney.throttleRate) || 0),\n },\n };\n}\n\n/** Ring buffer sizes are whole numbers between 1 and 1000. */\nfunction clampSize(value: unknown, fallback: number): number {\n const number = Math.floor(Number(value));\n return Number.isFinite(number) && number > 0 ? Math.min(number, 1000) : fallback;\n}\n","import type { LtI18nMessages, LtI18nService } from '@lenne.tech/ng-base';\n\n/**\n * The only texts needed before the dialog chunk is loaded: the floating button's label and its busy text. Kept equal to\n * `de.json` / `en.json` (checked by `i18n.spec.ts`).\n */\nexport const LT_BUG_EAGER_MESSAGES: Readonly<Record<'de' | 'en', LtI18nMessages>> = {\n de: { lt: { bug: { button: 'Fehler melden', capturing: 'Screenshot wird erstellt …' } } },\n en: { lt: { bug: { button: 'Report a bug', capturing: 'Taking screenshot …' } } },\n};\n\nconst loaded = new WeakSet<LtI18nService>();\nlet messagesModule: Promise<typeof import('./messages')> | null = null;\n\n/** Register the button texts (synchronously; texts the app configured keep priority). */\nexport function registerLtBugEagerMessages(i18n: LtI18nService, appMessages: Record<string, LtI18nMessages> = {}): void {\n for (const [locale, messages] of Object.entries(LT_BUG_EAGER_MESSAGES)) {\n const app = appMessages[locale];\n i18n.registerMessages(locale, app ? withAppTexts(messages, app) : messages);\n }\n}\n\n/** Load (lazy chunk) and register every `lt.bug.*` text once per `LtI18nService` instance. */\nexport async function loadLtBugMessages(i18n: LtI18nService, appMessages: Record<string, LtI18nMessages> = {}): Promise<void> {\n if (loaded.has(i18n)) {\n return;\n }\n messagesModule ??= import('./messages');\n try {\n (await messagesModule).registerLtBugMessages(i18n, appMessages);\n loaded.add(i18n);\n } catch {\n // Chunk failed to load: keep the keys, retry on the next call.\n messagesModule = null;\n }\n}\n\nfunction withAppTexts(base: LtI18nMessages, app: LtI18nMessages): LtI18nMessages {\n const result: LtI18nMessages = { ...base };\n for (const [key, value] of Object.entries(app)) {\n const current = result[key];\n if (current === undefined) {\n continue;\n }\n result[key] = typeof value === 'object' && typeof current === 'object' ? withAppTexts(current, value) : value;\n }\n return result;\n}\n","import { InjectionToken } from '@angular/core';\n\nimport type { LtBugTheme } from './bug-types';\nimport { LT_BUG_UI_ATTRIBUTE } from './constants';\n\n/** The part of `modern-screenshot` the reporter uses (structural, so the peer stays optional). */\nexport interface LtScreenshotLibrary {\n domToPng(node: Node, options?: Record<string, unknown>): Promise<string>;\n}\n\nlet library: Promise<LtScreenshotLibrary | null> | null = null;\n\n/** Load the optional peer `modern-screenshot` once; resolves `null` when it is not installed. */\nexport function loadLtScreenshotLibrary(): Promise<LtScreenshotLibrary | null> {\n library ??= import('modern-screenshot').then(\n (module) => {\n const candidate = module as unknown as Partial<LtScreenshotLibrary> & { default?: Partial<LtScreenshotLibrary> };\n const resolved = typeof candidate.domToPng === 'function' ? candidate : candidate.default;\n return typeof resolved?.domToPng === 'function' ? (resolved as LtScreenshotLibrary) : null;\n },\n () => null,\n );\n return library;\n}\n\n/**\n * Loader of the screenshot library (default: lazy `import('modern-screenshot')`, `null` when the optional peer is\n * missing: the screenshot feature is then hidden). Replace it in tests.\n */\nexport const LT_BUG_SCREENSHOT_LOADER = new InjectionToken<() => Promise<LtScreenshotLibrary | null>>('LT_BUG_SCREENSHOT_LOADER', {\n factory: () => loadLtScreenshotLibrary,\n});\n\n/**\n * PNG data URL of the visible viewport (current scroll position, device pixel ratio up to 2), without the reporter's\n * own UI. The background follows `theme` (`auto`: `html.dark` / `html.light`, then `color-scheme`, then the system).\n */\nexport function captureLtBugScreenshot(lib: LtScreenshotLibrary, view: Window, theme: LtBugTheme): Promise<string> {\n const doc = view.document;\n const dark = theme === 'dark' || (theme === 'auto' && isLtDarkColorMode(view));\n return lib.domToPng(doc.documentElement, {\n backgroundColor: dark ? '#0a0a0a' : '#ffffff',\n features: { restoreScrollPosition: true },\n fetch: { bypassingCache: true, requestInit: { mode: 'cors' } },\n filter: (node: Node) => !(node.nodeType === 1 && (node as Element).hasAttribute?.(LT_BUG_UI_ATTRIBUTE)),\n height: view.innerHeight,\n scale: Math.min(view.devicePixelRatio || 1, 2),\n style: { transform: 'none', transformOrigin: 'top left' },\n width: view.innerWidth,\n });\n}\n\n/** Whether the page currently renders dark (`html.dark`, `html.light`, computed `color-scheme`, system preference). */\nexport function isLtDarkColorMode(view: Window): boolean {\n const root = view.document?.documentElement;\n if (root?.classList.contains('dark')) {\n return true;\n }\n if (root?.classList.contains('light')) {\n return false;\n }\n const scheme = root && typeof view.getComputedStyle === 'function' ? view.getComputedStyle(root).colorScheme : '';\n if (scheme === 'dark' || scheme === 'light') {\n return scheme === 'dark';\n }\n return !!view.matchMedia?.('(prefers-color-scheme: dark)').matches;\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, ErrorHandler, inject, Injector, PLATFORM_ID, Service, signal } from '@angular/core';\nimport { Router, ROUTES } from '@angular/router';\nimport { LT_I18N_OPTIONS, LT_RUNTIME_CONFIG, LtI18nService, LtToastService } from '@lenne.tech/ng-base';\n\nimport { isLtBugActive, LT_BUG_DEV_MODE, LT_BUG_INPUT_OPTIONS, LT_BUG_OPTIONS } from './bug-options';\nimport type {\n LtBrowserInfo,\n LtBugAttachment,\n LtBugErrorInfo,\n LtBugReportData,\n LtBugReportResponse,\n LtConsoleLogEntry,\n LtNetworkRequestEntry,\n LtUserInteractionEvent,\n LtUserInteractionMetadata,\n LtUserInteractionType,\n} from './bug-types';\nimport type { LtBugCaptureEngine } from './capture-engine';\nimport { loadLtBugMessages } from './i18n/load';\nimport type { LtBugReportFormValue, LtBugReportIncludes } from './report';\nimport { captureLtBugScreenshot, LT_BUG_SCREENSHOT_LOADER } from './screenshot';\n\n/** Error of {@link LtBugReportService.submit}; `message` is the translated text shown to the user. */\nexport class LtBugSubmitError extends Error {\n constructor(\n message: string,\n readonly status: number,\n ) {\n super(message);\n this.name = 'LtBugSubmitError';\n }\n}\n\n/**\n * State and actions of the bug reporter, the counterpart of `useBugReport()` of `@lenne.tech/bug.lt`.\n *\n * `<lt-bug-report>` calls {@link start} when it is created: when the reporter is {@link active} it loads the capture engine\n * (a chunk of its own) and installs the hooks (console, `fetch`/`XMLHttpRequest`, user journey, `ErrorHandler`);\n * otherwise it loads and installs nothing. `<lt-bug-report>` renders the button and opens the dialog for {@link open}\n * requests.\n */\n@Service()\nexport class LtBugReportService {\n /** Resolved options. */\n readonly options = inject(LT_BUG_OPTIONS);\n\n private readonly document = inject(DOCUMENT);\n private readonly injector = inject(Injector);\n private readonly i18n = inject(LtI18nService);\n private readonly i18nOptions = inject(LT_I18N_OPTIONS);\n private readonly toast = inject(LtToastService);\n private readonly screenshotLoader = inject(LT_BUG_SCREENSHOT_LOADER);\n\n /**\n * Whether the reporter runs: `provideLtBug()` is present, the code runs in the browser and the runtime decision says\n * on (`enabled`, else app env not `production`, else dev mode only). Decided once at startup.\n */\n readonly active: boolean =\n inject(LT_BUG_INPUT_OPTIONS) !== null &&\n isPlatformBrowser(inject(PLATFORM_ID)) &&\n isLtBugActive(this.options, inject(LT_RUNTIME_CONFIG, { optional: true })?.appEnv, inject(LT_BUG_DEV_MODE));\n\n /** The dialog is open (or about to open). */\n readonly isOpen = signal(false);\n /** A report is being sent. */\n readonly isSubmitting = signal(false);\n /** Message of the last failed submission (shown in the dialog). */\n readonly error = signal<string | null>(null);\n /** The last captured error (pre-fills the form). */\n readonly lastError = signal<LtBugErrorInfo | null>(null);\n /** Screenshot taken before the dialog opened (data URL). */\n readonly screenshot = signal<string | null>(null);\n /** A screenshot is being taken. */\n readonly capturingScreenshot = signal(false);\n /** Whether `modern-screenshot` could be loaded (`null` until the first attempt). */\n readonly screenshotAvailable = signal<boolean | null>(null);\n /** An {@link open} request waits for `<lt-bug-report>` (read and reset by the component). */\n readonly openRequested = signal(false);\n\n private engine: LtBugCaptureEngine | null = null;\n private engineLoad: Promise<LtBugCaptureEngine | null> = Promise.resolve(null);\n private pendingErrors: { error: unknown; source?: string }[] = [];\n private started = false;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.stop());\n }\n\n /** Whether the capture hooks are installed. */\n get running(): boolean {\n return this.engine !== null;\n }\n\n /**\n * Load the capture engine and install the hooks for the enabled features (idempotent; does nothing while not\n * {@link active}). Errors reported before the engine is ready are handed over once it is.\n */\n start(): void {\n const view = this.document.defaultView;\n if (!this.active || this.started || !view) {\n return;\n }\n this.started = true;\n const router = this.injector.get(ROUTES, null) ? this.injector.get(Router, null) : null;\n const errorHandler = this.injector.get(ErrorHandler, null);\n this.engineLoad = import('./capture-engine').then(\n ({ startLtBugCapture }) => {\n if (!this.started) {\n return null;\n }\n this.engine = startLtBugCapture({ baseUri: this.document.baseURI, errorHandler, onError: (info) => this.onCapturedError(info), options: this.options, router, view });\n for (const { error, source } of this.pendingErrors.splice(0)) {\n this.engine.reportError(error, source);\n }\n return this.engine;\n },\n (error: unknown) => {\n console.warn('[@lenne.tech/ng-base/bug] Could not load the capture engine', error);\n return null;\n },\n );\n }\n\n /** Resolves once {@link start} has installed the hooks (`false` when inactive or the engine failed to load). */\n async whenReady(): Promise<boolean> {\n return (await this.engineLoad) !== null;\n }\n\n /** Remove every capture hook and restore the patched globals. */\n stop(): void {\n this.engine?.stop();\n this.engine = null;\n this.engineLoad = Promise.resolve(null);\n this.pendingErrors = [];\n this.started = false;\n }\n\n /**\n * Ask `<lt-bug-report>` to take a screenshot and open the dialog (also when the floating button is hidden with\n * `autoShow: false`); `error` pre-fills the form. Ignored while the reporter is not active.\n */\n open(error?: LtBugErrorInfo): void {\n if (!this.active) {\n return;\n }\n if (error) {\n this.lastError.set(error);\n }\n this.error.set(null);\n this.openRequested.set(true);\n }\n\n /** Report an error by hand (e.g. from a `catch` block): it becomes {@link lastError} and may auto-open the dialog. */\n reportError(error: unknown, source?: string): void {\n if (!this.active || !this.started) {\n return;\n }\n if (this.engine) {\n this.engine.reportError(error, source);\n } else {\n this.pendingErrors.push({ error, source });\n }\n }\n\n /** Record a custom user journey event (no-op while the journey is off). */\n recordInteraction(type: LtUserInteractionType, target: string, metadata?: LtUserInteractionMetadata): void {\n this.engine?.record(type, target, metadata);\n }\n\n /** The captured console entries (newest `maxConsoleLogs`). */\n getConsoleLogs(): LtConsoleLogEntry[] {\n return this.engine?.consoleLogs() ?? [];\n }\n\n /** The captured network requests (newest `maxNetworkRequests`). */\n getNetworkRequests(): LtNetworkRequestEntry[] {\n return this.engine?.networkRequests() ?? [];\n }\n\n /** The captured user journey (newest `userJourney.maxEvents`). */\n getUserJourney(): LtUserInteractionEvent[] {\n return this.engine?.userJourney() ?? [];\n }\n\n /** Browser details of the current page (`null` while inactive or outside the browser). */\n getBrowserInfo(): LtBrowserInfo | null {\n return this.engine?.browserInfo() ?? null;\n }\n\n /**\n * Take a viewport screenshot into {@link screenshot} (without the reporter's UI; network capture paused meanwhile).\n * Resolves `null` when screenshots are disabled, `modern-screenshot` is missing or the capture failed.\n */\n async captureScreenshot(): Promise<string | null> {\n const view = this.document.defaultView;\n if (!this.active || !this.options.enableScreenshot || !view) {\n return null;\n }\n this.capturingScreenshot.set(true);\n this.engine?.setNetworkPaused(true);\n try {\n const library = await this.screenshotLoader();\n this.screenshotAvailable.set(!!library);\n const dataUrl = library ? await captureLtBugScreenshot(library, view, this.options.theme) : null;\n this.screenshot.set(dataUrl);\n return dataUrl;\n } catch (error) {\n console.warn('[@lenne.tech/ng-base/bug] Screenshot capture failed', error);\n this.screenshot.set(null);\n return null;\n } finally {\n this.capturingScreenshot.set(false);\n this.engine?.setNetworkPaused(false);\n }\n }\n\n /** Assemble the payload from the form, the attachments and the collected data the user kept switched on. */\n async buildReport(form: LtBugReportFormValue, attachments: readonly LtBugAttachment[], includes: LtBugReportIncludes): Promise<LtBugReportData> {\n const engine = await this.engineLoad;\n if (engine) {\n return engine.buildReport(form, attachments, includes);\n }\n const { buildLtBugReport } = await import('./report');\n return buildLtBugReport(form, attachments, includes, {});\n }\n\n /**\n * POST the report to the endpoint. Shows a success or error toast; on failure sets {@link error} and rejects with a\n * {@link LtBugSubmitError} carrying the translated message.\n */\n async submit(report: LtBugReportData): Promise<LtBugReportResponse> {\n this.isSubmitting.set(true);\n this.error.set(null);\n try {\n // The texts of toasts and errors live in the lazy part (also when an app submits without the dialog).\n await loadLtBugMessages(this.i18n, this.i18nOptions.messages);\n const view = this.document.defaultView;\n if (!view?.fetch) {\n throw new LtBugSubmitError(this.i18n.t('lt.bug.errorSubmit'), 0);\n }\n let response: Response;\n try {\n response = await view.fetch(this.options.endpoint, {\n body: JSON.stringify(report),\n credentials: 'same-origin',\n headers: { accept: 'application/json', 'content-type': 'application/json' },\n method: 'POST',\n });\n } catch {\n throw new LtBugSubmitError(this.i18n.t('lt.bug.errorSubmit'), 0);\n }\n let body: LtBugReportResponse | null = null;\n try {\n body = (await response.json()) as LtBugReportResponse;\n } catch {\n body = null;\n }\n if (!response.ok || !body?.success) {\n throw new LtBugSubmitError(this.messageForStatus(response.status), response.status);\n }\n this.toast.show({\n color: 'success',\n description: body.identifier ? this.i18n.t('lt.bug.successDescription', { identifier: body.identifier }) : undefined,\n duration: 5000,\n title: this.i18n.t('lt.bug.successTitle'),\n });\n return body;\n } catch (error) {\n const failure = error instanceof LtBugSubmitError ? error : new LtBugSubmitError(this.i18n.t('lt.bug.errorSubmit'), 0);\n this.error.set(failure.message);\n this.toast.show({ color: 'error', description: failure.message, duration: 7000, title: this.i18n.t('lt.bug.errorTitle') });\n throw failure;\n } finally {\n this.isSubmitting.set(false);\n }\n }\n\n /** Forget screenshot, pre-fill error and submission error (after a sent report). */\n reset(): void {\n this.screenshot.set(null);\n this.lastError.set(null);\n this.error.set(null);\n }\n\n private onCapturedError(info: LtBugErrorInfo): void {\n // Errors may arrive while Angular renders a template: update the state outside of it.\n queueMicrotask(() => {\n this.lastError.set(info);\n if (this.options.autoOpenOnError && !this.isOpen()) {\n this.open(info);\n }\n });\n }\n\n private messageForStatus(status: number): string {\n switch (status) {\n case 400:\n return this.i18n.t('lt.bug.errorInvalid');\n case 404:\n return this.i18n.t('lt.bug.errorUnavailable');\n case 413:\n return this.i18n.t('lt.bug.errorTooLarge');\n case 429:\n return this.i18n.t('lt.bug.errorRateLimited');\n default:\n return this.i18n.t('lt.bug.errorSubmit');\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as LT_BUG_DEFAULTS, c as resolveLtBugOptions, i as registerLtBugEagerMessages, n as LtBugSubmitError, o as LT_BUG_DEFAULT_ENDPOINT, r as LT_BUG_SCREENSHOT_LOADER, s as LT_BUG_OPTIONS, t as LtBugReportService } from "./lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs";
1
+ import { a as LT_BUG_DEFAULTS, c as resolveLtBugOptions, i as registerLtBugEagerMessages, n as LtBugSubmitError, o as LT_BUG_DEFAULT_ENDPOINT, r as LT_BUG_SCREENSHOT_LOADER, s as LT_BUG_OPTIONS, t as LtBugReportService } from "./lenne.tech-ng-base-bug-bug-report-service-D6slR3GD.mjs";
2
2
  import { t as LT_BUG_UI_ATTRIBUTE } from "./lenne.tech-ng-base-bug-constants-Brm2KGHi.mjs";
3
3
  import * as i0 from "@angular/core";
4
4
  import { Component, Injector, booleanAttribute, computed, effect, inject, input, signal, untracked } from "@angular/core";
@@ -46,7 +46,7 @@ var LtBugReport = class LtBugReport {
46
46
  this.announcement.set("");
47
47
  try {
48
48
  await this.bug.captureScreenshot();
49
- const { openLtBugReportDialog } = await import("./lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs");
49
+ const { openLtBugReportDialog } = await import("./lenne.tech-ng-base-bug-bug-report-dialog-f6VNOWnW.mjs");
50
50
  const sent = await openLtBugReportDialog(this.injector).closed === true;
51
51
  if (sent) this.announcement.set(this.i18n.t("lt.bug.successTitle"));
52
52
  return sent;
@@ -59,7 +59,7 @@ var LtBugReport = class LtBugReport {
59
59
  }
60
60
  static ɵfac = i0.ɵɵngDeclareFactory({
61
61
  minVersion: "12.0.0",
62
- version: "22.2.4",
62
+ version: "22.2.5",
63
63
  ngImport: i0,
64
64
  type: LtBugReport,
65
65
  deps: [],
@@ -67,7 +67,7 @@ var LtBugReport = class LtBugReport {
67
67
  });
68
68
  static ɵcmp = i0.ɵɵngDeclareComponent({
69
69
  minVersion: "17.0.0",
70
- version: "22.2.4",
70
+ version: "22.2.5",
71
71
  type: LtBugReport,
72
72
  isStandalone: true,
73
73
  selector: "lt-bug-report",
@@ -131,7 +131,7 @@ var LtBugReport = class LtBugReport {
131
131
  };
132
132
  i0.ɵɵngDeclareClassMetadata({
133
133
  minVersion: "12.0.0",
134
- version: "22.2.4",
134
+ version: "22.2.5",
135
135
  ngImport: i0,
136
136
  type: LtBugReport,
137
137
  decorators: [{
@@ -194,7 +194,7 @@ var LtTusUploadComponent = class LtTusUploadComponent {
194
194
  }
195
195
  static ɵfac = i0.ɵɵngDeclareFactory({
196
196
  minVersion: "12.0.0",
197
- version: "22.2.4",
197
+ version: "22.2.5",
198
198
  ngImport: i0,
199
199
  type: LtTusUploadComponent,
200
200
  deps: [],
@@ -202,7 +202,7 @@ var LtTusUploadComponent = class LtTusUploadComponent {
202
202
  });
203
203
  static ɵcmp = i0.ɵɵngDeclareComponent({
204
204
  minVersion: "17.0.0",
205
- version: "22.2.4",
205
+ version: "22.2.5",
206
206
  type: LtTusUploadComponent,
207
207
  isStandalone: true,
208
208
  selector: "lt-tus-upload",
@@ -321,7 +321,7 @@ var LtTusUploadComponent = class LtTusUploadComponent {
321
321
  };
322
322
  i0.ɵɵngDeclareClassMetadata({
323
323
  minVersion: "12.0.0",
324
- version: "22.2.4",
324
+ version: "22.2.5",
325
325
  ngImport: i0,
326
326
  type: LtTusUploadComponent,
327
327
  decorators: [{
@@ -47,7 +47,7 @@ var LtFileService = class LtFileService {
47
47
  }
48
48
  }
49
49
  getFileUrl(id) {
50
- return `${this.baseUrl()}/id/${encodeURIComponent(id)}`;
50
+ return `${this.linkBaseUrl()}/id/${encodeURIComponent(id)}`;
51
51
  }
52
52
  getDownloadUrl(id, _filename) {
53
53
  return this.getFileUrl(id);
@@ -65,9 +65,13 @@ var LtFileService = class LtFileService {
65
65
  const basePath = this.options.filesBasePath || "/files";
66
66
  return this.api.buildUrl(basePath).replace(/\/+$/, "");
67
67
  }
68
+ linkBaseUrl() {
69
+ const basePath = this.options.filesBasePath || "/files";
70
+ return this.api.buildBrowserUrl(basePath).replace(/\/+$/, "");
71
+ }
68
72
  static ɵfac = i0.ɵɵngDeclareFactory({
69
73
  minVersion: "12.0.0",
70
- version: "22.2.4",
74
+ version: "22.2.5",
71
75
  ngImport: i0,
72
76
  type: LtFileService,
73
77
  deps: [],
@@ -75,14 +79,14 @@ var LtFileService = class LtFileService {
75
79
  });
76
80
  static ɵprov = i0.ɵɵngDeclareService({
77
81
  minVersion: "22.0.0",
78
- version: "22.2.4",
82
+ version: "22.2.5",
79
83
  ngImport: i0,
80
84
  type: LtFileService
81
85
  });
82
86
  };
83
87
  i0.ɵɵngDeclareClassMetadata({
84
88
  minVersion: "12.0.0",
85
- version: "22.2.4",
89
+ version: "22.2.5",
86
90
  ngImport: i0,
87
91
  type: LtFileService,
88
92
  decorators: [{ type: Service }]
@@ -1 +1 @@
1
- {"version":3,"file":"lenne.tech-ng-base-files.mjs","names":[],"sources":["../../../projects/ng-base/files/src/lib/files-options.ts","../../../projects/ng-base/files/src/lib/file-service.ts","../../../projects/ng-base/files/src/lib/tus-client.ts","../../../projects/ng-base/files/src/lib/tus-upload.ts"],"sourcesContent":["import { inject, InjectionToken, makeEnvironmentProviders, type EnvironmentProviders } from '@angular/core';\nimport { LT_RUNTIME_CONFIG, mergeLtFeatureOptions } from '@lenne.tech/ng-base';\n\n/** TUS options of `provideLtFiles({ tus })`; runtime overrides `NG_PUBLIC_LT_TUS_*`. */\nexport interface LtTusOptions {\n /** Chunk size in bytes (default 5 MB). */\n defaultChunkSize?: number;\n /**\n * TUS endpoint (default **`/tus`**, nest-server's default; nuxt-extensions defaulted to `/files/upload`). Relative values\n * are paths on the API while `resolveAgainstApi` is on; absolute URLs are used as they are.\n */\n defaultEndpoint?: string;\n /** Resolve a relative endpoint against the API base URL (default `true`). Turn off only behind an own same-origin proxy. */\n resolveAgainstApi?: boolean;\n}\n\n/** Options of `provideLtFiles()`. */\nexport interface LtFilesOptions {\n /** Base path of nest-server's file controller for info/download URLs (default `/files`). */\n filesBasePath?: string;\n /** TUS upload options. */\n tus?: LtTusOptions;\n}\n\n/** Resolved files options. */\nexport interface LtResolvedFilesOptions {\n filesBasePath: string;\n tus: Required<LtTusOptions>;\n}\n\n/** Defaults of {@link LtFilesOptions}. */\nexport const LT_FILES_DEFAULTS: LtResolvedFilesOptions = {\n filesBasePath: '/files',\n tus: { defaultChunkSize: 5 * 1024 * 1024, defaultEndpoint: '/tus', resolveAgainstApi: true },\n};\n\nconst LT_FILES_INPUT_OPTIONS = new InjectionToken<LtFilesOptions>('LT_FILES_INPUT_OPTIONS', { factory: () => ({}) });\n\n/** Resolved files options (defaults ← `provideLtFiles(options)` ← runtime `NG_PUBLIC_LT_TUS_*`). */\nexport const LT_FILES_OPTIONS = new InjectionToken<LtResolvedFilesOptions>('LT_FILES_OPTIONS', {\n factory: () => {\n const input = inject(LT_FILES_INPUT_OPTIONS);\n const runtime = inject(LT_RUNTIME_CONFIG, { optional: true });\n return {\n filesBasePath: input.filesBasePath ?? LT_FILES_DEFAULTS.filesBasePath,\n tus: mergeLtFeatureOptions(LT_FILES_DEFAULTS.tus, input.tus, runtime?.lt.tus),\n };\n },\n});\n\n/** Files providers (TUS upload defaults, file info/download base path). Optional when the defaults fit. */\nexport function provideLtFiles(options: LtFilesOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: LT_FILES_INPUT_OPTIONS, useValue: options }]);\n}\n","import { inject, isDevMode, Service } from '@angular/core';\nimport { LT_FETCH, LtApiUrlService } from '@lenne.tech/ng-base';\n\nimport { LT_FILES_OPTIONS } from './files-options';\nimport type { LtFileInfo, LtFileState } from './upload-types';\n\n/**\n * nest-server file helpers (counterpart of `useLtFile()`). URLs are resolved against the API base URL (fix: the Nuxt\n * version built them relative to the app origin), under `filesBasePath` of `provideLtFiles()` (default `/files`).\n */\n@Service()\nexport class LtFileService implements LtFileState {\n private readonly api = inject(LtApiUrlService);\n private readonly fetch = inject(LT_FETCH);\n private readonly options = inject(LT_FILES_OPTIONS);\n\n /** `GET <api>/files/info/:id` with credentials; `null` for empty ids or on error, a non-ObjectId string is returned as is. */\n async getFileInfo(id: string | undefined): Promise<LtFileInfo | null | string> {\n if (!id) {\n return null;\n }\n if (!isLtMongoId(id)) {\n return id;\n }\n try {\n const response = await this.fetch(`${this.baseUrl()}/info/${encodeURIComponent(id)}`, {\n credentials: 'include',\n headers: { accept: 'application/json' },\n method: 'GET',\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const info = (await response.json()) as LtFileInfo | null;\n return info && typeof info === 'object' ? info : null;\n } catch (error) {\n if (isDevMode()) {\n console.error('[@lenne.tech/ng-base] Error fetching file info:', error);\n }\n return null;\n }\n }\n\n /**\n * `<api>/files/id/:id` — nest-server's `GET files/id/:id` (CoreFileController), which streams the file (or redirects\n * to a presigned S3 URL) after the rights check (`file.downloadRoles`, default admin).\n */\n getFileUrl(id: string): string {\n return `${this.baseUrl()}/id/${encodeURIComponent(id)}`;\n }\n\n /**\n * Download URL. nest-server sets `Content-Disposition` from the stored filename on `GET files/id/:id`, so this is the\n * same URL as {@link getFileUrl}; `filename` is accepted for API compatibility with nuxt-extensions and ignored (the\n * server has no rename parameter — nuxt-extensions' `/files/download/:id` does not exist in nest-server 12.5).\n */\n getDownloadUrl(id: string, _filename?: string): string {\n return this.getFileUrl(id);\n }\n\n /** 1024-based `B`/`KB`/`MB`/`GB`/`TB` with two decimals. */\n formatFileSize(bytes: number): string {\n return formatLtFileSize(bytes);\n }\n\n /** `Ns`, `Nm Ns`, `Nh Nm`. */\n formatDuration(seconds: number): string {\n return formatLtDuration(seconds);\n }\n\n /** `/^[a-f\\d]{24}$/i`. */\n isValidMongoID(id: string): boolean {\n return isLtMongoId(id);\n }\n\n /** `<api><filesBasePath>` without a trailing slash; an absolute `filesBasePath` is used as is. */\n private baseUrl(): string {\n const basePath = this.options.filesBasePath || '/files';\n return this.api.buildUrl(basePath).replace(/\\/+$/, '');\n }\n}\n\n/** 1024-based file size with two decimals (`0 B` for non-positive input). */\nexport function formatLtFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) {\n return '0 B';\n }\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n return `${parseFloat((bytes / 1024 ** exponent).toFixed(2))} ${units[exponent]}`;\n}\n\n/** Human readable duration (`45s`, `2m 5s`, `1h 3m`). */\nexport function formatLtDuration(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 60) {\n return `${Math.max(0, Math.round(Number.isFinite(seconds) ? seconds : 0))}s`;\n }\n if (seconds < 3600) {\n return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;\n }\n return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;\n}\n\n/** Whether `id` is a MongoDB ObjectId (24 hex characters). */\nexport function isLtMongoId(id: string): boolean {\n return typeof id === 'string' && /^[a-f\\d]{24}$/i.test(id);\n}\n","import { InjectionToken } from '@angular/core';\n\n/** The request object tus-js-client hands to `onBeforeRequest` / `onAfterResponse`. */\nexport interface LtTusHttpRequest {\n getUnderlyingObject(): unknown;\n setHeader(header: string, value: string): void;\n}\n\n/** The response object tus-js-client hands to `onAfterResponse` (and inside `DetailedError.originalResponse`). */\nexport interface LtTusHttpResponse {\n getHeader(header: string): null | string | undefined;\n getStatus(): number;\n}\n\n/** A previous upload of the same file found in tus-js-client's URL storage. */\nexport interface LtTusPreviousUpload {\n creationTime?: string;\n}\n\n/** The tus-js-client upload options `injectLtTusUpload()` sets. */\nexport interface LtTusClientUploadOptions {\n chunkSize?: number;\n endpoint?: string;\n headers?: Record<string, string>;\n metadata?: Record<string, string>;\n onAfterResponse?: (req: LtTusHttpRequest, res: LtTusHttpResponse) => void;\n onBeforeRequest?: (req: LtTusHttpRequest) => void;\n onError?: (error: Error) => void;\n onProgress?: (bytesUploaded: number, bytesTotal: number) => void;\n onShouldRetry?: (error: Error & { originalResponse?: LtTusHttpResponse | null }, retryAttempt: number, options: unknown) => boolean;\n onSuccess?: () => void;\n removeFingerprintOnSuccess?: boolean;\n retryDelays?: number[];\n}\n\n/** The part of a tus-js-client `Upload` instance `injectLtTusUpload()` uses. */\nexport interface LtTusClientUpload {\n url: null | string;\n abort(shouldTerminate?: boolean): Promise<void>;\n findPreviousUploads(): Promise<LtTusPreviousUpload[]>;\n resumeFromPreviousUpload(previousUpload: LtTusPreviousUpload): void;\n start(): void;\n}\n\n/** The part of the `tus-js-client` module `injectLtTusUpload()` uses (structural, so the peer stays optional). */\nexport interface LtTusClient {\n Upload: new (file: File, options: LtTusClientUploadOptions) => LtTusClientUpload;\n}\n\n/** Error text when the optional peer `tus-js-client` is missing. */\nexport const LT_TUS_CLIENT_MISSING_MESSAGE =\n '[@lenne.tech/ng-base] tus-js-client is not installed. Install it to use uploads: pnpm add tus-js-client (or npm install tus-js-client).';\n\nlet tusClient: Promise<LtTusClient> | null = null;\n\n/** Load `tus-js-client` lazily (once); rejects with an install hint when the optional peer is missing. */\nexport function loadLtTusClient(): Promise<LtTusClient> {\n tusClient ??= import('tus-js-client').then(\n (module) => {\n const candidate = module as unknown as Partial<LtTusClient> & { default?: Partial<LtTusClient> };\n const client = candidate.Upload ? candidate : candidate.default;\n if (!client?.Upload) {\n throw new Error(LT_TUS_CLIENT_MISSING_MESSAGE);\n }\n return client as LtTusClient;\n },\n () => {\n throw new Error(LT_TUS_CLIENT_MISSING_MESSAGE);\n },\n );\n // A failed load is not cached, so installing the package and reloading the chunk can recover.\n tusClient.catch(() => (tusClient = null));\n return tusClient;\n}\n\n/**\n * Loader of the `tus-js-client` module used by `injectLtTusUpload()` (default: lazy `import('tus-js-client')`).\n * Replace it in tests with a fake client, or to supply a preconfigured build.\n */\nexport const LT_TUS_CLIENT_LOADER = new InjectionToken<() => Promise<LtTusClient>>('LT_TUS_CLIENT_LOADER', {\n factory: () => loadLtTusClient,\n});\n","import { assertInInjectionContext, computed, DestroyRef, inject, signal } from '@angular/core';\nimport { isLtAbsoluteUrl, LtApiUrlService } from '@lenne.tech/ng-base';\nimport { LtAuthStateService } from '@lenne.tech/ng-base/auth/provide';\n\nimport { LT_FILES_OPTIONS } from './files-options';\nimport { LT_TUS_CLIENT_LOADER, type LtTusClientUpload, type LtTusHttpRequest, type LtTusPreviousUpload } from './tus-client';\nimport type { LtTusUploadState, LtUploadItem, LtUploadOptions, LtUploadProgress } from './upload-types';\n\n/** A TUS upload queue with signal state, created by {@link injectLtTusUpload}. */\nexport type LtTusUpload = LtTusUploadState;\n\n/**\n * Response header in which nest-server (11.42.13+, `TUS_FILE_ID_HEADER`) names the stored file. Sent on the response that\n * completes the upload and listed in `Access-Control-Expose-Headers`.\n */\nexport const LT_TUS_FILE_ID_HEADER = 'X-File-Id';\n\n/** Default retry delays of a TUS upload in milliseconds. */\nexport const LT_TUS_RETRY_DELAYS: readonly number[] = [0, 1000, 3000, 5000, 10000];\n\nconst DEFAULT_PARALLEL_UPLOADS = 3;\n\ninterface ResolvedUploadOptions extends LtUploadOptions {\n autoStart: boolean;\n chunkSize: number;\n endpoint: string;\n parallelUploads: number;\n retryDelays: number[];\n}\n\n/** Exponential moving average (0.7 old / 0.3 new) of the upload speed in bytes per second. */\nclass LtSpeedTracker {\n private lastBytes = 0;\n private lastTime = 0;\n private smoothed = 0;\n private baseline = true;\n\n /** Start a new measurement from `bytes` (after a (re)start; the time of a pause does not count). */\n reset(bytes: number): void {\n this.lastBytes = bytes;\n this.lastTime = Date.now();\n this.smoothed = 0;\n this.baseline = true;\n }\n\n /** Record `bytes` uploaded so far and return the smoothed speed. */\n sample(bytes: number): number {\n const now = Date.now();\n if (this.baseline && bytes < this.lastBytes) {\n this.lastBytes = bytes; // a resumed upload reports its offset first\n }\n const seconds = (now - this.lastTime) / 1000;\n if (seconds > 0 && bytes >= this.lastBytes) {\n const current = (bytes - this.lastBytes) / seconds;\n this.smoothed = this.smoothed === 0 ? current : this.smoothed * 0.7 + current * 0.3;\n this.lastBytes = bytes;\n this.lastTime = now;\n this.baseline = false;\n }\n return Math.round(this.smoothed);\n }\n}\n\n/**\n * Create a TUS upload queue (counterpart of `useLtTusUpload()`), bound to the current injection context: running\n * uploads are aborted when the component/service is destroyed.\n *\n * `tus-js-client` (optional peer) is loaded lazily; without it `addFiles` rejects with an install hint.\n * Defaults: `autoStart` true, chunk size and endpoint from `provideLtFiles()`, 3 parallel uploads, retries\n * `[0, 1000, 3000, 5000, 10000]` (none on 4xx except 429), `withCredentials` for the session cookie, Bearer token\n * in JWT mode, the nest-server file id from the `X-File-Id` response header, resume via fingerprints.\n *\n * Options are read whenever an upload is created or started, so an object with getters (e.g. over component inputs)\n * stays current. `undefined` values keep the default. Uploads beyond `parallelUploads` wait in a queue and start as\n * soon as a slot is free; `startUpload`/`resumeUpload`/`retryUpload` of a single item start it right away.\n *\n * @example\n * protected readonly upload = injectLtTusUpload({ onSuccess: (item) => this.attach(item.fileId) });\n */\nexport function injectLtTusUpload(options: LtUploadOptions = {}): LtTusUpload {\n assertInInjectionContext(injectLtTusUpload);\n const filesOptions = inject(LT_FILES_OPTIONS);\n const api = inject(LtApiUrlService);\n const authState = inject(LtAuthStateService);\n const loadClient = inject(LT_TUS_CLIENT_LOADER);\n\n const items = signal<LtUploadItem[]>([]);\n const tusUploads = new Map<string, LtTusClientUpload>();\n const speedTrackers = new Map<string, LtSpeedTracker>();\n /** Start generation per upload: a pause/cancel invalidates a start whose resume lookup is still running. */\n const generations = new Map<string, number>();\n const queue: string[] = [];\n let sequence = 0;\n let destroyed = false;\n\n const uploads = items.asReadonly();\n const isUploading = computed(() => items().some((item) => item.status === 'uploading'));\n const totalProgress = computed<LtUploadProgress>(() => {\n const list = items();\n if (list.length === 0) {\n return idleProgress(0);\n }\n const bytesUploaded = list.reduce((sum, item) => sum + item.progress.bytesUploaded, 0);\n const bytesTotal = list.reduce((sum, item) => sum + item.progress.bytesTotal, 0);\n const speed = list.reduce((sum, item) => sum + item.progress.speed, 0);\n return {\n bytesTotal,\n bytesUploaded,\n percentage: bytesTotal > 0 ? Math.round((bytesUploaded / bytesTotal) * 100) : 0,\n remainingTime: speed > 0 ? Math.ceil((bytesTotal - bytesUploaded) / speed) : 0,\n speed,\n };\n });\n\n function config(): ResolvedUploadOptions {\n const defined = Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined)) as LtUploadOptions;\n return {\n ...defined,\n autoStart: defined.autoStart ?? true,\n chunkSize: defined.chunkSize || filesOptions.tus.defaultChunkSize,\n endpoint: defined.endpoint || filesOptions.tus.defaultEndpoint,\n parallelUploads: Math.max(1, defined.parallelUploads || DEFAULT_PARALLEL_UPLOADS),\n retryDelays: defined.retryDelays ?? [...LT_TUS_RETRY_DELAYS],\n };\n }\n\n /** A relative endpoint is a path on the API (like auth and AI requests) unless `tus.resolveAgainstApi` is off. */\n function resolveEndpoint(endpoint: string): string {\n if (!endpoint || filesOptions.tus.resolveAgainstApi === false || isLtAbsoluteUrl(endpoint)) {\n return endpoint;\n }\n return api.buildUrl(endpoint.startsWith('/') ? endpoint : `/${endpoint}`);\n }\n\n function get(id: string): LtUploadItem | undefined {\n return items().find((item) => item.id === id);\n }\n\n function update(id: string, changes: Partial<LtUploadItem>): LtUploadItem | undefined {\n let updated: LtUploadItem | undefined;\n items.update((list) =>\n list.map((item) => {\n if (item.id !== id) {\n return item;\n }\n updated = { ...item, ...changes };\n return updated;\n }),\n );\n return updated;\n }\n\n function invalidate(id: string): void {\n generations.set(id, (generations.get(id) ?? 0) + 1);\n }\n\n function dequeue(id: string): void {\n const index = queue.indexOf(id);\n if (index >= 0) {\n queue.splice(index, 1);\n }\n }\n\n function uploadingCount(): number {\n return items().filter((item) => item.status === 'uploading').length;\n }\n\n /** Start queued uploads while slots are free. */\n function pump(): void {\n if (destroyed) {\n return;\n }\n const limit = config().parallelUploads;\n while (queue.length > 0 && uploadingCount() < limit) {\n const id = queue.shift() as string;\n const item = get(id);\n if (item && (item.status === 'idle' || item.status === 'paused')) {\n begin(id);\n }\n }\n }\n\n function enqueue(ids: string[]): void {\n for (const id of ids) {\n if (!queue.includes(id)) {\n queue.push(id);\n }\n }\n pump();\n }\n\n function begin(id: string): void {\n const item = get(id);\n const tusUpload = tusUploads.get(id);\n if (destroyed || !item || !tusUpload || item.status === 'uploading' || item.status === 'completed') {\n return;\n }\n dequeue(id);\n invalidate(id);\n const generation = generations.get(id);\n speedTrackers.get(id)?.reset(item.progress.bytesUploaded);\n update(id, { error: undefined, startedAt: new Date(), status: 'uploading' });\n\n const stillWanted = (): boolean => !destroyed && generations.get(id) === generation && get(id)?.status === 'uploading';\n // An upload that already has its URL continues there; a new one looks for an interrupted upload of the same file.\n const lookup: Promise<LtTusPreviousUpload[]> = tusUpload.url ? Promise.resolve([]) : tusUpload.findPreviousUploads().catch(() => []);\n void lookup.then((previousUploads) => {\n if (!stillWanted()) {\n return;\n }\n const previous = latest(previousUploads);\n if (previous) {\n tusUpload.resumeFromPreviousUpload(previous);\n }\n tusUpload.start();\n });\n }\n\n function settle(id: string): void {\n invalidate(id);\n pump();\n }\n\n function createTusUpload(client: Awaited<ReturnType<typeof loadClient>>, item: LtUploadItem, resolved: ResolvedUploadOptions): LtTusClientUpload {\n // Read in `onAfterResponse`, not from `onSuccess`'s payload: tus-js-client passes `{ lastResponse }` to `onSuccess`\n // only from 4.2.0, and the peer range starts at 4.0.0.\n let fileId: string | undefined;\n const callerSetsAuthorization = Object.keys(resolved.headers ?? {}).some((name) => name.toLowerCase() === 'authorization');\n\n const upload: LtTusClientUpload = new client.Upload(item.file, {\n chunkSize: resolved.chunkSize,\n endpoint: resolveEndpoint(resolved.endpoint),\n headers: resolved.headers,\n metadata: {\n filename: item.file.name,\n filetype: item.file.type,\n ...resolved.metadata,\n ...item.metadata,\n },\n onAfterResponse: (_req, res) => {\n // Only the completing response carries it; XHR answers `null` for a header that was not sent or not exposed.\n fileId = res.getHeader(LT_TUS_FILE_ID_HEADER) || fileId;\n },\n onBeforeRequest: (req: LtTusHttpRequest) => {\n const xhr = req.getUnderlyingObject() as { withCredentials?: boolean } | null;\n if (xhr && typeof xhr === 'object' && 'withCredentials' in xhr) {\n xhr.withCredentials = true;\n }\n if (!callerSetsAuthorization && authState.getAuthMode() === 'jwt') {\n const token = authState.getJwtToken();\n if (token) {\n req.setHeader('Authorization', `Bearer ${token}`);\n }\n }\n },\n onError: (error) => {\n const failed = update(item.id, { error: error.message, progress: { ...(get(item.id)?.progress ?? item.progress), remainingTime: 0, speed: 0 }, status: 'error' });\n settle(item.id);\n if (failed) {\n config().onError?.(failed, error);\n }\n },\n onProgress: (bytesUploaded, bytesTotal) => {\n const current = get(item.id);\n if (!current || current.status !== 'uploading') {\n return;\n }\n const speed = speedTrackers.get(item.id)?.sample(bytesUploaded) ?? 0;\n const progress: LtUploadProgress = {\n bytesTotal,\n bytesUploaded,\n percentage: bytesTotal > 0 ? Math.min(100, Math.round((bytesUploaded / bytesTotal) * 100)) : 0,\n remainingTime: speed > 0 ? Math.ceil((bytesTotal - bytesUploaded) / speed) : 0,\n speed,\n };\n const updated = update(item.id, { progress });\n if (updated) {\n config().onProgress?.(updated);\n }\n },\n onShouldRetry: (error) => {\n const status = error.originalResponse?.getStatus?.();\n // No retry on 4xx (except 429 Too Many Requests): the request itself is wrong or not allowed.\n return !(status && status >= 400 && status < 500 && status !== 429);\n },\n onSuccess: () => {\n const current = get(item.id);\n if (!current) {\n return;\n }\n const bytesTotal = current.progress.bytesTotal || item.file.size;\n const completed = update(item.id, {\n completedAt: new Date(),\n error: undefined,\n fileId,\n progress: { bytesTotal, bytesUploaded: bytesTotal, percentage: 100, remainingTime: 0, speed: 0 },\n status: 'completed',\n url: upload.url ?? undefined,\n });\n settle(item.id);\n if (completed) {\n config().onSuccess?.(completed);\n }\n },\n // Keep the fingerprint only for interrupted uploads: a completed one must not be \"resumed\" by the next upload of\n // the same file, which would finish without a request and therefore without an `X-File-Id`.\n removeFingerprintOnSuccess: true,\n retryDelays: resolved.retryDelays,\n });\n return upload;\n }\n\n async function addFiles(files: File | File[]): Promise<string[]> {\n const list = (Array.isArray(files) ? files : [files]).filter(Boolean);\n if (list.length === 0) {\n return [];\n }\n const client = await loadClient();\n if (destroyed) {\n return [];\n }\n const resolved = config();\n const ids: string[] = [];\n for (const file of list) {\n const id = `upload_${Date.now().toString(36)}_${++sequence}_${Math.random().toString(36).slice(2, 8)}`;\n const item: LtUploadItem = { file, id, metadata: resolved.metadata, progress: idleProgress(file.size), status: 'idle' };\n tusUploads.set(id, createTusUpload(client, item, resolved));\n speedTrackers.set(id, new LtSpeedTracker());\n items.update((current) => [...current, item]);\n ids.push(id);\n }\n if (resolved.autoStart) {\n enqueue(ids);\n }\n return ids;\n }\n\n function startUpload(id: string): void {\n begin(id);\n }\n\n function startAll(): void {\n enqueue(\n items()\n .filter((item) => item.status === 'idle' || item.status === 'paused')\n .map((item) => item.id),\n );\n }\n\n function pauseUpload(id: string): void {\n const item = get(id);\n const tusUpload = tusUploads.get(id);\n if (!item || !tusUpload) {\n return;\n }\n if (item.status === 'uploading') {\n invalidate(id);\n void tusUpload.abort().catch(() => undefined);\n update(id, { progress: { ...item.progress, remainingTime: 0, speed: 0 }, status: 'paused' });\n pump();\n } else if (item.status === 'idle') {\n // A queued upload is taken out of the queue.\n dequeue(id);\n update(id, { status: 'paused' });\n }\n }\n\n function pauseAll(): void {\n items()\n .filter((item) => item.status === 'uploading')\n .forEach((item) => pauseUpload(item.id));\n }\n\n function resumeUpload(id: string): void {\n if (get(id)?.status === 'paused') {\n begin(id);\n }\n }\n\n function resumeAll(): void {\n enqueue(\n items()\n .filter((item) => item.status === 'paused')\n .map((item) => item.id),\n );\n }\n\n function cancelUpload(id: string): void {\n const tusUpload = tusUploads.get(id);\n invalidate(id);\n dequeue(id);\n if (tusUpload) {\n void tusUpload.abort().catch(() => undefined);\n }\n tusUploads.delete(id);\n speedTrackers.delete(id);\n generations.delete(id);\n items.update((list) => list.filter((item) => item.id !== id));\n pump();\n }\n\n function cancelAll(): void {\n items().forEach((item) => cancelUpload(item.id));\n }\n\n function removeUpload(id: string): void {\n cancelUpload(id);\n }\n\n function clearCompleted(): void {\n items()\n .filter((item) => item.status === 'completed')\n .forEach((item) => removeUpload(item.id));\n }\n\n function retryUpload(id: string): void {\n if (get(id)?.status === 'error') {\n update(id, { error: undefined, status: 'idle' });\n begin(id);\n }\n }\n\n inject(DestroyRef).onDestroy(() => {\n destroyed = true;\n queue.length = 0;\n for (const tusUpload of tusUploads.values()) {\n void tusUpload.abort().catch(() => undefined);\n }\n });\n\n return {\n addFiles,\n cancelAll,\n cancelUpload,\n clearCompleted,\n getUpload: get,\n isUploading,\n pauseAll,\n pauseUpload,\n removeUpload,\n resumeAll,\n resumeUpload,\n retryUpload,\n startAll,\n startUpload,\n totalProgress,\n uploads,\n };\n}\n\nfunction idleProgress(bytesTotal: number): LtUploadProgress {\n return { bytesTotal, bytesUploaded: 0, percentage: 0, remainingTime: 0, speed: 0 };\n}\n\nfunction latest(previousUploads: LtTusPreviousUpload[]): LtTusPreviousUpload | undefined {\n return [...previousUploads].sort((a, b) => Date.parse(b.creationTime ?? '') - Date.parse(a.creationTime ?? '') || 0)[0];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lenne.tech-ng-base-files.mjs","names":[],"sources":["../../../projects/ng-base/files/src/lib/files-options.ts","../../../projects/ng-base/files/src/lib/file-service.ts","../../../projects/ng-base/files/src/lib/tus-client.ts","../../../projects/ng-base/files/src/lib/tus-upload.ts"],"sourcesContent":["import { inject, InjectionToken, makeEnvironmentProviders, type EnvironmentProviders } from '@angular/core';\nimport { LT_RUNTIME_CONFIG, mergeLtFeatureOptions } from '@lenne.tech/ng-base';\n\n/** TUS options of `provideLtFiles({ tus })`; runtime overrides `NG_PUBLIC_LT_TUS_*`. */\nexport interface LtTusOptions {\n /** Chunk size in bytes (default 5 MB). */\n defaultChunkSize?: number;\n /**\n * TUS endpoint (default **`/tus`**, nest-server's default; nuxt-extensions defaulted to `/files/upload`). Relative values\n * are paths on the API while `resolveAgainstApi` is on; absolute URLs are used as they are.\n */\n defaultEndpoint?: string;\n /** Resolve a relative endpoint against the API base URL (default `true`). Turn off only behind an own same-origin proxy. */\n resolveAgainstApi?: boolean;\n}\n\n/** Options of `provideLtFiles()`. */\nexport interface LtFilesOptions {\n /** Base path of nest-server's file controller for info/download URLs (default `/files`). */\n filesBasePath?: string;\n /** TUS upload options. */\n tus?: LtTusOptions;\n}\n\n/** Resolved files options. */\nexport interface LtResolvedFilesOptions {\n filesBasePath: string;\n tus: Required<LtTusOptions>;\n}\n\n/** Defaults of {@link LtFilesOptions}. */\nexport const LT_FILES_DEFAULTS: LtResolvedFilesOptions = {\n filesBasePath: '/files',\n tus: { defaultChunkSize: 5 * 1024 * 1024, defaultEndpoint: '/tus', resolveAgainstApi: true },\n};\n\nconst LT_FILES_INPUT_OPTIONS = new InjectionToken<LtFilesOptions>('LT_FILES_INPUT_OPTIONS', { factory: () => ({}) });\n\n/** Resolved files options (defaults ← `provideLtFiles(options)` ← runtime `NG_PUBLIC_LT_TUS_*`). */\nexport const LT_FILES_OPTIONS = new InjectionToken<LtResolvedFilesOptions>('LT_FILES_OPTIONS', {\n factory: () => {\n const input = inject(LT_FILES_INPUT_OPTIONS);\n const runtime = inject(LT_RUNTIME_CONFIG, { optional: true });\n return {\n filesBasePath: input.filesBasePath ?? LT_FILES_DEFAULTS.filesBasePath,\n tus: mergeLtFeatureOptions(LT_FILES_DEFAULTS.tus, input.tus, runtime?.lt.tus),\n };\n },\n});\n\n/** Files providers (TUS upload defaults, file info/download base path). Optional when the defaults fit. */\nexport function provideLtFiles(options: LtFilesOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: LT_FILES_INPUT_OPTIONS, useValue: options }]);\n}\n","import { inject, isDevMode, Service } from '@angular/core';\nimport { LT_FETCH, LtApiUrlService } from '@lenne.tech/ng-base';\n\nimport { LT_FILES_OPTIONS } from './files-options';\nimport type { LtFileInfo, LtFileState } from './upload-types';\n\n/**\n * nest-server file helpers (counterpart of `useLtFile()`). URLs are resolved against the API base URL (fix: the Nuxt\n * version built them relative to the app origin), under `filesBasePath` of `provideLtFiles()` (default `/files`).\n */\n@Service()\nexport class LtFileService implements LtFileState {\n private readonly api = inject(LtApiUrlService);\n private readonly fetch = inject(LT_FETCH);\n private readonly options = inject(LT_FILES_OPTIONS);\n\n /** `GET <api>/files/info/:id` with credentials; `null` for empty ids or on error, a non-ObjectId string is returned as is. */\n async getFileInfo(id: string | undefined): Promise<LtFileInfo | null | string> {\n if (!id) {\n return null;\n }\n if (!isLtMongoId(id)) {\n return id;\n }\n try {\n const response = await this.fetch(`${this.baseUrl()}/info/${encodeURIComponent(id)}`, {\n credentials: 'include',\n headers: { accept: 'application/json' },\n method: 'GET',\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const info = (await response.json()) as LtFileInfo | null;\n return info && typeof info === 'object' ? info : null;\n } catch (error) {\n if (isDevMode()) {\n console.error('[@lenne.tech/ng-base] Error fetching file info:', error);\n }\n return null;\n }\n }\n\n /**\n * `<api>/files/id/:id` — nest-server's `GET files/id/:id` (CoreFileController), which streams the file (or redirects\n * to a presigned S3 URL) after the rights check (`file.downloadRoles`, default admin). The URL the browser uses, also\n * during SSR (`NG_PUBLIC_API_URL` or `/api`): it ends up in `href`/`src`, so the server-only `NG_API_URL` never does.\n */\n getFileUrl(id: string): string {\n return `${this.linkBaseUrl()}/id/${encodeURIComponent(id)}`;\n }\n\n /**\n * Download URL. nest-server sets `Content-Disposition` from the stored filename on `GET files/id/:id`, so this is the\n * same URL as {@link getFileUrl}; `filename` is accepted for API compatibility with nuxt-extensions and ignored (the\n * server has no rename parameter — nuxt-extensions' `/files/download/:id` does not exist in nest-server 12.5).\n */\n getDownloadUrl(id: string, _filename?: string): string {\n return this.getFileUrl(id);\n }\n\n /** 1024-based `B`/`KB`/`MB`/`GB`/`TB` with two decimals. */\n formatFileSize(bytes: number): string {\n return formatLtFileSize(bytes);\n }\n\n /** `Ns`, `Nm Ns`, `Nh Nm`. */\n formatDuration(seconds: number): string {\n return formatLtDuration(seconds);\n }\n\n /** `/^[a-f\\d]{24}$/i`. */\n isValidMongoID(id: string): boolean {\n return isLtMongoId(id);\n }\n\n /** `<api><filesBasePath>` without a trailing slash; an absolute `filesBasePath` is used as is. */\n /** Base for requests this service sends itself (`NG_API_URL` during SSR). */\n private baseUrl(): string {\n const basePath = this.options.filesBasePath || '/files';\n return this.api.buildUrl(basePath).replace(/\\/+$/, '');\n }\n\n /** Base for links the browser follows (see {@link getFileUrl}). */\n private linkBaseUrl(): string {\n const basePath = this.options.filesBasePath || '/files';\n return this.api.buildBrowserUrl(basePath).replace(/\\/+$/, '');\n }\n}\n\n/** 1024-based file size with two decimals (`0 B` for non-positive input). */\nexport function formatLtFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) {\n return '0 B';\n }\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n return `${parseFloat((bytes / 1024 ** exponent).toFixed(2))} ${units[exponent]}`;\n}\n\n/** Human readable duration (`45s`, `2m 5s`, `1h 3m`). */\nexport function formatLtDuration(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 60) {\n return `${Math.max(0, Math.round(Number.isFinite(seconds) ? seconds : 0))}s`;\n }\n if (seconds < 3600) {\n return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;\n }\n return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;\n}\n\n/** Whether `id` is a MongoDB ObjectId (24 hex characters). */\nexport function isLtMongoId(id: string): boolean {\n return typeof id === 'string' && /^[a-f\\d]{24}$/i.test(id);\n}\n","import { InjectionToken } from '@angular/core';\n\n/** The request object tus-js-client hands to `onBeforeRequest` / `onAfterResponse`. */\nexport interface LtTusHttpRequest {\n getUnderlyingObject(): unknown;\n setHeader(header: string, value: string): void;\n}\n\n/** The response object tus-js-client hands to `onAfterResponse` (and inside `DetailedError.originalResponse`). */\nexport interface LtTusHttpResponse {\n getHeader(header: string): null | string | undefined;\n getStatus(): number;\n}\n\n/** A previous upload of the same file found in tus-js-client's URL storage. */\nexport interface LtTusPreviousUpload {\n creationTime?: string;\n}\n\n/** The tus-js-client upload options `injectLtTusUpload()` sets. */\nexport interface LtTusClientUploadOptions {\n chunkSize?: number;\n endpoint?: string;\n headers?: Record<string, string>;\n metadata?: Record<string, string>;\n onAfterResponse?: (req: LtTusHttpRequest, res: LtTusHttpResponse) => void;\n onBeforeRequest?: (req: LtTusHttpRequest) => void;\n onError?: (error: Error) => void;\n onProgress?: (bytesUploaded: number, bytesTotal: number) => void;\n onShouldRetry?: (error: Error & { originalResponse?: LtTusHttpResponse | null }, retryAttempt: number, options: unknown) => boolean;\n onSuccess?: () => void;\n removeFingerprintOnSuccess?: boolean;\n retryDelays?: number[];\n}\n\n/** The part of a tus-js-client `Upload` instance `injectLtTusUpload()` uses. */\nexport interface LtTusClientUpload {\n url: null | string;\n abort(shouldTerminate?: boolean): Promise<void>;\n findPreviousUploads(): Promise<LtTusPreviousUpload[]>;\n resumeFromPreviousUpload(previousUpload: LtTusPreviousUpload): void;\n start(): void;\n}\n\n/** The part of the `tus-js-client` module `injectLtTusUpload()` uses (structural, so the peer stays optional). */\nexport interface LtTusClient {\n Upload: new (file: File, options: LtTusClientUploadOptions) => LtTusClientUpload;\n}\n\n/** Error text when the optional peer `tus-js-client` is missing. */\nexport const LT_TUS_CLIENT_MISSING_MESSAGE =\n '[@lenne.tech/ng-base] tus-js-client is not installed. Install it to use uploads: pnpm add tus-js-client (or npm install tus-js-client).';\n\nlet tusClient: Promise<LtTusClient> | null = null;\n\n/** Load `tus-js-client` lazily (once); rejects with an install hint when the optional peer is missing. */\nexport function loadLtTusClient(): Promise<LtTusClient> {\n tusClient ??= import('tus-js-client').then(\n (module) => {\n const candidate = module as unknown as Partial<LtTusClient> & { default?: Partial<LtTusClient> };\n const client = candidate.Upload ? candidate : candidate.default;\n if (!client?.Upload) {\n throw new Error(LT_TUS_CLIENT_MISSING_MESSAGE);\n }\n return client as LtTusClient;\n },\n () => {\n throw new Error(LT_TUS_CLIENT_MISSING_MESSAGE);\n },\n );\n // A failed load is not cached, so installing the package and reloading the chunk can recover.\n tusClient.catch(() => (tusClient = null));\n return tusClient;\n}\n\n/**\n * Loader of the `tus-js-client` module used by `injectLtTusUpload()` (default: lazy `import('tus-js-client')`).\n * Replace it in tests with a fake client, or to supply a preconfigured build.\n */\nexport const LT_TUS_CLIENT_LOADER = new InjectionToken<() => Promise<LtTusClient>>('LT_TUS_CLIENT_LOADER', {\n factory: () => loadLtTusClient,\n});\n","import { assertInInjectionContext, computed, DestroyRef, inject, signal } from '@angular/core';\nimport { isLtAbsoluteUrl, LtApiUrlService } from '@lenne.tech/ng-base';\nimport { LtAuthStateService } from '@lenne.tech/ng-base/auth/provide';\n\nimport { LT_FILES_OPTIONS } from './files-options';\nimport { LT_TUS_CLIENT_LOADER, type LtTusClientUpload, type LtTusHttpRequest, type LtTusPreviousUpload } from './tus-client';\nimport type { LtTusUploadState, LtUploadItem, LtUploadOptions, LtUploadProgress } from './upload-types';\n\n/** A TUS upload queue with signal state, created by {@link injectLtTusUpload}. */\nexport type LtTusUpload = LtTusUploadState;\n\n/**\n * Response header in which nest-server (11.42.13+, `TUS_FILE_ID_HEADER`) names the stored file. Sent on the response that\n * completes the upload and listed in `Access-Control-Expose-Headers`.\n */\nexport const LT_TUS_FILE_ID_HEADER = 'X-File-Id';\n\n/** Default retry delays of a TUS upload in milliseconds. */\nexport const LT_TUS_RETRY_DELAYS: readonly number[] = [0, 1000, 3000, 5000, 10000];\n\nconst DEFAULT_PARALLEL_UPLOADS = 3;\n\ninterface ResolvedUploadOptions extends LtUploadOptions {\n autoStart: boolean;\n chunkSize: number;\n endpoint: string;\n parallelUploads: number;\n retryDelays: number[];\n}\n\n/** Exponential moving average (0.7 old / 0.3 new) of the upload speed in bytes per second. */\nclass LtSpeedTracker {\n private lastBytes = 0;\n private lastTime = 0;\n private smoothed = 0;\n private baseline = true;\n\n /** Start a new measurement from `bytes` (after a (re)start; the time of a pause does not count). */\n reset(bytes: number): void {\n this.lastBytes = bytes;\n this.lastTime = Date.now();\n this.smoothed = 0;\n this.baseline = true;\n }\n\n /** Record `bytes` uploaded so far and return the smoothed speed. */\n sample(bytes: number): number {\n const now = Date.now();\n if (this.baseline && bytes < this.lastBytes) {\n this.lastBytes = bytes; // a resumed upload reports its offset first\n }\n const seconds = (now - this.lastTime) / 1000;\n if (seconds > 0 && bytes >= this.lastBytes) {\n const current = (bytes - this.lastBytes) / seconds;\n this.smoothed = this.smoothed === 0 ? current : this.smoothed * 0.7 + current * 0.3;\n this.lastBytes = bytes;\n this.lastTime = now;\n this.baseline = false;\n }\n return Math.round(this.smoothed);\n }\n}\n\n/**\n * Create a TUS upload queue (counterpart of `useLtTusUpload()`), bound to the current injection context: running\n * uploads are aborted when the component/service is destroyed.\n *\n * `tus-js-client` (optional peer) is loaded lazily; without it `addFiles` rejects with an install hint.\n * Defaults: `autoStart` true, chunk size and endpoint from `provideLtFiles()`, 3 parallel uploads, retries\n * `[0, 1000, 3000, 5000, 10000]` (none on 4xx except 429), `withCredentials` for the session cookie, Bearer token\n * in JWT mode, the nest-server file id from the `X-File-Id` response header, resume via fingerprints.\n *\n * Options are read whenever an upload is created or started, so an object with getters (e.g. over component inputs)\n * stays current. `undefined` values keep the default. Uploads beyond `parallelUploads` wait in a queue and start as\n * soon as a slot is free; `startUpload`/`resumeUpload`/`retryUpload` of a single item start it right away.\n *\n * @example\n * protected readonly upload = injectLtTusUpload({ onSuccess: (item) => this.attach(item.fileId) });\n */\nexport function injectLtTusUpload(options: LtUploadOptions = {}): LtTusUpload {\n assertInInjectionContext(injectLtTusUpload);\n const filesOptions = inject(LT_FILES_OPTIONS);\n const api = inject(LtApiUrlService);\n const authState = inject(LtAuthStateService);\n const loadClient = inject(LT_TUS_CLIENT_LOADER);\n\n const items = signal<LtUploadItem[]>([]);\n const tusUploads = new Map<string, LtTusClientUpload>();\n const speedTrackers = new Map<string, LtSpeedTracker>();\n /** Start generation per upload: a pause/cancel invalidates a start whose resume lookup is still running. */\n const generations = new Map<string, number>();\n const queue: string[] = [];\n let sequence = 0;\n let destroyed = false;\n\n const uploads = items.asReadonly();\n const isUploading = computed(() => items().some((item) => item.status === 'uploading'));\n const totalProgress = computed<LtUploadProgress>(() => {\n const list = items();\n if (list.length === 0) {\n return idleProgress(0);\n }\n const bytesUploaded = list.reduce((sum, item) => sum + item.progress.bytesUploaded, 0);\n const bytesTotal = list.reduce((sum, item) => sum + item.progress.bytesTotal, 0);\n const speed = list.reduce((sum, item) => sum + item.progress.speed, 0);\n return {\n bytesTotal,\n bytesUploaded,\n percentage: bytesTotal > 0 ? Math.round((bytesUploaded / bytesTotal) * 100) : 0,\n remainingTime: speed > 0 ? Math.ceil((bytesTotal - bytesUploaded) / speed) : 0,\n speed,\n };\n });\n\n function config(): ResolvedUploadOptions {\n const defined = Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined)) as LtUploadOptions;\n return {\n ...defined,\n autoStart: defined.autoStart ?? true,\n chunkSize: defined.chunkSize || filesOptions.tus.defaultChunkSize,\n endpoint: defined.endpoint || filesOptions.tus.defaultEndpoint,\n parallelUploads: Math.max(1, defined.parallelUploads || DEFAULT_PARALLEL_UPLOADS),\n retryDelays: defined.retryDelays ?? [...LT_TUS_RETRY_DELAYS],\n };\n }\n\n /** A relative endpoint is a path on the API (like auth and AI requests) unless `tus.resolveAgainstApi` is off. */\n function resolveEndpoint(endpoint: string): string {\n if (!endpoint || filesOptions.tus.resolveAgainstApi === false || isLtAbsoluteUrl(endpoint)) {\n return endpoint;\n }\n return api.buildUrl(endpoint.startsWith('/') ? endpoint : `/${endpoint}`);\n }\n\n function get(id: string): LtUploadItem | undefined {\n return items().find((item) => item.id === id);\n }\n\n function update(id: string, changes: Partial<LtUploadItem>): LtUploadItem | undefined {\n let updated: LtUploadItem | undefined;\n items.update((list) =>\n list.map((item) => {\n if (item.id !== id) {\n return item;\n }\n updated = { ...item, ...changes };\n return updated;\n }),\n );\n return updated;\n }\n\n function invalidate(id: string): void {\n generations.set(id, (generations.get(id) ?? 0) + 1);\n }\n\n function dequeue(id: string): void {\n const index = queue.indexOf(id);\n if (index >= 0) {\n queue.splice(index, 1);\n }\n }\n\n function uploadingCount(): number {\n return items().filter((item) => item.status === 'uploading').length;\n }\n\n /** Start queued uploads while slots are free. */\n function pump(): void {\n if (destroyed) {\n return;\n }\n const limit = config().parallelUploads;\n while (queue.length > 0 && uploadingCount() < limit) {\n const id = queue.shift() as string;\n const item = get(id);\n if (item && (item.status === 'idle' || item.status === 'paused')) {\n begin(id);\n }\n }\n }\n\n function enqueue(ids: string[]): void {\n for (const id of ids) {\n if (!queue.includes(id)) {\n queue.push(id);\n }\n }\n pump();\n }\n\n function begin(id: string): void {\n const item = get(id);\n const tusUpload = tusUploads.get(id);\n if (destroyed || !item || !tusUpload || item.status === 'uploading' || item.status === 'completed') {\n return;\n }\n dequeue(id);\n invalidate(id);\n const generation = generations.get(id);\n speedTrackers.get(id)?.reset(item.progress.bytesUploaded);\n update(id, { error: undefined, startedAt: new Date(), status: 'uploading' });\n\n const stillWanted = (): boolean => !destroyed && generations.get(id) === generation && get(id)?.status === 'uploading';\n // An upload that already has its URL continues there; a new one looks for an interrupted upload of the same file.\n const lookup: Promise<LtTusPreviousUpload[]> = tusUpload.url ? Promise.resolve([]) : tusUpload.findPreviousUploads().catch(() => []);\n void lookup.then((previousUploads) => {\n if (!stillWanted()) {\n return;\n }\n const previous = latest(previousUploads);\n if (previous) {\n tusUpload.resumeFromPreviousUpload(previous);\n }\n tusUpload.start();\n });\n }\n\n function settle(id: string): void {\n invalidate(id);\n pump();\n }\n\n function createTusUpload(client: Awaited<ReturnType<typeof loadClient>>, item: LtUploadItem, resolved: ResolvedUploadOptions): LtTusClientUpload {\n // Read in `onAfterResponse`, not from `onSuccess`'s payload: tus-js-client passes `{ lastResponse }` to `onSuccess`\n // only from 4.2.0, and the peer range starts at 4.0.0.\n let fileId: string | undefined;\n const callerSetsAuthorization = Object.keys(resolved.headers ?? {}).some((name) => name.toLowerCase() === 'authorization');\n\n const upload: LtTusClientUpload = new client.Upload(item.file, {\n chunkSize: resolved.chunkSize,\n endpoint: resolveEndpoint(resolved.endpoint),\n headers: resolved.headers,\n metadata: {\n filename: item.file.name,\n filetype: item.file.type,\n ...resolved.metadata,\n ...item.metadata,\n },\n onAfterResponse: (_req, res) => {\n // Only the completing response carries it; XHR answers `null` for a header that was not sent or not exposed.\n fileId = res.getHeader(LT_TUS_FILE_ID_HEADER) || fileId;\n },\n onBeforeRequest: (req: LtTusHttpRequest) => {\n const xhr = req.getUnderlyingObject() as { withCredentials?: boolean } | null;\n if (xhr && typeof xhr === 'object' && 'withCredentials' in xhr) {\n xhr.withCredentials = true;\n }\n if (!callerSetsAuthorization && authState.getAuthMode() === 'jwt') {\n const token = authState.getJwtToken();\n if (token) {\n req.setHeader('Authorization', `Bearer ${token}`);\n }\n }\n },\n onError: (error) => {\n const failed = update(item.id, { error: error.message, progress: { ...(get(item.id)?.progress ?? item.progress), remainingTime: 0, speed: 0 }, status: 'error' });\n settle(item.id);\n if (failed) {\n config().onError?.(failed, error);\n }\n },\n onProgress: (bytesUploaded, bytesTotal) => {\n const current = get(item.id);\n if (!current || current.status !== 'uploading') {\n return;\n }\n const speed = speedTrackers.get(item.id)?.sample(bytesUploaded) ?? 0;\n const progress: LtUploadProgress = {\n bytesTotal,\n bytesUploaded,\n percentage: bytesTotal > 0 ? Math.min(100, Math.round((bytesUploaded / bytesTotal) * 100)) : 0,\n remainingTime: speed > 0 ? Math.ceil((bytesTotal - bytesUploaded) / speed) : 0,\n speed,\n };\n const updated = update(item.id, { progress });\n if (updated) {\n config().onProgress?.(updated);\n }\n },\n onShouldRetry: (error) => {\n const status = error.originalResponse?.getStatus?.();\n // No retry on 4xx (except 429 Too Many Requests): the request itself is wrong or not allowed.\n return !(status && status >= 400 && status < 500 && status !== 429);\n },\n onSuccess: () => {\n const current = get(item.id);\n if (!current) {\n return;\n }\n const bytesTotal = current.progress.bytesTotal || item.file.size;\n const completed = update(item.id, {\n completedAt: new Date(),\n error: undefined,\n fileId,\n progress: { bytesTotal, bytesUploaded: bytesTotal, percentage: 100, remainingTime: 0, speed: 0 },\n status: 'completed',\n url: upload.url ?? undefined,\n });\n settle(item.id);\n if (completed) {\n config().onSuccess?.(completed);\n }\n },\n // Keep the fingerprint only for interrupted uploads: a completed one must not be \"resumed\" by the next upload of\n // the same file, which would finish without a request and therefore without an `X-File-Id`.\n removeFingerprintOnSuccess: true,\n retryDelays: resolved.retryDelays,\n });\n return upload;\n }\n\n async function addFiles(files: File | File[]): Promise<string[]> {\n const list = (Array.isArray(files) ? files : [files]).filter(Boolean);\n if (list.length === 0) {\n return [];\n }\n const client = await loadClient();\n if (destroyed) {\n return [];\n }\n const resolved = config();\n const ids: string[] = [];\n for (const file of list) {\n const id = `upload_${Date.now().toString(36)}_${++sequence}_${Math.random().toString(36).slice(2, 8)}`;\n const item: LtUploadItem = { file, id, metadata: resolved.metadata, progress: idleProgress(file.size), status: 'idle' };\n tusUploads.set(id, createTusUpload(client, item, resolved));\n speedTrackers.set(id, new LtSpeedTracker());\n items.update((current) => [...current, item]);\n ids.push(id);\n }\n if (resolved.autoStart) {\n enqueue(ids);\n }\n return ids;\n }\n\n function startUpload(id: string): void {\n begin(id);\n }\n\n function startAll(): void {\n enqueue(\n items()\n .filter((item) => item.status === 'idle' || item.status === 'paused')\n .map((item) => item.id),\n );\n }\n\n function pauseUpload(id: string): void {\n const item = get(id);\n const tusUpload = tusUploads.get(id);\n if (!item || !tusUpload) {\n return;\n }\n if (item.status === 'uploading') {\n invalidate(id);\n void tusUpload.abort().catch(() => undefined);\n update(id, { progress: { ...item.progress, remainingTime: 0, speed: 0 }, status: 'paused' });\n pump();\n } else if (item.status === 'idle') {\n // A queued upload is taken out of the queue.\n dequeue(id);\n update(id, { status: 'paused' });\n }\n }\n\n function pauseAll(): void {\n items()\n .filter((item) => item.status === 'uploading')\n .forEach((item) => pauseUpload(item.id));\n }\n\n function resumeUpload(id: string): void {\n if (get(id)?.status === 'paused') {\n begin(id);\n }\n }\n\n function resumeAll(): void {\n enqueue(\n items()\n .filter((item) => item.status === 'paused')\n .map((item) => item.id),\n );\n }\n\n function cancelUpload(id: string): void {\n const tusUpload = tusUploads.get(id);\n invalidate(id);\n dequeue(id);\n if (tusUpload) {\n void tusUpload.abort().catch(() => undefined);\n }\n tusUploads.delete(id);\n speedTrackers.delete(id);\n generations.delete(id);\n items.update((list) => list.filter((item) => item.id !== id));\n pump();\n }\n\n function cancelAll(): void {\n items().forEach((item) => cancelUpload(item.id));\n }\n\n function removeUpload(id: string): void {\n cancelUpload(id);\n }\n\n function clearCompleted(): void {\n items()\n .filter((item) => item.status === 'completed')\n .forEach((item) => removeUpload(item.id));\n }\n\n function retryUpload(id: string): void {\n if (get(id)?.status === 'error') {\n update(id, { error: undefined, status: 'idle' });\n begin(id);\n }\n }\n\n inject(DestroyRef).onDestroy(() => {\n destroyed = true;\n queue.length = 0;\n for (const tusUpload of tusUploads.values()) {\n void tusUpload.abort().catch(() => undefined);\n }\n });\n\n return {\n addFiles,\n cancelAll,\n cancelUpload,\n clearCompleted,\n getUpload: get,\n isUploading,\n pauseAll,\n pauseUpload,\n removeUpload,\n resumeAll,\n resumeUpload,\n retryUpload,\n startAll,\n startUpload,\n totalProgress,\n uploads,\n };\n}\n\nfunction idleProgress(bytesTotal: number): LtUploadProgress {\n return { bytesTotal, bytesUploaded: 0, percentage: 0, remainingTime: 0, speed: 0 };\n}\n\nfunction latest(previousUploads: LtTusPreviousUpload[]): LtTusPreviousUpload | undefined {\n return [...previousUploads].sort((a, b) => Date.parse(b.creationTime ?? '') - Date.parse(a.creationTime ?? '') || 0)[0];\n}\n"],"mappings":"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"}
@@ -51,7 +51,7 @@ var LtShareService = class LtShareService {
51
51
  }
52
52
  static ɵfac = i0.ɵɵngDeclareFactory({
53
53
  minVersion: "12.0.0",
54
- version: "22.2.4",
54
+ version: "22.2.5",
55
55
  ngImport: i0,
56
56
  type: LtShareService,
57
57
  deps: [],
@@ -59,14 +59,14 @@ var LtShareService = class LtShareService {
59
59
  });
60
60
  static ɵprov = i0.ɵɵngDeclareService({
61
61
  minVersion: "22.0.0",
62
- version: "22.2.4",
62
+ version: "22.2.5",
63
63
  ngImport: i0,
64
64
  type: LtShareService
65
65
  });
66
66
  };
67
67
  i0.ɵɵngDeclareClassMetadata({
68
68
  minVersion: "12.0.0",
69
- version: "22.2.4",
69
+ version: "22.2.5",
70
70
  ngImport: i0,
71
71
  type: LtShareService,
72
72
  decorators: [{ type: Service }]
@@ -59,7 +59,7 @@ var LtColorModeService = class LtColorModeService {
59
59
  }
60
60
  static ɵfac = i0.ɵɵngDeclareFactory({
61
61
  minVersion: "12.0.0",
62
- version: "22.2.4",
62
+ version: "22.2.5",
63
63
  ngImport: i0,
64
64
  type: LtColorModeService,
65
65
  deps: [],
@@ -67,14 +67,14 @@ var LtColorModeService = class LtColorModeService {
67
67
  });
68
68
  static ɵprov = i0.ɵɵngDeclareService({
69
69
  minVersion: "22.0.0",
70
- version: "22.2.4",
70
+ version: "22.2.5",
71
71
  ngImport: i0,
72
72
  type: LtColorModeService
73
73
  });
74
74
  };
75
75
  i0.ɵɵngDeclareClassMetadata({
76
76
  minVersion: "12.0.0",
77
- version: "22.2.4",
77
+ version: "22.2.5",
78
78
  ngImport: i0,
79
79
  type: LtColorModeService,
80
80
  decorators: [{ type: Service }],