@creezio/interactive-demo 0.22.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 ADDED
@@ -0,0 +1,138 @@
1
+ # @creezio/interactive-demo
2
+
3
+ Démo interactive **native** (module OS présent dans toutes les apps Creezio) :
4
+ un product tour « Storylane-like » mais joué **en live dans l'app** — un faux
5
+ curseur animé se déplace, clique et tape réellement dans l'interface pendant
6
+ que des cartes de narration présentent chaque fonctionnalité.
7
+
8
+ ## Ce que fournit le package
9
+
10
+ - **Types de scénario déclaratifs** (`DemoScenario` / `DemoStep`) : étapes
11
+ `say` (carte plein écran), `navigate`, `highlight` (spotlight + carte
12
+ ancrée), `click` (faux curseur + vrai clic DOM), `type` (frappe caractère
13
+ par caractère), `scroll`, `wait`. Format JSON-sérialisable : ajouter /
14
+ enlever / réordonner des étapes = éditer un tableau.
15
+ - **Contenu hybride DB** (ADR-module-natif-hybride) : la marque déclare ses
16
+ scénarios par défaut dans UN fichier explicite
17
+ (`server/src/electron/brand-interactive-demo-content.ts`), les overrides
18
+ vivent en `brand.db` (`interactive_demo_content`), le runtime sert
19
+ `merge(défauts, overrides)`. Le « déjà vu » par utilisateur va dans
20
+ `interactive_demo_preferences`.
21
+ - **Mount api-kernel** `/api/v1/modules/interactive-demo/*` : `GET/PUT/DELETE
22
+ scenarios[/:id]` + `GET/PUT preferences`.
23
+ - **UI React** (`@creezio/interactive-demo/ui`) : `InteractiveDemoRoot`
24
+ (auto-lancement première visite + lanceur flottant « Visite guidée »),
25
+ `DemoPlayer` (lecteur bas niveau), `startInteractiveDemo()` (déclenchement
26
+ programmatique), curseur/spotlight en Web Animations API + CSS
27
+ (`ui/interactive-demo.css`) — zéro dépendance d'animation tierce.
28
+ - **Scénario générique OS** : `genericOsTourScenario({ productName })` —
29
+ démo de base jour 1 (sidebar, tâches, mails, assistant) pour toute marque
30
+ sans scénario produit.
31
+ - **Collecteur de contributions modules** : `collectInteractiveDemoDefaults(contributions)`
32
+ agrège les scénarios `demo` des modules métier (validation
33
+ `validateDemoScenario`, dédup par id, ordre stable, erreurs agrégées
34
+ explicites) — la brique derrière `collectDemoScenarios()` du registre
35
+ `modules/index.ts` généré par la factory.
36
+
37
+ ## Défauts par modules (registre — câblage cible)
38
+
39
+ Chaque module métier **doit** déclarer ≥ 1 scénario dans son
40
+ `server/src/electron/modules/<id>.ts` (champ `demo` du `BrandModuleDef`).
41
+ Inclure `genericOsTourScenario({ productName })` (id `os-tour` partagé).
42
+ Une app Creezio sans démo interactive est invalide. La factory câble le
43
+ mount, les migrations et le CSS ; `BrandChrome` monte **un** `InteractiveDemoRoot`
44
+ dans `SessionProvider` (`CreezioUiBoot` ne remonte plus le lecteur).
45
+
46
+ ```ts
47
+ export const promotionsModule: BrandModuleDef = {
48
+ id: "promotions",
49
+ // entitySpecs, navItems…
50
+ demo: {
51
+ scenarios: [
52
+ {
53
+ id: "promotions-tour",
54
+ title: "Promotions",
55
+ steps: [{ id: "intro", kind: "say", title: "Créez vos promos" }],
56
+ },
57
+ ],
58
+ },
59
+ };
60
+ ```
61
+
62
+ Le registre `modules/index.ts` (généré factory) agrège tout via
63
+ `collectDemoScenarios()` — le mount n'est alors qu'**une ligne** côté app :
64
+
65
+ ```ts
66
+ // server/src/electron/brand-module-api.ts
67
+ import { collectDemoScenarios } from "./modules/index.js";
68
+ import { createInteractiveDemoMount } from "@creezio/interactive-demo";
69
+
70
+ api.registerModuleApi(
71
+ "interactive-demo",
72
+ createInteractiveDemoMount({ defaults: collectDemoScenarios() }),
73
+ );
74
+ ```
75
+
76
+ `collectDemoScenarios()` délègue à `collectInteractiveDemoDefaults()` :
77
+ scénarios invalides ou ids en conflit entre modules = **erreur explicite au
78
+ boot** (liste complète module:scénario), jamais une démo tronquée servie en
79
+ silence.
80
+
81
+ ## Un seul lecteur — dans SessionProvider
82
+
83
+ La factory pose **un** `InteractiveDemoRoot` dans `BrandChrome`, à
84
+ l'intérieur de `SessionProvider` (`useSession()` → `userKey` / `role`).
85
+ `CreezioUiBoot` ne remonte plus un second root (double curseur / Foove #101).
86
+ Pas de `brandDemoScenarios()` — SoT = `collectDemoScenarios()`.
87
+
88
+ ```tsx
89
+ // ui/components/brand-chrome.tsx (généré factory)
90
+ const { me } = useSession();
91
+ <InteractiveDemoRoot
92
+ launcher="sidebar"
93
+ userKey={me?.user}
94
+ role={me?.brandRole}
95
+ />
96
+ ```
97
+
98
+ PR kit #109 (curseur DOM unmount après Terminer) : **reste ouvert**, non
99
+ fusionné ici pour ne pas mélanger le lockstep 0.10.8.
100
+
101
+ Le scénario marqué `autoStart: true` se lance automatiquement à la première
102
+ visite (après le setup / l'onboarding) ; « déjà vu » est persisté par
103
+ utilisateur (preferences + localStorage). Le lanceur flottant permet de
104
+ rejouer la visite à tout moment.
105
+
106
+ ## Scénarios par rôle
107
+
108
+ Un scénario peut restreindre sa visibilité avec `roles?: string[]` (lanceur
109
+ + autoStart ; un lancement explicite `startInteractiveDemo(id)` ignore le
110
+ filtre). Le rôle courant est une **donnée de session**, pas une donnée
111
+ métier : la marque déclare `configureAuth({ resolveBrandRole })`
112
+ (`@creezio/auth`, callback qui résout le rôle depuis SA db — ex. lookup
113
+ `user_roles`), le kit sert `brand_role` dans `/api/v1/auth/me` (la valeur
114
+ suit la cible en impersonation) et `useSession().me.brandRole` côté UI.
115
+ Sans resolver : `brandRole` est `null` → aucun filtrage (comportement
116
+ historique).
117
+
118
+ ## Éditer la démo (ajouter / enlever des étapes)
119
+
120
+ - **Défauts** : éditer le tableau `steps` du fichier marque
121
+ `brand-interactive-demo-content.ts` (rebuild).
122
+ - **À chaud, sans code** : `PUT /api/v1/modules/interactive-demo/scenarios/<id>`
123
+ avec `{ "steps": [ … ] }` (remplacement complet du tableau — pas de merge
124
+ ambigu) ; `DELETE` pour revenir aux défauts. Un `PUT` sur un id inconnu
125
+ avec `title` + `steps` crée un scénario supplémentaire.
126
+
127
+ ## Commandes
128
+
129
+ ```bash
130
+ npm run build -w @creezio/interactive-demo
131
+ node --test scripts/test-phase-interactive-demo.mjs # gate kit
132
+ ```
133
+
134
+ ## Docs
135
+
136
+ - [AGENTS.md](./AGENTS.md) — frontières et pièges (agents IA)
137
+ - [docs/FILES.md](./docs/FILES.md) — inventaire des fichiers
138
+ - ADR : [docs/adr/ADR-module-natif-hybride.md](../../docs/adr/ADR-module-natif-hybride.md)
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Contenu démo interactive hybride (ADR-module-natif-hybride).
3
+ *
4
+ * Le kit fournit le moteur générique : la marque déclare ses scénarios par
5
+ * défaut dans UN fichier explicite
6
+ * (`server/src/electron/brand-interactive-demo-content.ts`), les overrides
7
+ * vivent en `brand.db` (table `interactive_demo_content`, une ligne par
8
+ * scénario), et le runtime sert toujours `merge(défauts marque, overrides
9
+ * DB)`. Le « déjà vu » par utilisateur atterrit dans
10
+ * `interactive_demo_preferences` via le même mount.
11
+ *
12
+ * Zéro texte métier ici (ADR-no-brand-domain-in-native-packages) —
13
+ * imports type-only pour ne pas créer de cycle runtime.
14
+ */
15
+ import type { ApiMount } from "@creezio/api-kernel";
16
+ import type { SqliteMigration } from "@creezio/platform-core";
17
+ import type { DemoScenario, DemoScenarioOverride } from "./types.js";
18
+ export declare const INTERACTIVE_DEMO_SCHEMA_SQL = "-- Sc\u00E9narios d\u00E9mo interactive hybride + preferences (@creezio/interactive-demo)\n\nCREATE TABLE IF NOT EXISTS interactive_demo_content (\n key TEXT PRIMARY KEY,\n value_json TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS interactive_demo_preferences (\n id TEXT PRIMARY KEY,\n user_key TEXT NOT NULL,\n key TEXT NOT NULL,\n value_json TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(user_key, key)\n);\n";
19
+ /** Migrations brand.db du module (à composer dans `brand-migrations.ts` marque). */
20
+ export declare function interactiveDemoMigrations(): SqliteMigration[];
21
+ /**
22
+ * Merge pur défauts marque + overrides DB.
23
+ *
24
+ * - scénario par `id` : champs présents dans l'override priment ;
25
+ * - `steps` d'un override REMPLACE le tableau des défauts (édition
26
+ * explicite — pas de merge étape par étape) ;
27
+ * - `enabled: false` désactive un scénario par défaut sans le supprimer ;
28
+ * - un override inconnu des défauts portant `title` + `steps` valides est
29
+ * ajouté en fin (scénario créé par l'admin).
30
+ */
31
+ export declare function mergeDemoScenarios(defaults: DemoScenario[], overrides?: DemoScenarioOverride[] | null): DemoScenario[];
32
+ export type InteractiveDemoMountOptions = {
33
+ /** Défauts marque (fichier explicite `brand-interactive-demo-content.ts`). */
34
+ defaults: DemoScenario[];
35
+ };
36
+ /**
37
+ * Mount api-kernel `/api/v1/modules/interactive-demo/*` (dbLayer brand) :
38
+ *
39
+ * - `GET scenarios` → `{ ok, scenarios: merge(défauts, overrides), overrides: [ids] }`
40
+ * - `GET scenarios/:id` → `{ ok, scenario }` (mergé) ou 404
41
+ * - `PUT scenarios/:id` → stocke l'override (steps = remplacement), répond le scénario mergé
42
+ * - `DELETE scenarios/:id` → supprime l'override (retour aux défauts)
43
+ * - `GET preferences?user=<k>` → `{ ok, user, answers }` (clés `seen:<scenarioId>`…)
44
+ * - `PUT preferences` → `{ user, answers }` upsert par clé
45
+ *
46
+ * db absent → 503 `db_unavailable` ; body invalide → 400 (jamais de throw).
47
+ */
48
+ export declare function createInteractiveDemoMount(opts: InteractiveDemoMountOptions): ApiMount;
49
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKrE,eAAO,MAAM,2BAA2B,4cAgBvC,CAAC;AAEF,oFAAoF;AACpF,wBAAgB,yBAAyB,IAAI,eAAe,EAAE,CAO7D;AAYD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EAAE,EACxB,SAAS,CAAC,EAAE,oBAAoB,EAAE,GAAG,IAAI,GACxC,YAAY,EAAE,CAkChB;AAiDD,MAAM,MAAM,2BAA2B,GAAG;IACxC,8EAA8E;IAC9E,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,2BAA2B,GAChC,QAAQ,CAmMV"}
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Contenu démo interactive hybride (ADR-module-natif-hybride).
3
+ *
4
+ * Le kit fournit le moteur générique : la marque déclare ses scénarios par
5
+ * défaut dans UN fichier explicite
6
+ * (`server/src/electron/brand-interactive-demo-content.ts`), les overrides
7
+ * vivent en `brand.db` (table `interactive_demo_content`, une ligne par
8
+ * scénario), et le runtime sert toujours `merge(défauts marque, overrides
9
+ * DB)`. Le « déjà vu » par utilisateur atterrit dans
10
+ * `interactive_demo_preferences` via le même mount.
11
+ *
12
+ * Zéro texte métier ici (ADR-no-brand-domain-in-native-packages) —
13
+ * imports type-only pour ne pas créer de cycle runtime.
14
+ */
15
+ import { validateDemoScenario } from "./types.js";
16
+ /* ------------------------------------------------------------- migrations */
17
+ export const INTERACTIVE_DEMO_SCHEMA_SQL = `-- Scénarios démo interactive hybride + preferences (@creezio/interactive-demo)
18
+
19
+ CREATE TABLE IF NOT EXISTS interactive_demo_content (
20
+ key TEXT PRIMARY KEY,
21
+ value_json TEXT NOT NULL,
22
+ updated_at TEXT NOT NULL
23
+ );
24
+
25
+ CREATE TABLE IF NOT EXISTS interactive_demo_preferences (
26
+ id TEXT PRIMARY KEY,
27
+ user_key TEXT NOT NULL,
28
+ key TEXT NOT NULL,
29
+ value_json TEXT NOT NULL,
30
+ updated_at TEXT NOT NULL,
31
+ UNIQUE(user_key, key)
32
+ );
33
+ `;
34
+ /** Migrations brand.db du module (à composer dans `brand-migrations.ts` marque). */
35
+ export function interactiveDemoMigrations() {
36
+ return [
37
+ {
38
+ id: "interactive_demo_001_content_prefs",
39
+ sql: INTERACTIVE_DEMO_SCHEMA_SQL,
40
+ },
41
+ ];
42
+ }
43
+ /* ------------------------------------------------------------------ merge */
44
+ function cloneScenario(s) {
45
+ return {
46
+ ...s,
47
+ ...(Array.isArray(s.roles) ? { roles: [...s.roles] } : {}),
48
+ steps: s.steps.map((st) => ({ ...st })),
49
+ };
50
+ }
51
+ /**
52
+ * Merge pur défauts marque + overrides DB.
53
+ *
54
+ * - scénario par `id` : champs présents dans l'override priment ;
55
+ * - `steps` d'un override REMPLACE le tableau des défauts (édition
56
+ * explicite — pas de merge étape par étape) ;
57
+ * - `enabled: false` désactive un scénario par défaut sans le supprimer ;
58
+ * - un override inconnu des défauts portant `title` + `steps` valides est
59
+ * ajouté en fin (scénario créé par l'admin).
60
+ */
61
+ export function mergeDemoScenarios(defaults, overrides) {
62
+ const base = defaults.map(cloneScenario);
63
+ if (!Array.isArray(overrides) || overrides.length === 0)
64
+ return base;
65
+ const byId = new Map();
66
+ for (const o of overrides) {
67
+ if (o && typeof o.id === "string" && o.id)
68
+ byId.set(o.id, o);
69
+ }
70
+ const merged = base.map((s) => {
71
+ const o = byId.get(s.id);
72
+ if (!o)
73
+ return s;
74
+ byId.delete(s.id);
75
+ return {
76
+ ...s,
77
+ ...(typeof o.title === "string" && o.title ? { title: o.title } : {}),
78
+ ...(typeof o.description === "string" ? { description: o.description } : {}),
79
+ ...(typeof o.enabled === "boolean" ? { enabled: o.enabled } : {}),
80
+ ...(typeof o.autoStart === "boolean" ? { autoStart: o.autoStart } : {}),
81
+ ...(Array.isArray(o.roles) ? { roles: [...o.roles] } : {}),
82
+ ...(Array.isArray(o.steps) && o.steps.length > 0
83
+ ? { steps: o.steps.map((st) => ({ ...st })) }
84
+ : {}),
85
+ };
86
+ });
87
+ // Scénarios ajoutés par override (inconnus des défauts) : exigés complets.
88
+ for (const o of byId.values()) {
89
+ const candidate = { enabled: true, ...o };
90
+ if (validateDemoScenario(candidate).length === 0) {
91
+ merged.push(cloneScenario(candidate));
92
+ }
93
+ }
94
+ return merged;
95
+ }
96
+ /* ------------------------------------------------------------------ mount */
97
+ const CONTENT_PREFIX = "scenario:";
98
+ function nowIso() {
99
+ return new Date().toISOString();
100
+ }
101
+ function newId() {
102
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
103
+ }
104
+ /** Lit tous les overrides DB (lignes illisibles ignorées — jamais de throw). */
105
+ function readOverrides(db) {
106
+ try {
107
+ const rows = db
108
+ .prepare(`SELECT key, value_json FROM interactive_demo_content
109
+ WHERE key LIKE ? ORDER BY key`)
110
+ .all(`${CONTENT_PREFIX}%`);
111
+ const out = [];
112
+ for (const r of rows) {
113
+ try {
114
+ const parsed = JSON.parse(r.value_json);
115
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
116
+ const id = r.key.slice(CONTENT_PREFIX.length);
117
+ out.push({ ...parsed, id });
118
+ }
119
+ }
120
+ catch {
121
+ /* ligne illisible ignorée */
122
+ }
123
+ }
124
+ return out;
125
+ }
126
+ catch {
127
+ return [];
128
+ }
129
+ }
130
+ /**
131
+ * Mount api-kernel `/api/v1/modules/interactive-demo/*` (dbLayer brand) :
132
+ *
133
+ * - `GET scenarios` → `{ ok, scenarios: merge(défauts, overrides), overrides: [ids] }`
134
+ * - `GET scenarios/:id` → `{ ok, scenario }` (mergé) ou 404
135
+ * - `PUT scenarios/:id` → stocke l'override (steps = remplacement), répond le scénario mergé
136
+ * - `DELETE scenarios/:id` → supprime l'override (retour aux défauts)
137
+ * - `GET preferences?user=<k>` → `{ ok, user, answers }` (clés `seen:<scenarioId>`…)
138
+ * - `PUT preferences` → `{ user, answers }` upsert par clé
139
+ *
140
+ * db absent → 503 `db_unavailable` ; body invalide → 400 (jamais de throw).
141
+ */
142
+ export function createInteractiveDemoMount(opts) {
143
+ const defaults = opts.defaults;
144
+ return {
145
+ dbLayer: "brand",
146
+ operations: [
147
+ {
148
+ id: "list-scenarios",
149
+ method: "GET",
150
+ path: "/scenarios",
151
+ description: "Lister les scénarios de démo interactive",
152
+ },
153
+ {
154
+ id: "get-scenario",
155
+ method: "GET",
156
+ path: "/scenarios/:id",
157
+ description: "Lire un scénario de démo",
158
+ },
159
+ {
160
+ id: "put-scenario",
161
+ method: "PUT",
162
+ path: "/scenarios/:id",
163
+ description: "Écrire un override de scénario",
164
+ },
165
+ {
166
+ id: "delete-scenario",
167
+ method: "DELETE",
168
+ path: "/scenarios/:id",
169
+ description: "Supprimer un override de scénario",
170
+ },
171
+ {
172
+ id: "get-preferences",
173
+ method: "GET",
174
+ path: "/preferences",
175
+ description: "Lire les préférences démo d'un utilisateur",
176
+ },
177
+ {
178
+ id: "put-preferences",
179
+ method: "PUT",
180
+ path: "/preferences",
181
+ description: "Enregistrer les préférences démo",
182
+ },
183
+ ],
184
+ handle: async ({ req, subPath, db }) => {
185
+ if (!db) {
186
+ return { status: 503, body: { ok: false, error: "db_unavailable" } };
187
+ }
188
+ const method = req.method.toUpperCase();
189
+ const parts = subPath.split("/").filter(Boolean);
190
+ const head = parts[0] || "";
191
+ if (head === "scenarios" && parts.length === 1) {
192
+ if (method === "GET") {
193
+ const overrides = readOverrides(db);
194
+ return {
195
+ status: 200,
196
+ body: {
197
+ ok: true,
198
+ scenarios: mergeDemoScenarios(defaults, overrides),
199
+ overrides: overrides.map((o) => o.id),
200
+ },
201
+ };
202
+ }
203
+ return { status: 405, body: { ok: false, error: "method_not_allowed" } };
204
+ }
205
+ if (head === "scenarios" && parts.length === 2) {
206
+ const id = decodeURIComponent(parts[1]);
207
+ if (method === "GET") {
208
+ const merged = mergeDemoScenarios(defaults, readOverrides(db));
209
+ const scenario = merged.find((s) => s.id === id);
210
+ if (!scenario) {
211
+ return { status: 404, body: { ok: false, error: "not_found" } };
212
+ }
213
+ return { status: 200, body: { ok: true, scenario } };
214
+ }
215
+ if (method === "PUT") {
216
+ const body = req.body;
217
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
218
+ return { status: 400, body: { ok: false, error: "invalid_body" } };
219
+ }
220
+ const override = { ...body, id };
221
+ // Un scénario inconnu des défauts doit être complet et valide.
222
+ const isKnown = defaults.some((s) => s.id === id);
223
+ if (!isKnown) {
224
+ const errors = validateDemoScenario({ enabled: true, ...override });
225
+ if (errors.length > 0) {
226
+ return {
227
+ status: 400,
228
+ body: { ok: false, error: "scenario_invalide", details: errors },
229
+ };
230
+ }
231
+ }
232
+ else if (Array.isArray(override.steps) || override.roles !== undefined) {
233
+ const defaultScenario = defaults.find((s) => s.id === id);
234
+ const errors = validateDemoScenario({
235
+ ...defaultScenario,
236
+ ...override,
237
+ });
238
+ if (errors.length > 0) {
239
+ return {
240
+ status: 400,
241
+ body: { ok: false, error: "scenario_invalide", details: errors },
242
+ };
243
+ }
244
+ }
245
+ db
246
+ .prepare(`INSERT INTO interactive_demo_content (key, value_json, updated_at)
247
+ VALUES (?, ?, ?)
248
+ ON CONFLICT(key) DO UPDATE SET
249
+ value_json = excluded.value_json,
250
+ updated_at = excluded.updated_at`)
251
+ .run(`${CONTENT_PREFIX}${id}`, JSON.stringify(override), nowIso());
252
+ const merged = mergeDemoScenarios(defaults, readOverrides(db));
253
+ return {
254
+ status: 200,
255
+ body: {
256
+ ok: true,
257
+ scenario: merged.find((s) => s.id === id) ?? null,
258
+ hasOverride: true,
259
+ },
260
+ };
261
+ }
262
+ if (method === "DELETE") {
263
+ db
264
+ .prepare(`DELETE FROM interactive_demo_content WHERE key = ?`)
265
+ .run(`${CONTENT_PREFIX}${id}`);
266
+ const merged = mergeDemoScenarios(defaults, readOverrides(db));
267
+ return {
268
+ status: 200,
269
+ body: {
270
+ ok: true,
271
+ scenario: merged.find((s) => s.id === id) ?? null,
272
+ hasOverride: false,
273
+ },
274
+ };
275
+ }
276
+ return { status: 405, body: { ok: false, error: "method_not_allowed" } };
277
+ }
278
+ if (head === "preferences" && parts.length === 1) {
279
+ if (method === "GET") {
280
+ const raw = req.query?.user;
281
+ const user = String(Array.isArray(raw) ? raw[0] ?? "" : raw ?? "").trim();
282
+ if (!user) {
283
+ return { status: 400, body: { ok: false, error: "user_required" } };
284
+ }
285
+ const rows = db
286
+ .prepare(`SELECT key, value_json FROM interactive_demo_preferences
287
+ WHERE user_key = ? ORDER BY key`)
288
+ .all(user);
289
+ const answers = {};
290
+ for (const r of rows) {
291
+ try {
292
+ answers[r.key] = JSON.parse(r.value_json);
293
+ }
294
+ catch {
295
+ answers[r.key] = r.value_json;
296
+ }
297
+ }
298
+ return { status: 200, body: { ok: true, user, answers } };
299
+ }
300
+ if (method === "PUT") {
301
+ const body = (req.body || {});
302
+ const user = String(body.user ?? "").trim();
303
+ const answers = body.answers;
304
+ if (!user || !answers || typeof answers !== "object" || Array.isArray(answers)) {
305
+ return { status: 400, body: { ok: false, error: "invalid_body" } };
306
+ }
307
+ const ts = nowIso();
308
+ let saved = 0;
309
+ for (const [key, value] of Object.entries(answers)) {
310
+ if (value === undefined)
311
+ continue;
312
+ db
313
+ .prepare(`INSERT INTO interactive_demo_preferences (id, user_key, key, value_json, updated_at)
314
+ VALUES (?, ?, ?, ?, ?)
315
+ ON CONFLICT(user_key, key) DO UPDATE SET
316
+ value_json = excluded.value_json,
317
+ updated_at = excluded.updated_at`)
318
+ .run(newId(), user, key, JSON.stringify(value), ts);
319
+ saved++;
320
+ }
321
+ return { status: 200, body: { ok: true, user, saved } };
322
+ }
323
+ return { status: 405, body: { ok: false, error: "method_not_allowed" } };
324
+ }
325
+ return { status: 404, body: { ok: false, error: "not_found" } };
326
+ },
327
+ };
328
+ }
329
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Collecteur des contributions démo des modules métier.
3
+ *
4
+ * Chaque module du registre marque (`BrandModuleDef.demo`) peut contribuer
5
+ * des scénarios de démo interactive ; ce collecteur les agrège en la liste
6
+ * de DÉFAUTS servie par `createInteractiveDemoMount` :
7
+ *
8
+ * ```ts
9
+ * createInteractiveDemoMount({ defaults: collectDemoScenarios() });
10
+ * ```
11
+ *
12
+ * Contrat :
13
+ * - **validation** : chaque scénario passe `validateDemoScenario` — un
14
+ * scénario invalide est une erreur de module (bug), pas un cas runtime ;
15
+ * - **dédup par id** : un même id contribué deux fois par le MÊME module
16
+ * (module listé en double dans le registre) est ignoré silencieusement ;
17
+ * un même id contribué par DEUX modules différents est un conflit, sauf
18
+ * `os-tour` (`genericOsTourScenario` partagé — premier gagne) ;
19
+ * - **erreurs claires** : toutes les erreurs sont agrégées puis levées en
20
+ * UNE Error listant module + scénario + codes de validation — le boot
21
+ * échoue explicitement plutôt que de servir une démo tronquée ;
22
+ * - **ordre stable** : la sortie suit l'ordre du registre (contributions)
23
+ * puis l'ordre de déclaration dans chaque module — jamais de tri implicite.
24
+ *
25
+ * Zéro texte métier ici : les scénarios vivent dans les modules marque.
26
+ */
27
+ import type { DemoScenario } from "./types.js";
28
+ /**
29
+ * Contribution démo d'un module métier — forme aplatie du champ
30
+ * `demo?: { scenarios: DemoScenario[] }` de `BrandModuleDef`, préfixée de
31
+ * l'id du module pour la dédup et les messages d'erreur.
32
+ */
33
+ export type DemoModuleContribution = {
34
+ /** Id du module contributeur (clé de dédup et préfixe d'erreur). */
35
+ moduleId: string;
36
+ /** Scénarios par défaut contribués par le module. */
37
+ scenarios: DemoScenario[];
38
+ };
39
+ /**
40
+ * Agrège les scénarios démo contribués par les modules métier.
41
+ *
42
+ * Renvoie les scénarios valides dans l'ordre stable contributions →
43
+ * déclaration. Lève une Error unique listant TOUTES les erreurs si un
44
+ * scénario est invalide (`validateDemoScenario`) ou si deux modules
45
+ * différents contribuent le même id.
46
+ */
47
+ export declare function collectInteractiveDemoDefaults(contributions: readonly DemoModuleContribution[]): DemoScenario[];
48
+ //# sourceMappingURL=contributions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../src/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,SAAS,sBAAsB,EAAE,GAC/C,YAAY,EAAE,CA0DhB"}