@seifer-webapp-factory/user-settings 0.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/README.md +122 -0
- package/backend/templates/config/config-fragment.ts +22 -0
- package/backend/templates/nestjs/tokens.ts +15 -0
- package/backend/templates/nestjs/user-settings.controller.ts +119 -0
- package/backend/templates/nestjs/user-settings.module.ts +107 -0
- package/backend/templates/persistence/migrations/0001_user_settings.sql +16 -0
- package/backend/templates/persistence/migrations/index.ts +37 -0
- package/backend/templates/persistence/pg-settings-store.ts +62 -0
- package/backend/templates/security/security.ts +79 -0
- package/dist/backend/src/descriptors.d.ts +11 -0
- package/dist/backend/src/descriptors.d.ts.map +1 -0
- package/dist/backend/src/descriptors.js +38 -0
- package/dist/backend/src/descriptors.js.map +1 -0
- package/dist/backend/src/errors.d.ts +18 -0
- package/dist/backend/src/errors.d.ts.map +1 -0
- package/dist/backend/src/errors.js +29 -0
- package/dist/backend/src/errors.js.map +1 -0
- package/dist/backend/src/index.d.ts +10 -0
- package/dist/backend/src/index.d.ts.map +1 -0
- package/dist/backend/src/index.js +9 -0
- package/dist/backend/src/index.js.map +1 -0
- package/dist/backend/src/ports.d.ts +58 -0
- package/dist/backend/src/ports.d.ts.map +1 -0
- package/dist/backend/src/ports.js +2 -0
- package/dist/backend/src/ports.js.map +1 -0
- package/dist/backend/src/services.d.ts +26 -0
- package/dist/backend/src/services.d.ts.map +1 -0
- package/dist/backend/src/services.js +104 -0
- package/dist/backend/src/services.js.map +1 -0
- package/dist/contract/config.d.ts +35 -0
- package/dist/contract/config.d.ts.map +1 -0
- package/dist/contract/config.js +20 -0
- package/dist/contract/config.js.map +1 -0
- package/dist/contract/endpoints.d.ts +188 -0
- package/dist/contract/endpoints.d.ts.map +1 -0
- package/dist/contract/endpoints.js +21 -0
- package/dist/contract/endpoints.js.map +1 -0
- package/dist/contract/errors.d.ts +22 -0
- package/dist/contract/errors.d.ts.map +1 -0
- package/dist/contract/errors.js +26 -0
- package/dist/contract/errors.js.map +1 -0
- package/dist/contract/events.d.ts +38 -0
- package/dist/contract/events.d.ts.map +1 -0
- package/dist/contract/events.js +7 -0
- package/dist/contract/events.js.map +1 -0
- package/dist/contract/index.d.ts +11 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +11 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/schemas.d.ts +255 -0
- package/dist/contract/schemas.d.ts.map +1 -0
- package/dist/contract/schemas.js +54 -0
- package/dist/contract/schemas.js.map +1 -0
- package/dist/frontend/src/client.d.ts +22 -0
- package/dist/frontend/src/client.d.ts.map +1 -0
- package/dist/frontend/src/client.js +64 -0
- package/dist/frontend/src/client.js.map +1 -0
- package/dist/frontend/src/descriptors.d.ts +12 -0
- package/dist/frontend/src/descriptors.d.ts.map +1 -0
- package/dist/frontend/src/descriptors.js +44 -0
- package/dist/frontend/src/descriptors.js.map +1 -0
- package/dist/frontend/src/index.d.ts +16 -0
- package/dist/frontend/src/index.d.ts.map +1 -0
- package/dist/frontend/src/index.js +12 -0
- package/dist/frontend/src/index.js.map +1 -0
- package/dist/frontend/src/ssr.d.ts +43 -0
- package/dist/frontend/src/ssr.d.ts.map +1 -0
- package/dist/frontend/src/ssr.js +90 -0
- package/dist/frontend/src/ssr.js.map +1 -0
- package/dist/frontend/src/store.d.ts +46 -0
- package/dist/frontend/src/store.d.ts.map +1 -0
- package/dist/frontend/src/store.js +111 -0
- package/dist/frontend/src/store.js.map +1 -0
- package/dist/frontend/src/useSettings.d.ts +46 -0
- package/dist/frontend/src/useSettings.d.ts.map +1 -0
- package/dist/frontend/src/useSettings.js +100 -0
- package/dist/frontend/src/useSettings.js.map +1 -0
- package/dist/manifest.d.ts +59 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +45 -0
- package/dist/manifest.js.map +1 -0
- package/dist/scaffolder/core/errors.d.ts +24 -0
- package/dist/scaffolder/core/errors.d.ts.map +1 -0
- package/dist/scaffolder/core/errors.js +38 -0
- package/dist/scaffolder/core/errors.js.map +1 -0
- package/dist/scaffolder/core/materialize.d.ts +40 -0
- package/dist/scaffolder/core/materialize.d.ts.map +1 -0
- package/dist/scaffolder/core/materialize.js +38 -0
- package/dist/scaffolder/core/materialize.js.map +1 -0
- package/dist/scaffolder/core/ports.d.ts +28 -0
- package/dist/scaffolder/core/ports.d.ts.map +1 -0
- package/dist/scaffolder/core/ports.js +28 -0
- package/dist/scaffolder/core/ports.js.map +1 -0
- package/dist/scaffolder/core/presence.d.ts +21 -0
- package/dist/scaffolder/core/presence.d.ts.map +1 -0
- package/dist/scaffolder/core/presence.js +20 -0
- package/dist/scaffolder/core/presence.js.map +1 -0
- package/dist/scaffolder/index.d.ts +20 -0
- package/dist/scaffolder/index.d.ts.map +1 -0
- package/dist/scaffolder/index.js +6 -0
- package/dist/scaffolder/index.js.map +1 -0
- package/frontend/templates/i18n/en.json +20 -0
- package/frontend/templates/i18n/nl.json +20 -0
- package/frontend/templates/pages/settings.vue +62 -0
- package/frontend/templates/runtime.ts +19 -0
- package/package.json +79 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-afhandeling van de render-kritieke keys (theme/locale/dir), first-paint-veilig. De aanpak is
|
|
3
|
+
* sterker dan de localStorage+script van next-themes/Nuxt color-mode:
|
|
4
|
+
*
|
|
5
|
+
* - Terugkerende bezoeker: de server leest de cookie en zet de juiste `<html>`-class in de initiële HTML —
|
|
6
|
+
* écht geen flash, geen script nodig.
|
|
7
|
+
* - Eerste bezoek: het OS-thema is server-side niet leesbaar zonder de `Sec-CH-Prefers-Color-Scheme`
|
|
8
|
+
* client-hint, dus (a) `color-scheme: light dark` + `light-dark()` als zero-JS default die het OS bij
|
|
9
|
+
* first paint volgt, en (b) een synchroon `<head>`-script (primaire route, geen last-resort) dat de
|
|
10
|
+
* `<html>`-class alleen zet als een opgeslagen voorkeur de media-query tegenspreekt.
|
|
11
|
+
*
|
|
12
|
+
* Puur & framework-agnostisch: geen DOM-afhankelijkheid, geen module-globale state — makkelijk te testen.
|
|
13
|
+
*/
|
|
14
|
+
export interface RenderState {
|
|
15
|
+
theme: string | null;
|
|
16
|
+
locale: string | null;
|
|
17
|
+
dir: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** Parse een rauwe `Cookie`-header naar een naam→waarde-map (server-side). */
|
|
20
|
+
export declare function parseCookies(cookieHeader: string | null | undefined): Record<string, string>;
|
|
21
|
+
/** Neem de eerste taalcode uit een `Accept-Language`-header (alleen een HINT, nooit een override). */
|
|
22
|
+
export declare function firstAcceptLanguage(acceptLanguage: string | null | undefined): string | null;
|
|
23
|
+
export interface ResolveRenderStateOptions {
|
|
24
|
+
readonly cookieHeader?: string | null;
|
|
25
|
+
readonly cookiePrefix: string;
|
|
26
|
+
readonly acceptLanguage?: string | null;
|
|
27
|
+
readonly defaults?: Partial<RenderState>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve de render-state die de server in de initiële HTML zet. `theme` uit de cookie (of default; `null`
|
|
31
|
+
* → laat CSS `light-dark()` het OS volgen). `locale` uit de cookie, anders de `Accept-Language`-hint,
|
|
32
|
+
* anders de default. `dir` uit de cookie of default.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveRenderState(options: ResolveRenderStateOptions): RenderState;
|
|
35
|
+
/** `<meta name="color-scheme">`-content zodat native controls/scrollbars vóór CSS goed staan. */
|
|
36
|
+
export declare function colorSchemeMeta(theme: string | null): string;
|
|
37
|
+
/**
|
|
38
|
+
* Het synchrone `<head>`-script (primaire first-visit-route). Leest de theme-cookie client-side en zet de
|
|
39
|
+
* `<html data-theme>` vóór first paint, maar ALLEEN als er een opgeslagen voorkeur is — anders laat het
|
|
40
|
+
* `light-dark()` het OS volgen. Injecteer met een CSP-nonce en zet `suppressHydrationWarning` op `<html>`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function renderHeadScript(cookiePrefix: string): string;
|
|
43
|
+
//# sourceMappingURL=ssr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../../../frontend/src/ssr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAW5F;AAED,sGAAsG;AACtG,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAM5F;AAaD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,WAAW,CAQlF;AAED,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI5D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAW7D"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-afhandeling van de render-kritieke keys (theme/locale/dir), first-paint-veilig. De aanpak is
|
|
3
|
+
* sterker dan de localStorage+script van next-themes/Nuxt color-mode:
|
|
4
|
+
*
|
|
5
|
+
* - Terugkerende bezoeker: de server leest de cookie en zet de juiste `<html>`-class in de initiële HTML —
|
|
6
|
+
* écht geen flash, geen script nodig.
|
|
7
|
+
* - Eerste bezoek: het OS-thema is server-side niet leesbaar zonder de `Sec-CH-Prefers-Color-Scheme`
|
|
8
|
+
* client-hint, dus (a) `color-scheme: light dark` + `light-dark()` als zero-JS default die het OS bij
|
|
9
|
+
* first paint volgt, en (b) een synchroon `<head>`-script (primaire route, geen last-resort) dat de
|
|
10
|
+
* `<html>`-class alleen zet als een opgeslagen voorkeur de media-query tegenspreekt.
|
|
11
|
+
*
|
|
12
|
+
* Puur & framework-agnostisch: geen DOM-afhankelijkheid, geen module-globale state — makkelijk te testen.
|
|
13
|
+
*/
|
|
14
|
+
/** Parse een rauwe `Cookie`-header naar een naam→waarde-map (server-side). */
|
|
15
|
+
export function parseCookies(cookieHeader) {
|
|
16
|
+
const out = {};
|
|
17
|
+
if (!cookieHeader)
|
|
18
|
+
return out;
|
|
19
|
+
for (const part of cookieHeader.split(';')) {
|
|
20
|
+
const eq = part.indexOf('=');
|
|
21
|
+
if (eq === -1)
|
|
22
|
+
continue;
|
|
23
|
+
const name = part.slice(0, eq).trim();
|
|
24
|
+
const value = part.slice(eq + 1).trim();
|
|
25
|
+
if (name.length > 0)
|
|
26
|
+
out[name] = decodeURIComponent(value);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** Neem de eerste taalcode uit een `Accept-Language`-header (alleen een HINT, nooit een override). */
|
|
31
|
+
export function firstAcceptLanguage(acceptLanguage) {
|
|
32
|
+
if (!acceptLanguage)
|
|
33
|
+
return null;
|
|
34
|
+
const first = acceptLanguage.split(',')[0];
|
|
35
|
+
if (first === undefined)
|
|
36
|
+
return null;
|
|
37
|
+
const tag = first.split(';')[0]?.trim();
|
|
38
|
+
return tag && tag.length > 0 ? tag : null;
|
|
39
|
+
}
|
|
40
|
+
/** De opgeslagen cel op de cookie is JSON `{ v, ver, t }`; haal er de waarde uit (of null). */
|
|
41
|
+
function cookieValue(raw) {
|
|
42
|
+
if (raw === undefined)
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(raw);
|
|
46
|
+
return typeof parsed.v === 'string' ? parsed.v : null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return typeof raw === 'string' ? raw : null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve de render-state die de server in de initiële HTML zet. `theme` uit de cookie (of default; `null`
|
|
54
|
+
* → laat CSS `light-dark()` het OS volgen). `locale` uit de cookie, anders de `Accept-Language`-hint,
|
|
55
|
+
* anders de default. `dir` uit de cookie of default.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveRenderState(options) {
|
|
58
|
+
const { cookiePrefix, cookieHeader, acceptLanguage, defaults } = options;
|
|
59
|
+
const cookies = parseCookies(cookieHeader);
|
|
60
|
+
const theme = cookieValue(cookies[`${cookiePrefix}theme`]) ?? defaults?.theme ?? null;
|
|
61
|
+
const locale = cookieValue(cookies[`${cookiePrefix}locale`]) ?? firstAcceptLanguage(acceptLanguage) ?? defaults?.locale ?? null;
|
|
62
|
+
const dir = cookieValue(cookies[`${cookiePrefix}dir`]) ?? defaults?.dir ?? null;
|
|
63
|
+
return { theme, locale, dir };
|
|
64
|
+
}
|
|
65
|
+
/** `<meta name="color-scheme">`-content zodat native controls/scrollbars vóór CSS goed staan. */
|
|
66
|
+
export function colorSchemeMeta(theme) {
|
|
67
|
+
if (theme === 'dark')
|
|
68
|
+
return 'dark light';
|
|
69
|
+
if (theme === 'light')
|
|
70
|
+
return 'light dark';
|
|
71
|
+
return 'light dark';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Het synchrone `<head>`-script (primaire first-visit-route). Leest de theme-cookie client-side en zet de
|
|
75
|
+
* `<html data-theme>` vóór first paint, maar ALLEEN als er een opgeslagen voorkeur is — anders laat het
|
|
76
|
+
* `light-dark()` het OS volgen. Injecteer met een CSP-nonce en zet `suppressHydrationWarning` op `<html>`.
|
|
77
|
+
*/
|
|
78
|
+
export function renderHeadScript(cookiePrefix) {
|
|
79
|
+
const cookieName = `${cookiePrefix}theme`;
|
|
80
|
+
return [
|
|
81
|
+
'(function(){try{',
|
|
82
|
+
`var m=document.cookie.match(/(?:^|; )${cookieName}=([^;]*)/);`,
|
|
83
|
+
'if(!m)return;',
|
|
84
|
+
'var raw=decodeURIComponent(m[1]);var t=raw;',
|
|
85
|
+
'try{var p=JSON.parse(raw);if(p&&typeof p.v==="string")t=p.v;}catch(e){}',
|
|
86
|
+
'if(t==="dark"||t==="light"){document.documentElement.setAttribute("data-theme",t);}',
|
|
87
|
+
'}catch(e){}})();',
|
|
88
|
+
].join('');
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=ssr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr.js","sourceRoot":"","sources":["../../../frontend/src/ssr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* De `SettingsStore`-poort + de twee adapters (het mechanisme). Eén seam, twee runtime-backends:
|
|
3
|
+
*
|
|
4
|
+
* - {@link createClientAdapter}: cookie (render-kritieke keys, SSR-leesbaar) + localStorage (functionele
|
|
5
|
+
* prefs), voor de anonieme gebruiker. Junk uit storage valt terug op de descriptor-`default`.
|
|
6
|
+
* - {@link createServerAdapter}: de getypte client over `GET/PUT /settings`, voor de geauthenticeerde
|
|
7
|
+
* gebruiker; render-keys blijven op de cookie gespiegeld zodat SSR post-login blijft werken.
|
|
8
|
+
*
|
|
9
|
+
* De poort is uniform async (zodat één seam een netwerk-adapter kan hosten) en houdt zich aan de
|
|
10
|
+
* client∩server-intersectie. Waarden zijn host-getypt → op de poort-grens `unknown`.
|
|
11
|
+
*/
|
|
12
|
+
import type { SettingsRecord, SettingsSchema } from '../../contract/index.js';
|
|
13
|
+
import type { UserSettingsClient } from './client.js';
|
|
14
|
+
/** De swappable seam die beide adapters implementeren. */
|
|
15
|
+
export interface SettingsStore {
|
|
16
|
+
get(key: string): Promise<SettingsRecord | null>;
|
|
17
|
+
set(key: string, value: unknown, baseVersion?: number): Promise<SettingsRecord>;
|
|
18
|
+
all(): Promise<Record<string, SettingsRecord>>;
|
|
19
|
+
/** Wist één key (of alles). Client: verwijdert de lokale kopie. Server: reset naar de descriptor-default. */
|
|
20
|
+
clear(key?: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Minimale key-value-poort (cookie of localStorage). Bewust synchroon; SSR levert een cookie-only variant. */
|
|
23
|
+
export interface KeyValueStore {
|
|
24
|
+
get(key: string): string | null;
|
|
25
|
+
set(key: string, value: string): void;
|
|
26
|
+
remove(key: string): void;
|
|
27
|
+
}
|
|
28
|
+
export interface CreateClientAdapterOptions {
|
|
29
|
+
readonly descriptors: SettingsSchema;
|
|
30
|
+
/** Server-leesbare cookie-toegang (app-kernel). */
|
|
31
|
+
readonly cookie: KeyValueStore;
|
|
32
|
+
/** localStorage (browser). */
|
|
33
|
+
readonly local: KeyValueStore;
|
|
34
|
+
readonly cookiePrefix: string;
|
|
35
|
+
readonly renderKeys: readonly string[];
|
|
36
|
+
readonly clock: () => number;
|
|
37
|
+
}
|
|
38
|
+
/** De anonieme client-adapter: cookie voor render-keys, localStorage voor de rest. */
|
|
39
|
+
export declare function createClientAdapter(options: CreateClientAdapterOptions): SettingsStore;
|
|
40
|
+
export interface CreateServerAdapterOptions {
|
|
41
|
+
readonly client: UserSettingsClient;
|
|
42
|
+
readonly descriptors: SettingsSchema;
|
|
43
|
+
}
|
|
44
|
+
/** De geauthenticeerde server-adapter: de getypte client over GET/PUT /settings. */
|
|
45
|
+
export declare function createServerAdapter(options: CreateServerAdapterOptions): SettingsStore;
|
|
46
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../frontend/src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAqB,cAAc,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEjG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,0DAA0D;AAC1D,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAChF,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IAC/C,6GAA6G;IAC7G,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,+GAA+G;AAC/G,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA2BD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC;CAC9B;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa,CAsDtF;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACtC;AAED,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa,CAkCtF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { coerceValue, indexDescriptors } from './descriptors.js';
|
|
2
|
+
function parseCell(raw) {
|
|
3
|
+
if (raw === null)
|
|
4
|
+
return null;
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(raw);
|
|
7
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
8
|
+
return null;
|
|
9
|
+
const cell = parsed;
|
|
10
|
+
if (typeof cell.ver !== 'number' || typeof cell.t !== 'number')
|
|
11
|
+
return null;
|
|
12
|
+
return { v: cell.v, ver: cell.ver, t: cell.t, del: cell.del === true ? true : undefined };
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function toRecord(descriptor, cell) {
|
|
19
|
+
const value = coerceValue(descriptor, cell.v, cell.ver);
|
|
20
|
+
return { value, version: cell.ver, updatedAt: cell.t, ...(cell.del ? { deleted: true } : {}) };
|
|
21
|
+
}
|
|
22
|
+
/** De anonieme client-adapter: cookie voor render-keys, localStorage voor de rest. */
|
|
23
|
+
export function createClientAdapter(options) {
|
|
24
|
+
const { descriptors, cookie, local, cookiePrefix, renderKeys, clock } = options;
|
|
25
|
+
const index = indexDescriptors(descriptors);
|
|
26
|
+
const renderSet = new Set(renderKeys);
|
|
27
|
+
function isRender(descriptor) {
|
|
28
|
+
return descriptor.namespace === 'render' || descriptor.cookie === true || renderSet.has(descriptor.key);
|
|
29
|
+
}
|
|
30
|
+
function backendFor(descriptor) {
|
|
31
|
+
return isRender(descriptor)
|
|
32
|
+
? { store: cookie, storageKey: `${cookiePrefix}${descriptor.key}` }
|
|
33
|
+
: { store: local, storageKey: `${cookiePrefix}${descriptor.key}` };
|
|
34
|
+
}
|
|
35
|
+
async function get(key) {
|
|
36
|
+
const descriptor = index.get(key);
|
|
37
|
+
if (descriptor === undefined)
|
|
38
|
+
return null;
|
|
39
|
+
const { store, storageKey } = backendFor(descriptor);
|
|
40
|
+
const cell = parseCell(store.get(storageKey));
|
|
41
|
+
if (cell === null)
|
|
42
|
+
return null;
|
|
43
|
+
return toRecord(descriptor, cell);
|
|
44
|
+
}
|
|
45
|
+
async function set(key, value, baseVersion) {
|
|
46
|
+
const descriptor = index.get(key);
|
|
47
|
+
if (descriptor === undefined)
|
|
48
|
+
throw new Error(`[user-settings] unknown_key: ${key}`);
|
|
49
|
+
const parsed = descriptor.schema.safeParse(value);
|
|
50
|
+
if (!parsed.success)
|
|
51
|
+
throw new Error(`[user-settings] validation_failed: ${key}`);
|
|
52
|
+
const { store, storageKey } = backendFor(descriptor);
|
|
53
|
+
const current = parseCell(store.get(storageKey));
|
|
54
|
+
const nextVersion = (baseVersion ?? current?.ver ?? 0) + 1;
|
|
55
|
+
const cell = { v: parsed.data, ver: nextVersion, t: clock() };
|
|
56
|
+
store.set(storageKey, JSON.stringify(cell));
|
|
57
|
+
return { value: parsed.data, version: nextVersion, updatedAt: cell.t };
|
|
58
|
+
}
|
|
59
|
+
async function all() {
|
|
60
|
+
const out = {};
|
|
61
|
+
for (const descriptor of descriptors) {
|
|
62
|
+
const record = await get(descriptor.key);
|
|
63
|
+
if (record !== null)
|
|
64
|
+
out[descriptor.key] = record;
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
async function clear(key) {
|
|
69
|
+
const targets = key !== undefined ? [index.get(key)].filter(Boolean) : descriptors;
|
|
70
|
+
for (const descriptor of targets) {
|
|
71
|
+
const { store, storageKey } = backendFor(descriptor);
|
|
72
|
+
store.remove(storageKey);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { get, set, all, clear };
|
|
76
|
+
}
|
|
77
|
+
/** De geauthenticeerde server-adapter: de getypte client over GET/PUT /settings. */
|
|
78
|
+
export function createServerAdapter(options) {
|
|
79
|
+
const { client, descriptors } = options;
|
|
80
|
+
const index = indexDescriptors(descriptors);
|
|
81
|
+
async function all() {
|
|
82
|
+
const res = await client.getSettings();
|
|
83
|
+
const out = {};
|
|
84
|
+
for (const [key, record] of Object.entries(res.settings)) {
|
|
85
|
+
out[key] = {
|
|
86
|
+
value: record.value,
|
|
87
|
+
version: record.version,
|
|
88
|
+
updatedAt: record.updatedAt,
|
|
89
|
+
...(record.deleted ? { deleted: true } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
async function get(key) {
|
|
95
|
+
const map = await all();
|
|
96
|
+
return map[key] ?? null;
|
|
97
|
+
}
|
|
98
|
+
async function set(key, value, baseVersion) {
|
|
99
|
+
return client.putSetting(key, { value, ...(baseVersion !== undefined ? { baseVersion } : {}) });
|
|
100
|
+
}
|
|
101
|
+
async function clear(key) {
|
|
102
|
+
// Geen DELETE-endpoint in v0.1: 'clear' reset naar de descriptor-default (tombstone-kolom is voor
|
|
103
|
+
// merge-semantiek + een toekomstig DELETE-endpoint gereserveerd).
|
|
104
|
+
const targets = key !== undefined ? [index.get(key)].filter(Boolean) : descriptors;
|
|
105
|
+
for (const descriptor of targets) {
|
|
106
|
+
await client.putSetting(descriptor.key, { value: descriptor.default });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { get, set, all, clear };
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../frontend/src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* De `useSettings()`-seam (het mechanisme, provide/inject). Eén reactieve keyed store die de UI leest/schrijft,
|
|
3
|
+
* transparant achter de actieve adapter (client óf server). De host kiest de adapter op runtime via `bind`;
|
|
4
|
+
* bij login voegt `syncOnLogin` de anonieme client-map per-key samen in de server-store en herpunt de seam —
|
|
5
|
+
* render-keys blijven cookie-gespiegeld zodat SSR post-login blijft werken. Geen module-globale mutable state:
|
|
6
|
+
* de API wordt per app-instance geprovide.
|
|
7
|
+
*/
|
|
8
|
+
import { type App, type InjectionKey, type Ref, type WritableComputedRef } from 'vue';
|
|
9
|
+
import type { MergeResponse, SettingsSchema } from '../../contract/index.js';
|
|
10
|
+
import type { UserSettingsClient } from './client.js';
|
|
11
|
+
import type { SettingsStore } from './store.js';
|
|
12
|
+
export interface UserSettingsApi {
|
|
13
|
+
/** Huidige (reactieve) waarde van een key; valt terug op de descriptor-`default`. */
|
|
14
|
+
get<T = unknown>(key: string): T;
|
|
15
|
+
/** Schrijf een key via de actieve adapter en werk de reactieve state bij. */
|
|
16
|
+
set(key: string, value: unknown): Promise<void>;
|
|
17
|
+
/** Snapshot van alle huidige waarden (met defaults ingevuld). */
|
|
18
|
+
all(): Record<string, unknown>;
|
|
19
|
+
/** Wis één key (of alles) via de actieve adapter; state valt terug op defaults. */
|
|
20
|
+
clear(key?: string): Promise<void>;
|
|
21
|
+
/** Laad een adapter in de reactieve state en maak hem de actieve. */
|
|
22
|
+
bind(adapter: SettingsStore): Promise<void>;
|
|
23
|
+
/** Guest→login: merge de anonieme map in de server-store, herpunt naar de server-adapter. */
|
|
24
|
+
syncOnLogin(options: SyncOnLoginOptions): Promise<MergeResponse | undefined>;
|
|
25
|
+
readonly ready: Ref<boolean>;
|
|
26
|
+
}
|
|
27
|
+
export interface SyncOnLoginOptions {
|
|
28
|
+
readonly from: SettingsStore;
|
|
29
|
+
readonly to: SettingsStore;
|
|
30
|
+
readonly client: UserSettingsClient;
|
|
31
|
+
/** Idempotentie-token: een retry na een verloren response is een no-op (geen replay-storm). */
|
|
32
|
+
readonly opId: string;
|
|
33
|
+
/** Render-keys blijven na de merge op de cookie staan (niet lokaal gewist). */
|
|
34
|
+
readonly renderKeys?: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
export interface CreateUserSettingsOptions {
|
|
37
|
+
readonly descriptors: SettingsSchema;
|
|
38
|
+
readonly initialAdapter: SettingsStore;
|
|
39
|
+
}
|
|
40
|
+
export declare function createUserSettings(options: CreateUserSettingsOptions): UserSettingsApi;
|
|
41
|
+
export declare const USER_SETTINGS_KEY: InjectionKey<UserSettingsApi>;
|
|
42
|
+
export declare function provideUserSettings(app: App, api: UserSettingsApi): void;
|
|
43
|
+
export declare function useSettings(): UserSettingsApi;
|
|
44
|
+
/** Reactieve twee-weg-binding voor één key: lezen geeft de huidige waarde, schrijven upsert via de adapter. */
|
|
45
|
+
export declare function useSetting<T = unknown>(key: string): WritableComputedRef<T>;
|
|
46
|
+
//# sourceMappingURL=useSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSettings.d.ts","sourceRoot":"","sources":["../../../frontend/src/useSettings.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAmC,KAAK,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,GAAG,EAAE,KAAK,mBAAmB,EAAE,MAAM,KAAK,CAAC;AACvH,OAAO,KAAK,EAAc,aAAa,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC;IACjC,6EAA6E;IAC7E,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,iEAAiE;IACjE,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,qEAAqE;IACrE,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,6FAA6F;IAC7F,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAC7E,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC;CACxC;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,eAAe,CAuEtF;AAED,eAAO,MAAM,iBAAiB,EAAE,YAAY,CAAC,eAAe,CAA+B,CAAC;AAE5F,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,GAAG,IAAI,CAExE;AAED,wBAAgB,WAAW,IAAI,eAAe,CAM7C;AAED,+GAA+G;AAC/G,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAQ3E"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* De `useSettings()`-seam (het mechanisme, provide/inject). Eén reactieve keyed store die de UI leest/schrijft,
|
|
3
|
+
* transparant achter de actieve adapter (client óf server). De host kiest de adapter op runtime via `bind`;
|
|
4
|
+
* bij login voegt `syncOnLogin` de anonieme client-map per-key samen in de server-store en herpunt de seam —
|
|
5
|
+
* render-keys blijven cookie-gespiegeld zodat SSR post-login blijft werken. Geen module-globale mutable state:
|
|
6
|
+
* de API wordt per app-instance geprovide.
|
|
7
|
+
*/
|
|
8
|
+
import { computed, inject, reactive, ref } from 'vue';
|
|
9
|
+
import { indexDescriptors } from './descriptors.js';
|
|
10
|
+
export function createUserSettings(options) {
|
|
11
|
+
const { descriptors, initialAdapter } = options;
|
|
12
|
+
const index = indexDescriptors(descriptors);
|
|
13
|
+
const state = reactive({});
|
|
14
|
+
const ready = ref(false);
|
|
15
|
+
let active = initialAdapter;
|
|
16
|
+
function resetToDefaults() {
|
|
17
|
+
for (const descriptor of descriptors)
|
|
18
|
+
state[descriptor.key] = descriptor.default;
|
|
19
|
+
}
|
|
20
|
+
resetToDefaults();
|
|
21
|
+
async function bind(adapter) {
|
|
22
|
+
active = adapter;
|
|
23
|
+
resetToDefaults();
|
|
24
|
+
const map = await adapter.all();
|
|
25
|
+
for (const [key, record] of Object.entries(map)) {
|
|
26
|
+
if (record.deleted !== true)
|
|
27
|
+
state[key] = record.value;
|
|
28
|
+
}
|
|
29
|
+
ready.value = true;
|
|
30
|
+
}
|
|
31
|
+
function get(key) {
|
|
32
|
+
if (key in state)
|
|
33
|
+
return state[key];
|
|
34
|
+
return index.get(key)?.default ?? undefined;
|
|
35
|
+
}
|
|
36
|
+
async function set(key, value) {
|
|
37
|
+
const record = await active.set(key, value);
|
|
38
|
+
state[key] = record.value;
|
|
39
|
+
}
|
|
40
|
+
function all() {
|
|
41
|
+
return { ...state };
|
|
42
|
+
}
|
|
43
|
+
async function clear(key) {
|
|
44
|
+
await active.clear(key);
|
|
45
|
+
if (key !== undefined) {
|
|
46
|
+
const descriptor = index.get(key);
|
|
47
|
+
if (descriptor !== undefined)
|
|
48
|
+
state[key] = descriptor.default;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
resetToDefaults();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function syncOnLogin(opts) {
|
|
55
|
+
const anonymous = await opts.from.all();
|
|
56
|
+
const entries = {};
|
|
57
|
+
for (const [key, record] of Object.entries(anonymous)) {
|
|
58
|
+
entries[key] = {
|
|
59
|
+
value: record.value,
|
|
60
|
+
version: record.version,
|
|
61
|
+
updatedAt: record.updatedAt,
|
|
62
|
+
...(record.deleted ? { deleted: true } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const result = await opts.client.mergeSettings({ opId: opts.opId, entries });
|
|
66
|
+
await bind(opts.to);
|
|
67
|
+
// Wis de lokale kopie, behalve de render-keys (die blijven cookie-gespiegeld voor SSR).
|
|
68
|
+
const renderSet = new Set(opts.renderKeys ?? []);
|
|
69
|
+
for (const key of Object.keys(anonymous)) {
|
|
70
|
+
const descriptor = index.get(key);
|
|
71
|
+
const isRender = descriptor?.namespace === 'render' || descriptor?.cookie === true || renderSet.has(key);
|
|
72
|
+
if (!isRender)
|
|
73
|
+
await opts.from.clear(key);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
return { get, set, all, clear, bind, syncOnLogin, ready };
|
|
78
|
+
}
|
|
79
|
+
export const USER_SETTINGS_KEY = Symbol('user-settings.api');
|
|
80
|
+
export function provideUserSettings(app, api) {
|
|
81
|
+
app.provide(USER_SETTINGS_KEY, api);
|
|
82
|
+
}
|
|
83
|
+
export function useSettings() {
|
|
84
|
+
const api = inject(USER_SETTINGS_KEY);
|
|
85
|
+
if (api === undefined) {
|
|
86
|
+
throw new Error('[user-settings] useSettings(): geen API geprovide. Roep provideUserSettings(app, api) aan bij bootstrap.');
|
|
87
|
+
}
|
|
88
|
+
return api;
|
|
89
|
+
}
|
|
90
|
+
/** Reactieve twee-weg-binding voor één key: lezen geeft de huidige waarde, schrijven upsert via de adapter. */
|
|
91
|
+
export function useSetting(key) {
|
|
92
|
+
const api = useSettings();
|
|
93
|
+
return computed({
|
|
94
|
+
get: () => api.get(key),
|
|
95
|
+
set: (value) => {
|
|
96
|
+
void api.set(key, value);
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=useSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSettings.js","sourceRoot":"","sources":["../../../frontend/src/useSettings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* user-settings — de capability-MANIFEST.
|
|
3
|
+
*
|
|
4
|
+
* De machine-leesbare descriptor die de scaffolder leest om de module te assembleren, te wiren en te
|
|
5
|
+
* upgraden. Componeert kits (persistence/http-kernel/config/i18n backend; app-kernel/data/http-client/i18n
|
|
6
|
+
* frontend) en heeft GEEN `requires.modules` — de eerste module met een backend-slice die volledig
|
|
7
|
+
* kit-only is (ontkoppeld van account; consent wordt gelinkt, niet als harde module-dep). `backendRoutes`
|
|
8
|
+
* wordt uit de endpoint-catalogus afgeleid zodat manifest en contract niet kunnen driften.
|
|
9
|
+
*
|
|
10
|
+
* Het interface + de validator komen uit de gedeelde spec `@seifer-webapp-factory/capability-spec` (de
|
|
11
|
+
* enige bron) en worden hier her-geëxporteerd zodat imports vanuit deze module blijven werken. Bevat GEEN
|
|
12
|
+
* mechanisme: puur declaratief.
|
|
13
|
+
*/
|
|
14
|
+
import { validateManifest } from '@seifer-webapp-factory/capability-spec';
|
|
15
|
+
import type { CapabilityModuleManifest } from '@seifer-webapp-factory/capability-spec';
|
|
16
|
+
export { validateManifest };
|
|
17
|
+
export type { CapabilityModuleManifest };
|
|
18
|
+
/** De concrete manifest van de user-settings-module. */
|
|
19
|
+
export declare const userSettingsManifest: {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
templateVersion: string;
|
|
23
|
+
provides: {
|
|
24
|
+
routes: string[];
|
|
25
|
+
backendRoutes: readonly string[];
|
|
26
|
+
capabilities: string[];
|
|
27
|
+
};
|
|
28
|
+
requires: {
|
|
29
|
+
kits: {
|
|
30
|
+
backend: string[];
|
|
31
|
+
frontend: string[];
|
|
32
|
+
};
|
|
33
|
+
ports: string[];
|
|
34
|
+
};
|
|
35
|
+
contract: string;
|
|
36
|
+
config: import("zod").ZodObject<{
|
|
37
|
+
serverSync: import("zod").ZodDefault<import("zod").ZodBoolean>;
|
|
38
|
+
cookiePrefix: import("zod").ZodDefault<import("zod").ZodString>;
|
|
39
|
+
renderKeys: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
40
|
+
cookieMaxAgeDays: import("zod").ZodDefault<import("zod").ZodNumber>;
|
|
41
|
+
onboardingScope: import("zod").ZodDefault<import("zod").ZodEnum<["server", "device"]>>;
|
|
42
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
43
|
+
serverSync: boolean;
|
|
44
|
+
cookiePrefix: string;
|
|
45
|
+
renderKeys: string[];
|
|
46
|
+
cookieMaxAgeDays: number;
|
|
47
|
+
onboardingScope: "server" | "device";
|
|
48
|
+
}, {
|
|
49
|
+
serverSync?: boolean | undefined;
|
|
50
|
+
cookiePrefix?: string | undefined;
|
|
51
|
+
renderKeys?: string[] | undefined;
|
|
52
|
+
cookieMaxAgeDays?: number | undefined;
|
|
53
|
+
onboardingScope?: "server" | "device" | undefined;
|
|
54
|
+
}>;
|
|
55
|
+
migrations: string[];
|
|
56
|
+
surface: string[];
|
|
57
|
+
mechanism: string[];
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAGvF,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC5B,YAAY,EAAE,wBAAwB,EAAE,CAAC;AAKzC,wDAAwD;AACxD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwBG,CAAC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* user-settings — de capability-MANIFEST.
|
|
3
|
+
*
|
|
4
|
+
* De machine-leesbare descriptor die de scaffolder leest om de module te assembleren, te wiren en te
|
|
5
|
+
* upgraden. Componeert kits (persistence/http-kernel/config/i18n backend; app-kernel/data/http-client/i18n
|
|
6
|
+
* frontend) en heeft GEEN `requires.modules` — de eerste module met een backend-slice die volledig
|
|
7
|
+
* kit-only is (ontkoppeld van account; consent wordt gelinkt, niet als harde module-dep). `backendRoutes`
|
|
8
|
+
* wordt uit de endpoint-catalogus afgeleid zodat manifest en contract niet kunnen driften.
|
|
9
|
+
*
|
|
10
|
+
* Het interface + de validator komen uit de gedeelde spec `@seifer-webapp-factory/capability-spec` (de
|
|
11
|
+
* enige bron) en worden hier her-geëxporteerd zodat imports vanuit deze module blijven werken. Bevat GEEN
|
|
12
|
+
* mechanisme: puur declaratief.
|
|
13
|
+
*/
|
|
14
|
+
import { validateManifest } from '@seifer-webapp-factory/capability-spec';
|
|
15
|
+
import { USER_SETTINGS_ENDPOINTS, userSettingsConfigSchema } from './contract/index.js';
|
|
16
|
+
export { validateManifest };
|
|
17
|
+
/** Backend-routes afgeleid uit de endpoint-catalogus (gededupliceerd op pad). */
|
|
18
|
+
const backendRoutes = [...new Set(Object.values(USER_SETTINGS_ENDPOINTS).map((ep) => ep.path))];
|
|
19
|
+
/** De concrete manifest van de user-settings-module. */
|
|
20
|
+
export const userSettingsManifest = {
|
|
21
|
+
name: 'user-settings',
|
|
22
|
+
version: '0.1.0',
|
|
23
|
+
templateVersion: '0.1.0',
|
|
24
|
+
provides: {
|
|
25
|
+
// De optionele referentie-pagina; een host mag hem remappen of weglaten (headless-first).
|
|
26
|
+
routes: ['/settings'],
|
|
27
|
+
backendRoutes,
|
|
28
|
+
capabilities: ['settings.preferences', 'settings.render', 'settings.sync'],
|
|
29
|
+
},
|
|
30
|
+
requires: {
|
|
31
|
+
kits: {
|
|
32
|
+
// backend-kits alleen relevant wanneer de server-adapter draait (serverSync).
|
|
33
|
+
backend: ['persistence', 'http-kernel', 'config', 'i18n'],
|
|
34
|
+
frontend: ['app-kernel', 'data', 'http-client', 'i18n'],
|
|
35
|
+
},
|
|
36
|
+
ports: ['SettingsStore', 'SubjectProvider', 'Database', 'DesignSystem'],
|
|
37
|
+
// geen `modules`: componeert kits, ontkoppeld van account.
|
|
38
|
+
},
|
|
39
|
+
contract: './contract/index.ts',
|
|
40
|
+
config: userSettingsConfigSchema,
|
|
41
|
+
migrations: ['user_settings'],
|
|
42
|
+
surface: ['backend/templates/**', 'frontend/templates/**'],
|
|
43
|
+
mechanism: ['backend/src/**', 'frontend/src/**', 'contract/**'],
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAE1E,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAExF,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAG5B,iFAAiF;AACjF,MAAM,aAAa,GAAsB,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEnH,wDAAwD;AACxD,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;IAChB,eAAe,EAAE,OAAO;IACxB,QAAQ,EAAE;QACR,0FAA0F;QAC1F,MAAM,EAAE,CAAC,WAAW,CAAC;QACrB,aAAa;QACb,YAAY,EAAE,CAAC,sBAAsB,EAAE,iBAAiB,EAAE,eAAe,CAAC;KAC3E;IACD,QAAQ,EAAE;QACR,IAAI,EAAE;YACJ,8EAA8E;YAC9E,OAAO,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC;YACzD,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC;SACxD;QACD,KAAK,EAAE,CAAC,eAAe,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc,CAAC;QACvE,2DAA2D;KAC5D;IACD,QAAQ,EAAE,qBAAqB;IAC/B,MAAM,EAAE,wBAAwB;IAChC,UAAU,EAAE,CAAC,eAAe,CAAC;IAC7B,OAAO,EAAE,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;IAC1D,SAAS,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,CAAC;CAC7B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fouttypes voor de user-settings-scaffolder. Draag altijd genoeg context voor een expliciete melding
|
|
3
|
+
* (paden, poorten, modules), maar NOOIT invoerwaarden of secrets. Deterministisch: geen timestamps/random.
|
|
4
|
+
*/
|
|
5
|
+
/** Geworpen wanneer te materialiseren surface-bestanden al bestaan en `overwrite` niet gezet is. */
|
|
6
|
+
export declare class UserSettingsMaterializationConflictError extends Error {
|
|
7
|
+
readonly paths: readonly string[];
|
|
8
|
+
constructor(paths: readonly string[]);
|
|
9
|
+
}
|
|
10
|
+
/** Geworpen wanneer het project een door het manifest vereiste requires-port niet levert. */
|
|
11
|
+
export declare class MissingRequiredPortError extends Error {
|
|
12
|
+
readonly missing: readonly string[];
|
|
13
|
+
constructor(missing: readonly string[]);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Geworpen door `checkRequiredModules` wanneer een vereiste capability-module niet aanwezig is.
|
|
17
|
+
* user-settings heeft GEEN `requires.modules` (kit-only), dus dit vuurt bij deze module in de praktijk niet
|
|
18
|
+
* — het blijft aanwezig voor pariteit met de siblings en voor het geval een host de check toch aanroept.
|
|
19
|
+
*/
|
|
20
|
+
export declare class MissingRequiredModuleError extends Error {
|
|
21
|
+
readonly missing: readonly string[];
|
|
22
|
+
constructor(missing: readonly string[]);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../scaffolder/core/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oGAAoG;AACpG,qBAAa,wCAAyC,SAAQ,KAAK;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;gBACtB,KAAK,EAAE,SAAS,MAAM,EAAE;CAQrC;AAED,6FAA6F;AAC7F,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;gBACxB,OAAO,EAAE,SAAS,MAAM,EAAE;CAKvC;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;gBACxB,OAAO,EAAE,SAAS,MAAM,EAAE;CAQvC"}
|