@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.
- package/CLAUDE.md +5 -0
- package/README.md +34 -7
- package/fesm2022/lenne.tech-ng-base-ai.mjs +27 -27
- package/fesm2022/lenne.tech-ng-base-auth-provide.mjs +15 -14
- package/fesm2022/lenne.tech-ng-base-auth-provide.mjs.map +1 -1
- package/fesm2022/lenne.tech-ng-base-auth.mjs +17 -13
- package/fesm2022/lenne.tech-ng-base-auth.mjs.map +1 -1
- package/fesm2022/{lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs → lenne.tech-ng-base-bug-bug-report-dialog-f6VNOWnW.mjs} +11 -11
- 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
- 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
- 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
- package/fesm2022/lenne.tech-ng-base-bug.mjs +5 -5
- package/fesm2022/lenne.tech-ng-base-files-ui.mjs +3 -3
- package/fesm2022/lenne.tech-ng-base-files.mjs +8 -4
- package/fesm2022/lenne.tech-ng-base-files.mjs.map +1 -1
- package/fesm2022/lenne.tech-ng-base-share.mjs +3 -3
- package/fesm2022/lenne.tech-ng-base-ui-provide.mjs +3 -3
- package/fesm2022/lenne.tech-ng-base-ui.mjs +1206 -1216
- package/fesm2022/lenne.tech-ng-base-ui.mjs.map +1 -1
- package/fesm2022/lenne.tech-ng-base-utils.mjs +9 -9
- package/fesm2022/lenne.tech-ng-base.mjs +27 -24
- package/fesm2022/lenne.tech-ng-base.mjs.map +1 -1
- package/i18n/auth/de.json +1 -3
- package/i18n/auth/en.json +1 -3
- package/i18n/de.json +1 -2
- package/i18n/en.json +1 -2
- package/i18n/ui/de.json +0 -5
- package/i18n/ui/en.json +0 -5
- package/package.json +5 -1
- package/types/lenne.tech-ng-base-auth-provide.d.ts +2 -3
- package/types/lenne.tech-ng-base-auth.d.ts +4 -1
- package/types/lenne.tech-ng-base-files.d.ts +5 -1
- package/types/lenne.tech-ng-base.d.ts +8 -1
|
@@ -106,7 +106,7 @@ var LtScrollService = class LtScrollService {
|
|
|
106
106
|
}
|
|
107
107
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
108
108
|
minVersion: "12.0.0",
|
|
109
|
-
version: "22.2.
|
|
109
|
+
version: "22.2.5",
|
|
110
110
|
ngImport: i0,
|
|
111
111
|
type: LtScrollService,
|
|
112
112
|
deps: [],
|
|
@@ -114,14 +114,14 @@ var LtScrollService = class LtScrollService {
|
|
|
114
114
|
});
|
|
115
115
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
116
116
|
minVersion: "22.0.0",
|
|
117
|
-
version: "22.2.
|
|
117
|
+
version: "22.2.5",
|
|
118
118
|
ngImport: i0,
|
|
119
119
|
type: LtScrollService
|
|
120
120
|
});
|
|
121
121
|
};
|
|
122
122
|
i0.ɵɵngDeclareClassMetadata({
|
|
123
123
|
minVersion: "12.0.0",
|
|
124
|
-
version: "22.2.
|
|
124
|
+
version: "22.2.5",
|
|
125
125
|
ngImport: i0,
|
|
126
126
|
type: LtScrollService,
|
|
127
127
|
decorators: [{ type: Service }]
|
|
@@ -188,7 +188,7 @@ var LtSeoService = class LtSeoService {
|
|
|
188
188
|
}
|
|
189
189
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
190
190
|
minVersion: "12.0.0",
|
|
191
|
-
version: "22.2.
|
|
191
|
+
version: "22.2.5",
|
|
192
192
|
ngImport: i0,
|
|
193
193
|
type: LtSeoService,
|
|
194
194
|
deps: [],
|
|
@@ -196,14 +196,14 @@ var LtSeoService = class LtSeoService {
|
|
|
196
196
|
});
|
|
197
197
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
198
198
|
minVersion: "22.0.0",
|
|
199
|
-
version: "22.2.
|
|
199
|
+
version: "22.2.5",
|
|
200
200
|
ngImport: i0,
|
|
201
201
|
type: LtSeoService
|
|
202
202
|
});
|
|
203
203
|
};
|
|
204
204
|
i0.ɵɵngDeclareClassMetadata({
|
|
205
205
|
minVersion: "12.0.0",
|
|
206
|
-
version: "22.2.
|
|
206
|
+
version: "22.2.5",
|
|
207
207
|
ngImport: i0,
|
|
208
208
|
type: LtSeoService,
|
|
209
209
|
decorators: [{ type: Service }]
|
|
@@ -262,7 +262,7 @@ var LtStorageService = class LtStorageService {
|
|
|
262
262
|
}
|
|
263
263
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
264
264
|
minVersion: "12.0.0",
|
|
265
|
-
version: "22.2.
|
|
265
|
+
version: "22.2.5",
|
|
266
266
|
ngImport: i0,
|
|
267
267
|
type: LtStorageService,
|
|
268
268
|
deps: [],
|
|
@@ -270,14 +270,14 @@ var LtStorageService = class LtStorageService {
|
|
|
270
270
|
});
|
|
271
271
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
272
272
|
minVersion: "22.0.0",
|
|
273
|
-
version: "22.2.
|
|
273
|
+
version: "22.2.5",
|
|
274
274
|
ngImport: i0,
|
|
275
275
|
type: LtStorageService
|
|
276
276
|
});
|
|
277
277
|
};
|
|
278
278
|
i0.ɵɵngDeclareClassMetadata({
|
|
279
279
|
minVersion: "12.0.0",
|
|
280
|
-
version: "22.2.
|
|
280
|
+
version: "22.2.5",
|
|
281
281
|
ngImport: i0,
|
|
282
282
|
type: LtStorageService,
|
|
283
283
|
decorators: [{ type: Service }]
|
|
@@ -181,6 +181,15 @@ var LtApiUrlService = class LtApiUrlService {
|
|
|
181
181
|
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
182
182
|
return `${this.resolveAndWarn().baseUrl}${normalized}`;
|
|
183
183
|
}
|
|
184
|
+
buildBrowserUrl(path) {
|
|
185
|
+
if (isLtAbsoluteUrl(path)) return path;
|
|
186
|
+
return `${resolveLtApiBaseUrl({
|
|
187
|
+
apiProxy: this.runtimeConfig?.apiProxy ?? null,
|
|
188
|
+
apiUrl: this.runtimeConfig?.apiUrl ?? "",
|
|
189
|
+
devMode: isDevMode(),
|
|
190
|
+
scope: "browser"
|
|
191
|
+
}).baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
192
|
+
}
|
|
184
193
|
isProxy() {
|
|
185
194
|
return this.resolve().proxy;
|
|
186
195
|
}
|
|
@@ -197,7 +206,7 @@ var LtApiUrlService = class LtApiUrlService {
|
|
|
197
206
|
}
|
|
198
207
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
199
208
|
minVersion: "12.0.0",
|
|
200
|
-
version: "22.2.
|
|
209
|
+
version: "22.2.5",
|
|
201
210
|
ngImport: i0,
|
|
202
211
|
type: LtApiUrlService,
|
|
203
212
|
deps: [],
|
|
@@ -205,14 +214,14 @@ var LtApiUrlService = class LtApiUrlService {
|
|
|
205
214
|
});
|
|
206
215
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
207
216
|
minVersion: "22.0.0",
|
|
208
|
-
version: "22.2.
|
|
217
|
+
version: "22.2.5",
|
|
209
218
|
ngImport: i0,
|
|
210
219
|
type: LtApiUrlService
|
|
211
220
|
});
|
|
212
221
|
};
|
|
213
222
|
i0.ɵɵngDeclareClassMetadata({
|
|
214
223
|
minVersion: "12.0.0",
|
|
215
|
-
version: "22.2.
|
|
224
|
+
version: "22.2.5",
|
|
216
225
|
ngImport: i0,
|
|
217
226
|
type: LtApiUrlService,
|
|
218
227
|
decorators: [{ type: Service }]
|
|
@@ -264,7 +273,7 @@ var LtCookieService = class LtCookieService {
|
|
|
264
273
|
}
|
|
265
274
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
266
275
|
minVersion: "12.0.0",
|
|
267
|
-
version: "22.2.
|
|
276
|
+
version: "22.2.5",
|
|
268
277
|
ngImport: i0,
|
|
269
278
|
type: LtCookieService,
|
|
270
279
|
deps: [],
|
|
@@ -272,14 +281,14 @@ var LtCookieService = class LtCookieService {
|
|
|
272
281
|
});
|
|
273
282
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
274
283
|
minVersion: "22.0.0",
|
|
275
|
-
version: "22.2.
|
|
284
|
+
version: "22.2.5",
|
|
276
285
|
ngImport: i0,
|
|
277
286
|
type: LtCookieService
|
|
278
287
|
});
|
|
279
288
|
};
|
|
280
289
|
i0.ɵɵngDeclareClassMetadata({
|
|
281
290
|
minVersion: "12.0.0",
|
|
282
|
-
version: "22.2.
|
|
291
|
+
version: "22.2.5",
|
|
283
292
|
ngImport: i0,
|
|
284
293
|
type: LtCookieService,
|
|
285
294
|
decorators: [{ type: Service }]
|
|
@@ -322,14 +331,8 @@ const ltCredentialsFetch = (input, init) => fetch(input, {
|
|
|
322
331
|
credentials: "include",
|
|
323
332
|
...init
|
|
324
333
|
});
|
|
325
|
-
var de_default = { lt: { "error": {
|
|
326
|
-
|
|
327
|
-
"loadFailed": "Fehlerübersetzungen konnten nicht geladen werden"
|
|
328
|
-
} } };
|
|
329
|
-
var en_default = { lt: { "error": {
|
|
330
|
-
"title": "Error",
|
|
331
|
-
"loadFailed": "Failed to load error translations"
|
|
332
|
-
} } };
|
|
334
|
+
var de_default = { lt: { "error": { "title": "Fehler" } } };
|
|
335
|
+
var en_default = { lt: { "error": { "title": "Error" } } };
|
|
333
336
|
const LT_I18N_ADAPTER = new InjectionToken("LT_I18N_ADAPTER");
|
|
334
337
|
const LT_I18N_OPTIONS = new InjectionToken("LT_I18N_OPTIONS", { factory: () => ({}) });
|
|
335
338
|
const LT_I18N_MESSAGES = {
|
|
@@ -372,7 +375,7 @@ var LtI18nService = class LtI18nService {
|
|
|
372
375
|
}
|
|
373
376
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
374
377
|
minVersion: "12.0.0",
|
|
375
|
-
version: "22.2.
|
|
378
|
+
version: "22.2.5",
|
|
376
379
|
ngImport: i0,
|
|
377
380
|
type: LtI18nService,
|
|
378
381
|
deps: [],
|
|
@@ -380,14 +383,14 @@ var LtI18nService = class LtI18nService {
|
|
|
380
383
|
});
|
|
381
384
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
382
385
|
minVersion: "22.0.0",
|
|
383
|
-
version: "22.2.
|
|
386
|
+
version: "22.2.5",
|
|
384
387
|
ngImport: i0,
|
|
385
388
|
type: LtI18nService
|
|
386
389
|
});
|
|
387
390
|
};
|
|
388
391
|
i0.ɵɵngDeclareClassMetadata({
|
|
389
392
|
minVersion: "12.0.0",
|
|
390
|
-
version: "22.2.
|
|
393
|
+
version: "22.2.5",
|
|
391
394
|
ngImport: i0,
|
|
392
395
|
type: LtI18nService,
|
|
393
396
|
decorators: [{ type: Service }]
|
|
@@ -439,7 +442,7 @@ var LtToastService = class LtToastService {
|
|
|
439
442
|
}
|
|
440
443
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
441
444
|
minVersion: "12.0.0",
|
|
442
|
-
version: "22.2.
|
|
445
|
+
version: "22.2.5",
|
|
443
446
|
ngImport: i0,
|
|
444
447
|
type: LtToastService,
|
|
445
448
|
deps: [],
|
|
@@ -447,14 +450,14 @@ var LtToastService = class LtToastService {
|
|
|
447
450
|
});
|
|
448
451
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
449
452
|
minVersion: "22.0.0",
|
|
450
|
-
version: "22.2.
|
|
453
|
+
version: "22.2.5",
|
|
451
454
|
ngImport: i0,
|
|
452
455
|
type: LtToastService
|
|
453
456
|
});
|
|
454
457
|
};
|
|
455
458
|
i0.ɵɵngDeclareClassMetadata({
|
|
456
459
|
minVersion: "12.0.0",
|
|
457
|
-
version: "22.2.
|
|
460
|
+
version: "22.2.5",
|
|
458
461
|
ngImport: i0,
|
|
459
462
|
type: LtToastService,
|
|
460
463
|
decorators: [{ type: Service }]
|
|
@@ -537,7 +540,7 @@ var LtErrorTranslationService = class LtErrorTranslationService {
|
|
|
537
540
|
}
|
|
538
541
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
539
542
|
minVersion: "12.0.0",
|
|
540
|
-
version: "22.2.
|
|
543
|
+
version: "22.2.5",
|
|
541
544
|
ngImport: i0,
|
|
542
545
|
type: LtErrorTranslationService,
|
|
543
546
|
deps: [],
|
|
@@ -545,14 +548,14 @@ var LtErrorTranslationService = class LtErrorTranslationService {
|
|
|
545
548
|
});
|
|
546
549
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
547
550
|
minVersion: "22.0.0",
|
|
548
|
-
version: "22.2.
|
|
551
|
+
version: "22.2.5",
|
|
549
552
|
ngImport: i0,
|
|
550
553
|
type: LtErrorTranslationService
|
|
551
554
|
});
|
|
552
555
|
};
|
|
553
556
|
i0.ɵɵngDeclareClassMetadata({
|
|
554
557
|
minVersion: "12.0.0",
|
|
555
|
-
version: "22.2.
|
|
558
|
+
version: "22.2.5",
|
|
556
559
|
ngImport: i0,
|
|
557
560
|
type: LtErrorTranslationService,
|
|
558
561
|
decorators: [{ type: Service }]
|
|
@@ -582,7 +585,7 @@ function messageOf(value) {
|
|
|
582
585
|
if (nested && typeof nested === "object") return messageOf(nested);
|
|
583
586
|
return "";
|
|
584
587
|
}
|
|
585
|
-
const LT_NG_BASE_VERSION = "22.0
|
|
588
|
+
const LT_NG_BASE_VERSION = "22.1.0";
|
|
586
589
|
const LT_HYDRATED_FLAG = "__ltNgBaseHydrated";
|
|
587
590
|
function provideLtNgBase(config = {}) {
|
|
588
591
|
return makeEnvironmentProviders([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lenne.tech-ng-base.mjs","names":[],"sources":["../../../projects/ng-base/src/lib/warn-once.ts","../../../projects/ng-base/src/lib/pre-hydration-input.ts","../../../projects/ng-base/src/lib/runtime-config.ts","../../../projects/ng-base/src/lib/api-url.ts","../../../projects/ng-base/src/lib/cookie.ts","../../../projects/ng-base/src/lib/crypto.ts","../../../projects/ng-base/src/lib/fetch.ts","../tmp-esm2022/lib/i18n/de.json","../tmp-esm2022/lib/i18n/en.json","../../../projects/ng-base/src/lib/i18n.ts","../../../projects/ng-base/src/lib/toast.ts","../../../projects/ng-base/src/lib/error-translation.ts","../../../projects/ng-base/src/lib/version.ts","../../../projects/ng-base/src/lib/provide-lt-ng-base.ts","../../../projects/ng-base/src/lib/tw.ts"],"sourcesContent":["const warnedKeys = new Set<string>();\n\n/**\n * Log `message` the first time `key` is seen; ignore later calls. The conditions behind these messages (missing API\n * URL, implicit dev proxy) are constant for the process, so repeating them only drowns other output.\n */\nexport function warnLtOnce(key: string, message: string, level: 'error' | 'warn' = 'warn'): void {\n if (warnedKeys.has(key)) {\n return;\n }\n warnedKeys.add(key);\n console[level](message);\n}\n\n/** Test only: forget which one-shot messages were emitted. */\nexport function resetLtWarnOnceState(): void {\n warnedKeys.clear();\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { ApplicationRef, inject, PLATFORM_ID, provideAppInitializer, type EnvironmentProviders } from '@angular/core';\n\n/** Options of the pre-hydration input preservation (`provideLtNgBase({ preHydrationInput })`). */\nexport interface LtPreHydrationInputOptions {\n /** Keep what users type before hydration (default `true`). */\n enabled?: boolean;\n /**\n * How long after hydration to keep restoring, in milliseconds (default `1500`; `0` restores once). Clamped to 30000.\n * When it elapses the snapshot is dropped, so preserved values (passwords included) are not kept for the session.\n */\n maxRestoreMs?: number;\n}\n\ntype LtEditableField = HTMLInputElement | HTMLTextAreaElement;\n\n/** Every `input`/`textarea` under `root` whose value differs from its server-rendered default, with that value. */\nexport function collectLtEditedFields(root: ParentNode): Map<LtEditableField, string> {\n const edited = new Map<LtEditableField, string>();\n for (const node of Array.from(root.querySelectorAll('input, textarea'))) {\n const field = node as LtEditableField;\n if (field.value !== field.defaultValue) {\n edited.set(field, field.value);\n }\n }\n return edited;\n}\n\n/**\n * Write `value` back into `field` and dispatch a bubbling `input` event, so `(input)` handlers, `model()` and Signal\n * Forms adopt it. Returns `false` when the field is detached or already holds the value.\n */\nexport function restoreLtField(field: LtEditableField, value: string): boolean {\n if (!field.isConnected || field.value === value) {\n return false;\n }\n field.value = value;\n field.dispatchEvent(new Event('input', { bubbles: true }));\n return true;\n}\n\n/**\n * Pre-hydration input preservation (browser only).\n *\n * Text typed into a server-rendered field before hydration is in the DOM, but the first change detection after\n * hydration writes the component's (empty) value over it — `[value]` bindings and Signal Forms do — before the replayed\n * events run, so `withEventReplay()` alone loses it (measured in the ng-base playground E2E). The fields are snapshot\n * at app initialisation (the SSR DOM is reused by hydration, so the element references stay valid) and restored once\n * the app is stable, then every 250 ms for `maxRestoreMs` for fields hydrated late; fields stay ordinary editable\n * fields throughout (autofill and screen readers untouched).\n */\nexport function provideLtPreHydrationInput(options: LtPreHydrationInputOptions = {}): EnvironmentProviders {\n return provideAppInitializer(() => {\n if (options.enabled === false || !isPlatformBrowser(inject(PLATFORM_ID)) || typeof document === 'undefined') {\n return;\n }\n const edited = collectLtEditedFields(document);\n if (edited.size === 0) {\n return;\n }\n const configured = options.maxRestoreMs ?? 1500;\n const maxRestoreMs = Number.isFinite(configured) && configured > 0 ? Math.min(configured, 30_000) : 0;\n const sweep = (): void => {\n for (const [field, value] of edited) {\n if (restoreLtField(field, value) || !field.isConnected) {\n edited.delete(field);\n }\n }\n };\n void inject(ApplicationRef)\n .whenStable()\n .then(() => {\n sweep();\n if (maxRestoreMs <= 0 || edited.size === 0) {\n edited.clear();\n return;\n }\n const interval = setInterval(sweep, 250);\n setTimeout(() => {\n clearInterval(interval);\n edited.clear();\n }, maxRestoreMs);\n });\n });\n}\n\n/**\n * Inline script (for `<head>`) that blocks the native submission of forms without `action` until the app handles them.\n * A server-rendered login form submitted before hydration would otherwise send a native GET with the password in the\n * URL. `ltAngularRequestHandler()` writes it into every SSR HTML response; add it to `index.html` yourself without SSR.\n */\nexport const LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT =\n \"document.addEventListener('submit',function(e){var f=e.target;if(f instanceof HTMLFormElement&&!f.hasAttribute('action')){e.preventDefault();}},true);\";\n","import { inject, InjectionToken, REQUEST_CONTEXT } from '@angular/core';\n\nimport { LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT } from './pre-hydration-input';\n\n/**\n * Feature options that can be overridden per stage at runtime (`NG_PUBLIC_LT_<FEATURE>_<OPTION>`).\n *\n * Each feature provider (`provideLtAuth`, `provideLtFiles`, `provideLtAi`, `provideLtSync`, `provideLtNgBase`)\n * merges, in this order: its defaults, the options passed in code, and the values found here.\n */\nexport interface LtPublicFeatureConfig {\n ai?: Record<string, unknown>;\n auth?: Record<string, unknown>;\n errorTranslation?: Record<string, unknown>;\n sync?: Record<string, unknown>;\n tus?: Record<string, unknown>;\n [feature: string]: Record<string, unknown> | undefined;\n}\n\n/**\n * Public runtime configuration of the app, the Angular counterpart of Nuxt's `runtimeConfig.public`.\n *\n * With SSR the server reads it per request from the `NG_*` environment (`readLtRequestContext()` in\n * `@lenne.tech/ng-base/server`), passes it to `AngularNodeAppEngine.handle(req, context)` as request context\n * (`REQUEST_CONTEXT`) and writes it as JSON `<script id=\"ng-runtime-config\">` into every HTML response, also for\n * `RenderMode.Client` routes; the browser reads that script. One build can therefore be promoted from stage to stage.\n * Without SSR the values come from `provideLtNgBase({ runtime })`.\n *\n * Projects may carry additional public values (e.g. `plausibleApiHost`); they travel the same way.\n * Never put a secret here: everything in this object reaches the browser.\n */\nexport interface LtRuntimeConfig {\n [key: string]: unknown;\n /** Commit of the running build (`NG_PUBLIC_APP_COMMIT`, fallback `APP_VERSION_COMMIT`), `unknown` when unset. */\n appCommit?: string;\n /**\n * Deployment stage (`NG_PUBLIC_APP_ENV`), using nest-server's NODE_ENV block names: `local`, `ci`, `e2e`, `develop`,\n * `test`, `production`. Only `production` counts as production; features that are off in production fail closed.\n */\n appEnv?: string;\n /** Version of the running build (from the server, e.g. package.json). */\n appVersion?: string;\n /** Public API base URL (`NG_PUBLIC_API_URL`), e.g. `https://api.example.com`. Empty = relative to the app origin. */\n apiUrl: string;\n /**\n * Route browser API calls through the dev-server proxy under `/api` (`NG_PUBLIC_API_PROXY`).\n * `null` = not configured: the proxy is used in dev mode (`isDevMode()`) with a one-time warning.\n * Never used during SSR.\n */\n apiProxy: boolean | null;\n /** Opt-in cookie namespace (`NG_PUBLIC_COOKIE_PREFIX`); must mirror the backend `COOKIE_PREFIX`. */\n cookiePrefix: string;\n /** Feature options overridden at runtime (`NG_PUBLIC_LT_*`). */\n lt: LtPublicFeatureConfig;\n /** Absolute public URL of the app (`NG_PUBLIC_SITE_URL`), used for redirect/callback URLs, SEO and security.txt. */\n siteUrl: string;\n /** Prefix for `localStorage` keys (`NG_PUBLIC_STORAGE_PREFIX`). */\n storagePrefix: string;\n}\n\n/** Defaults of {@link LtRuntimeConfig}. */\nexport const LT_RUNTIME_CONFIG_DEFAULTS: LtRuntimeConfig = {\n apiProxy: null,\n apiUrl: '',\n cookiePrefix: '',\n lt: {},\n siteUrl: '',\n storagePrefix: '',\n};\n\n/** The resolved public runtime configuration (defaults ← `provideLtNgBase({ runtime })` ← request context / `<script id=\"ng-runtime-config\">`). */\nexport const LT_RUNTIME_CONFIG = new InjectionToken<LtRuntimeConfig>('LT_RUNTIME_CONFIG');\n\n/** What the SSR server passes to `AngularNodeAppEngine.handle(req, context)` (see `readLtRequestContext()`). */\nexport interface LtRequestContext {\n /** Public runtime configuration (also written into the HTML as `<script id=\"ng-runtime-config\">`). */\n runtimeConfig: Partial<LtRuntimeConfig>;\n /** Internal API URL for SSR requests only (`NG_API_URL`), never sent to the browser. */\n serverApiUrl?: string;\n}\n\n/**\n * Runtime configuration on the server: public part plus the server-only API URL. Read from `REQUEST_CONTEXT`\n * (an {@link LtRequestContext}) by default; `provideLtServerRuntimeConfig()` provides it from the environment instead\n * for servers that pass no request context. `null` in the browser.\n */\nexport interface LtServerRuntimeConfig {\n /** Public part. */\n public: Partial<LtRuntimeConfig>;\n /** Internal API URL for SSR requests only (`NG_API_URL`), never sent to the browser. */\n serverApiUrl: string;\n}\n\n/** Server-only runtime configuration (see {@link LtServerRuntimeConfig}). `null` in the browser. */\nexport const LT_SERVER_RUNTIME_CONFIG = new InjectionToken<LtServerRuntimeConfig | null>('LT_SERVER_RUNTIME_CONFIG', {\n factory: () => {\n const context = inject(REQUEST_CONTEXT, { optional: true }) as Partial<LtRequestContext> | null;\n if (!context || typeof context !== 'object' || !context.runtimeConfig) {\n return null;\n }\n return { public: context.runtimeConfig, serverApiUrl: context.serverApiUrl ?? '' };\n },\n});\n\n/** Id of the JSON `<script>` that carries the public runtime configuration into the browser. */\nexport const LT_RUNTIME_CONFIG_SCRIPT_ID = 'ng-runtime-config';\n\n/** Read the runtime configuration the server wrote into the document (`null` when missing or unreadable). */\nexport function readLtRuntimeConfigFromDocument(doc: Document | null | undefined): Partial<LtRuntimeConfig> | null {\n const text = doc?.getElementById?.(LT_RUNTIME_CONFIG_SCRIPT_ID)?.textContent;\n if (!text) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(text);\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Partial<LtRuntimeConfig>) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Serialise the public runtime configuration for an inline `<script type=\"application/json\">`: `<`, `>`, `&`, U+2028\n * and U+2029 are escaped, so a value can never close the script element, open a comment or form an HTML entity.\n */\nexport function serializeLtRuntimeConfig(config: Partial<LtRuntimeConfig>): string {\n return JSON.stringify(config)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Insert the runtime configuration as the first child of `<head>` (documents without `<head>` stay untouched), by\n * default preceded by the pre-hydration submit guard (`LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT`).\n */\nexport function injectLtRuntimeConfigScript(html: string, config: Partial<LtRuntimeConfig>, options: { submitGuard?: boolean } = {}): string {\n const head = /<head(\\s[^>]*)?>/i.exec(html);\n if (!head) {\n return html;\n }\n const at = head.index + head[0].length;\n const guard = options.submitGuard === false ? '' : `<script>${LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT}</script>`;\n return `${html.slice(0, at)}${guard}<script id=\"${LT_RUNTIME_CONFIG_SCRIPT_ID}\" type=\"application/json\">${serializeLtRuntimeConfig(config)}</script>${html.slice(at)}`;\n}\n\n/**\n * Merge partial runtime configurations onto a base. `lt` is merged per feature; `undefined` values never\n * override (an unset env variable keeps the code default).\n */\nexport function mergeLtRuntimeConfig(base: LtRuntimeConfig, ...overrides: (Partial<LtRuntimeConfig> | null | undefined)[]): LtRuntimeConfig {\n const result: LtRuntimeConfig = { ...base, lt: { ...base.lt } };\n for (const override of overrides) {\n if (!override) {\n continue;\n }\n for (const [key, value] of Object.entries(override) as [keyof LtRuntimeConfig, unknown][]) {\n if (value === undefined) {\n continue;\n }\n if (key === 'lt') {\n for (const [feature, options] of Object.entries((value ?? {}) as LtPublicFeatureConfig)) {\n result.lt[feature] = { ...result.lt[feature], ...definedEntries(options ?? {}) };\n }\n continue;\n }\n (result as unknown as Record<string, unknown>)[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Merge feature options: defaults ← code options ← runtime overrides. Nested plain objects are merged one level\n * deep (e.g. `auth.interceptor`), arrays and primitives replace. `undefined` never overrides.\n */\nexport function mergeLtFeatureOptions<T extends object>(defaults: T, ...overrides: (object | null | undefined)[]): T {\n const result: Record<string, unknown> = { ...(defaults as Record<string, unknown>) };\n for (const override of overrides) {\n if (!override) {\n continue;\n }\n for (const [key, value] of Object.entries(override)) {\n if (value === undefined) {\n continue;\n }\n const current = result[key];\n result[key] = isPlainObject(current) && isPlainObject(value) ? { ...current, ...definedEntries(value) } : value;\n }\n }\n return result as T;\n}\n\nfunction definedEntries(source: object): Record<string, unknown> {\n return Object.fromEntries(Object.entries(source).filter(([, value]) => value !== undefined));\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { isPlatformServer } from '@angular/common';\nimport { inject, isDevMode, PLATFORM_ID, Service } from '@angular/core';\n\nimport { warnLtOnce } from './warn-once';\nimport { LT_RUNTIME_CONFIG, LT_SERVER_RUNTIME_CONFIG } from './runtime-config';\n\n/** Render scope an API URL is resolved for. */\nexport type LtRenderScope = 'browser' | 'server';\n\n/** Outcome of resolving the API base URL for the current render scope. */\nexport interface LtApiUrlResolution {\n /** Base URL to prefix onto API paths: `'/api'` in proxy mode, `''` when unresolved. No trailing slash. */\n baseUrl: string;\n /** `true` when no API URL is configured for {@link scope}. */\n missing: boolean;\n /** `true` when the dev-server proxy prefix `/api` is in use. */\n proxy: boolean;\n /** Scope the resolution was performed for. */\n scope: LtRenderScope;\n}\n\n/** Inputs of {@link resolveLtApiBaseUrl}. */\nexport interface LtApiUrlInputs {\n /** `NG_PUBLIC_API_PROXY` (`null` = unset). */\n apiProxy: boolean | null;\n /** `NG_PUBLIC_API_URL`. */\n apiUrl: string;\n /** Whether the app runs in dev mode (`isDevMode()`), used when `apiProxy` is unset. */\n devMode: boolean;\n /** Render scope. */\n scope: LtRenderScope;\n /** `NG_API_URL` (server only). */\n serverApiUrl?: string;\n}\n\nconst MISSING_API_URL_WARNING: Record<LtRenderScope, string> = {\n browser:\n '[@lenne.tech/ng-base] No API URL configured. Set NG_PUBLIC_API_URL. API paths stay relative and resolve against the app ' +\n 'origin, so auth and setup calls 404 unless the app is served behind a same-origin reverse proxy.',\n server:\n '[@lenne.tech/ng-base] No API URL configured for SSR. Set NG_API_URL or NG_PUBLIC_API_URL. API paths stay relative, so ' + 'server-side requests never reach the backend.',\n};\n\n/**\n * Pure resolution of the API base URL (no DI, no logging).\n *\n * - Server: `NG_API_URL` → `NG_PUBLIC_API_URL` → `''`. The proxy is never used during SSR.\n * - Browser: `/api` when the proxy is on (explicitly, or implicitly in dev mode while unset), else `NG_PUBLIC_API_URL` → `''`.\n *\n * Trailing slashes are stripped so `${baseUrl}${path}` never produces a double slash.\n */\nexport function resolveLtApiBaseUrl(inputs: LtApiUrlInputs): LtApiUrlResolution {\n if (inputs.scope === 'server') {\n const apiUrl = inputs.serverApiUrl || inputs.apiUrl || '';\n return { baseUrl: stripTrailingSlashes(apiUrl), missing: !apiUrl, proxy: false, scope: 'server' };\n }\n if (inputs.apiProxy === true || (inputs.apiProxy === null && inputs.devMode)) {\n return { baseUrl: '/api', missing: false, proxy: true, scope: 'browser' };\n }\n const apiUrl = inputs.apiUrl || '';\n return { baseUrl: stripTrailingSlashes(apiUrl), missing: !apiUrl, proxy: false, scope: 'browser' };\n}\n\n/** Whether `url` is absolute (`scheme:` or protocol-relative `//`). */\nexport function isLtAbsoluteUrl(url: string): boolean {\n return /^[a-z][a-z\\d+\\-.]*:/i.test(url) || url.startsWith('//');\n}\n\n/**\n * The single URL builder used by auth, system setup, error translation, files, AI and sync.\n *\n * Reads {@link LT_RUNTIME_CONFIG} and, on the server, {@link LT_SERVER_RUNTIME_CONFIG}. A missing API URL is warned\n * about once per scope; an implicit dev proxy (unset `NG_PUBLIC_API_PROXY` in dev mode) once as well.\n */\n@Service()\nexport class LtApiUrlService {\n private readonly runtimeConfig = inject(LT_RUNTIME_CONFIG, { optional: true });\n private readonly serverConfig = inject(LT_SERVER_RUNTIME_CONFIG, { optional: true });\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n /** Resolve the base URL for the current scope without logging. */\n resolve(): LtApiUrlResolution {\n return resolveLtApiBaseUrl({\n apiProxy: this.runtimeConfig?.apiProxy ?? null,\n apiUrl: this.runtimeConfig?.apiUrl ?? '',\n devMode: isDevMode(),\n scope: this.isServer ? 'server' : 'browser',\n serverApiUrl: this.serverConfig?.serverApiUrl,\n });\n }\n\n /** Base URL (no trailing slash) for the current scope. */\n baseUrl(): string {\n return this.resolveAndWarn().baseUrl;\n }\n\n /** Build a full API URL for a path such as `/iam/sign-in/email`. Absolute URLs are returned unchanged. */\n buildUrl(path: string): string {\n if (isLtAbsoluteUrl(path)) {\n return path;\n }\n const normalized = path.startsWith('/') ? path : `/${path}`;\n return `${this.resolveAndWarn().baseUrl}${normalized}`;\n }\n\n /** Whether browser requests go through the dev-server proxy under `/api`. Always `false` on the server. */\n isProxy(): boolean {\n return this.resolve().proxy;\n }\n\n /** Report a missing API URL (`dev`: warning, production: error), at most once per scope. Used by the config check. */\n reportMissing(): boolean {\n const resolution = this.resolve();\n if (resolution.missing) {\n warnLtOnce(`missing-api-url:${resolution.scope}`, MISSING_API_URL_WARNING[resolution.scope], isDevMode() ? 'warn' : 'error');\n }\n return resolution.missing;\n }\n\n private resolveAndWarn(): LtApiUrlResolution {\n const resolution = this.resolve();\n if (resolution.missing) {\n warnLtOnce(`missing-api-url:${resolution.scope}`, MISSING_API_URL_WARNING[resolution.scope]);\n }\n if (resolution.proxy && this.runtimeConfig?.apiProxy === null) {\n warnLtOnce(\n 'api-proxy-implicit',\n '[@lenne.tech/ng-base] API proxy (/api) activated implicitly because the app runs in dev mode. ' +\n 'Set NG_PUBLIC_API_PROXY=true to make it explicit, or NG_PUBLIC_API_PROXY=false to call the API directly.',\n );\n }\n return resolution;\n }\n}\n\nfunction stripTrailingSlashes(url: string): string {\n return url.replace(/\\/+$/, '');\n}\n","import { DOCUMENT, isPlatformServer } from '@angular/common';\nimport { inject, PLATFORM_ID, REQUEST, Service } from '@angular/core';\n\n/** Options for {@link LtCookieService.set}. */\nexport interface LtCookieOptions {\n /** Cookie domain. Omit for a host-only cookie. */\n domain?: string;\n /** Lifetime in seconds. Omit for a session cookie. */\n maxAge?: number;\n /** Cookie path (default `/`). */\n path?: string;\n /** SameSite attribute (default `lax`). */\n sameSite?: 'lax' | 'none' | 'strict';\n /** Secure attribute (default: `true` on `https:`). */\n secure?: boolean;\n}\n\n/**\n * SSR-aware cookie access.\n *\n * - Browser: reads and writes `document.cookie`.\n * - Server: reads the `Cookie` header of the incoming request (`REQUEST`); never writes. A server render must not\n * set auth cookies (it would write `{ user: null }` over a valid browser state), so `set`/`delete` are no-ops there.\n */\n@Service()\nexport class LtCookieService {\n private readonly document = inject(DOCUMENT);\n private readonly request = inject(REQUEST, { optional: true });\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n /** Raw cookie string (`a=1; b=2`) of the current request or document. */\n raw(): string {\n if (this.isServer) {\n return this.request?.headers.get('cookie') ?? '';\n }\n try {\n return this.document.cookie ?? '';\n } catch {\n return '';\n }\n }\n\n /** First decoded value of `name`, or `null`. */\n get(name: string): string | null {\n return this.getAll(name)[0] ?? null;\n }\n\n /**\n * Every decoded value of `name`, in header order. A host-only cookie and a domain-scoped twin can coexist; callers\n * that must pick the right one (the auth state) look at all of them.\n */\n getAll(name: string): string[] {\n return parseLtCookies(this.raw(), name);\n }\n\n /** Write a cookie (browser only). The value is URI-encoded. */\n set(name: string, value: string, options: LtCookieOptions = {}): void {\n if (this.isServer) {\n return;\n }\n const secure = options.secure ?? this.document.location?.protocol === 'https:';\n const parts = [`${name}=${encodeURIComponent(value)}`, `path=${options.path ?? '/'}`, `samesite=${options.sameSite ?? 'lax'}`];\n if (options.maxAge !== undefined) {\n parts.push(`max-age=${Math.max(0, Math.floor(options.maxAge))}`);\n }\n if (options.domain) {\n parts.push(`domain=${options.domain}`);\n }\n if (secure) {\n parts.push('secure');\n }\n this.document.cookie = parts.join('; ');\n }\n\n /** Delete a cookie on the given path/domain slot (browser only). */\n delete(name: string, options: Pick<LtCookieOptions, 'domain' | 'path'> = {}): void {\n if (this.isServer) {\n return;\n }\n const parts = [`${name}=`, `path=${options.path ?? '/'}`, 'expires=Thu, 01 Jan 1970 00:00:00 GMT', 'max-age=0'];\n if (options.domain) {\n parts.push(`domain=${options.domain}`);\n }\n this.document.cookie = parts.join('; ');\n }\n}\n\n/** Parse every value of cookie `name` out of a raw cookie string (decoded; malformed encodings are kept raw). */\nexport function parseLtCookies(cookieString: string, name: string): string[] {\n const prefix = `${name}=`;\n const values: string[] = [];\n for (const entry of (cookieString || '').split(/;\\s*/)) {\n if (!entry.startsWith(prefix)) {\n continue;\n }\n const value = entry.slice(prefix.length);\n try {\n values.push(decodeURIComponent(value));\n } catch {\n values.push(value);\n }\n }\n return values;\n}\n","/**\n * SHA-256 of a string as lowercase hex (WebCrypto).\n *\n * nest-server expects every password to arrive SHA-256 hashed (it re-hashes server-side). The auth service applies\n * this to each credential field; use it yourself only for custom endpoints that follow the same rule. Because the\n * hash hides the length, enforce password rules in the form, not on the server.\n */\nexport async function ltSha256(message: string): Promise<string> {\n const data = new TextEncoder().encode(message);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/** Encode bytes as base64url without padding (WebAuthn). */\nexport function ltArrayBufferToBase64Url(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/** Decode base64url (with or without padding) into bytes (WebAuthn). */\nexport function ltBase64UrlToUint8Array(base64url: string): Uint8Array<ArrayBuffer> {\n const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(new ArrayBuffer(binary.length));\n for (let index = 0; index < binary.length; index++) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n","import { InjectionToken } from '@angular/core';\n\n/** A `fetch`-compatible function. */\nexport type LtFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * The fetch used by every library call that does not go through `HttpClient`: error translation, system setup,\n * AI (incl. the SSE stream), offline sync and its hint stream, the Better Auth client.\n *\n * The default sends cookies (`credentials: 'include'`). `provideLtAuth()` replaces it with the auth-aware fetch\n * (Bearer token in JWT mode, cookie → JWT switch on 401, stale-JWT refresh, session-loss handling), so SSE and sync\n * get the same auth behaviour as `HttpClient` calls behind `ltAuthInterceptor`.\n */\nexport const LT_FETCH = new InjectionToken<LtFetch>('LT_FETCH', {\n factory: () => ltCredentialsFetch,\n});\n\n/** `fetch` with `credentials: 'include'` unless the caller decided otherwise. */\nexport const ltCredentialsFetch: LtFetch = (input, init) => fetch(input, { credentials: 'include', ...init });\n","","","import { isPlatformBrowser } from '@angular/common';\nimport { computed, inject, InjectionToken, PLATFORM_ID, Service, signal, type Signal } from '@angular/core';\n\nimport de from './i18n/de.json';\nimport en from './i18n/en.json';\n\n/** Nested message tree as in the shipped `i18n/de.json` / `i18n/en.json`. */\nexport interface LtI18nMessages {\n [key: string]: LtI18nMessages | string;\n}\n\n/**\n * Bridge to the app's translation library (Transloco, ngx-translate, …).\n *\n * Bind it when the app already has an i18n setup, so library texts follow the app language and can be overridden\n * in the app's own translation files (keys `lt.error.*` of the core and `lt.<entry>.*` of the entry points: `lt.auth.*`,\n * `lt.files.*`, `lt.share.*`, `lt.ui.*`, `lt.bug.*`; shipped as `i18n/{de,en}.json` and `i18n/<entry>/{de,en}.json`).\n *\n * @example Transloco\n * { provide: LT_I18N_ADAPTER, useFactory: () => {\n * const transloco = inject(TranslocoService);\n * const lang = toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n * return { locale: lang, translate: (key, params) => transloco.translate(key, params) };\n * } }\n */\nexport interface LtI18nAdapter {\n /** Active locale (`de`, `en`, …). */\n readonly locale: Signal<string>;\n /** Translate a key; return `null` (or the key itself) when the app has no translation, then the library text is used. */\n translate(key: string, params?: Record<string, unknown>): string | null;\n}\n\n/** Optional adapter to the app's i18n library. */\nexport const LT_I18N_ADAPTER = new InjectionToken<LtI18nAdapter>('LT_I18N_ADAPTER');\n\n/** Options of the built-in i18n (part of `provideLtNgBase({ i18n })`). */\nexport interface LtI18nOptions {\n /** Locale used when neither the adapter nor the browser language (de/en) decides. Default `de`. */\n defaultLocale?: string;\n /** Additional or overriding messages per locale, merged over the shipped de/en texts. */\n messages?: Record<string, LtI18nMessages>;\n}\n\n/** i18n options token (set by `provideLtNgBase`). */\nexport const LT_I18N_OPTIONS = new InjectionToken<LtI18nOptions>('LT_I18N_OPTIONS', { factory: () => ({}) });\n\n/** The core's shipped messages (`lt.error.*`); the entry points register theirs with `registerLibraryMessages()`. */\nexport const LT_I18N_MESSAGES: Readonly<Record<'de' | 'en', LtI18nMessages>> = { de, en };\n\n/**\n * Signal-based translations for library texts, with an optional adapter to the app's i18n library.\n *\n * Lookup order for `t(key)`: adapter → messages of the active locale → German messages → the key.\n * Locale order: adapter → `setLocale()` → browser language (de/en, browser only) → `defaultLocale` → `de`.\n */\n@Service()\nexport class LtI18nService {\n private readonly adapter = inject(LT_I18N_ADAPTER, { optional: true });\n private readonly options = inject(LT_I18N_OPTIONS);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly explicitLocale = signal<string | null>(null);\n private readonly extraMessages = signal<Record<string, LtI18nMessages>>(this.options.messages ?? {});\n /**\n * Texts of the entry points (`lt.ui.*`, `lt.auth.*` …) registered by {@link registerLibraryMessages}. A plain object:\n * every entry registers its texts before its own components read them, so nothing has to react to a registration.\n */\n private readonly libraryMessages: Record<string, LtI18nMessages> = {};\n private readonly libraryIds = new Set<string>();\n\n /** Active locale. */\n readonly locale = computed(() => this.adapter?.locale() || this.explicitLocale() || this.detectBrowserLocale() || this.options.defaultLocale || 'de');\n\n /** Set the locale (ignored for lookups while an adapter provides one). */\n setLocale(locale: string): void {\n this.explicitLocale.set(locale);\n }\n\n /** Merge messages for a locale over the shipped ones. */\n registerMessages(locale: string, messages: LtI18nMessages): void {\n this.extraMessages.update((current) => ({ ...current, [locale]: deepMerge(current[locale] ?? {}, messages) }));\n }\n\n /**\n * Register the default texts of an entry point once per `id` (`ui`, `auth`, `files` …). They rank below the app's\n * own texts (`provideLtNgBase({ i18n: { messages } })`, `registerMessages()`, the adapter), like the shipped core\n * texts. Entry points call it when they are first used (synchronously, on the server and in the browser alike), so\n * their texts travel with their code instead of the initial bundle.\n */\n registerLibraryMessages(id: string, messages: Readonly<Record<string, LtI18nMessages>>): void {\n if (this.libraryIds.has(id)) {\n return;\n }\n this.libraryIds.add(id);\n for (const [locale, tree] of Object.entries(messages)) {\n this.libraryMessages[locale] = deepMerge(this.libraryMessages[locale] ?? {}, tree);\n }\n }\n\n /** Translate `key`, interpolating `{name}` placeholders from `params`. */\n t(key: string, params?: Record<string, unknown>): string {\n const fromAdapter = this.adapter?.translate(key, params);\n if (fromAdapter && fromAdapter !== key) {\n return fromAdapter;\n }\n const locale = this.locale();\n const message =\n lookup(this.extraMessages()[locale], key) ??\n lookup((LT_I18N_MESSAGES as Record<string, LtI18nMessages>)[locale], key) ??\n lookup(this.libraryMessages[locale], key) ??\n lookup(this.extraMessages()['de'], key) ??\n lookup(LT_I18N_MESSAGES.de, key) ??\n lookup(this.libraryMessages['de'], key) ??\n key;\n return interpolate(message, params);\n }\n\n private detectBrowserLocale(): string | null {\n if (!this.isBrowser || typeof navigator === 'undefined' || !navigator.language) {\n return null;\n }\n const language = navigator.language.split('-')[0]?.toLowerCase() ?? '';\n return language === 'de' || language === 'en' ? language : null;\n }\n}\n\nfunction lookup(messages: LtI18nMessages | undefined, key: string): string | null {\n let node: LtI18nMessages | string | undefined = messages;\n for (const part of key.split('.')) {\n if (!node || typeof node === 'string') {\n return null;\n }\n node = node[part];\n }\n return typeof node === 'string' ? node : null;\n}\n\nfunction interpolate(message: string, params?: Record<string, unknown>): string {\n if (!params) {\n return message;\n }\n return message.replace(/\\{(\\w+)\\}/g, (match, name: string) => (name in params ? String(params[name]) : match));\n}\n\nfunction deepMerge(target: LtI18nMessages, source: LtI18nMessages): LtI18nMessages {\n const result: LtI18nMessages = { ...target };\n for (const [key, value] of Object.entries(source)) {\n const current = result[key];\n result[key] = typeof value === 'object' && typeof current === 'object' ? deepMerge(current, value) : value;\n }\n return result;\n}\n","import { inject, InjectionToken, Service } from '@angular/core';\n\n/** Toast colors (same set as Nuxt UI). */\nexport type LtToastColor = 'error' | 'info' | 'neutral' | 'primary' | 'secondary' | 'success' | 'warning';\n\n/** A toast message. */\nexport interface LtToastOptions {\n color?: LtToastColor;\n description?: string;\n /** Display time in milliseconds (adapter default when omitted). */\n duration?: number;\n title?: string;\n}\n\n/**\n * Bridge to the app's toaster. `provideLtUi()` binds it to Spartan Sonner (`<hlm-toaster />`); bind your own for\n * another UI library. Return `false` when nothing could be shown, the caller then logs to the console.\n */\nexport interface LtToastAdapter {\n show(options: LtToastOptions): boolean;\n}\n\n/** Optional toaster bridge (see {@link LtToastAdapter}). */\nexport const LT_TOAST_ADAPTER = new InjectionToken<LtToastAdapter>('LT_TOAST_ADAPTER');\n\n/** Shows toasts through {@link LT_TOAST_ADAPTER}; without an adapter, `show` returns `false`. */\n@Service()\nexport class LtToastService {\n private readonly adapter = inject(LT_TOAST_ADAPTER, { optional: true });\n\n /** Show a toast. Returns `false` when no toaster is bound or it threw. */\n show(options: LtToastOptions): boolean {\n if (!this.adapter) {\n return false;\n }\n try {\n return this.adapter.show(options) !== false;\n } catch {\n return false;\n }\n }\n\n /** Shortcut for a success toast. */\n success(title: string, description?: string): boolean {\n return this.show({ color: 'success', description, title });\n }\n\n /** Shortcut for an error toast. */\n error(title: string, description?: string): boolean {\n return this.show({ color: 'error', description, title });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { computed, inject, InjectionToken, PLATFORM_ID, Service, signal } from '@angular/core';\n\nimport { LtApiUrlService } from './api-url';\nimport { LT_FETCH } from './fetch';\nimport { LtI18nService } from './i18n';\nimport { LtToastService } from './toast';\n\n/** Response of nest-server `GET /i18n/errors/:locale`. */\nexport interface LtErrorTranslationResponse {\n errors: Record<string, string>;\n}\n\n/** Result of {@link LtErrorTranslationService.parseError}. */\nexport interface LtParsedError {\n /** Error code such as `LTNS_0100`, or `null` when the message carries none. */\n code: string | null;\n /** Message as sent by the backend (without the `#CODE:` marker). */\n developerMessage: string;\n /** Translated message for the active locale; falls back to the developer message, never empty for a message-bearing error. */\n translatedMessage: string;\n}\n\n/** Options of the error translation (part of `provideLtNgBase({ errorTranslation })`, runtime `NG_PUBLIC_LT_ERROR_TRANSLATION_*`). */\nexport interface LtErrorTranslationOptions {\n /** Locale used when i18n does not decide (default `de`). */\n defaultLocale?: string;\n /** Load and apply backend translations (default `true`). */\n enabled?: boolean;\n /** Load the translations for the detected locale while the app starts, browser only (default `true`). */\n preload?: boolean;\n}\n\n/** Resolved error translation options. */\nexport const LT_ERROR_TRANSLATION_OPTIONS = new InjectionToken<Required<LtErrorTranslationOptions>>('LT_ERROR_TRANSLATION_OPTIONS', {\n factory: () => ({ defaultLocale: 'de', enabled: true, preload: true }),\n});\n\nconst ERROR_CODE_REGEX = /^#([A-Z_]+_\\d+):\\s*(.+)$/s;\n\n/** A translation load that takes longer is aborted (the next call retries). */\nexport const LT_ERROR_TRANSLATION_TIMEOUT_MS = 10_000;\n\n/**\n * Translates nest-server error messages of the form `#LTNS_0100: Developer message` with the backend's\n * `GET /i18n/errors/:locale` table.\n */\n@Service()\nexport class LtErrorTranslationService {\n private readonly api = inject(LtApiUrlService);\n private readonly fetch = inject(LT_FETCH);\n private readonly i18n = inject(LtI18nService);\n private readonly toast = inject(LtToastService);\n private readonly options = inject(LT_ERROR_TRANSLATION_OPTIONS);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly translations = signal<Record<string, Record<string, string>>>({});\n private readonly inFlight = new Map<string, Promise<void>>();\n\n /** Locale used for lookups. */\n readonly currentLocale = computed(() => this.i18n.locale() || this.options.defaultLocale || 'de');\n /** Whether translations for the current locale are loaded. */\n readonly isLoaded = computed(() => !!this.translations()[this.currentLocale()]);\n /** Whether a load is running. */\n readonly isLoading = signal(false);\n\n /** Load the backend translations for `locale` (default: current locale). Cached per locale, one request in flight per locale. */\n async loadTranslations(locale?: string): Promise<void> {\n if (!this.options.enabled) {\n return;\n }\n const target = locale || this.currentLocale();\n if (this.translations()[target]) {\n return;\n }\n const running = this.inFlight.get(target);\n if (running) {\n return running;\n }\n const load = (async () => {\n this.isLoading.set(true);\n try {\n const response = await this.fetch(this.api.buildUrl(`/i18n/errors/${encodeURIComponent(target)}`), {\n headers: { accept: 'application/json' },\n signal: AbortSignal.timeout(LT_ERROR_TRANSLATION_TIMEOUT_MS),\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const body = (await response.json()) as Partial<LtErrorTranslationResponse> | null;\n if (body?.errors && typeof body.errors === 'object') {\n this.translations.update((current) => ({ ...current, [target]: body.errors as Record<string, string> }));\n }\n } catch (error) {\n console.warn(`[@lenne.tech/ng-base] Failed to load error translations for ${target}:`, error);\n } finally {\n this.inFlight.delete(target);\n this.isLoading.set(this.inFlight.size > 0);\n }\n })();\n this.inFlight.set(target, load);\n return load;\n }\n\n /** Parse any error shape (string, `Error`, `HttpErrorResponse`, response body) into code + messages. */\n parseError(errorOrMessage: unknown): LtParsedError {\n const message = extractLtErrorMessage(errorOrMessage);\n const match = message.match(ERROR_CODE_REGEX);\n if (!match) {\n return { code: null, developerMessage: message, translatedMessage: message };\n }\n const code = match[1] ?? '';\n const developerMessage = (match[2] ?? '').trim();\n const translatedMessage = this.translations()[this.currentLocale()]?.[code] || developerMessage;\n return { code, developerMessage, translatedMessage };\n }\n\n /** Translated message for an error (never empty for a message-bearing error). */\n translateError(errorOrMessage: unknown): string {\n return this.parseError(errorOrMessage).translatedMessage;\n }\n\n /** Show the translated error as an error toast (browser only); logs to the console when no toaster is bound. */\n showErrorToast(errorOrMessage: unknown, title?: string): void {\n if (!this.isBrowser) {\n return;\n }\n const parsed = this.parseError(errorOrMessage);\n const shown = this.toast.show({ color: 'error', description: parsed.translatedMessage, title: title || this.i18n.t('lt.error.title') });\n if (!shown) {\n console.error('[@lenne.tech/ng-base]', parsed.translatedMessage);\n }\n }\n}\n\n/**\n * Extract the human-relevant message from an error.\n *\n * The response body wins over a transport message: `HttpErrorResponse.message` is\n * `Http failure response for …: 400 Bad Request`, while the backend message (the only one that can carry a\n * `#LTNS_` marker) is in `error.message`. Better Auth errors (`{ code, message }`) and arrays of messages\n * (class-validator) are handled too.\n */\nexport function extractLtErrorMessage(errorOrMessage: unknown): string {\n if (typeof errorOrMessage === 'string') {\n return errorOrMessage;\n }\n if (errorOrMessage === null || errorOrMessage === undefined) {\n return '';\n }\n if (typeof errorOrMessage !== 'object') {\n return String(errorOrMessage);\n }\n const source = errorOrMessage as Record<string, unknown>;\n for (const candidate of [source['error'], source['data'], source['body']]) {\n const fromBody = messageOf(candidate);\n if (fromBody) {\n return fromBody;\n }\n }\n return messageOf(source) || (typeof source['statusText'] === 'string' && source['statusText']) || String(errorOrMessage);\n}\n\nfunction messageOf(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n if (!value || typeof value !== 'object') {\n return '';\n }\n const message = (value as Record<string, unknown>)['message'];\n if (typeof message === 'string') {\n return message;\n }\n if (Array.isArray(message)) {\n return message.filter((part) => typeof part === 'string').join(', ');\n }\n const nested = (value as Record<string, unknown>)['error'];\n if (nested && typeof nested === 'object') {\n return messageOf(nested);\n }\n return '';\n}\n","/** Version of `@lenne.tech/ng-base`; kept in sync with `package.json` by `scripts/sync-version.mjs`. */\nexport const LT_NG_BASE_VERSION = '22.0.4';\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { ApplicationRef, inject, makeEnvironmentProviders, PLATFORM_ID, provideAppInitializer, type EnvironmentProviders } from '@angular/core';\n\nimport { LtApiUrlService } from './api-url';\nimport { LT_ERROR_TRANSLATION_OPTIONS, LtErrorTranslationService, type LtErrorTranslationOptions } from './error-translation';\nimport { LT_I18N_OPTIONS, type LtI18nOptions } from './i18n';\nimport { provideLtPreHydrationInput, type LtPreHydrationInputOptions } from './pre-hydration-input';\nimport {\n LT_RUNTIME_CONFIG,\n LT_RUNTIME_CONFIG_DEFAULTS,\n LT_SERVER_RUNTIME_CONFIG,\n mergeLtFeatureOptions,\n mergeLtRuntimeConfig,\n readLtRuntimeConfigFromDocument,\n type LtRuntimeConfig,\n} from './runtime-config';\nimport { LT_NG_BASE_VERSION } from './version';\n\n/** Options of {@link provideLtNgBase}. */\nexport interface LtNgBaseConfig {\n /**\n * Check at startup that an API URL is configured for the current scope (default `true`): a warning in dev mode, a\n * console error in production. Turn it off for apps without a backend (ng-base/ui only).\n */\n configCheck?: boolean;\n /** Backend error translation (`#LTNS_xxxx`). Turn it off (`enabled: false`) for apps without nest-server. */\n errorTranslation?: LtErrorTranslationOptions;\n /** Library i18n (locale, extra messages). Bind `LT_I18N_ADAPTER` to use the app's i18n library. */\n i18n?: LtI18nOptions;\n /** Keep what users type into server-rendered fields before hydration (default on, see `provideLtPreHydrationInput`). */\n preHydrationInput?: LtPreHydrationInputOptions;\n /**\n * Static runtime configuration: the base that the per-request values of the SSR server (`NG_*` via\n * `readLtRequestContext()`, `<script id=\"ng-runtime-config\">` in the browser) override. Use it for apps without SSR,\n * tests, or defaults.\n */\n runtime?: Partial<LtRuntimeConfig>;\n}\n\n/** Name of the window flag set once the app is stable (hydrated) in the browser; read by `waitForHydration()`. */\nexport const LT_HYDRATED_FLAG = '__ltNgBaseHydrated';\n\n/**\n * Core providers of `@lenne.tech/ng-base`.\n *\n * - {@link LT_RUNTIME_CONFIG}: defaults ← `config.runtime` ← request context (server) / `<script id=\"ng-runtime-config\">` (browser).\n * - Error translation options and preload of the backend translations (browser only, in the background: the start\n * never waits for the API; until they arrive errors show the developer message).\n * - i18n options.\n * - API URL config check.\n * - Pre-hydration input preservation (typed text survives hydration).\n * - Hydration flag `window.__ltNgBaseHydrated` for the Playwright helpers.\n *\n * Feature providers live in the secondary entry points: `provideLtAuth()` (`/auth`), `provideLtFiles()` (`/files`),\n * `provideLtAi()` (`/ai`), `provideLtSync()` (`/sync`), `provideLtUi()` (`/ui`), `provideLtServerRuntimeConfig()` (`/server`).\n *\n * @example app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([ltAuthInterceptor])),\n * provideLtNgBase(),\n * provideLtAuth({ systemSetup: { enabled: true } }),\n * provideLtUi(),\n * ],\n * };\n */\nexport function provideLtNgBase(config: LtNgBaseConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: LT_RUNTIME_CONFIG,\n useFactory: () => {\n const base = mergeLtRuntimeConfig(LT_RUNTIME_CONFIG_DEFAULTS, config.runtime);\n if (!isPlatformBrowser(inject(PLATFORM_ID))) {\n return mergeLtRuntimeConfig(base, inject(LT_SERVER_RUNTIME_CONFIG, { optional: true })?.public);\n }\n return mergeLtRuntimeConfig(base, readLtRuntimeConfigFromDocument(inject(DOCUMENT)));\n },\n },\n {\n provide: LT_ERROR_TRANSLATION_OPTIONS,\n useFactory: () =>\n mergeLtFeatureOptions<Required<LtErrorTranslationOptions>>(\n { defaultLocale: config.i18n?.defaultLocale ?? 'de', enabled: true, preload: true },\n config.errorTranslation,\n inject(LT_RUNTIME_CONFIG).lt.errorTranslation,\n ),\n },\n { provide: LT_I18N_OPTIONS, useValue: config.i18n ?? {} },\n provideLtPreHydrationInput(config.preHydrationInput),\n provideAppInitializer(() => {\n if (config.configCheck !== false) {\n inject(LtApiUrlService).reportMissing();\n }\n }),\n provideAppInitializer(() => {\n const options = inject(LT_ERROR_TRANSLATION_OPTIONS);\n if (!isPlatformBrowser(inject(PLATFORM_ID)) || !options.enabled || !options.preload) {\n return;\n }\n // Not awaited: a slow or hanging API must never hold the app start (the translations are a signal).\n void inject(LtErrorTranslationService).loadTranslations();\n }),\n provideAppInitializer(() => {\n if (!isPlatformBrowser(inject(PLATFORM_ID))) {\n return;\n }\n void inject(ApplicationRef)\n .whenStable()\n .then(() => {\n (globalThis as Record<string, unknown>)[LT_HYDRATED_FLAG] = true;\n (globalThis as Record<string, unknown>)['__ltNgBaseVersion'] = LT_NG_BASE_VERSION;\n });\n }),\n ]);\n}\n","/**\n * Identity function and tagged template for Tailwind class strings outside templates, so the Tailwind IntelliSense\n * extension recognises them (configure `\"tailwindCSS.experimental.classRegex\": [\"tw`([^`]*)`\", \"tw\\\\(['\\\"]([^'\\\"]*)\"]`).\n *\n * @example\n * const card = tw`rounded-lg border p-4`;\n * const active = tw('bg-primary text-primary-foreground');\n */\nexport function tw(strings: TemplateStringsArray | string, ...values: unknown[]): string {\n if (typeof strings === 'string') {\n return strings;\n }\n return strings.reduce((result, part, index) => result + part + (index < values.length ? String(values[index]) : ''), '');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lenne.tech-ng-base.mjs","names":[],"sources":["../../../projects/ng-base/src/lib/warn-once.ts","../../../projects/ng-base/src/lib/pre-hydration-input.ts","../../../projects/ng-base/src/lib/runtime-config.ts","../../../projects/ng-base/src/lib/api-url.ts","../../../projects/ng-base/src/lib/cookie.ts","../../../projects/ng-base/src/lib/crypto.ts","../../../projects/ng-base/src/lib/fetch.ts","../tmp-esm2022/lib/i18n/de.json","../tmp-esm2022/lib/i18n/en.json","../../../projects/ng-base/src/lib/i18n.ts","../../../projects/ng-base/src/lib/toast.ts","../../../projects/ng-base/src/lib/error-translation.ts","../../../projects/ng-base/src/lib/version.ts","../../../projects/ng-base/src/lib/provide-lt-ng-base.ts","../../../projects/ng-base/src/lib/tw.ts"],"sourcesContent":["const warnedKeys = new Set<string>();\n\n/**\n * Log `message` the first time `key` is seen; ignore later calls. The conditions behind these messages (missing API\n * URL, implicit dev proxy) are constant for the process, so repeating them only drowns other output.\n */\nexport function warnLtOnce(key: string, message: string, level: 'error' | 'warn' = 'warn'): void {\n if (warnedKeys.has(key)) {\n return;\n }\n warnedKeys.add(key);\n console[level](message);\n}\n\n/** Test only: forget which one-shot messages were emitted. */\nexport function resetLtWarnOnceState(): void {\n warnedKeys.clear();\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { ApplicationRef, inject, PLATFORM_ID, provideAppInitializer, type EnvironmentProviders } from '@angular/core';\n\n/** Options of the pre-hydration input preservation (`provideLtNgBase({ preHydrationInput })`). */\nexport interface LtPreHydrationInputOptions {\n /** Keep what users type before hydration (default `true`). */\n enabled?: boolean;\n /**\n * How long after hydration to keep restoring, in milliseconds (default `1500`; `0` restores once). Clamped to 30000.\n * When it elapses the snapshot is dropped, so preserved values (passwords included) are not kept for the session.\n */\n maxRestoreMs?: number;\n}\n\ntype LtEditableField = HTMLInputElement | HTMLTextAreaElement;\n\n/** Every `input`/`textarea` under `root` whose value differs from its server-rendered default, with that value. */\nexport function collectLtEditedFields(root: ParentNode): Map<LtEditableField, string> {\n const edited = new Map<LtEditableField, string>();\n for (const node of Array.from(root.querySelectorAll('input, textarea'))) {\n const field = node as LtEditableField;\n if (field.value !== field.defaultValue) {\n edited.set(field, field.value);\n }\n }\n return edited;\n}\n\n/**\n * Write `value` back into `field` and dispatch a bubbling `input` event, so `(input)` handlers, `model()` and Signal\n * Forms adopt it. Returns `false` when the field is detached or already holds the value.\n */\nexport function restoreLtField(field: LtEditableField, value: string): boolean {\n if (!field.isConnected || field.value === value) {\n return false;\n }\n field.value = value;\n field.dispatchEvent(new Event('input', { bubbles: true }));\n return true;\n}\n\n/**\n * Pre-hydration input preservation (browser only).\n *\n * Text typed into a server-rendered field before hydration is in the DOM, but the first change detection after\n * hydration writes the component's (empty) value over it — `[value]` bindings and Signal Forms do — before the replayed\n * events run, so `withEventReplay()` alone loses it (measured in the ng-base playground E2E). The fields are snapshot\n * at app initialisation (the SSR DOM is reused by hydration, so the element references stay valid) and restored once\n * the app is stable, then every 250 ms for `maxRestoreMs` for fields hydrated late; fields stay ordinary editable\n * fields throughout (autofill and screen readers untouched).\n */\nexport function provideLtPreHydrationInput(options: LtPreHydrationInputOptions = {}): EnvironmentProviders {\n return provideAppInitializer(() => {\n if (options.enabled === false || !isPlatformBrowser(inject(PLATFORM_ID)) || typeof document === 'undefined') {\n return;\n }\n const edited = collectLtEditedFields(document);\n if (edited.size === 0) {\n return;\n }\n const configured = options.maxRestoreMs ?? 1500;\n const maxRestoreMs = Number.isFinite(configured) && configured > 0 ? Math.min(configured, 30_000) : 0;\n const sweep = (): void => {\n for (const [field, value] of edited) {\n if (restoreLtField(field, value) || !field.isConnected) {\n edited.delete(field);\n }\n }\n };\n void inject(ApplicationRef)\n .whenStable()\n .then(() => {\n sweep();\n if (maxRestoreMs <= 0 || edited.size === 0) {\n edited.clear();\n return;\n }\n const interval = setInterval(sweep, 250);\n setTimeout(() => {\n clearInterval(interval);\n edited.clear();\n }, maxRestoreMs);\n });\n });\n}\n\n/**\n * Inline script (for `<head>`) that blocks the native submission of forms without `action` until the app handles them.\n * A server-rendered login form submitted before hydration would otherwise send a native GET with the password in the\n * URL. `ltAngularRequestHandler()` writes it into every SSR HTML response; add it to `index.html` yourself without SSR.\n */\nexport const LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT =\n \"document.addEventListener('submit',function(e){var f=e.target;if(f instanceof HTMLFormElement&&!f.hasAttribute('action')){e.preventDefault();}},true);\";\n","import { inject, InjectionToken, REQUEST_CONTEXT } from '@angular/core';\n\nimport { LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT } from './pre-hydration-input';\n\n/**\n * Feature options that can be overridden per stage at runtime (`NG_PUBLIC_LT_<FEATURE>_<OPTION>`).\n *\n * Each feature provider (`provideLtAuth`, `provideLtFiles`, `provideLtAi`, `provideLtSync`, `provideLtNgBase`)\n * merges, in this order: its defaults, the options passed in code, and the values found here.\n */\nexport interface LtPublicFeatureConfig {\n ai?: Record<string, unknown>;\n auth?: Record<string, unknown>;\n errorTranslation?: Record<string, unknown>;\n sync?: Record<string, unknown>;\n tus?: Record<string, unknown>;\n [feature: string]: Record<string, unknown> | undefined;\n}\n\n/**\n * Public runtime configuration of the app, the Angular counterpart of Nuxt's `runtimeConfig.public`.\n *\n * With SSR the server reads it per request from the `NG_*` environment (`readLtRequestContext()` in\n * `@lenne.tech/ng-base/server`), passes it to `AngularNodeAppEngine.handle(req, context)` as request context\n * (`REQUEST_CONTEXT`) and writes it as JSON `<script id=\"ng-runtime-config\">` into every HTML response, also for\n * `RenderMode.Client` routes; the browser reads that script. One build can therefore be promoted from stage to stage.\n * Without SSR the values come from `provideLtNgBase({ runtime })`.\n *\n * Projects may carry additional public values (e.g. `plausibleApiHost`); they travel the same way.\n * Never put a secret here: everything in this object reaches the browser.\n */\nexport interface LtRuntimeConfig {\n [key: string]: unknown;\n /** Commit of the running build (`NG_PUBLIC_APP_COMMIT`, fallback `APP_VERSION_COMMIT`), `unknown` when unset. */\n appCommit?: string;\n /**\n * Deployment stage (`NG_PUBLIC_APP_ENV`), using nest-server's NODE_ENV block names: `local`, `ci`, `e2e`, `develop`,\n * `test`, `production`. Only `production` counts as production; features that are off in production fail closed.\n */\n appEnv?: string;\n /** Version of the running build (from the server, e.g. package.json). */\n appVersion?: string;\n /** Public API base URL (`NG_PUBLIC_API_URL`), e.g. `https://api.example.com`. Empty = relative to the app origin. */\n apiUrl: string;\n /**\n * Route browser API calls through the dev-server proxy under `/api` (`NG_PUBLIC_API_PROXY`).\n * `null` = not configured: the proxy is used in dev mode (`isDevMode()`) with a one-time warning.\n * Never used during SSR.\n */\n apiProxy: boolean | null;\n /** Opt-in cookie namespace (`NG_PUBLIC_COOKIE_PREFIX`); must mirror the backend `COOKIE_PREFIX`. */\n cookiePrefix: string;\n /** Feature options overridden at runtime (`NG_PUBLIC_LT_*`). */\n lt: LtPublicFeatureConfig;\n /** Absolute public URL of the app (`NG_PUBLIC_SITE_URL`), used for redirect/callback URLs, SEO and security.txt. */\n siteUrl: string;\n /** Prefix for `localStorage` keys (`NG_PUBLIC_STORAGE_PREFIX`). */\n storagePrefix: string;\n}\n\n/** Defaults of {@link LtRuntimeConfig}. */\nexport const LT_RUNTIME_CONFIG_DEFAULTS: LtRuntimeConfig = {\n apiProxy: null,\n apiUrl: '',\n cookiePrefix: '',\n lt: {},\n siteUrl: '',\n storagePrefix: '',\n};\n\n/** The resolved public runtime configuration (defaults ← `provideLtNgBase({ runtime })` ← request context / `<script id=\"ng-runtime-config\">`). */\nexport const LT_RUNTIME_CONFIG = new InjectionToken<LtRuntimeConfig>('LT_RUNTIME_CONFIG');\n\n/** What the SSR server passes to `AngularNodeAppEngine.handle(req, context)` (see `readLtRequestContext()`). */\nexport interface LtRequestContext {\n /** Public runtime configuration (also written into the HTML as `<script id=\"ng-runtime-config\">`). */\n runtimeConfig: Partial<LtRuntimeConfig>;\n /** Internal API URL for SSR requests only (`NG_API_URL`), never sent to the browser. */\n serverApiUrl?: string;\n}\n\n/**\n * Runtime configuration on the server: public part plus the server-only API URL. Read from `REQUEST_CONTEXT`\n * (an {@link LtRequestContext}) by default; `provideLtServerRuntimeConfig()` provides it from the environment instead\n * for servers that pass no request context. `null` in the browser.\n */\nexport interface LtServerRuntimeConfig {\n /** Public part. */\n public: Partial<LtRuntimeConfig>;\n /** Internal API URL for SSR requests only (`NG_API_URL`), never sent to the browser. */\n serverApiUrl: string;\n}\n\n/** Server-only runtime configuration (see {@link LtServerRuntimeConfig}). `null` in the browser. */\nexport const LT_SERVER_RUNTIME_CONFIG = new InjectionToken<LtServerRuntimeConfig | null>('LT_SERVER_RUNTIME_CONFIG', {\n factory: () => {\n const context = inject(REQUEST_CONTEXT, { optional: true }) as Partial<LtRequestContext> | null;\n if (!context || typeof context !== 'object' || !context.runtimeConfig) {\n return null;\n }\n return { public: context.runtimeConfig, serverApiUrl: context.serverApiUrl ?? '' };\n },\n});\n\n/** Id of the JSON `<script>` that carries the public runtime configuration into the browser. */\nexport const LT_RUNTIME_CONFIG_SCRIPT_ID = 'ng-runtime-config';\n\n/** Read the runtime configuration the server wrote into the document (`null` when missing or unreadable). */\nexport function readLtRuntimeConfigFromDocument(doc: Document | null | undefined): Partial<LtRuntimeConfig> | null {\n const text = doc?.getElementById?.(LT_RUNTIME_CONFIG_SCRIPT_ID)?.textContent;\n if (!text) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(text);\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Partial<LtRuntimeConfig>) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Serialise the public runtime configuration for an inline `<script type=\"application/json\">`: `<`, `>`, `&`, U+2028\n * and U+2029 are escaped, so a value can never close the script element, open a comment or form an HTML entity.\n */\nexport function serializeLtRuntimeConfig(config: Partial<LtRuntimeConfig>): string {\n return JSON.stringify(config)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Insert the runtime configuration as the first child of `<head>` (documents without `<head>` stay untouched), by\n * default preceded by the pre-hydration submit guard (`LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT`).\n */\nexport function injectLtRuntimeConfigScript(html: string, config: Partial<LtRuntimeConfig>, options: { submitGuard?: boolean } = {}): string {\n const head = /<head(\\s[^>]*)?>/i.exec(html);\n if (!head) {\n return html;\n }\n const at = head.index + head[0].length;\n const guard = options.submitGuard === false ? '' : `<script>${LT_PRE_HYDRATION_SUBMIT_GUARD_SCRIPT}</script>`;\n return `${html.slice(0, at)}${guard}<script id=\"${LT_RUNTIME_CONFIG_SCRIPT_ID}\" type=\"application/json\">${serializeLtRuntimeConfig(config)}</script>${html.slice(at)}`;\n}\n\n/**\n * Merge partial runtime configurations onto a base. `lt` is merged per feature; `undefined` values never\n * override (an unset env variable keeps the code default).\n */\nexport function mergeLtRuntimeConfig(base: LtRuntimeConfig, ...overrides: (Partial<LtRuntimeConfig> | null | undefined)[]): LtRuntimeConfig {\n const result: LtRuntimeConfig = { ...base, lt: { ...base.lt } };\n for (const override of overrides) {\n if (!override) {\n continue;\n }\n for (const [key, value] of Object.entries(override) as [keyof LtRuntimeConfig, unknown][]) {\n if (value === undefined) {\n continue;\n }\n if (key === 'lt') {\n for (const [feature, options] of Object.entries((value ?? {}) as LtPublicFeatureConfig)) {\n result.lt[feature] = { ...result.lt[feature], ...definedEntries(options ?? {}) };\n }\n continue;\n }\n (result as unknown as Record<string, unknown>)[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Merge feature options: defaults ← code options ← runtime overrides. Nested plain objects are merged one level\n * deep (e.g. `auth.interceptor`), arrays and primitives replace. `undefined` never overrides.\n */\nexport function mergeLtFeatureOptions<T extends object>(defaults: T, ...overrides: (object | null | undefined)[]): T {\n const result: Record<string, unknown> = { ...(defaults as Record<string, unknown>) };\n for (const override of overrides) {\n if (!override) {\n continue;\n }\n for (const [key, value] of Object.entries(override)) {\n if (value === undefined) {\n continue;\n }\n const current = result[key];\n result[key] = isPlainObject(current) && isPlainObject(value) ? { ...current, ...definedEntries(value) } : value;\n }\n }\n return result as T;\n}\n\nfunction definedEntries(source: object): Record<string, unknown> {\n return Object.fromEntries(Object.entries(source).filter(([, value]) => value !== undefined));\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { isPlatformServer } from '@angular/common';\nimport { inject, isDevMode, PLATFORM_ID, Service } from '@angular/core';\n\nimport { warnLtOnce } from './warn-once';\nimport { LT_RUNTIME_CONFIG, LT_SERVER_RUNTIME_CONFIG } from './runtime-config';\n\n/** Render scope an API URL is resolved for. */\nexport type LtRenderScope = 'browser' | 'server';\n\n/** Outcome of resolving the API base URL for the current render scope. */\nexport interface LtApiUrlResolution {\n /** Base URL to prefix onto API paths: `'/api'` in proxy mode, `''` when unresolved. No trailing slash. */\n baseUrl: string;\n /** `true` when no API URL is configured for {@link scope}. */\n missing: boolean;\n /** `true` when the dev-server proxy prefix `/api` is in use. */\n proxy: boolean;\n /** Scope the resolution was performed for. */\n scope: LtRenderScope;\n}\n\n/** Inputs of {@link resolveLtApiBaseUrl}. */\nexport interface LtApiUrlInputs {\n /** `NG_PUBLIC_API_PROXY` (`null` = unset). */\n apiProxy: boolean | null;\n /** `NG_PUBLIC_API_URL`. */\n apiUrl: string;\n /** Whether the app runs in dev mode (`isDevMode()`), used when `apiProxy` is unset. */\n devMode: boolean;\n /** Render scope. */\n scope: LtRenderScope;\n /** `NG_API_URL` (server only). */\n serverApiUrl?: string;\n}\n\nconst MISSING_API_URL_WARNING: Record<LtRenderScope, string> = {\n browser:\n '[@lenne.tech/ng-base] No API URL configured. Set NG_PUBLIC_API_URL. API paths stay relative and resolve against the app ' +\n 'origin, so auth and setup calls 404 unless the app is served behind a same-origin reverse proxy.',\n server:\n '[@lenne.tech/ng-base] No API URL configured for SSR. Set NG_API_URL or NG_PUBLIC_API_URL. API paths stay relative, so ' + 'server-side requests never reach the backend.',\n};\n\n/**\n * Pure resolution of the API base URL (no DI, no logging).\n *\n * - Server: `NG_API_URL` → `NG_PUBLIC_API_URL` → `''`. The proxy is never used during SSR.\n * - Browser: `/api` when the proxy is on (explicitly, or implicitly in dev mode while unset), else `NG_PUBLIC_API_URL` → `''`.\n *\n * Trailing slashes are stripped so `${baseUrl}${path}` never produces a double slash.\n */\nexport function resolveLtApiBaseUrl(inputs: LtApiUrlInputs): LtApiUrlResolution {\n if (inputs.scope === 'server') {\n const apiUrl = inputs.serverApiUrl || inputs.apiUrl || '';\n return { baseUrl: stripTrailingSlashes(apiUrl), missing: !apiUrl, proxy: false, scope: 'server' };\n }\n if (inputs.apiProxy === true || (inputs.apiProxy === null && inputs.devMode)) {\n return { baseUrl: '/api', missing: false, proxy: true, scope: 'browser' };\n }\n const apiUrl = inputs.apiUrl || '';\n return { baseUrl: stripTrailingSlashes(apiUrl), missing: !apiUrl, proxy: false, scope: 'browser' };\n}\n\n/** Whether `url` is absolute (`scheme:` or protocol-relative `//`). */\nexport function isLtAbsoluteUrl(url: string): boolean {\n return /^[a-z][a-z\\d+\\-.]*:/i.test(url) || url.startsWith('//');\n}\n\n/**\n * The single URL builder used by auth, system setup, error translation, files, AI and sync.\n *\n * Reads {@link LT_RUNTIME_CONFIG} and, on the server, {@link LT_SERVER_RUNTIME_CONFIG}. A missing API URL is warned\n * about once per scope; an implicit dev proxy (unset `NG_PUBLIC_API_PROXY` in dev mode) once as well.\n */\n@Service()\nexport class LtApiUrlService {\n private readonly runtimeConfig = inject(LT_RUNTIME_CONFIG, { optional: true });\n private readonly serverConfig = inject(LT_SERVER_RUNTIME_CONFIG, { optional: true });\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n /** Resolve the base URL for the current scope without logging. */\n resolve(): LtApiUrlResolution {\n return resolveLtApiBaseUrl({\n apiProxy: this.runtimeConfig?.apiProxy ?? null,\n apiUrl: this.runtimeConfig?.apiUrl ?? '',\n devMode: isDevMode(),\n scope: this.isServer ? 'server' : 'browser',\n serverApiUrl: this.serverConfig?.serverApiUrl,\n });\n }\n\n /** Base URL (no trailing slash) for the current scope. */\n baseUrl(): string {\n return this.resolveAndWarn().baseUrl;\n }\n\n /** Build a full API URL for a path such as `/iam/sign-in/email`. Absolute URLs are returned unchanged. */\n buildUrl(path: string): string {\n if (isLtAbsoluteUrl(path)) {\n return path;\n }\n const normalized = path.startsWith('/') ? path : `/${path}`;\n return `${this.resolveAndWarn().baseUrl}${normalized}`;\n }\n\n /**\n * Build the URL the browser uses for `path`, also during SSR: `NG_PUBLIC_API_URL` (or `/api` behind the dev proxy),\n * never the server-only `NG_API_URL`. For links that end up in the HTML (`href`, `src`), which the browser follows\n * itself: an internal API host would be unreachable there and must not leak into the page. Absolute URLs are returned\n * unchanged.\n */\n buildBrowserUrl(path: string): string {\n if (isLtAbsoluteUrl(path)) {\n return path;\n }\n const resolution = resolveLtApiBaseUrl({\n apiProxy: this.runtimeConfig?.apiProxy ?? null,\n apiUrl: this.runtimeConfig?.apiUrl ?? '',\n devMode: isDevMode(),\n scope: 'browser',\n });\n return `${resolution.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n }\n\n /** Whether browser requests go through the dev-server proxy under `/api`. Always `false` on the server. */\n isProxy(): boolean {\n return this.resolve().proxy;\n }\n\n /** Report a missing API URL (`dev`: warning, production: error), at most once per scope. Used by the config check. */\n reportMissing(): boolean {\n const resolution = this.resolve();\n if (resolution.missing) {\n warnLtOnce(`missing-api-url:${resolution.scope}`, MISSING_API_URL_WARNING[resolution.scope], isDevMode() ? 'warn' : 'error');\n }\n return resolution.missing;\n }\n\n private resolveAndWarn(): LtApiUrlResolution {\n const resolution = this.resolve();\n if (resolution.missing) {\n warnLtOnce(`missing-api-url:${resolution.scope}`, MISSING_API_URL_WARNING[resolution.scope]);\n }\n if (resolution.proxy && this.runtimeConfig?.apiProxy === null) {\n warnLtOnce(\n 'api-proxy-implicit',\n '[@lenne.tech/ng-base] API proxy (/api) activated implicitly because the app runs in dev mode. ' +\n 'Set NG_PUBLIC_API_PROXY=true to make it explicit, or NG_PUBLIC_API_PROXY=false to call the API directly.',\n );\n }\n return resolution;\n }\n}\n\nfunction stripTrailingSlashes(url: string): string {\n return url.replace(/\\/+$/, '');\n}\n","import { DOCUMENT, isPlatformServer } from '@angular/common';\nimport { inject, PLATFORM_ID, REQUEST, Service } from '@angular/core';\n\n/** Options for {@link LtCookieService.set}. */\nexport interface LtCookieOptions {\n /** Cookie domain. Omit for a host-only cookie. */\n domain?: string;\n /** Lifetime in seconds. Omit for a session cookie. */\n maxAge?: number;\n /** Cookie path (default `/`). */\n path?: string;\n /** SameSite attribute (default `lax`). */\n sameSite?: 'lax' | 'none' | 'strict';\n /** Secure attribute (default: `true` on `https:`). */\n secure?: boolean;\n}\n\n/**\n * SSR-aware cookie access.\n *\n * - Browser: reads and writes `document.cookie`.\n * - Server: reads the `Cookie` header of the incoming request (`REQUEST`); never writes. A server render must not\n * set auth cookies (it would write `{ user: null }` over a valid browser state), so `set`/`delete` are no-ops there.\n */\n@Service()\nexport class LtCookieService {\n private readonly document = inject(DOCUMENT);\n private readonly request = inject(REQUEST, { optional: true });\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n /** Raw cookie string (`a=1; b=2`) of the current request or document. */\n raw(): string {\n if (this.isServer) {\n return this.request?.headers.get('cookie') ?? '';\n }\n try {\n return this.document.cookie ?? '';\n } catch {\n return '';\n }\n }\n\n /** First decoded value of `name`, or `null`. */\n get(name: string): string | null {\n return this.getAll(name)[0] ?? null;\n }\n\n /**\n * Every decoded value of `name`, in header order. A host-only cookie and a domain-scoped twin can coexist; callers\n * that must pick the right one (the auth state) look at all of them.\n */\n getAll(name: string): string[] {\n return parseLtCookies(this.raw(), name);\n }\n\n /** Write a cookie (browser only). The value is URI-encoded. */\n set(name: string, value: string, options: LtCookieOptions = {}): void {\n if (this.isServer) {\n return;\n }\n const secure = options.secure ?? this.document.location?.protocol === 'https:';\n const parts = [`${name}=${encodeURIComponent(value)}`, `path=${options.path ?? '/'}`, `samesite=${options.sameSite ?? 'lax'}`];\n if (options.maxAge !== undefined) {\n parts.push(`max-age=${Math.max(0, Math.floor(options.maxAge))}`);\n }\n if (options.domain) {\n parts.push(`domain=${options.domain}`);\n }\n if (secure) {\n parts.push('secure');\n }\n this.document.cookie = parts.join('; ');\n }\n\n /** Delete a cookie on the given path/domain slot (browser only). */\n delete(name: string, options: Pick<LtCookieOptions, 'domain' | 'path'> = {}): void {\n if (this.isServer) {\n return;\n }\n const parts = [`${name}=`, `path=${options.path ?? '/'}`, 'expires=Thu, 01 Jan 1970 00:00:00 GMT', 'max-age=0'];\n if (options.domain) {\n parts.push(`domain=${options.domain}`);\n }\n this.document.cookie = parts.join('; ');\n }\n}\n\n/** Parse every value of cookie `name` out of a raw cookie string (decoded; malformed encodings are kept raw). */\nexport function parseLtCookies(cookieString: string, name: string): string[] {\n const prefix = `${name}=`;\n const values: string[] = [];\n for (const entry of (cookieString || '').split(/;\\s*/)) {\n if (!entry.startsWith(prefix)) {\n continue;\n }\n const value = entry.slice(prefix.length);\n try {\n values.push(decodeURIComponent(value));\n } catch {\n values.push(value);\n }\n }\n return values;\n}\n","/**\n * SHA-256 of a string as lowercase hex (WebCrypto).\n *\n * nest-server expects every password to arrive SHA-256 hashed (it re-hashes server-side). The auth service applies\n * this to each credential field; use it yourself only for custom endpoints that follow the same rule. Because the\n * hash hides the length, enforce password rules in the form, not on the server.\n */\nexport async function ltSha256(message: string): Promise<string> {\n const data = new TextEncoder().encode(message);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/** Encode bytes as base64url without padding (WebAuthn). */\nexport function ltArrayBufferToBase64Url(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/** Decode base64url (with or without padding) into bytes (WebAuthn). */\nexport function ltBase64UrlToUint8Array(base64url: string): Uint8Array<ArrayBuffer> {\n const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(new ArrayBuffer(binary.length));\n for (let index = 0; index < binary.length; index++) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n","import { InjectionToken } from '@angular/core';\n\n/** A `fetch`-compatible function. */\nexport type LtFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * The fetch used by every library call that does not go through `HttpClient`: error translation, system setup,\n * AI (incl. the SSE stream), offline sync and its hint stream, the Better Auth client.\n *\n * The default sends cookies (`credentials: 'include'`). `provideLtAuth()` replaces it with the auth-aware fetch\n * (Bearer token in JWT mode, cookie → JWT switch on 401, stale-JWT refresh, session-loss handling), so SSE and sync\n * get the same auth behaviour as `HttpClient` calls behind `ltAuthInterceptor`.\n */\nexport const LT_FETCH = new InjectionToken<LtFetch>('LT_FETCH', {\n factory: () => ltCredentialsFetch,\n});\n\n/** `fetch` with `credentials: 'include'` unless the caller decided otherwise. */\nexport const ltCredentialsFetch: LtFetch = (input, init) => fetch(input, { credentials: 'include', ...init });\n","","","import { isPlatformBrowser } from '@angular/common';\nimport { computed, inject, InjectionToken, PLATFORM_ID, Service, signal, type Signal } from '@angular/core';\n\nimport de from './i18n/de.json';\nimport en from './i18n/en.json';\n\n/** Nested message tree as in the shipped `i18n/de.json` / `i18n/en.json`. */\nexport interface LtI18nMessages {\n [key: string]: LtI18nMessages | string;\n}\n\n/**\n * Bridge to the app's translation library (Transloco, ngx-translate, …).\n *\n * Bind it when the app already has an i18n setup, so library texts follow the app language and can be overridden\n * in the app's own translation files (keys `lt.error.*` of the core and `lt.<entry>.*` of the entry points: `lt.auth.*`,\n * `lt.files.*`, `lt.share.*`, `lt.ui.*`, `lt.bug.*`; shipped as `i18n/{de,en}.json` and `i18n/<entry>/{de,en}.json`).\n *\n * @example Transloco\n * { provide: LT_I18N_ADAPTER, useFactory: () => {\n * const transloco = inject(TranslocoService);\n * const lang = toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n * return { locale: lang, translate: (key, params) => transloco.translate(key, params) };\n * } }\n */\nexport interface LtI18nAdapter {\n /** Active locale (`de`, `en`, …). */\n readonly locale: Signal<string>;\n /** Translate a key; return `null` (or the key itself) when the app has no translation, then the library text is used. */\n translate(key: string, params?: Record<string, unknown>): string | null;\n}\n\n/** Optional adapter to the app's i18n library. */\nexport const LT_I18N_ADAPTER = new InjectionToken<LtI18nAdapter>('LT_I18N_ADAPTER');\n\n/** Options of the built-in i18n (part of `provideLtNgBase({ i18n })`). */\nexport interface LtI18nOptions {\n /** Locale used when neither the adapter nor the browser language (de/en) decides. Default `de`. */\n defaultLocale?: string;\n /** Additional or overriding messages per locale, merged over the shipped de/en texts. */\n messages?: Record<string, LtI18nMessages>;\n}\n\n/** i18n options token (set by `provideLtNgBase`). */\nexport const LT_I18N_OPTIONS = new InjectionToken<LtI18nOptions>('LT_I18N_OPTIONS', { factory: () => ({}) });\n\n/** The core's shipped messages (`lt.error.*`); the entry points register theirs with `registerLibraryMessages()`. */\nexport const LT_I18N_MESSAGES: Readonly<Record<'de' | 'en', LtI18nMessages>> = { de, en };\n\n/**\n * Signal-based translations for library texts, with an optional adapter to the app's i18n library.\n *\n * Lookup order for `t(key)`: adapter → messages of the active locale → German messages → the key.\n * Locale order: adapter → `setLocale()` → browser language (de/en, browser only) → `defaultLocale` → `de`.\n */\n@Service()\nexport class LtI18nService {\n private readonly adapter = inject(LT_I18N_ADAPTER, { optional: true });\n private readonly options = inject(LT_I18N_OPTIONS);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly explicitLocale = signal<string | null>(null);\n private readonly extraMessages = signal<Record<string, LtI18nMessages>>(this.options.messages ?? {});\n /**\n * Texts of the entry points (`lt.ui.*`, `lt.auth.*` …) registered by {@link registerLibraryMessages}. A plain object:\n * every entry registers its texts before its own components read them, so nothing has to react to a registration.\n */\n private readonly libraryMessages: Record<string, LtI18nMessages> = {};\n private readonly libraryIds = new Set<string>();\n\n /** Active locale. */\n readonly locale = computed(() => this.adapter?.locale() || this.explicitLocale() || this.detectBrowserLocale() || this.options.defaultLocale || 'de');\n\n /** Set the locale (ignored for lookups while an adapter provides one). */\n setLocale(locale: string): void {\n this.explicitLocale.set(locale);\n }\n\n /** Merge messages for a locale over the shipped ones. */\n registerMessages(locale: string, messages: LtI18nMessages): void {\n this.extraMessages.update((current) => ({ ...current, [locale]: deepMerge(current[locale] ?? {}, messages) }));\n }\n\n /**\n * Register the default texts of an entry point once per `id` (`ui`, `auth`, `files` …). They rank below the app's\n * own texts (`provideLtNgBase({ i18n: { messages } })`, `registerMessages()`, the adapter), like the shipped core\n * texts. Entry points call it when they are first used (synchronously, on the server and in the browser alike), so\n * their texts travel with their code instead of the initial bundle.\n */\n registerLibraryMessages(id: string, messages: Readonly<Record<string, LtI18nMessages>>): void {\n if (this.libraryIds.has(id)) {\n return;\n }\n this.libraryIds.add(id);\n for (const [locale, tree] of Object.entries(messages)) {\n this.libraryMessages[locale] = deepMerge(this.libraryMessages[locale] ?? {}, tree);\n }\n }\n\n /** Translate `key`, interpolating `{name}` placeholders from `params`. */\n t(key: string, params?: Record<string, unknown>): string {\n const fromAdapter = this.adapter?.translate(key, params);\n if (fromAdapter && fromAdapter !== key) {\n return fromAdapter;\n }\n const locale = this.locale();\n const message =\n lookup(this.extraMessages()[locale], key) ??\n lookup((LT_I18N_MESSAGES as Record<string, LtI18nMessages>)[locale], key) ??\n lookup(this.libraryMessages[locale], key) ??\n lookup(this.extraMessages()['de'], key) ??\n lookup(LT_I18N_MESSAGES.de, key) ??\n lookup(this.libraryMessages['de'], key) ??\n key;\n return interpolate(message, params);\n }\n\n private detectBrowserLocale(): string | null {\n if (!this.isBrowser || typeof navigator === 'undefined' || !navigator.language) {\n return null;\n }\n const language = navigator.language.split('-')[0]?.toLowerCase() ?? '';\n return language === 'de' || language === 'en' ? language : null;\n }\n}\n\nfunction lookup(messages: LtI18nMessages | undefined, key: string): string | null {\n let node: LtI18nMessages | string | undefined = messages;\n for (const part of key.split('.')) {\n if (!node || typeof node === 'string') {\n return null;\n }\n node = node[part];\n }\n return typeof node === 'string' ? node : null;\n}\n\nfunction interpolate(message: string, params?: Record<string, unknown>): string {\n if (!params) {\n return message;\n }\n return message.replace(/\\{(\\w+)\\}/g, (match, name: string) => (name in params ? String(params[name]) : match));\n}\n\nfunction deepMerge(target: LtI18nMessages, source: LtI18nMessages): LtI18nMessages {\n const result: LtI18nMessages = { ...target };\n for (const [key, value] of Object.entries(source)) {\n const current = result[key];\n result[key] = typeof value === 'object' && typeof current === 'object' ? deepMerge(current, value) : value;\n }\n return result;\n}\n","import { inject, InjectionToken, Service } from '@angular/core';\n\n/** Toast colors (same set as Nuxt UI). */\nexport type LtToastColor = 'error' | 'info' | 'neutral' | 'primary' | 'secondary' | 'success' | 'warning';\n\n/** A toast message. */\nexport interface LtToastOptions {\n color?: LtToastColor;\n description?: string;\n /** Display time in milliseconds (adapter default when omitted). */\n duration?: number;\n title?: string;\n}\n\n/**\n * Bridge to the app's toaster. `provideLtUi()` binds it to Spartan Sonner (`<hlm-toaster />`); bind your own for\n * another UI library. Return `false` when nothing could be shown, the caller then logs to the console.\n */\nexport interface LtToastAdapter {\n show(options: LtToastOptions): boolean;\n}\n\n/** Optional toaster bridge (see {@link LtToastAdapter}). */\nexport const LT_TOAST_ADAPTER = new InjectionToken<LtToastAdapter>('LT_TOAST_ADAPTER');\n\n/** Shows toasts through {@link LT_TOAST_ADAPTER}; without an adapter, `show` returns `false`. */\n@Service()\nexport class LtToastService {\n private readonly adapter = inject(LT_TOAST_ADAPTER, { optional: true });\n\n /** Show a toast. Returns `false` when no toaster is bound or it threw. */\n show(options: LtToastOptions): boolean {\n if (!this.adapter) {\n return false;\n }\n try {\n return this.adapter.show(options) !== false;\n } catch {\n return false;\n }\n }\n\n /** Shortcut for a success toast. */\n success(title: string, description?: string): boolean {\n return this.show({ color: 'success', description, title });\n }\n\n /** Shortcut for an error toast. */\n error(title: string, description?: string): boolean {\n return this.show({ color: 'error', description, title });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { computed, inject, InjectionToken, PLATFORM_ID, Service, signal } from '@angular/core';\n\nimport { LtApiUrlService } from './api-url';\nimport { LT_FETCH } from './fetch';\nimport { LtI18nService } from './i18n';\nimport { LtToastService } from './toast';\n\n/** Response of nest-server `GET /i18n/errors/:locale`. */\nexport interface LtErrorTranslationResponse {\n errors: Record<string, string>;\n}\n\n/** Result of {@link LtErrorTranslationService.parseError}. */\nexport interface LtParsedError {\n /** Error code such as `LTNS_0100`, or `null` when the message carries none. */\n code: string | null;\n /** Message as sent by the backend (without the `#CODE:` marker). */\n developerMessage: string;\n /** Translated message for the active locale; falls back to the developer message, never empty for a message-bearing error. */\n translatedMessage: string;\n}\n\n/** Options of the error translation (part of `provideLtNgBase({ errorTranslation })`, runtime `NG_PUBLIC_LT_ERROR_TRANSLATION_*`). */\nexport interface LtErrorTranslationOptions {\n /** Locale used when i18n does not decide (default `de`). */\n defaultLocale?: string;\n /** Load and apply backend translations (default `true`). */\n enabled?: boolean;\n /** Load the translations for the detected locale while the app starts, browser only (default `true`). */\n preload?: boolean;\n}\n\n/** Resolved error translation options. */\nexport const LT_ERROR_TRANSLATION_OPTIONS = new InjectionToken<Required<LtErrorTranslationOptions>>('LT_ERROR_TRANSLATION_OPTIONS', {\n factory: () => ({ defaultLocale: 'de', enabled: true, preload: true }),\n});\n\nconst ERROR_CODE_REGEX = /^#([A-Z_]+_\\d+):\\s*(.+)$/s;\n\n/** A translation load that takes longer is aborted (the next call retries). */\nexport const LT_ERROR_TRANSLATION_TIMEOUT_MS = 10_000;\n\n/**\n * Translates nest-server error messages of the form `#LTNS_0100: Developer message` with the backend's\n * `GET /i18n/errors/:locale` table.\n */\n@Service()\nexport class LtErrorTranslationService {\n private readonly api = inject(LtApiUrlService);\n private readonly fetch = inject(LT_FETCH);\n private readonly i18n = inject(LtI18nService);\n private readonly toast = inject(LtToastService);\n private readonly options = inject(LT_ERROR_TRANSLATION_OPTIONS);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly translations = signal<Record<string, Record<string, string>>>({});\n private readonly inFlight = new Map<string, Promise<void>>();\n\n /** Locale used for lookups. */\n readonly currentLocale = computed(() => this.i18n.locale() || this.options.defaultLocale || 'de');\n /** Whether translations for the current locale are loaded. */\n readonly isLoaded = computed(() => !!this.translations()[this.currentLocale()]);\n /** Whether a load is running. */\n readonly isLoading = signal(false);\n\n /** Load the backend translations for `locale` (default: current locale). Cached per locale, one request in flight per locale. */\n async loadTranslations(locale?: string): Promise<void> {\n if (!this.options.enabled) {\n return;\n }\n const target = locale || this.currentLocale();\n if (this.translations()[target]) {\n return;\n }\n const running = this.inFlight.get(target);\n if (running) {\n return running;\n }\n const load = (async () => {\n this.isLoading.set(true);\n try {\n const response = await this.fetch(this.api.buildUrl(`/i18n/errors/${encodeURIComponent(target)}`), {\n headers: { accept: 'application/json' },\n signal: AbortSignal.timeout(LT_ERROR_TRANSLATION_TIMEOUT_MS),\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const body = (await response.json()) as Partial<LtErrorTranslationResponse> | null;\n if (body?.errors && typeof body.errors === 'object') {\n this.translations.update((current) => ({ ...current, [target]: body.errors as Record<string, string> }));\n }\n } catch (error) {\n console.warn(`[@lenne.tech/ng-base] Failed to load error translations for ${target}:`, error);\n } finally {\n this.inFlight.delete(target);\n this.isLoading.set(this.inFlight.size > 0);\n }\n })();\n this.inFlight.set(target, load);\n return load;\n }\n\n /** Parse any error shape (string, `Error`, `HttpErrorResponse`, response body) into code + messages. */\n parseError(errorOrMessage: unknown): LtParsedError {\n const message = extractLtErrorMessage(errorOrMessage);\n const match = message.match(ERROR_CODE_REGEX);\n if (!match) {\n return { code: null, developerMessage: message, translatedMessage: message };\n }\n const code = match[1] ?? '';\n const developerMessage = (match[2] ?? '').trim();\n const translatedMessage = this.translations()[this.currentLocale()]?.[code] || developerMessage;\n return { code, developerMessage, translatedMessage };\n }\n\n /** Translated message for an error (never empty for a message-bearing error). */\n translateError(errorOrMessage: unknown): string {\n return this.parseError(errorOrMessage).translatedMessage;\n }\n\n /** Show the translated error as an error toast (browser only); logs to the console when no toaster is bound. */\n showErrorToast(errorOrMessage: unknown, title?: string): void {\n if (!this.isBrowser) {\n return;\n }\n const parsed = this.parseError(errorOrMessage);\n const shown = this.toast.show({ color: 'error', description: parsed.translatedMessage, title: title || this.i18n.t('lt.error.title') });\n if (!shown) {\n console.error('[@lenne.tech/ng-base]', parsed.translatedMessage);\n }\n }\n}\n\n/**\n * Extract the human-relevant message from an error.\n *\n * The response body wins over a transport message: `HttpErrorResponse.message` is\n * `Http failure response for …: 400 Bad Request`, while the backend message (the only one that can carry a\n * `#LTNS_` marker) is in `error.message`. Better Auth errors (`{ code, message }`) and arrays of messages\n * (class-validator) are handled too.\n */\nexport function extractLtErrorMessage(errorOrMessage: unknown): string {\n if (typeof errorOrMessage === 'string') {\n return errorOrMessage;\n }\n if (errorOrMessage === null || errorOrMessage === undefined) {\n return '';\n }\n if (typeof errorOrMessage !== 'object') {\n return String(errorOrMessage);\n }\n const source = errorOrMessage as Record<string, unknown>;\n for (const candidate of [source['error'], source['data'], source['body']]) {\n const fromBody = messageOf(candidate);\n if (fromBody) {\n return fromBody;\n }\n }\n return messageOf(source) || (typeof source['statusText'] === 'string' && source['statusText']) || String(errorOrMessage);\n}\n\nfunction messageOf(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n if (!value || typeof value !== 'object') {\n return '';\n }\n const message = (value as Record<string, unknown>)['message'];\n if (typeof message === 'string') {\n return message;\n }\n if (Array.isArray(message)) {\n return message.filter((part) => typeof part === 'string').join(', ');\n }\n const nested = (value as Record<string, unknown>)['error'];\n if (nested && typeof nested === 'object') {\n return messageOf(nested);\n }\n return '';\n}\n","/** Version of `@lenne.tech/ng-base`; kept in sync with `package.json` by `scripts/sync-version.mjs`. */\nexport const LT_NG_BASE_VERSION = '22.1.0';\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { ApplicationRef, inject, makeEnvironmentProviders, PLATFORM_ID, provideAppInitializer, type EnvironmentProviders } from '@angular/core';\n\nimport { LtApiUrlService } from './api-url';\nimport { LT_ERROR_TRANSLATION_OPTIONS, LtErrorTranslationService, type LtErrorTranslationOptions } from './error-translation';\nimport { LT_I18N_OPTIONS, type LtI18nOptions } from './i18n';\nimport { provideLtPreHydrationInput, type LtPreHydrationInputOptions } from './pre-hydration-input';\nimport {\n LT_RUNTIME_CONFIG,\n LT_RUNTIME_CONFIG_DEFAULTS,\n LT_SERVER_RUNTIME_CONFIG,\n mergeLtFeatureOptions,\n mergeLtRuntimeConfig,\n readLtRuntimeConfigFromDocument,\n type LtRuntimeConfig,\n} from './runtime-config';\nimport { LT_NG_BASE_VERSION } from './version';\n\n/** Options of {@link provideLtNgBase}. */\nexport interface LtNgBaseConfig {\n /**\n * Check at startup that an API URL is configured for the current scope (default `true`): a warning in dev mode, a\n * console error in production. Turn it off for apps without a backend (ng-base/ui only).\n */\n configCheck?: boolean;\n /** Backend error translation (`#LTNS_xxxx`). Turn it off (`enabled: false`) for apps without nest-server. */\n errorTranslation?: LtErrorTranslationOptions;\n /** Library i18n (locale, extra messages). Bind `LT_I18N_ADAPTER` to use the app's i18n library. */\n i18n?: LtI18nOptions;\n /** Keep what users type into server-rendered fields before hydration (default on, see `provideLtPreHydrationInput`). */\n preHydrationInput?: LtPreHydrationInputOptions;\n /**\n * Static runtime configuration: the base that the per-request values of the SSR server (`NG_*` via\n * `readLtRequestContext()`, `<script id=\"ng-runtime-config\">` in the browser) override. Use it for apps without SSR,\n * tests, or defaults.\n */\n runtime?: Partial<LtRuntimeConfig>;\n}\n\n/** Name of the window flag set once the app is stable (hydrated) in the browser; read by `waitForHydration()`. */\nexport const LT_HYDRATED_FLAG = '__ltNgBaseHydrated';\n\n/**\n * Core providers of `@lenne.tech/ng-base`.\n *\n * - {@link LT_RUNTIME_CONFIG}: defaults ← `config.runtime` ← request context (server) / `<script id=\"ng-runtime-config\">` (browser).\n * - Error translation options and preload of the backend translations (browser only, in the background: the start\n * never waits for the API; until they arrive errors show the developer message).\n * - i18n options.\n * - API URL config check.\n * - Pre-hydration input preservation (typed text survives hydration).\n * - Hydration flag `window.__ltNgBaseHydrated` for the Playwright helpers.\n *\n * Feature providers live in the secondary entry points: `provideLtAuth()` (`/auth`), `provideLtFiles()` (`/files`),\n * `provideLtAi()` (`/ai`), `provideLtSync()` (`/sync`), `provideLtUi()` (`/ui`), `provideLtServerRuntimeConfig()` (`/server`).\n *\n * @example app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([ltAuthInterceptor])),\n * provideLtNgBase(),\n * provideLtAuth({ systemSetup: { enabled: true } }),\n * provideLtUi(),\n * ],\n * };\n */\nexport function provideLtNgBase(config: LtNgBaseConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: LT_RUNTIME_CONFIG,\n useFactory: () => {\n const base = mergeLtRuntimeConfig(LT_RUNTIME_CONFIG_DEFAULTS, config.runtime);\n if (!isPlatformBrowser(inject(PLATFORM_ID))) {\n return mergeLtRuntimeConfig(base, inject(LT_SERVER_RUNTIME_CONFIG, { optional: true })?.public);\n }\n return mergeLtRuntimeConfig(base, readLtRuntimeConfigFromDocument(inject(DOCUMENT)));\n },\n },\n {\n provide: LT_ERROR_TRANSLATION_OPTIONS,\n useFactory: () =>\n mergeLtFeatureOptions<Required<LtErrorTranslationOptions>>(\n { defaultLocale: config.i18n?.defaultLocale ?? 'de', enabled: true, preload: true },\n config.errorTranslation,\n inject(LT_RUNTIME_CONFIG).lt.errorTranslation,\n ),\n },\n { provide: LT_I18N_OPTIONS, useValue: config.i18n ?? {} },\n provideLtPreHydrationInput(config.preHydrationInput),\n provideAppInitializer(() => {\n if (config.configCheck !== false) {\n inject(LtApiUrlService).reportMissing();\n }\n }),\n provideAppInitializer(() => {\n const options = inject(LT_ERROR_TRANSLATION_OPTIONS);\n if (!isPlatformBrowser(inject(PLATFORM_ID)) || !options.enabled || !options.preload) {\n return;\n }\n // Not awaited: a slow or hanging API must never hold the app start (the translations are a signal).\n void inject(LtErrorTranslationService).loadTranslations();\n }),\n provideAppInitializer(() => {\n if (!isPlatformBrowser(inject(PLATFORM_ID))) {\n return;\n }\n void inject(ApplicationRef)\n .whenStable()\n .then(() => {\n (globalThis as Record<string, unknown>)[LT_HYDRATED_FLAG] = true;\n (globalThis as Record<string, unknown>)['__ltNgBaseVersion'] = LT_NG_BASE_VERSION;\n });\n }),\n ]);\n}\n","/**\n * Identity function and tagged template for Tailwind class strings outside templates, so the Tailwind IntelliSense\n * extension recognises them (configure `\"tailwindCSS.experimental.classRegex\": [\"tw`([^`]*)`\", \"tw\\\\(['\\\"]([^'\\\"]*)\"]`).\n *\n * @example\n * const card = tw`rounded-lg border p-4`;\n * const active = tw('bg-primary text-primary-foreground');\n */\nexport function tw(strings: TemplateStringsArray | string, ...values: unknown[]): string {\n if (typeof strings === 'string') {\n return strings;\n }\n return strings.reduce((result, part, index) => result + part + (index < values.length ? String(values[index]) : ''), '');\n}\n"],"mappings":"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"}
|
package/i18n/auth/de.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"lt": {
|
|
3
3
|
"auth": {
|
|
4
|
-
"loggingOut": "Abmelden...",
|
|
5
4
|
"noPasskeySelected": "Kein Passkey ausgewählt",
|
|
6
5
|
"passkeyAborted": "Passkey-Authentifizierung wurde abgebrochen",
|
|
7
6
|
"passkeyAlreadyRegistered": "Du hast bereits einen Passkey für diese Website registriert. Lösche ihn zuerst oder verwende einen anderen Authenticator.",
|
|
@@ -10,8 +9,7 @@
|
|
|
10
9
|
"passkeyFailed": "Passkey-Anmeldung fehlgeschlagen",
|
|
11
10
|
"passkeyRegisterFailed": "Passkey-Registrierung fehlgeschlagen",
|
|
12
11
|
"passkeyUnsupported": "Passkeys werden in diesem Browser nicht unterstützt",
|
|
13
|
-
"registerOptionsError": "Konnte Registrierungsoptionen nicht laden"
|
|
14
|
-
"sessionExpired": "Sitzung abgelaufen"
|
|
12
|
+
"registerOptionsError": "Konnte Registrierungsoptionen nicht laden"
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
}
|
package/i18n/auth/en.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"lt": {
|
|
3
3
|
"auth": {
|
|
4
|
-
"loggingOut": "Logging out...",
|
|
5
4
|
"noPasskeySelected": "No passkey selected",
|
|
6
5
|
"passkeyAborted": "Passkey authentication was cancelled",
|
|
7
6
|
"passkeyAlreadyRegistered": "You already registered a passkey for this website. Delete it first or use another authenticator.",
|
|
@@ -10,8 +9,7 @@
|
|
|
10
9
|
"passkeyFailed": "Passkey login failed",
|
|
11
10
|
"passkeyRegisterFailed": "Passkey registration failed",
|
|
12
11
|
"passkeyUnsupported": "Passkeys are not supported in this browser",
|
|
13
|
-
"registerOptionsError": "Could not load registration options"
|
|
14
|
-
"sessionExpired": "Session expired"
|
|
12
|
+
"registerOptionsError": "Could not load registration options"
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
}
|
package/i18n/de.json
CHANGED