@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,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domein-events. De module publiceert deze (bv. voor audit-log of host-hooks). Payloads dragen
|
|
3
|
+
* identificatie + metadata, NOOIT de waarde van een preference (een locale/theme kan fingerprint-achtig
|
|
4
|
+
* zijn) — alleen `subjectId`, `key`, `namespace`, `version`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const USER_SETTINGS_EVENTS: readonly ["setting.updated", "settings.merged", "settings.cleared"];
|
|
7
|
+
export type UserSettingsEventName = (typeof USER_SETTINGS_EVENTS)[number];
|
|
8
|
+
export interface UserSettingsEventBase {
|
|
9
|
+
readonly name: UserSettingsEventName;
|
|
10
|
+
/** ISO-8601 timestamp — door de host/klok geïnjecteerd, niet hier gegenereerd. */
|
|
11
|
+
readonly occurredAt: string;
|
|
12
|
+
readonly subjectId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SettingUpdatedEvent extends UserSettingsEventBase {
|
|
15
|
+
readonly name: 'setting.updated';
|
|
16
|
+
readonly key: string;
|
|
17
|
+
readonly namespace: string;
|
|
18
|
+
readonly version: number;
|
|
19
|
+
}
|
|
20
|
+
export interface SettingsMergedEvent extends UserSettingsEventBase {
|
|
21
|
+
readonly name: 'settings.merged';
|
|
22
|
+
/** Namen van de keys die de merge aanraakte — nooit de waarden. */
|
|
23
|
+
readonly mergedKeys: readonly string[];
|
|
24
|
+
/** Keys waar een conflict speelde (same-key race). */
|
|
25
|
+
readonly conflicts: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export interface SettingsClearedEvent extends UserSettingsEventBase {
|
|
28
|
+
readonly name: 'settings.cleared';
|
|
29
|
+
/** Aanwezig bij het wissen van één key; afwezig bij het wissen van alles. */
|
|
30
|
+
readonly key?: string;
|
|
31
|
+
readonly version?: number;
|
|
32
|
+
}
|
|
33
|
+
export type UserSettingsEvent = SettingUpdatedEvent | SettingsMergedEvent | SettingsClearedEvent;
|
|
34
|
+
/** Poort die de host levert om events te consumeren (audit-log/analytics/…). */
|
|
35
|
+
export interface UserSettingsEventSink {
|
|
36
|
+
emit(event: UserSettingsEvent): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../contract/events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,oBAAoB,qEAAsE,CAAC;AAExG,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAChE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AACD,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAChE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AACD,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;AAEjG,gFAAgF;AAChF,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACtC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domein-events. De module publiceert deze (bv. voor audit-log of host-hooks). Payloads dragen
|
|
3
|
+
* identificatie + metadata, NOOIT de waarde van een preference (een locale/theme kan fingerprint-achtig
|
|
4
|
+
* zijn) — alleen `subjectId`, `key`, `namespace`, `version`.
|
|
5
|
+
*/
|
|
6
|
+
export const USER_SETTINGS_EVENTS = ['setting.updated', 'settings.merged', 'settings.cleared'];
|
|
7
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../contract/events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,kBAAkB,CAAU,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* user-settings — het contract (de seam). Eén bron waaruit backend-validatie én frontend-client hun types
|
|
3
|
+
* afleiden. Bevat geen mechanisme, alleen schema's/typen/taxonomie/events/config. Het contract is de
|
|
4
|
+
* invariant tussen de twee helften én tussen de twee runtime-adapters (client vs server).
|
|
5
|
+
*/
|
|
6
|
+
export * from './schemas.js';
|
|
7
|
+
export * from './errors.js';
|
|
8
|
+
export * from './events.js';
|
|
9
|
+
export * from './config.js';
|
|
10
|
+
export * from './endpoints.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../contract/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* user-settings — het contract (de seam). Eén bron waaruit backend-validatie én frontend-client hun types
|
|
3
|
+
* afleiden. Bevat geen mechanisme, alleen schema's/typen/taxonomie/events/config. Het contract is de
|
|
4
|
+
* invariant tussen de twee helften én tussen de twee runtime-adapters (client vs server).
|
|
5
|
+
*/
|
|
6
|
+
export * from './schemas.js';
|
|
7
|
+
export * from './errors.js';
|
|
8
|
+
export * from './events.js';
|
|
9
|
+
export * from './config.js';
|
|
10
|
+
export * from './endpoints.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../contract/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* user-settings — de seam-schema's. Anders dan account (vaste DTO's) is dit contract
|
|
4
|
+
* GEPARAMETRISEERD door een host-gedeclareerd sleutel-schema: de module levert de engine, de host
|
|
5
|
+
* declareert zijn keys als {@link SettingDescriptor}s. Hier staan alleen de vormen/typen — geen mechanisme.
|
|
6
|
+
*
|
|
7
|
+
* Waarden zijn host-getypt, dus op de contract-grens `unknown`: elke adapter valideert een waarde tegen
|
|
8
|
+
* de descriptor-`schema` van de host bij lezen/schrijven. De store is een KEYED MAP (nooit één blob) met
|
|
9
|
+
* per-key `version` (server-beheerd, monotoon) — dit is precies een LWW-Element-Map.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SETTING_NAMESPACES: readonly ["render", "pref"];
|
|
12
|
+
export type SettingNamespace = (typeof SETTING_NAMESPACES)[number];
|
|
13
|
+
export declare const settingNamespaceSchema: z.ZodEnum<["render", "pref"]>;
|
|
14
|
+
/**
|
|
15
|
+
* Eén descriptor per instelling die het product blootstelt. `schema` is een door de host geleverde
|
|
16
|
+
* ZodType (niet zelf geserialiseerd). `version` + `migrate` maken descriptor-migratie eersteklas: als de
|
|
17
|
+
* vorm van een key verandert draait de migratie bij het LADEN (niet per read). `deprecated` tombstoned
|
|
18
|
+
* een key voor veilige verwijdering — nooit hard weggooien, dat crasht de loader op oude opgeslagen data.
|
|
19
|
+
*/
|
|
20
|
+
export interface SettingDescriptor<T = unknown> {
|
|
21
|
+
readonly key: string;
|
|
22
|
+
readonly namespace: SettingNamespace;
|
|
23
|
+
readonly default: T;
|
|
24
|
+
readonly schema: z.ZodType<T>;
|
|
25
|
+
/** Forceer cookie-opslag (impliciet voor namespace 'render'). */
|
|
26
|
+
readonly cookie?: boolean;
|
|
27
|
+
/** Descriptor-vormversie (default 1). */
|
|
28
|
+
readonly version?: number;
|
|
29
|
+
/** Laad-tijd migratie wanneer de opgeslagen vorm van een oudere versie komt. */
|
|
30
|
+
readonly migrate?: (stored: unknown, fromVersion: number) => T;
|
|
31
|
+
/** Tombstone: key uitgefaseerd maar loader blijft oude waarden veilig verwerken. */
|
|
32
|
+
readonly deprecated?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type SettingsSchema = readonly SettingDescriptor[];
|
|
35
|
+
/** value is host-getypt → `unknown` op de contract-grens. `deleted` is de versioned tombstone. */
|
|
36
|
+
export declare const settingsRecordSchema: z.ZodObject<{
|
|
37
|
+
value: z.ZodUnknown;
|
|
38
|
+
version: z.ZodNumber;
|
|
39
|
+
updatedAt: z.ZodNumber;
|
|
40
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
version: number;
|
|
43
|
+
updatedAt: number;
|
|
44
|
+
value?: unknown;
|
|
45
|
+
deleted?: boolean | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
version: number;
|
|
48
|
+
updatedAt: number;
|
|
49
|
+
value?: unknown;
|
|
50
|
+
deleted?: boolean | undefined;
|
|
51
|
+
}>;
|
|
52
|
+
export interface SettingsRecord<T = unknown> {
|
|
53
|
+
readonly value: T;
|
|
54
|
+
readonly version: number;
|
|
55
|
+
readonly updatedAt: number;
|
|
56
|
+
readonly deleted?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/** De keyed map (nooit één blob) — twee devices die verschíllende keys bewerken botsen nooit. */
|
|
59
|
+
export declare const settingsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
60
|
+
value: z.ZodUnknown;
|
|
61
|
+
version: z.ZodNumber;
|
|
62
|
+
updatedAt: z.ZodNumber;
|
|
63
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
64
|
+
}, "strip", z.ZodTypeAny, {
|
|
65
|
+
version: number;
|
|
66
|
+
updatedAt: number;
|
|
67
|
+
value?: unknown;
|
|
68
|
+
deleted?: boolean | undefined;
|
|
69
|
+
}, {
|
|
70
|
+
version: number;
|
|
71
|
+
updatedAt: number;
|
|
72
|
+
value?: unknown;
|
|
73
|
+
deleted?: boolean | undefined;
|
|
74
|
+
}>>;
|
|
75
|
+
export type SettingsMap = Record<string, SettingsRecord>;
|
|
76
|
+
export declare const settingsMapResponseSchema: z.ZodObject<{
|
|
77
|
+
settings: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
78
|
+
value: z.ZodUnknown;
|
|
79
|
+
version: z.ZodNumber;
|
|
80
|
+
updatedAt: z.ZodNumber;
|
|
81
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
82
|
+
}, "strip", z.ZodTypeAny, {
|
|
83
|
+
version: number;
|
|
84
|
+
updatedAt: number;
|
|
85
|
+
value?: unknown;
|
|
86
|
+
deleted?: boolean | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
version: number;
|
|
89
|
+
updatedAt: number;
|
|
90
|
+
value?: unknown;
|
|
91
|
+
deleted?: boolean | undefined;
|
|
92
|
+
}>>;
|
|
93
|
+
}, "strip", z.ZodTypeAny, {
|
|
94
|
+
settings: Record<string, {
|
|
95
|
+
version: number;
|
|
96
|
+
updatedAt: number;
|
|
97
|
+
value?: unknown;
|
|
98
|
+
deleted?: boolean | undefined;
|
|
99
|
+
}>;
|
|
100
|
+
}, {
|
|
101
|
+
settings: Record<string, {
|
|
102
|
+
version: number;
|
|
103
|
+
updatedAt: number;
|
|
104
|
+
value?: unknown;
|
|
105
|
+
deleted?: boolean | undefined;
|
|
106
|
+
}>;
|
|
107
|
+
}>;
|
|
108
|
+
export type SettingsMapResponse = z.infer<typeof settingsMapResponseSchema>;
|
|
109
|
+
/** `baseVersion` = de versie die de client las; de server kan zo een stale overwrite detecteren. */
|
|
110
|
+
export declare const putSettingRequestSchema: z.ZodObject<{
|
|
111
|
+
value: z.ZodUnknown;
|
|
112
|
+
baseVersion: z.ZodOptional<z.ZodNumber>;
|
|
113
|
+
}, "strip", z.ZodTypeAny, {
|
|
114
|
+
value?: unknown;
|
|
115
|
+
baseVersion?: number | undefined;
|
|
116
|
+
}, {
|
|
117
|
+
value?: unknown;
|
|
118
|
+
baseVersion?: number | undefined;
|
|
119
|
+
}>;
|
|
120
|
+
export type PutSettingRequest = z.infer<typeof putSettingRequestSchema>;
|
|
121
|
+
export declare const putSettingResponseSchema: z.ZodObject<{
|
|
122
|
+
value: z.ZodUnknown;
|
|
123
|
+
version: z.ZodNumber;
|
|
124
|
+
updatedAt: z.ZodNumber;
|
|
125
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
126
|
+
}, "strip", z.ZodTypeAny, {
|
|
127
|
+
version: number;
|
|
128
|
+
updatedAt: number;
|
|
129
|
+
value?: unknown;
|
|
130
|
+
deleted?: boolean | undefined;
|
|
131
|
+
}, {
|
|
132
|
+
version: number;
|
|
133
|
+
updatedAt: number;
|
|
134
|
+
value?: unknown;
|
|
135
|
+
deleted?: boolean | undefined;
|
|
136
|
+
}>;
|
|
137
|
+
export type PutSettingResponse = SettingsRecord;
|
|
138
|
+
/** Anonieme entry zoals de client-adapter hem kent: waarde + client-klok (voor per-key LWW). */
|
|
139
|
+
export declare const mergeEntrySchema: z.ZodObject<{
|
|
140
|
+
value: z.ZodUnknown;
|
|
141
|
+
version: z.ZodNumber;
|
|
142
|
+
updatedAt: z.ZodNumber;
|
|
143
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
144
|
+
}, "strip", z.ZodTypeAny, {
|
|
145
|
+
version: number;
|
|
146
|
+
updatedAt: number;
|
|
147
|
+
value?: unknown;
|
|
148
|
+
deleted?: boolean | undefined;
|
|
149
|
+
}, {
|
|
150
|
+
version: number;
|
|
151
|
+
updatedAt: number;
|
|
152
|
+
value?: unknown;
|
|
153
|
+
deleted?: boolean | undefined;
|
|
154
|
+
}>;
|
|
155
|
+
export type MergeEntry = z.infer<typeof mergeEntrySchema>;
|
|
156
|
+
/** `opId` maakt de merge idempotent — een retry na een verloren response is een no-op (geen replay-storm). */
|
|
157
|
+
export declare const mergeRequestSchema: z.ZodObject<{
|
|
158
|
+
opId: z.ZodString;
|
|
159
|
+
entries: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
160
|
+
value: z.ZodUnknown;
|
|
161
|
+
version: z.ZodNumber;
|
|
162
|
+
updatedAt: z.ZodNumber;
|
|
163
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
164
|
+
}, "strip", z.ZodTypeAny, {
|
|
165
|
+
version: number;
|
|
166
|
+
updatedAt: number;
|
|
167
|
+
value?: unknown;
|
|
168
|
+
deleted?: boolean | undefined;
|
|
169
|
+
}, {
|
|
170
|
+
version: number;
|
|
171
|
+
updatedAt: number;
|
|
172
|
+
value?: unknown;
|
|
173
|
+
deleted?: boolean | undefined;
|
|
174
|
+
}>>;
|
|
175
|
+
}, "strip", z.ZodTypeAny, {
|
|
176
|
+
entries: Record<string, {
|
|
177
|
+
version: number;
|
|
178
|
+
updatedAt: number;
|
|
179
|
+
value?: unknown;
|
|
180
|
+
deleted?: boolean | undefined;
|
|
181
|
+
}>;
|
|
182
|
+
opId: string;
|
|
183
|
+
}, {
|
|
184
|
+
entries: Record<string, {
|
|
185
|
+
version: number;
|
|
186
|
+
updatedAt: number;
|
|
187
|
+
value?: unknown;
|
|
188
|
+
deleted?: boolean | undefined;
|
|
189
|
+
}>;
|
|
190
|
+
opId: string;
|
|
191
|
+
}>;
|
|
192
|
+
export type MergeRequest = z.infer<typeof mergeRequestSchema>;
|
|
193
|
+
export declare const mergeConflictSchema: z.ZodObject<{
|
|
194
|
+
key: z.ZodString;
|
|
195
|
+
resolution: z.ZodEnum<["server_won", "client_filled", "rejected"]>;
|
|
196
|
+
}, "strip", z.ZodTypeAny, {
|
|
197
|
+
key: string;
|
|
198
|
+
resolution: "server_won" | "client_filled" | "rejected";
|
|
199
|
+
}, {
|
|
200
|
+
key: string;
|
|
201
|
+
resolution: "server_won" | "client_filled" | "rejected";
|
|
202
|
+
}>;
|
|
203
|
+
export type MergeConflict = z.infer<typeof mergeConflictSchema>;
|
|
204
|
+
export declare const mergeResponseSchema: z.ZodObject<{
|
|
205
|
+
settings: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
206
|
+
value: z.ZodUnknown;
|
|
207
|
+
version: z.ZodNumber;
|
|
208
|
+
updatedAt: z.ZodNumber;
|
|
209
|
+
deleted: z.ZodOptional<z.ZodBoolean>;
|
|
210
|
+
}, "strip", z.ZodTypeAny, {
|
|
211
|
+
version: number;
|
|
212
|
+
updatedAt: number;
|
|
213
|
+
value?: unknown;
|
|
214
|
+
deleted?: boolean | undefined;
|
|
215
|
+
}, {
|
|
216
|
+
version: number;
|
|
217
|
+
updatedAt: number;
|
|
218
|
+
value?: unknown;
|
|
219
|
+
deleted?: boolean | undefined;
|
|
220
|
+
}>>;
|
|
221
|
+
conflicts: z.ZodArray<z.ZodObject<{
|
|
222
|
+
key: z.ZodString;
|
|
223
|
+
resolution: z.ZodEnum<["server_won", "client_filled", "rejected"]>;
|
|
224
|
+
}, "strip", z.ZodTypeAny, {
|
|
225
|
+
key: string;
|
|
226
|
+
resolution: "server_won" | "client_filled" | "rejected";
|
|
227
|
+
}, {
|
|
228
|
+
key: string;
|
|
229
|
+
resolution: "server_won" | "client_filled" | "rejected";
|
|
230
|
+
}>, "many">;
|
|
231
|
+
}, "strip", z.ZodTypeAny, {
|
|
232
|
+
settings: Record<string, {
|
|
233
|
+
version: number;
|
|
234
|
+
updatedAt: number;
|
|
235
|
+
value?: unknown;
|
|
236
|
+
deleted?: boolean | undefined;
|
|
237
|
+
}>;
|
|
238
|
+
conflicts: {
|
|
239
|
+
key: string;
|
|
240
|
+
resolution: "server_won" | "client_filled" | "rejected";
|
|
241
|
+
}[];
|
|
242
|
+
}, {
|
|
243
|
+
settings: Record<string, {
|
|
244
|
+
version: number;
|
|
245
|
+
updatedAt: number;
|
|
246
|
+
value?: unknown;
|
|
247
|
+
deleted?: boolean | undefined;
|
|
248
|
+
}>;
|
|
249
|
+
conflicts: {
|
|
250
|
+
key: string;
|
|
251
|
+
resolution: "server_won" | "client_filled" | "rejected";
|
|
252
|
+
}[];
|
|
253
|
+
}>;
|
|
254
|
+
export type MergeResponse = z.infer<typeof mergeResponseSchema>;
|
|
255
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../contract/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AAGH,eAAO,MAAM,kBAAkB,6BAA8B,CAAC;AAC9D,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AACnE,eAAO,MAAM,sBAAsB,+BAA6B,CAAC;AAGjE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,OAAO;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC,CAAC;IAC/D,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AACD,MAAM,MAAM,cAAc,GAAG,SAAS,iBAAiB,EAAE,CAAC;AAG1D,kGAAkG;AAClG,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;EAK/B,CAAC;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,iGAAiG;AACjG,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;GAA6C,CAAC;AAC5E,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAGzD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4C,CAAC;AACnF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAG5E,oGAAoG;AACpG,eAAO,MAAM,uBAAuB;;;;;;;;;EAGlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;EAAuB,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAGhD,gGAAgG;AAChG,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,8GAA8G;AAC9G,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,eAAO,MAAM,mBAAmB;;;;;;;;;EAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* user-settings — de seam-schema's. Anders dan account (vaste DTO's) is dit contract
|
|
4
|
+
* GEPARAMETRISEERD door een host-gedeclareerd sleutel-schema: de module levert de engine, de host
|
|
5
|
+
* declareert zijn keys als {@link SettingDescriptor}s. Hier staan alleen de vormen/typen — geen mechanisme.
|
|
6
|
+
*
|
|
7
|
+
* Waarden zijn host-getypt, dus op de contract-grens `unknown`: elke adapter valideert een waarde tegen
|
|
8
|
+
* de descriptor-`schema` van de host bij lezen/schrijven. De store is een KEYED MAP (nooit één blob) met
|
|
9
|
+
* per-key `version` (server-beheerd, monotoon) — dit is precies een LWW-Element-Map.
|
|
10
|
+
*/
|
|
11
|
+
// --- Namespaces (drijven storage per key; 'consent' bestaat NIET — dat hoort bij de account-module) ---
|
|
12
|
+
export const SETTING_NAMESPACES = ['render', 'pref'];
|
|
13
|
+
export const settingNamespaceSchema = z.enum(SETTING_NAMESPACES);
|
|
14
|
+
// --- Per-key record (wire + store) ---
|
|
15
|
+
/** value is host-getypt → `unknown` op de contract-grens. `deleted` is de versioned tombstone. */
|
|
16
|
+
export const settingsRecordSchema = z.object({
|
|
17
|
+
value: z.unknown(),
|
|
18
|
+
version: z.number().int().nonnegative(),
|
|
19
|
+
updatedAt: z.number().int().nonnegative(),
|
|
20
|
+
deleted: z.boolean().optional(),
|
|
21
|
+
});
|
|
22
|
+
/** De keyed map (nooit één blob) — twee devices die verschíllende keys bewerken botsen nooit. */
|
|
23
|
+
export const settingsMapSchema = z.record(z.string(), settingsRecordSchema);
|
|
24
|
+
// --- GET /settings ---
|
|
25
|
+
export const settingsMapResponseSchema = z.object({ settings: settingsMapSchema });
|
|
26
|
+
// --- PUT /settings/:key ---
|
|
27
|
+
/** `baseVersion` = de versie die de client las; de server kan zo een stale overwrite detecteren. */
|
|
28
|
+
export const putSettingRequestSchema = z.object({
|
|
29
|
+
value: z.unknown(),
|
|
30
|
+
baseVersion: z.number().int().nonnegative().optional(),
|
|
31
|
+
});
|
|
32
|
+
export const putSettingResponseSchema = settingsRecordSchema;
|
|
33
|
+
// --- POST /settings/merge (guest→login) ---
|
|
34
|
+
/** Anonieme entry zoals de client-adapter hem kent: waarde + client-klok (voor per-key LWW). */
|
|
35
|
+
export const mergeEntrySchema = z.object({
|
|
36
|
+
value: z.unknown(),
|
|
37
|
+
version: z.number().int().nonnegative(),
|
|
38
|
+
updatedAt: z.number().int().nonnegative(),
|
|
39
|
+
deleted: z.boolean().optional(),
|
|
40
|
+
});
|
|
41
|
+
/** `opId` maakt de merge idempotent — een retry na een verloren response is een no-op (geen replay-storm). */
|
|
42
|
+
export const mergeRequestSchema = z.object({
|
|
43
|
+
opId: z.string().min(1),
|
|
44
|
+
entries: z.record(z.string(), mergeEntrySchema),
|
|
45
|
+
});
|
|
46
|
+
export const mergeConflictSchema = z.object({
|
|
47
|
+
key: z.string(),
|
|
48
|
+
resolution: z.enum(['server_won', 'client_filled', 'rejected']),
|
|
49
|
+
});
|
|
50
|
+
export const mergeResponseSchema = z.object({
|
|
51
|
+
settings: settingsMapSchema,
|
|
52
|
+
conflicts: z.array(mergeConflictSchema),
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../contract/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AAEH,yGAAyG;AACzG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAU,CAAC;AAE9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;AAyBjE,wCAAwC;AACxC,kGAAkG;AAClG,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACvC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAQH,iGAAiG;AACjG,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC;AAG5E,wBAAwB;AACxB,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAGnF,6BAA6B;AAC7B,oGAAoG;AACpG,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,oBAAoB,CAAC;AAG7D,6CAA6C;AAC7C,gGAAgG;AAChG,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACvC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAEH,8GAA8G;AAC9G,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC;CAChD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;CAChE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,iBAAiB;IAC3B,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC;CACxC,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HttpClient } from '@seifer-webapp-factory/kits/frontend/http-client';
|
|
2
|
+
import type { MergeRequest, MergeResponse, PutSettingRequest, PutSettingResponse, SettingsMapResponse, UserSettingsErrorCode } from '../../contract/index.js';
|
|
3
|
+
/** Getypte fout bij een niet-2xx-respons: draagt de contract-`code` + i18nKey. Nooit PII/details. */
|
|
4
|
+
export declare class UserSettingsClientError extends Error {
|
|
5
|
+
readonly code: UserSettingsErrorCode;
|
|
6
|
+
readonly i18nKey: string;
|
|
7
|
+
readonly httpStatus: number;
|
|
8
|
+
readonly fields?: Readonly<Record<string, string[]>>;
|
|
9
|
+
constructor(code: UserSettingsErrorCode, i18nKey: string, httpStatus: number, fields?: Readonly<Record<string, string[]>>);
|
|
10
|
+
}
|
|
11
|
+
/** De publieke, getypte client-API (alleen server-adapter). */
|
|
12
|
+
export interface UserSettingsClient {
|
|
13
|
+
getSettings(): Promise<SettingsMapResponse>;
|
|
14
|
+
putSetting(key: string, input: PutSettingRequest): Promise<PutSettingResponse>;
|
|
15
|
+
mergeSettings(input: MergeRequest): Promise<MergeResponse>;
|
|
16
|
+
}
|
|
17
|
+
export interface CreateUserSettingsClientOptions {
|
|
18
|
+
readonly http: HttpClient;
|
|
19
|
+
}
|
|
20
|
+
/** Maakt een getypte user-settings-client rond de geïnjecteerde kit-`HttpClient`. */
|
|
21
|
+
export declare function createUserSettingsClient(options: CreateUserSettingsClientOptions): UserSettingsClient;
|
|
22
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../frontend/src/client.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kDAAkD,CAAC;AAMnF,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EAEnB,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,qGAAqG;AACrG,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;gBAEnD,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAS9C;AAED,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IACjC,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC/E,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAgBD,qFAAqF;AACrF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,kBAAkB,CAoCrG"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* De getypte user-settings-client (het mechanisme, server-adapter-transport). Leidt zijn methoden, paden en
|
|
3
|
+
* foutcodes af uit het contract (`USER_SETTINGS_ENDPOINTS` + `USER_SETTINGS_ERROR_TAXONOMY`) — één bron,
|
|
4
|
+
* geen drift. De client kent geen netwerk: hij krijgt de kit-`HttpClient` geïnjecteerd (base-URL +
|
|
5
|
+
* cookie/CSRF-transport). Bij een niet-2xx-respons mapt hij de kit-`HttpError` naar een getypte
|
|
6
|
+
* `UserSettingsClientError`. Geen module-globale mutable state.
|
|
7
|
+
*/
|
|
8
|
+
import { HttpError } from '@seifer-webapp-factory/kits/frontend/http-client';
|
|
9
|
+
import { USER_SETTINGS_ENDPOINTS, USER_SETTINGS_ERROR_CODES, USER_SETTINGS_ERROR_TAXONOMY, } from '../../contract/index.js';
|
|
10
|
+
/** Getypte fout bij een niet-2xx-respons: draagt de contract-`code` + i18nKey. Nooit PII/details. */
|
|
11
|
+
export class UserSettingsClientError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
i18nKey;
|
|
14
|
+
httpStatus;
|
|
15
|
+
fields;
|
|
16
|
+
constructor(code, i18nKey, httpStatus, fields) {
|
|
17
|
+
super(`[user-settings] ${code} (HTTP ${httpStatus})`);
|
|
18
|
+
this.name = 'UserSettingsClientError';
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.i18nKey = i18nKey;
|
|
21
|
+
this.httpStatus = httpStatus;
|
|
22
|
+
this.fields = fields;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const ERROR_CODES = USER_SETTINGS_ERROR_CODES;
|
|
26
|
+
function resolveErrorCode(status, body) {
|
|
27
|
+
const bodyCode = body?.code;
|
|
28
|
+
if (typeof bodyCode === 'string' && ERROR_CODES.includes(bodyCode))
|
|
29
|
+
return bodyCode;
|
|
30
|
+
const byStatus = USER_SETTINGS_ERROR_CODES.find((code) => USER_SETTINGS_ERROR_TAXONOMY[code].httpStatus === status);
|
|
31
|
+
return byStatus ?? 'validation_failed';
|
|
32
|
+
}
|
|
33
|
+
/** Maakt een getypte user-settings-client rond de geïnjecteerde kit-`HttpClient`. */
|
|
34
|
+
export function createUserSettingsClient(options) {
|
|
35
|
+
const { http } = options;
|
|
36
|
+
async function call(endpoint, path, body) {
|
|
37
|
+
let data;
|
|
38
|
+
let status;
|
|
39
|
+
try {
|
|
40
|
+
const res = await http.request({ method: endpoint.method, path, body });
|
|
41
|
+
data = res.data;
|
|
42
|
+
status = res.status;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof HttpError && err.status !== undefined) {
|
|
46
|
+
const code = resolveErrorCode(err.status, err.body);
|
|
47
|
+
const errorBody = err.body;
|
|
48
|
+
throw new UserSettingsClientError(code, USER_SETTINGS_ERROR_TAXONOMY[code].i18nKey, err.status, errorBody?.fields);
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
const parsed = endpoint.response.safeParse(data);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
throw new UserSettingsClientError('validation_failed', USER_SETTINGS_ERROR_TAXONOMY.validation_failed.i18nKey, status);
|
|
55
|
+
}
|
|
56
|
+
return parsed.data;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
getSettings: () => call(USER_SETTINGS_ENDPOINTS.getSettings, USER_SETTINGS_ENDPOINTS.getSettings.path),
|
|
60
|
+
putSetting: (key, input) => call(USER_SETTINGS_ENDPOINTS.putSetting, USER_SETTINGS_ENDPOINTS.putSetting.path.replace(':key', encodeURIComponent(key)), input),
|
|
61
|
+
mergeSettings: (input) => call(USER_SETTINGS_ENDPOINTS.mergeSettings, USER_SETTINGS_ENDPOINTS.mergeSettings.path, input),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../frontend/src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SettingDescriptor, SettingsSchema } from '../../contract/index.js';
|
|
2
|
+
/** Valideer de host-descriptorset bij init; een namespace buiten render/pref (bv. 'consent') wordt geweigerd. */
|
|
3
|
+
export declare function assertValidDescriptors(descriptors: SettingsSchema): void;
|
|
4
|
+
/** Index de descriptors op key voor snelle lookup. */
|
|
5
|
+
export declare function indexDescriptors(descriptors: SettingsSchema): Map<string, SettingDescriptor>;
|
|
6
|
+
/**
|
|
7
|
+
* Coerce een rauwe opgeslagen waarde naar een geldige waarde voor deze descriptor. Draait `migrate` bij een
|
|
8
|
+
* oudere opgeslagen versie, valideert daarna tegen de descriptor-`schema`, en valt bij elke fout terug op de
|
|
9
|
+
* descriptor-`default` (nooit gooien — junk → default).
|
|
10
|
+
*/
|
|
11
|
+
export declare function coerceValue(descriptor: SettingDescriptor, rawValue: unknown, storedVersion: number): unknown;
|
|
12
|
+
//# sourceMappingURL=descriptors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptors.d.ts","sourceRoot":"","sources":["../../../frontend/src/descriptors.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAIjF,iHAAiH;AACjH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,cAAc,GAAG,IAAI,CASxE;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAI5F;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,iBAAiB,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAY5G"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Descriptor-hulplaag (frontend mechanism). Bewust een dunne kopie van de backend-guards (backend/src) —
|
|
3
|
+
* de twee helften zijn losse bundels en elke adapter valideert zelfstandig tegen het host-descriptor-schema.
|
|
4
|
+
* Aan de clientkant valt een ongeldige/oude opgeslagen waarde terug op de descriptor-`default` (junk → default),
|
|
5
|
+
* i.p.v. te falen: een getamperde cookie/localStorage-waarde mag de app nooit laten crashen.
|
|
6
|
+
*/
|
|
7
|
+
import { SETTING_NAMESPACES } from '../../contract/index.js';
|
|
8
|
+
const VALID_NAMESPACES = SETTING_NAMESPACES;
|
|
9
|
+
/** Valideer de host-descriptorset bij init; een namespace buiten render/pref (bv. 'consent') wordt geweigerd. */
|
|
10
|
+
export function assertValidDescriptors(descriptors) {
|
|
11
|
+
for (const descriptor of descriptors) {
|
|
12
|
+
if (!VALID_NAMESPACES.includes(descriptor.namespace)) {
|
|
13
|
+
throw new Error(`[user-settings] consent_namespace_rejected: descriptor '${descriptor.key}' heeft namespace ` +
|
|
14
|
+
`'${descriptor.namespace}'. Consent hoort bij de account-module, niet bij user-settings.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Index de descriptors op key voor snelle lookup. */
|
|
19
|
+
export function indexDescriptors(descriptors) {
|
|
20
|
+
const map = new Map();
|
|
21
|
+
for (const descriptor of descriptors)
|
|
22
|
+
map.set(descriptor.key, descriptor);
|
|
23
|
+
return map;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Coerce een rauwe opgeslagen waarde naar een geldige waarde voor deze descriptor. Draait `migrate` bij een
|
|
27
|
+
* oudere opgeslagen versie, valideert daarna tegen de descriptor-`schema`, en valt bij elke fout terug op de
|
|
28
|
+
* descriptor-`default` (nooit gooien — junk → default).
|
|
29
|
+
*/
|
|
30
|
+
export function coerceValue(descriptor, rawValue, storedVersion) {
|
|
31
|
+
const currentVersion = descriptor.version ?? 1;
|
|
32
|
+
let candidate = rawValue;
|
|
33
|
+
if (descriptor.migrate !== undefined && storedVersion < currentVersion) {
|
|
34
|
+
try {
|
|
35
|
+
candidate = descriptor.migrate(rawValue, storedVersion);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return descriptor.default;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const parsed = descriptor.schema.safeParse(candidate);
|
|
42
|
+
return parsed.success ? parsed.data : descriptor.default;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=descriptors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptors.js","sourceRoot":"","sources":["../../../frontend/src/descriptors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,MAAM,gBAAgB,GAAsB,kBAAkB,CAAC;AAE/D,iHAAiH;AACjH,MAAM,UAAU,sBAAsB,CAAC,WAA2B;IAChE,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,2DAA2D,UAAU,CAAC,GAAG,oBAAoB;gBAC3F,IAAI,UAAU,CAAC,SAAS,iEAAiE,CAC5F,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAC,WAA2B;IAC1D,MAAM,GAAG,GAAG,IAAI,GAAG,EAA6B,CAAC;IACjD,KAAK,MAAM,UAAU,IAAI,WAAW;QAAE,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1E,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,UAA6B,EAAE,QAAiB,EAAE,aAAqB;IACjG,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;IAC/C,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,IAAI,UAAU,CAAC,OAAO,KAAK,SAAS,IAAI,aAAa,GAAG,cAAc,EAAE,CAAC;QACvE,IAAI,CAAC;YACH,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,UAAU,CAAC,OAAO,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* user-settings — de frontend-helft (het mechanisme). Barrel die de getypte client, de twee store-adapters,
|
|
3
|
+
* de `useSettings()`-seam en de SSR-helpers bundelt. Alle types leidt de consumer via het contract af; hier
|
|
4
|
+
* alleen het uitvoerbare mechanisme dat de frontend-kits samenbindt. De surface-pagina (frontend/templates)
|
|
5
|
+
* consumeert dit.
|
|
6
|
+
*/
|
|
7
|
+
export { createUserSettingsClient, UserSettingsClientError } from './client.js';
|
|
8
|
+
export type { UserSettingsClient, CreateUserSettingsClientOptions } from './client.js';
|
|
9
|
+
export { createClientAdapter, createServerAdapter } from './store.js';
|
|
10
|
+
export type { SettingsStore, KeyValueStore, CreateClientAdapterOptions, CreateServerAdapterOptions, } from './store.js';
|
|
11
|
+
export { createUserSettings, provideUserSettings, useSettings, useSetting, USER_SETTINGS_KEY, } from './useSettings.js';
|
|
12
|
+
export type { UserSettingsApi, SyncOnLoginOptions, CreateUserSettingsOptions } from './useSettings.js';
|
|
13
|
+
export { assertValidDescriptors, indexDescriptors, coerceValue } from './descriptors.js';
|
|
14
|
+
export { resolveRenderState, parseCookies, firstAcceptLanguage, colorSchemeMeta, renderHeadScript, } from './ssr.js';
|
|
15
|
+
export type { RenderState, ResolveRenderStateOptions } from './ssr.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../frontend/src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAChF,YAAY,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtE,YAAY,EACV,aAAa,EACb,aAAa,EACb,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAEvG,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* user-settings — de frontend-helft (het mechanisme). Barrel die de getypte client, de twee store-adapters,
|
|
3
|
+
* de `useSettings()`-seam en de SSR-helpers bundelt. Alle types leidt de consumer via het contract af; hier
|
|
4
|
+
* alleen het uitvoerbare mechanisme dat de frontend-kits samenbindt. De surface-pagina (frontend/templates)
|
|
5
|
+
* consumeert dit.
|
|
6
|
+
*/
|
|
7
|
+
export { createUserSettingsClient, UserSettingsClientError } from './client.js';
|
|
8
|
+
export { createClientAdapter, createServerAdapter } from './store.js';
|
|
9
|
+
export { createUserSettings, provideUserSettings, useSettings, useSetting, USER_SETTINGS_KEY, } from './useSettings.js';
|
|
10
|
+
export { assertValidDescriptors, indexDescriptors, coerceValue } from './descriptors.js';
|
|
11
|
+
export { resolveRenderState, parseCookies, firstAcceptLanguage, colorSchemeMeta, renderHeadScript, } from './ssr.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../frontend/src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQtE,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,gBAAgB,GACjB,MAAM,UAAU,CAAC"}
|