@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
|
+
* 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 class UserSettingsMaterializationConflictError extends Error {
|
|
7
|
+
paths;
|
|
8
|
+
constructor(paths) {
|
|
9
|
+
super(`Materialisatie gestopt: ${paths.length} surface-bestand(en) bestaan al en zouden overschreven ` +
|
|
10
|
+
`worden zonder overwrite-vlag: ${paths.join(', ')}`);
|
|
11
|
+
this.name = 'UserSettingsMaterializationConflictError';
|
|
12
|
+
this.paths = paths;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Geworpen wanneer het project een door het manifest vereiste requires-port niet levert. */
|
|
16
|
+
export class MissingRequiredPortError extends Error {
|
|
17
|
+
missing;
|
|
18
|
+
constructor(missing) {
|
|
19
|
+
super(`Materialisatie geweigerd: de host levert niet alle vereiste ports. Ontbrekend: ${missing.join(', ')}`);
|
|
20
|
+
this.name = 'MissingRequiredPortError';
|
|
21
|
+
this.missing = missing;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Geworpen door `checkRequiredModules` wanneer een vereiste capability-module niet aanwezig is.
|
|
26
|
+
* user-settings heeft GEEN `requires.modules` (kit-only), dus dit vuurt bij deze module in de praktijk niet
|
|
27
|
+
* — het blijft aanwezig voor pariteit met de siblings en voor het geval een host de check toch aanroept.
|
|
28
|
+
*/
|
|
29
|
+
export class MissingRequiredModuleError extends Error {
|
|
30
|
+
missing;
|
|
31
|
+
constructor(missing) {
|
|
32
|
+
super(`Assemblage geweigerd: deze module vereist andere capability-module(s) die niet aanwezig zijn. ` +
|
|
33
|
+
`Ontbrekend: ${missing.join(', ')}`);
|
|
34
|
+
this.name = 'MissingRequiredModuleError';
|
|
35
|
+
this.missing = missing;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../scaffolder/core/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oGAAoG;AACpG,MAAM,OAAO,wCAAyC,SAAQ,KAAK;IACxD,KAAK,CAAoB;IAClC,YAAY,KAAwB;QAClC,KAAK,CACH,2BAA2B,KAAK,CAAC,MAAM,yDAAyD;YAC9F,iCAAiC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0CAA0C,CAAC;QACvD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,6FAA6F;AAC7F,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,OAAO,CAAoB;IACpC,YAAY,OAA0B;QACpC,KAAK,CAAC,kFAAkF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9G,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAC1C,OAAO,CAAoB;IACpC,YAAY,OAA0B;QACpC,KAAK,CACH,gGAAgG;YAC9F,eAAe,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtC,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;QACzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* materialize — de GENERATE-stap van de version-aware materializer. Schrijft de surface-`FileDescriptor`s
|
|
3
|
+
* van de user-settings-module via een geïnjecteerde fs-port in een doelproject; vanaf dat moment is de
|
|
4
|
+
* surface projecteigendom en mag ze divergeren. De functie stempelt de `templateVersion` per bestand,
|
|
5
|
+
* verzamelt ÁLLE collisions en weigert all-or-nothing zonder `overwrite`, en weigert te starten als een
|
|
6
|
+
* vereiste requires-port ontbreekt. Puur op de fs-port na: deterministisch, geen module-globale state.
|
|
7
|
+
*/
|
|
8
|
+
import type { CapabilityModuleManifest } from '../../manifest.js';
|
|
9
|
+
import type { UserSettingsScaffolderFsPort, FileDescriptor } from './ports.js';
|
|
10
|
+
export interface MaterializedFile {
|
|
11
|
+
readonly templateVersion: string;
|
|
12
|
+
readonly base: string;
|
|
13
|
+
}
|
|
14
|
+
export interface MaterializationRecord {
|
|
15
|
+
readonly module: string;
|
|
16
|
+
readonly templateVersion: string;
|
|
17
|
+
readonly files: Readonly<Record<string, MaterializedFile>>;
|
|
18
|
+
}
|
|
19
|
+
export interface MaterializeOptions {
|
|
20
|
+
readonly manifest: CapabilityModuleManifest;
|
|
21
|
+
readonly surfaceFiles: readonly FileDescriptor[];
|
|
22
|
+
readonly fsPort: UserSettingsScaffolderFsPort;
|
|
23
|
+
readonly templateVersion?: string;
|
|
24
|
+
readonly overwrite?: boolean;
|
|
25
|
+
readonly providedPorts?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export interface MaterializeResult {
|
|
28
|
+
readonly record: MaterializationRecord;
|
|
29
|
+
readonly written: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Materialiseert de surface van de user-settings-module in een project.
|
|
33
|
+
*
|
|
34
|
+
* @throws MissingRequiredPortError als een vereiste port ontbreekt (geen enkele write gebeurt)
|
|
35
|
+
* @throws UserSettingsMaterializationConflictError als bestanden al bestaan en `overwrite` niet gezet is
|
|
36
|
+
*/
|
|
37
|
+
export declare function materializeUserSettingsModule(options: MaterializeOptions): MaterializeResult;
|
|
38
|
+
/** Convenience: het stempelrecord platgeslagen tot een pure `bestand → templateVersion`-map. */
|
|
39
|
+
export declare function stampedVersions(record: MaterializationRecord): Record<string, string>;
|
|
40
|
+
//# sourceMappingURL=materialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize.d.ts","sourceRoot":"","sources":["../../../scaffolder/core/materialize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,4BAA4B,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG/E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,4BAA4B,CAAC;IAC9C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,kBAAkB,GAAG,iBAAiB,CAwB5F;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIrF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { UserSettingsMaterializationConflictError, MissingRequiredPortError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Materialiseert de surface van de user-settings-module in een project.
|
|
4
|
+
*
|
|
5
|
+
* @throws MissingRequiredPortError als een vereiste port ontbreekt (geen enkele write gebeurt)
|
|
6
|
+
* @throws UserSettingsMaterializationConflictError als bestanden al bestaan en `overwrite` niet gezet is
|
|
7
|
+
*/
|
|
8
|
+
export function materializeUserSettingsModule(options) {
|
|
9
|
+
const { manifest, surfaceFiles, fsPort, overwrite = false, providedPorts } = options;
|
|
10
|
+
const templateVersion = options.templateVersion ?? manifest.templateVersion;
|
|
11
|
+
if (providedPorts !== undefined) {
|
|
12
|
+
const provided = new Set(providedPorts);
|
|
13
|
+
const missing = manifest.requires.ports.filter((port) => !provided.has(port));
|
|
14
|
+
if (missing.length > 0)
|
|
15
|
+
throw new MissingRequiredPortError(missing);
|
|
16
|
+
}
|
|
17
|
+
if (!overwrite) {
|
|
18
|
+
const conflicts = surfaceFiles.filter((f) => fsPort.exists(f.path)).map((f) => f.path);
|
|
19
|
+
if (conflicts.length > 0)
|
|
20
|
+
throw new UserSettingsMaterializationConflictError(conflicts);
|
|
21
|
+
}
|
|
22
|
+
const files = {};
|
|
23
|
+
const written = [];
|
|
24
|
+
for (const descriptor of surfaceFiles) {
|
|
25
|
+
fsPort.write(descriptor.path, descriptor.contents);
|
|
26
|
+
files[descriptor.path] = { templateVersion, base: descriptor.contents };
|
|
27
|
+
written.push(descriptor.path);
|
|
28
|
+
}
|
|
29
|
+
return { record: { module: manifest.name, templateVersion, files }, written };
|
|
30
|
+
}
|
|
31
|
+
/** Convenience: het stempelrecord platgeslagen tot een pure `bestand → templateVersion`-map. */
|
|
32
|
+
export function stampedVersions(record) {
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [path, file] of Object.entries(record.files))
|
|
35
|
+
out[path] = file.templateVersion;
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=materialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize.js","sourceRoot":"","sources":["../../../scaffolder/core/materialize.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,wCAAwC,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AA2BjG;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,OAA2B;IACvE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IACrF,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,QAAQ,CAAC,eAAe,CAAC;IAE5E,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvF,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,wCAAwC,CAAC,SAAS,CAAC,CAAC;IAC1F,CAAC;IAED,MAAM,KAAK,GAAqC,EAAE,CAAC;IACnD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;QACnD,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC;AAChF,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,eAAe,CAAC,MAA6B;IAC3D,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC;IAC1F,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ports — de geïnjecteerde randen van de scaffolder. De core-functies raken NOOIT een echt filesystem:
|
|
3
|
+
* ze krijgen een {@link UserSettingsScaffolderFsPort} injected en werken uitsluitend met {@link FileDescriptor}s.
|
|
4
|
+
* Zo blijft de materializer deterministisch, testbaar met een in-memory port en vrij van module-globale state.
|
|
5
|
+
*
|
|
6
|
+
* NB (bekende schuld — Layer 1b): deze fs-port + descriptor zijn per module gekopieerd-met-hernoeming
|
|
7
|
+
* (`UserSettingsScaffolderFsPort` ↔ `AccountScaffolderFsPort` ↔ `AuthzScaffolderFsPort` ↔ `AuthScaffolderFsPort`).
|
|
8
|
+
* De generieke extractie naar een gedeeld scaffolder-package is een aparte, breaking refactor; zie
|
|
9
|
+
* capability-modules/README.
|
|
10
|
+
*/
|
|
11
|
+
/** Eén te materialiseren bestand: relatief pad + volledige inhoud. Pure data, geen side-effects. */
|
|
12
|
+
export interface FileDescriptor {
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly contents: string;
|
|
15
|
+
}
|
|
16
|
+
/** Minimale, injecteerbare fs-port. Bewust synchroon en klein zodat hij triviaal te mocken is. */
|
|
17
|
+
export interface UserSettingsScaffolderFsPort {
|
|
18
|
+
exists(path: string): boolean;
|
|
19
|
+
read(path: string): string;
|
|
20
|
+
write(path: string, contents: string): void;
|
|
21
|
+
}
|
|
22
|
+
/** Een in-memory fs-port met een `snapshot()` erbij, voor tests én de re-scaffold-naar-temp-stap. */
|
|
23
|
+
export interface MemoryFsPort extends UserSettingsScaffolderFsPort {
|
|
24
|
+
snapshot(): Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
/** Maakt een verse, geïsoleerde in-memory fs-port (geen module-globale mutable state). */
|
|
27
|
+
export declare function createMemoryFsPort(seed?: Readonly<Record<string, string>>): MemoryFsPort;
|
|
28
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../../scaffolder/core/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,oGAAoG;AACpG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,kGAAkG;AAClG,MAAM,WAAW,4BAA4B;IAC3C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,qGAAqG;AACrG,MAAM,WAAW,YAAa,SAAQ,4BAA4B;IAChE,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,YAAY,CAcxF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ports — de geïnjecteerde randen van de scaffolder. De core-functies raken NOOIT een echt filesystem:
|
|
3
|
+
* ze krijgen een {@link UserSettingsScaffolderFsPort} injected en werken uitsluitend met {@link FileDescriptor}s.
|
|
4
|
+
* Zo blijft de materializer deterministisch, testbaar met een in-memory port en vrij van module-globale state.
|
|
5
|
+
*
|
|
6
|
+
* NB (bekende schuld — Layer 1b): deze fs-port + descriptor zijn per module gekopieerd-met-hernoeming
|
|
7
|
+
* (`UserSettingsScaffolderFsPort` ↔ `AccountScaffolderFsPort` ↔ `AuthzScaffolderFsPort` ↔ `AuthScaffolderFsPort`).
|
|
8
|
+
* De generieke extractie naar een gedeeld scaffolder-package is een aparte, breaking refactor; zie
|
|
9
|
+
* capability-modules/README.
|
|
10
|
+
*/
|
|
11
|
+
/** Maakt een verse, geïsoleerde in-memory fs-port (geen module-globale mutable state). */
|
|
12
|
+
export function createMemoryFsPort(seed) {
|
|
13
|
+
const store = new Map(seed ? Object.entries(seed) : []);
|
|
14
|
+
return {
|
|
15
|
+
exists: (path) => store.has(path),
|
|
16
|
+
read: (path) => {
|
|
17
|
+
const value = store.get(path);
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
throw new Error(`Bestand niet gevonden op de fs-port: ${path}`);
|
|
20
|
+
return value;
|
|
21
|
+
},
|
|
22
|
+
write: (path, contents) => {
|
|
23
|
+
store.set(path, contents);
|
|
24
|
+
},
|
|
25
|
+
snapshot: () => Object.fromEntries([...store.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../../../scaffolder/core/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoBH,0FAA0F;AAC1F,MAAM,UAAU,kBAAkB,CAAC,IAAuC;IACxE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAiB,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxE,OAAO;QACL,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACjC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,KAAK,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,EAAE,CAAC,CAAC;YACzF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;YACxB,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC5B,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC1G,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* presence — de module→module presence-check. user-settings heeft GEEN `requires.modules` (kit-only,
|
|
3
|
+
* ontkoppeld van account), dus deze check is bij deze module in de praktijk een no-op. Ze blijft aanwezig
|
|
4
|
+
* voor pariteit met de siblings (account vereist authentication) en zodat een host die de check
|
|
5
|
+
* generiek aanroept, dezelfde API krijgt.
|
|
6
|
+
*
|
|
7
|
+
* Puur & deterministisch: geen fs, geen module-globale mutable state, geen random/timestamps.
|
|
8
|
+
*/
|
|
9
|
+
import type { CapabilityModuleManifest } from '../../manifest.js';
|
|
10
|
+
/**
|
|
11
|
+
* Controleert dat elke `manifest.requires.modules`-entry in `presentModules` zit. Ontbreekt er één of
|
|
12
|
+
* meer, dan wordt een {@link MissingRequiredModuleError} geworpen met ÁLLE ontbrekende modules. Geen
|
|
13
|
+
* `requires.modules` → no-op (het geval voor user-settings).
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkRequiredModules(manifest: CapabilityModuleManifest, presentModules: readonly string[]): void;
|
|
16
|
+
export interface AssemblableOptions {
|
|
17
|
+
readonly presentModules: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
/** Eén duidelijk instappunt voor de scaffolder-orkestratie vóór het assembleren. */
|
|
20
|
+
export declare function assertAssemblable(manifest: CapabilityModuleManifest, options: AssemblableOptions): void;
|
|
21
|
+
//# sourceMappingURL=presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../../scaffolder/core/presence.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAGlE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,wBAAwB,EAAE,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAMhH;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAED,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,wBAAwB,EAAE,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAEvG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MissingRequiredModuleError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Controleert dat elke `manifest.requires.modules`-entry in `presentModules` zit. Ontbreekt er één of
|
|
4
|
+
* meer, dan wordt een {@link MissingRequiredModuleError} geworpen met ÁLLE ontbrekende modules. Geen
|
|
5
|
+
* `requires.modules` → no-op (het geval voor user-settings).
|
|
6
|
+
*/
|
|
7
|
+
export function checkRequiredModules(manifest, presentModules) {
|
|
8
|
+
const required = manifest.requires.modules;
|
|
9
|
+
if (required === undefined || required.length === 0)
|
|
10
|
+
return;
|
|
11
|
+
const present = new Set(presentModules);
|
|
12
|
+
const missing = required.filter((module) => !present.has(module));
|
|
13
|
+
if (missing.length > 0)
|
|
14
|
+
throw new MissingRequiredModuleError(missing);
|
|
15
|
+
}
|
|
16
|
+
/** Eén duidelijk instappunt voor de scaffolder-orkestratie vóór het assembleren. */
|
|
17
|
+
export function assertAssemblable(manifest, options) {
|
|
18
|
+
checkRequiredModules(manifest, options.presentModules);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../../scaffolder/core/presence.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAEzD;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAkC,EAAE,cAAiC;IACxG,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;IAC3C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,0BAA0B,CAAC,OAAO,CAAC,CAAC;AACxE,CAAC;AAMD,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,QAAkC,EAAE,OAA2B;IAC/F,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scaffolder (user-settings) — de version-aware materializer van de user-settings-capability-module.
|
|
3
|
+
* Composeert het manifest tot een werkende generate + de (voor deze module no-op) module→module
|
|
4
|
+
* presence-check. Alle core-functies zijn puur/deterministisch; het wegschrijven gaat via een
|
|
5
|
+
* geïnjecteerde fs-port. Dit is een build/dev-tool, geen runtime-mechanisme.
|
|
6
|
+
*
|
|
7
|
+
* BEKENDE SCHULD (Layer 1b): de scaffolder-core is per module gekopieerd-met-hernoeming
|
|
8
|
+
* (`materializeUserSettingsModule` ↔ `materializeAccountModule` ↔ `materializeAuthzModule` ↔
|
|
9
|
+
* `materializeAuthModule`; `UserSettings*`/`Account*`/`Authz*`/`Auth*`-foutklassen). De three-way-merge +
|
|
10
|
+
* config/extend-lagen horen bij dezelfde generieke extractie. Zie capability-modules/README — dit is
|
|
11
|
+
* bewust NIET stilzwijgend nóg een keer gedupliceerd.
|
|
12
|
+
*/
|
|
13
|
+
export type { FileDescriptor, UserSettingsScaffolderFsPort, MemoryFsPort } from './core/ports.js';
|
|
14
|
+
export { createMemoryFsPort } from './core/ports.js';
|
|
15
|
+
export { materializeUserSettingsModule, stampedVersions } from './core/materialize.js';
|
|
16
|
+
export type { MaterializeOptions, MaterializeResult, MaterializationRecord, MaterializedFile, } from './core/materialize.js';
|
|
17
|
+
export { checkRequiredModules, assertAssemblable } from './core/presence.js';
|
|
18
|
+
export type { AssemblableOptions } from './core/presence.js';
|
|
19
|
+
export { UserSettingsMaterializationConflictError, MissingRequiredPortError, MissingRequiredModuleError, } from './core/errors.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../scaffolder/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,YAAY,EAAE,cAAc,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvF,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EACL,wCAAwC,EACxC,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createMemoryFsPort } from './core/ports.js';
|
|
2
|
+
export { materializeUserSettingsModule, stampedVersions } from './core/materialize.js';
|
|
3
|
+
// Module→module presence-check: user-settings heeft geen requires.modules (no-op), behouden voor pariteit.
|
|
4
|
+
export { checkRequiredModules, assertAssemblable } from './core/presence.js';
|
|
5
|
+
export { UserSettingsMaterializationConflictError, MissingRequiredPortError, MissingRequiredModuleError, } from './core/errors.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../scaffolder/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAQvF,2GAA2G;AAC3G,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EACL,wCAAwC,EACxC,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"userSettings.title": "Settings",
|
|
3
|
+
"userSettings.field.theme": "Theme",
|
|
4
|
+
"userSettings.theme.light": "Light",
|
|
5
|
+
"userSettings.theme.dark": "Dark",
|
|
6
|
+
"userSettings.theme.system": "Match system",
|
|
7
|
+
"userSettings.field.locale": "Language",
|
|
8
|
+
"userSettings.field.density": "Density",
|
|
9
|
+
"userSettings.density.comfortable": "Comfortable",
|
|
10
|
+
"userSettings.density.compact": "Compact",
|
|
11
|
+
"userSettings.saved": "Settings saved",
|
|
12
|
+
"userSettings.error.unknown_key": "That setting isn't available.",
|
|
13
|
+
"userSettings.error.validation_failed": "That value isn't allowed.",
|
|
14
|
+
"userSettings.error.consent_namespace_rejected": "Consent choices are managed in your account, not here.",
|
|
15
|
+
"userSettings.error.stale_write": "Your settings changed on another device. Please retry.",
|
|
16
|
+
"userSettings.error.unauthenticated": "Please sign in again.",
|
|
17
|
+
"userSettings.error.csrf_failed": "Your session expired. Please refresh and try again.",
|
|
18
|
+
"userSettings.error.store_unavailable": "Settings can't be saved right now. Please try again later.",
|
|
19
|
+
"userSettings.error.unknown": "Something went wrong. Please try again."
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"userSettings.title": "Instellingen",
|
|
3
|
+
"userSettings.field.theme": "Thema",
|
|
4
|
+
"userSettings.theme.light": "Licht",
|
|
5
|
+
"userSettings.theme.dark": "Donker",
|
|
6
|
+
"userSettings.theme.system": "Systeem volgen",
|
|
7
|
+
"userSettings.field.locale": "Taal",
|
|
8
|
+
"userSettings.field.density": "Dichtheid",
|
|
9
|
+
"userSettings.density.comfortable": "Comfortabel",
|
|
10
|
+
"userSettings.density.compact": "Compact",
|
|
11
|
+
"userSettings.saved": "Instellingen opgeslagen",
|
|
12
|
+
"userSettings.error.unknown_key": "Die instelling bestaat niet.",
|
|
13
|
+
"userSettings.error.validation_failed": "Die waarde is niet toegestaan.",
|
|
14
|
+
"userSettings.error.consent_namespace_rejected": "Toestemmingen beheer je in je account, niet hier.",
|
|
15
|
+
"userSettings.error.stale_write": "Je instellingen zijn op een ander apparaat gewijzigd. Probeer opnieuw.",
|
|
16
|
+
"userSettings.error.unauthenticated": "Log opnieuw in.",
|
|
17
|
+
"userSettings.error.csrf_failed": "Je sessie is verlopen. Ververs de pagina en probeer opnieuw.",
|
|
18
|
+
"userSettings.error.store_unavailable": "Instellingen kunnen nu niet worden opgeslagen. Probeer later opnieuw.",
|
|
19
|
+
"userSettings.error.unknown": "Er ging iets mis. Probeer opnieuw."
|
|
20
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Optionele referentie-instellingen-pagina (surface, gematerialiseerd, mag divergeren). Headless-first: de
|
|
4
|
+
* primaire surface is de `useSetting()`-composable; deze pagina toont hoe je die tegen de canonieke keys
|
|
5
|
+
* (theme/locale/density) bindt. Design-system-agnostisch: plain semantische HTML; de host swapt zijn eigen
|
|
6
|
+
* componenten in of remapt/schrapt de pagina. a11y + i18n eersteklas.
|
|
7
|
+
*
|
|
8
|
+
* De keys zijn host-gedeclareerd; pas deze bindings aan op je eigen descriptor-set.
|
|
9
|
+
*/
|
|
10
|
+
import { useSetting } from '../../src/useSettings.js';
|
|
11
|
+
import { useTranslate } from '../runtime.js';
|
|
12
|
+
|
|
13
|
+
const t = useTranslate();
|
|
14
|
+
|
|
15
|
+
// Canonieke keys — pas aan op je host-descriptor-set.
|
|
16
|
+
const theme = useSetting<string>('theme');
|
|
17
|
+
const locale = useSetting<string>('locale');
|
|
18
|
+
const density = useSetting<string>('density');
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<section class="user-settings-page">
|
|
23
|
+
<header class="user-settings-page__header">
|
|
24
|
+
<h1 class="user-settings-page__title">{{ t('userSettings.title') }}</h1>
|
|
25
|
+
</header>
|
|
26
|
+
|
|
27
|
+
<form class="user-settings-form" novalidate @submit.prevent>
|
|
28
|
+
<fieldset class="user-settings-field">
|
|
29
|
+
<legend class="user-settings-field__label">{{ t('userSettings.field.theme') }}</legend>
|
|
30
|
+
<label>
|
|
31
|
+
<input v-model="theme" type="radio" value="light" name="theme" />
|
|
32
|
+
<span>{{ t('userSettings.theme.light') }}</span>
|
|
33
|
+
</label>
|
|
34
|
+
<label>
|
|
35
|
+
<input v-model="theme" type="radio" value="dark" name="theme" />
|
|
36
|
+
<span>{{ t('userSettings.theme.dark') }}</span>
|
|
37
|
+
</label>
|
|
38
|
+
<label>
|
|
39
|
+
<input v-model="theme" type="radio" value="system" name="theme" />
|
|
40
|
+
<span>{{ t('userSettings.theme.system') }}</span>
|
|
41
|
+
</label>
|
|
42
|
+
</fieldset>
|
|
43
|
+
|
|
44
|
+
<label class="user-settings-field">
|
|
45
|
+
<span class="user-settings-field__label">{{ t('userSettings.field.locale') }}</span>
|
|
46
|
+
<input v-model="locale" name="locale" type="text" data-testid="user-settings-locale" />
|
|
47
|
+
</label>
|
|
48
|
+
|
|
49
|
+
<fieldset class="user-settings-field">
|
|
50
|
+
<legend class="user-settings-field__label">{{ t('userSettings.field.density') }}</legend>
|
|
51
|
+
<label>
|
|
52
|
+
<input v-model="density" type="radio" value="comfortable" name="density" />
|
|
53
|
+
<span>{{ t('userSettings.density.comfortable') }}</span>
|
|
54
|
+
</label>
|
|
55
|
+
<label>
|
|
56
|
+
<input v-model="density" type="radio" value="compact" name="density" />
|
|
57
|
+
<span>{{ t('userSettings.density.compact') }}</span>
|
|
58
|
+
</label>
|
|
59
|
+
</fieldset>
|
|
60
|
+
</form>
|
|
61
|
+
</section>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kleine runtime-helpers voor de surface-pagina (gematerialiseerd, mag divergeren). Houdt de pagina
|
|
3
|
+
* design-system- en i18n-framework-agnostisch: de host kan een echte vertaler/toaster injecteren; anders
|
|
4
|
+
* valt alles terug op een veilige no-op/identiteit. Geen module-globale mutable state.
|
|
5
|
+
*/
|
|
6
|
+
import { inject, type InjectionKey } from 'vue';
|
|
7
|
+
|
|
8
|
+
/** Injecteerbare vertaalfunctie (sleutel → tekst). Default: geef de sleutel terug (zichtbaar in tests). */
|
|
9
|
+
export const USER_SETTINGS_TRANSLATE_KEY: InjectionKey<(key: string) => string> = Symbol('user-settings.translate');
|
|
10
|
+
/** Injecteerbare success-toaster. Default: no-op. */
|
|
11
|
+
export const USER_SETTINGS_TOASTS_KEY: InjectionKey<(message: string) => void> = Symbol('user-settings.toasts');
|
|
12
|
+
|
|
13
|
+
export function useTranslate(): (key: string) => string {
|
|
14
|
+
return inject(USER_SETTINGS_TRANSLATE_KEY, (key: string) => key);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useOptionalToasts(): ((message: string) => void) | undefined {
|
|
18
|
+
return inject(USER_SETTINGS_TOASTS_KEY, undefined);
|
|
19
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seifer-webapp-factory/user-settings",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "user-settings — vijfde capability-module: een herbruikbare preferences-laag (theme/locale/dir, density, layout, dismissed hints, onboarding) met één useSettings()-seam over twee runtime-adapters (client: cookie+localStorage anoniem; server: NestJS+persistence per gebruiker), SSR-veilige render-keys en guest→login LWW-merge. Geparametriseerd door een host-gedeclareerd descriptor-schema (de module levert de engine, de host de keys). Consent hoort NIET hier — dat delegeert naar de account-module.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./contract": {
|
|
9
|
+
"types": "./dist/contract/index.d.ts",
|
|
10
|
+
"import": "./dist/contract/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./backend": {
|
|
13
|
+
"types": "./dist/backend/src/index.d.ts",
|
|
14
|
+
"import": "./dist/backend/src/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./frontend": {
|
|
17
|
+
"types": "./dist/frontend/src/index.d.ts",
|
|
18
|
+
"import": "./dist/frontend/src/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./manifest": {
|
|
21
|
+
"types": "./dist/manifest.d.ts",
|
|
22
|
+
"import": "./dist/manifest.js"
|
|
23
|
+
},
|
|
24
|
+
"./scaffolder": {
|
|
25
|
+
"types": "./dist/scaffolder/index.d.ts",
|
|
26
|
+
"import": "./dist/scaffolder/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.build.json",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
|
+
"typecheck:backend": "tsc -p tsconfig.backend.json",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:e2e": "vitest run -c vitest.e2e.config.ts"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@seifer-webapp-factory/capability-spec": "^0.1.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"vue": "^3.4.0",
|
|
41
|
+
"zod": "^3.23.0",
|
|
42
|
+
"@seifer-webapp-factory/kits": "^0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@nestjs/common": "^10.4.22",
|
|
46
|
+
"@nestjs/core": "^10.4.22",
|
|
47
|
+
"@nestjs/platform-express": "^10.4.22",
|
|
48
|
+
"@nestjs/testing": "^10.4.22",
|
|
49
|
+
"@playwright/test": "^1.50.0",
|
|
50
|
+
"@types/pg": "^8.20.0",
|
|
51
|
+
"@types/supertest": "^6.0.0",
|
|
52
|
+
"@vitejs/plugin-vue": "^5.2.0",
|
|
53
|
+
"@vue/test-utils": "^2.4.0",
|
|
54
|
+
"express": "^4.21.0",
|
|
55
|
+
"happy-dom": "^15.0.0",
|
|
56
|
+
"pg": "^8.22.0",
|
|
57
|
+
"reflect-metadata": "^0.2.2",
|
|
58
|
+
"rxjs": "^7.8.2",
|
|
59
|
+
"supertest": "^7.0.0",
|
|
60
|
+
"testcontainers": "^10.28.0",
|
|
61
|
+
"tsx": "^4.23.0",
|
|
62
|
+
"typescript": "^5.7.0",
|
|
63
|
+
"vite": "^5.4.0",
|
|
64
|
+
"vitest": "^2.1.0",
|
|
65
|
+
"vue": "^3.4.0",
|
|
66
|
+
"vue-router": "^4.4.0",
|
|
67
|
+
"zod": "^3.24.0",
|
|
68
|
+
"@seifer-webapp-factory/kits": "^0.1.0"
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=20"
|
|
72
|
+
},
|
|
73
|
+
"files": [
|
|
74
|
+
"dist",
|
|
75
|
+
"backend/templates",
|
|
76
|
+
"frontend/templates",
|
|
77
|
+
"README.md"
|
|
78
|
+
]
|
|
79
|
+
}
|