@mostajs/appconfig 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.
Files changed (60) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +49 -0
  3. package/dist/apply.d.ts +15 -0
  4. package/dist/apply.d.ts.map +1 -0
  5. package/dist/apply.js +32 -0
  6. package/dist/apply.js.map +1 -0
  7. package/dist/client.d.ts +19 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +40 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/envfile.d.ts +23 -0
  12. package/dist/envfile.d.ts.map +1 -0
  13. package/dist/envfile.js +55 -0
  14. package/dist/envfile.js.map +1 -0
  15. package/dist/index.d.ts +23 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +21 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/registry.d.ts +15 -0
  20. package/dist/registry.d.ts.map +1 -0
  21. package/dist/registry.js +19 -0
  22. package/dist/registry.js.map +1 -0
  23. package/dist/resolve.d.ts +6 -0
  24. package/dist/resolve.d.ts.map +1 -0
  25. package/dist/resolve.js +32 -0
  26. package/dist/resolve.js.map +1 -0
  27. package/dist/server.d.ts +6 -0
  28. package/dist/server.d.ts.map +1 -0
  29. package/dist/server.js +6 -0
  30. package/dist/server.js.map +1 -0
  31. package/dist/specs/chat.d.ts +7 -0
  32. package/dist/specs/chat.d.ts.map +1 -0
  33. package/dist/specs/chat.js +9 -0
  34. package/dist/specs/chat.js.map +1 -0
  35. package/dist/specs/chatbot.d.ts +7 -0
  36. package/dist/specs/chatbot.d.ts.map +1 -0
  37. package/dist/specs/chatbot.js +14 -0
  38. package/dist/specs/chatbot.js.map +1 -0
  39. package/dist/specs/db.d.ts +8 -0
  40. package/dist/specs/db.d.ts.map +1 -0
  41. package/dist/specs/db.js +9 -0
  42. package/dist/specs/db.js.map +1 -0
  43. package/dist/specs/index.d.ts +8 -0
  44. package/dist/specs/index.d.ts.map +1 -0
  45. package/dist/specs/index.js +22 -0
  46. package/dist/specs/index.js.map +1 -0
  47. package/dist/specs/sourcing.d.ts +7 -0
  48. package/dist/specs/sourcing.d.ts.map +1 -0
  49. package/dist/specs/sourcing.js +9 -0
  50. package/dist/specs/sourcing.js.map +1 -0
  51. package/dist/types.d.ts +60 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +8 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/validate.d.ts +12 -0
  56. package/dist/validate.d.ts.map +1 -0
  57. package/dist/validate.js +37 -0
  58. package/dist/validate.js.map +1 -0
  59. package/llms.txt +22 -0
  60. package/package.json +59 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog — @mostajs/appconfig
2
+
3
+ **Auteur** : Dr Hamid MADANI <drmdh@msn.com>
4
+
5
+ ## 0.1.0 — 2026-06-08 — intégration CRM + déployé
6
+ - **Intégré au CRM TRADING et déployé** (`crm.amia.fr`) : page `/admin/config` (gardée `app.config`), `lib/appconfig.ts` (`registerBuiltinSettings` + `AppConfigIO` réel : lecture/écriture atomique du `.env` du process + redémarrage **détaché/différé** `pm2 restart crm --update-env` re-sourçant `.env`), server action `saveConfigAction` (n'écrit que les clés **non secrètes réellement modifiées**, option redémarrage). Carte `/admin` + entrée Sidebar « Paramètres ».
7
+ - Vérifié en prod : rendu OK, secrets en présent/absent **sans fuite de valeur**, `.env` = `/home/hmd/prod/crm/.env` (pm2 cwd correct), pm2 dans le PATH.
8
+ - ⚠ Le `.env` runtime est réécrit par `deploy/env.prod` à chaque déploiement : les éditions UI vivent jusqu'au prochain déploiement (reporter les changements durables dans `deploy/env.prod`).
9
+
10
+ ## 0.0.1 — 2026-06-08
11
+ - Scaffold (DEVRULES §5) après livrables #1-#3 (`docs/`). Interface d'administration de la configuration `.env` des modules `@mostajs/*`. **Autosuffisant** (sans `@mostajs/socle`), compose `@mostajs/config`.
12
+ - **Spec déclarative** de réglage (`SettingSpec` : key/label/category/type/default/choices/help/secret) + **registre** (style dialecte/provider : `registerSettings`/`collectSettings`).
13
+ - **Packs built-in** : `CHATBOT_SETTINGS`, `SOURCING_SETTINGS`, `CHAT_SETTINGS`, `DB_SETTINGS`, `STORAGE_SETTINGS` (+ `registerBuiltinSettings()`).
14
+ - **Résolution** `resolveSettings()` via `getEnv` (cascade `MOSTA_ENV`), groupée par catégorie ; **secrets → présent/absent**, jamais la valeur.
15
+ - **Validation** `validateChange` (type/choices/secret/retour-ligne). **Réécriture `.env`** `upsertEnv` (préserve commentaires/ordre, upsert+append, échappement) + `diffEnv` (aperçu).
16
+ - **Application** `applyChanges(io, changes, {restart})` : tout-ou-rien, ordre read→write→restart, refus secret/clé inconnue. **I/O `.env` + redémarrage injectés (DI)** via `AppConfigIO` (chemin & pm2 hors module).
17
+ - **UI socle-free** `./client` : `<AppConfigForm>` (groupes, secrets en présent/absent, action injectée).
18
+ - **Test** `test-scripts/test-appconfig.mjs` (zéro fs/réseau) : **27/27**. Build `tsc` vert.
19
+ - Reste : **0.1.0** intégration CRM (`/admin/config` + I/O réel `.env` prod + `pm2 restart`, garde `app.config`) ; 0.2 diff visuel/audit ; 0.3 feature flags (option).
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # @mostajs/appconfig
2
+
3
+ **Auteur** : Dr Hamid MADANI <drmdh@msn.com>
4
+ **Licence** : AGPL-3.0-or-later
5
+
6
+ Interface d'administration de la configuration **`.env`** des modules `@mostajs/*` (chatbot, sourcing, chat, base de données…). **Autosuffisant** (sans `@mostajs/socle`), compose `@mostajs/config`.
7
+
8
+ ## Principe
9
+
10
+ 1. Chaque module **contribue** une *spec déclarative* de ses réglages (clé env, libellé, type, défaut, choix, aide, `secret`).
11
+ 2. `resolveSettings()` lit les valeurs courantes via `getEnv` (cascade `MOSTA_ENV`). Les **secrets** ne renvoient que *présent/absent* — jamais la valeur.
12
+ 3. L'UI (`./client`, socle-free) édite les réglages non sensibles ; une server action applique.
13
+ 4. `applyChanges(io, changes, {restart})` valide puis **réécrit le `.env`** (préserve commentaires/ordre) et **redémarre** — l'I/O (`readEnv`/`writeEnv`/`restart`) est **injectée par l'app** (DI : le chemin et pm2 restent hors module).
14
+
15
+ ## Usage (app)
16
+
17
+ ```ts
18
+ import { registerBuiltinSettings, resolveSettings, applyChanges } from "@mostajs/appconfig";
19
+ import { AppConfigForm } from "@mostajs/appconfig/client";
20
+
21
+ registerBuiltinSettings(); // ou registerSettings([...vos specs])
22
+ const groups = resolveSettings(); // pour l'UI
23
+
24
+ // server action
25
+ async function saveConfigAction(fd: FormData) {
26
+ "use server";
27
+ const changes = [...]; // extraits du FormData pour les clés non-secrètes
28
+ await applyChanges(io, changes, { restart: fd.get("__restart") === "1" });
29
+ }
30
+
31
+ // <AppConfigForm groups={groups} action={saveConfigAction} note="L'enregistrement peut redémarrer l'app." />
32
+ ```
33
+
34
+ `io` (DI) :
35
+ ```ts
36
+ const io = {
37
+ readEnv: () => fs.promises.readFile(ENV_PATH, "utf8"),
38
+ writeEnv: (t) => writeAtomic(ENV_PATH, t), // tmp + rename
39
+ restart: () => exec("pm2 restart crm"),
40
+ };
41
+ ```
42
+
43
+ ## Sécurité
44
+
45
+ - Permission `app.config` (côté app) sur la page et l'action.
46
+ - Secrets (`secret:true`) : jamais lus/écrits/renvoyés au client. Édition via le `.env` serveur (SSH).
47
+ - Validation avant écriture ; valeurs échappées ; seules les clés des specs enregistrées sont éditables.
48
+
49
+ Voir `docs/` (livrables #1-#3) et `CHANGELOG.md`.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @mostajs/appconfig — application d'un lot de changements : validation → réécriture `.env` (via I/O
3
+ * injectée) → redémarrage optionnel. Refuse d'écrire si une valeur est invalide ou cible un secret.
4
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
5
+ */
6
+ import type { AppConfigIO, ApplyReport, EnvChange } from "./types.js";
7
+ /**
8
+ * Valide puis applique les changements. `restart` n'est appelé que si fourni et succès.
9
+ * Tout-ou-rien : en cas d'erreur de validation, **rien** n'est écrit.
10
+ */
11
+ export declare function applyChanges(io: AppConfigIO, changes: EnvChange[], opts?: {
12
+ restart?: boolean;
13
+ allowSecrets?: boolean;
14
+ }): Promise<ApplyReport>;
15
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAKvF;;;GAGG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,SAAS,EAAE,EACpB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvD,OAAO,CAAC,WAAW,CAAC,CAkBtB"}
package/dist/apply.js ADDED
@@ -0,0 +1,32 @@
1
+ import { getSpec } from "./registry.js";
2
+ import { validateChange } from "./validate.js";
3
+ import { upsertEnv } from "./envfile.js";
4
+ /**
5
+ * Valide puis applique les changements. `restart` n'est appelé que si fourni et succès.
6
+ * Tout-ou-rien : en cas d'erreur de validation, **rien** n'est écrit.
7
+ */
8
+ export async function applyChanges(io, changes, opts = {}) {
9
+ const errors = [];
10
+ for (const c of changes) {
11
+ const spec = getSpec(c.key);
12
+ if (!spec) {
13
+ errors.push({ key: c.key, message: "Réglage inconnu (non enregistré)." });
14
+ continue;
15
+ }
16
+ const err = validateChange(spec, c.value, { allowSecrets: opts.allowSecrets });
17
+ if (err)
18
+ errors.push(err);
19
+ }
20
+ if (errors.length)
21
+ return { ok: false, applied: [], errors, restarted: false };
22
+ const current = await io.readEnv();
23
+ const next = upsertEnv(current, changes);
24
+ await io.writeEnv(next);
25
+ let restarted = false;
26
+ if (opts.restart && io.restart) {
27
+ await io.restart();
28
+ restarted = true;
29
+ }
30
+ return { ok: true, applied: changes, errors: [], restarted };
31
+ }
32
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAe,EACf,OAAoB,EACpB,OAAsD,EAAE;IAExD,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,mCAAmC,EAAE,CAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QACnG,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAC/E,IAAI,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAE/E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAExB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;QAAC,SAAS,GAAG,IAAI,CAAC;IAAC,CAAC;IAEzE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+ import type { ResolvedGroup } from "./types.js";
3
+ export interface AppConfigFormProps {
4
+ groups: ResolvedGroup[];
5
+ /** Server action : reçoit le FormData (clés = noms de variables). Applique + (option) redémarre. */
6
+ action: (formData: FormData) => void | Promise<void>;
7
+ /** Message d'avertissement (ex. « l'enregistrement redémarre l'application »). */
8
+ note?: string;
9
+ /** Classe racine (le style est fourni par l'app — aucun CSS embarqué). */
10
+ className?: string;
11
+ /**
12
+ * Autorise l'édition (ÉCRITURE SEULE) des secrets : champ mot de passe, jamais pré-rempli ;
13
+ * laisser vide = conserver la valeur existante. Défaut `false` (secrets en présent/absent).
14
+ */
15
+ allowSecretEdit?: boolean;
16
+ }
17
+ export declare function AppConfigForm({ groups, action, note, className, allowSecretEdit }: AppConfigFormProps): React.JSX.Element;
18
+ export default AppConfigForm;
19
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,oGAAoG;IACpG,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAkDD,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,kBAAkB,qBAgBrG;AAED,eAAe,aAAa,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @mostajs/appconfig/client — UI d'administration de la configuration, **socle-free** (React pur,
3
+ * aucune dépendance à @mostajs/socle/menu/radix). Reçoit les groupes résolus + une server action
4
+ * d'enregistrement (DI). Les secrets s'affichent en présent/absent (jamais d'input de valeur).
5
+ *
6
+ * Usage (server component de l'app) :
7
+ * <AppConfigForm groups={resolveSettings()} action={saveConfigAction} note="…" />
8
+ *
9
+ * @author Dr Hamid MADANI <drmdh@msn.com>
10
+ * @license AGPL-3.0-or-later
11
+ */
12
+ "use client";
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ function Field({ s, allowSecretEdit }) {
15
+ if (s.secret) {
16
+ if (!allowSecretEdit) {
17
+ return (_jsxs("div", { className: "acfg-field acfg-secret", children: [_jsxs("label", { children: [s.label, " ", _jsx("code", { children: s.key })] }), _jsx("span", { className: s.present ? "acfg-badge ok" : "acfg-badge ko", children: s.present ? "● présent" : "○ absent" }), s.help ? _jsx("small", { children: s.help }) : null] }));
18
+ }
19
+ // Écriture seule : input mot de passe, jamais pré-rempli ; vide = conserver.
20
+ return (_jsxs("div", { className: "acfg-field acfg-secret", children: [_jsxs("label", { htmlFor: s.key, children: [s.label, " ", _jsx("code", { children: s.key }), " ", _jsx("span", { className: s.present ? "acfg-badge ok" : "acfg-badge ko", children: s.present ? "● présent" : "○ absent" })] }), _jsx("input", { name: s.key, type: "password", autoComplete: "off", defaultValue: "", placeholder: s.present ? "•••••• (laisser vide pour conserver)" : "définir une valeur" }), s.help ? _jsx("small", { children: s.help }) : null] }));
21
+ }
22
+ const v = s.value ?? "";
23
+ let input;
24
+ if (s.type === "boolean") {
25
+ const on = /^(1|true|on|yes)$/i.test(v);
26
+ input = (_jsxs("select", { name: s.key, defaultValue: on ? "1" : "0", children: [_jsx("option", { value: "1", children: "Activ\u00E9" }), _jsx("option", { value: "0", children: "D\u00E9sactiv\u00E9" })] }));
27
+ }
28
+ else if (s.type === "enum") {
29
+ input = (_jsx("select", { name: s.key, defaultValue: v, children: (s.choices ?? []).map((c) => _jsx("option", { value: c, children: c }, c)) }));
30
+ }
31
+ else {
32
+ input = _jsx("input", { name: s.key, type: s.type === "number" ? "number" : "text", defaultValue: v, placeholder: s.placeholder });
33
+ }
34
+ return (_jsxs("div", { className: "acfg-field", children: [_jsxs("label", { htmlFor: s.key, children: [s.label, " ", _jsx("code", { children: s.key })] }), input, s.help ? _jsx("small", { children: s.help }) : null] }));
35
+ }
36
+ export function AppConfigForm({ groups, action, note, className, allowSecretEdit }) {
37
+ return (_jsxs("form", { action: action, className: className ?? "acfg", children: [note ? _jsx("p", { className: "acfg-note", children: note }) : null, groups.map((g) => (_jsxs("section", { className: "acfg-group", children: [_jsx("h3", { children: g.category }), g.settings.map((s) => _jsx(Field, { s: s, allowSecretEdit: allowSecretEdit }, s.key))] }, g.category))), _jsxs("div", { className: "acfg-actions", children: [_jsxs("label", { className: "acfg-restart", children: [_jsx("input", { type: "checkbox", name: "__restart", value: "1" }), " Red\u00E9marrer apr\u00E8s enregistrement"] }), _jsx("button", { type: "submit", className: "btn primary", children: "Enregistrer" })] })] }));
38
+ }
39
+ export default AppConfigForm;
40
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,YAAY,CAAC;;AAmBb,SAAS,KAAK,CAAC,EAAE,CAAC,EAAE,eAAe,EAAqD;IACtF,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACb,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,OAAO,CACL,eAAK,SAAS,EAAC,wBAAwB,aACrC,4BAAQ,CAAC,CAAC,KAAK,OAAE,yBAAO,CAAC,CAAC,GAAG,GAAQ,IAAQ,EAC7C,eAAM,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,YAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,GAAQ,EAC5G,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,0BAAQ,CAAC,CAAC,IAAI,GAAS,CAAC,CAAC,CAAC,IAAI,IACpC,CACP,CAAC;QACJ,CAAC;QACD,6EAA6E;QAC7E,OAAO,CACL,eAAK,SAAS,EAAC,wBAAwB,aACrC,iBAAO,OAAO,EAAE,CAAC,CAAC,GAAG,aAAG,CAAC,CAAC,KAAK,OAAE,yBAAO,CAAC,CAAC,GAAG,GAAQ,OAAC,eAAM,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,YAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,GAAQ,IAAQ,EAC3K,gBAAO,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAC,UAAU,EAAC,YAAY,EAAC,KAAK,EAAC,YAAY,EAAC,EAAE,EAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,oBAAoB,GAAI,EAChK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,0BAAQ,CAAC,CAAC,IAAI,GAAS,CAAC,CAAC,CAAC,IAAI,IACpC,CACP,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;IACxB,IAAI,KAAsB,CAAC;IAC3B,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxC,KAAK,GAAG,CACN,kBAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,aAC/C,iBAAQ,KAAK,EAAC,GAAG,4BAAgB,EACjC,iBAAQ,KAAK,EAAC,GAAG,oCAAmB,IAC7B,CACV,CAAC;IACJ,CAAC;SAAM,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC7B,KAAK,GAAG,CACN,iBAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC,YACjC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAgB,KAAK,EAAE,CAAC,YAAG,CAAC,IAAf,CAAC,CAAwB,CAAC,GAC9D,CACV,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,gBAAO,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,GAAI,CAAC;IAC7H,CAAC;IACD,OAAO,CACL,eAAK,SAAS,EAAC,YAAY,aACzB,iBAAO,OAAO,EAAE,CAAC,CAAC,GAAG,aAAG,CAAC,CAAC,KAAK,OAAE,yBAAO,CAAC,CAAC,GAAG,GAAQ,IAAQ,EAC5D,KAAK,EACL,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,0BAAQ,CAAC,CAAC,IAAI,GAAS,CAAC,CAAC,CAAC,IAAI,IACpC,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAsB;IACpG,OAAO,CACL,gBAAM,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,IAAI,MAAM,aACjD,IAAI,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,WAAW,YAAE,IAAI,GAAK,CAAC,CAAC,CAAC,IAAI,EACjD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACjB,mBAA0B,SAAS,EAAC,YAAY,aAC9C,uBAAK,CAAC,CAAC,QAAQ,GAAM,EACpB,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAC,KAAK,IAAa,CAAC,EAAE,CAAC,EAAE,eAAe,EAAE,eAAe,IAA7C,CAAC,CAAC,GAAG,CAA4C,CAAC,KAFzE,CAAC,CAAC,QAAQ,CAGd,CACX,CAAC,EACF,eAAK,SAAS,EAAC,cAAc,aAC3B,iBAAO,SAAS,EAAC,cAAc,aAAC,gBAAO,IAAI,EAAC,UAAU,EAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAC,GAAG,GAAG,kDAAwC,EAC5H,iBAAQ,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,aAAa,4BAAqB,IAC9D,IACD,CACR,CAAC;AACJ,CAAC;AAED,eAAe,aAAa,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @mostajs/appconfig — réécriture d'un fichier `.env` : upsert par clé en **préservant commentaires
3
+ * et ordre** des lignes existantes, ajout en fin si la clé est absente. Logique pure (aucun I/O),
4
+ * testable. Échappe les valeurs (guillemets si caractères spéciaux).
5
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
6
+ */
7
+ import type { EnvChange } from "./types.js";
8
+ /** Échappe une valeur `.env` : guillemets si espaces/`#`/`=`/vide ; double-quote échappées. */
9
+ export declare function escapeEnvValue(value: string): string;
10
+ /**
11
+ * Applique les changements à un texte `.env` et renvoie le nouveau texte. Les lignes existantes
12
+ * (commentaires inclus) sont conservées dans l'ordre ; une clé déjà présente est remplacée sur place ;
13
+ * une clé nouvelle est ajoutée en fin (sous un en-tête).
14
+ */
15
+ export declare function upsertEnv(text: string, changes: EnvChange[]): string;
16
+ export interface EnvDiff {
17
+ key: string;
18
+ before?: string;
19
+ after: string;
20
+ }
21
+ /** Diff des changements vs le texte actuel (pour aperçu avant application). */
22
+ export declare function diffEnv(text: string, changes: EnvChange[]): EnvDiff[];
23
+ //# sourceMappingURL=envfile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envfile.d.ts","sourceRoot":"","sources":["../src/envfile.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,+FAA+F;AAC/F,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;AAQD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAuBpE;AAED,MAAM,WAAW,OAAO;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAWxE,+EAA+E;AAC/E,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,CAErE"}
@@ -0,0 +1,55 @@
1
+ /** Échappe une valeur `.env` : guillemets si espaces/`#`/`=`/vide ; double-quote échappées. */
2
+ export function escapeEnvValue(value) {
3
+ const v = value.replace(/[\r\n]/g, " ");
4
+ if (v === "" || /[\s#"'=]/.test(v))
5
+ return `"${v.replace(/"/g, '\\"')}"`;
6
+ return v;
7
+ }
8
+ /** Vrai si la ligne (hors commentaire) définit `key`. */
9
+ function isAssignment(line, key) {
10
+ const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/);
11
+ return !!m && m[1] === key;
12
+ }
13
+ /**
14
+ * Applique les changements à un texte `.env` et renvoie le nouveau texte. Les lignes existantes
15
+ * (commentaires inclus) sont conservées dans l'ordre ; une clé déjà présente est remplacée sur place ;
16
+ * une clé nouvelle est ajoutée en fin (sous un en-tête).
17
+ */
18
+ export function upsertEnv(text, changes) {
19
+ const map = new Map(changes.map((c) => [c.key, c.value]));
20
+ const done = new Set();
21
+ const eol = text.includes("\r\n") ? "\r\n" : "\n";
22
+ const lines = text.split(/\r?\n/);
23
+ const out = lines.map((line) => {
24
+ for (const [key, value] of map) {
25
+ if (!done.has(key) && isAssignment(line, key)) {
26
+ done.add(key);
27
+ return `${key}=${escapeEnvValue(value)}`;
28
+ }
29
+ }
30
+ return line;
31
+ });
32
+ const appended = changes.filter((c) => !done.has(c.key));
33
+ if (appended.length) {
34
+ if (out.length && out[out.length - 1].trim() !== "")
35
+ out.push("");
36
+ out.push("# — @mostajs/appconfig —");
37
+ for (const c of appended)
38
+ out.push(`${c.key}=${escapeEnvValue(c.value)}`);
39
+ }
40
+ return out.join(eol);
41
+ }
42
+ /** Lit la valeur brute (non déséchappée finement) d'une clé dans un texte `.env`. */
43
+ function readRaw(text, key) {
44
+ for (const line of text.split(/\r?\n/)) {
45
+ const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=(.*)$/);
46
+ if (m && m[1] === key)
47
+ return m[2].trim();
48
+ }
49
+ return undefined;
50
+ }
51
+ /** Diff des changements vs le texte actuel (pour aperçu avant application). */
52
+ export function diffEnv(text, changes) {
53
+ return changes.map((c) => ({ key: c.key, before: readRaw(text, c.key), after: escapeEnvValue(c.value) }));
54
+ }
55
+ //# sourceMappingURL=envfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envfile.js","sourceRoot":"","sources":["../src/envfile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @mostajs/appconfig — interface d'administration de la configuration `.env` des modules @mostajs/*.
3
+ * Spec déclarative contribuée par module → résolution (secrets = présent/absent) → édition validée →
4
+ * réécriture `.env` (I/O injectée) → redémarrage. Autosuffisant (sans @mostajs/socle), compose @mostajs/config.
5
+ *
6
+ * Usage app :
7
+ * import { registerBuiltinSettings, resolveSettings, applyChanges } from "@mostajs/appconfig";
8
+ * registerBuiltinSettings(); // ou registerSettings([...vos specs])
9
+ * const groups = resolveSettings(); // pour l'UI
10
+ * await applyChanges(io, changes, { restart:true });// io = { readEnv, writeEnv, restart } (DI app)
11
+ *
12
+ * @author Dr Hamid MADANI <drmdh@msn.com>
13
+ * @license AGPL-3.0-or-later
14
+ */
15
+ export { registerSettings, collectSettings, getSpec, clearSettings } from "./registry.js";
16
+ export { registerBuiltinSettings, CHATBOT_SETTINGS, SOURCING_SETTINGS, CHAT_SETTINGS, DB_SETTINGS, STORAGE_SETTINGS } from "./specs/index.js";
17
+ export { resolveSetting, resolveSettings } from "./resolve.js";
18
+ export { validateChange } from "./validate.js";
19
+ export { upsertEnv, diffEnv, escapeEnvValue } from "./envfile.js";
20
+ export type { EnvDiff } from "./envfile.js";
21
+ export { applyChanges } from "./apply.js";
22
+ export type { SettingType, SettingSpec, ResolvedSetting, ResolvedGroup, EnvChange, ValidationError, AppConfigIO, ApplyReport, } from "./types.js";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC9I,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,GAC/G,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @mostajs/appconfig — interface d'administration de la configuration `.env` des modules @mostajs/*.
3
+ * Spec déclarative contribuée par module → résolution (secrets = présent/absent) → édition validée →
4
+ * réécriture `.env` (I/O injectée) → redémarrage. Autosuffisant (sans @mostajs/socle), compose @mostajs/config.
5
+ *
6
+ * Usage app :
7
+ * import { registerBuiltinSettings, resolveSettings, applyChanges } from "@mostajs/appconfig";
8
+ * registerBuiltinSettings(); // ou registerSettings([...vos specs])
9
+ * const groups = resolveSettings(); // pour l'UI
10
+ * await applyChanges(io, changes, { restart:true });// io = { readEnv, writeEnv, restart } (DI app)
11
+ *
12
+ * @author Dr Hamid MADANI <drmdh@msn.com>
13
+ * @license AGPL-3.0-or-later
14
+ */
15
+ export { registerSettings, collectSettings, getSpec, clearSettings } from "./registry.js";
16
+ export { registerBuiltinSettings, CHATBOT_SETTINGS, SOURCING_SETTINGS, CHAT_SETTINGS, DB_SETTINGS, STORAGE_SETTINGS } from "./specs/index.js";
17
+ export { resolveSetting, resolveSettings } from "./resolve.js";
18
+ export { validateChange } from "./validate.js";
19
+ export { upsertEnv, diffEnv, escapeEnvValue } from "./envfile.js";
20
+ export { applyChanges } from "./apply.js";
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC9I,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @mostajs/appconfig — registre des specs de réglages (style registre de dialectes/providers).
3
+ * Chaque module/app enregistre ses specs ; l'UI collecte l'ensemble, groupé par catégorie.
4
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
5
+ */
6
+ import type { SettingSpec } from "./types.js";
7
+ /** Enregistre une ou plusieurs specs (idempotent par `key`). */
8
+ export declare function registerSettings(specs: SettingSpec[]): void;
9
+ /** Toutes les specs enregistrées (ordre d'insertion). */
10
+ export declare function collectSettings(): SettingSpec[];
11
+ /** Spec d'une clé donnée (ou null). */
12
+ export declare function getSpec(key: string): SettingSpec | null;
13
+ /** Vide le registre (tests). */
14
+ export declare function clearSettings(): void;
15
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CAE3D;AAED,yDAAyD;AACzD,wBAAgB,eAAe,IAAI,WAAW,EAAE,CAE/C;AAED,uCAAuC;AACvC,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAEvD;AAED,gCAAgC;AAChC,wBAAgB,aAAa,IAAI,IAAI,CAEpC"}
@@ -0,0 +1,19 @@
1
+ const SPECS = new Map(); // clé env → spec (dernier enregistré gagne)
2
+ /** Enregistre une ou plusieurs specs (idempotent par `key`). */
3
+ export function registerSettings(specs) {
4
+ for (const s of specs)
5
+ SPECS.set(s.key, s);
6
+ }
7
+ /** Toutes les specs enregistrées (ordre d'insertion). */
8
+ export function collectSettings() {
9
+ return Array.from(SPECS.values());
10
+ }
11
+ /** Spec d'une clé donnée (ou null). */
12
+ export function getSpec(key) {
13
+ return SPECS.get(key) ?? null;
14
+ }
15
+ /** Vide le registre (tests). */
16
+ export function clearSettings() {
17
+ SPECS.clear();
18
+ }
19
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAOA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC,CAAC,4CAA4C;AAE1F,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AAChC,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,aAAa;IAC3B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ResolvedGroup, ResolvedSetting, SettingSpec } from "./types.js";
2
+ /** Résout une spec en valeur affichable (secret → présent/absent, sans la valeur). */
3
+ export declare function resolveSetting(spec: SettingSpec): ResolvedSetting;
4
+ /** Résout des specs (ou toutes les enregistrées) et les groupe par catégorie (ordre d'apparition). */
5
+ export declare function resolveSettings(specs?: SettingSpec[]): ResolvedGroup[];
6
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9E,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CAOjE;AAED,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,KAAK,GAAE,WAAW,EAAsB,GAAG,aAAa,EAAE,CASzF"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @mostajs/appconfig — résolution des valeurs courantes via @mostajs/config (`getEnv`, cascade
3
+ * MOSTA_ENV). Les **secrets** ne renvoient JAMAIS leur valeur : seulement `present` (non vide ?).
4
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
5
+ */
6
+ import { getEnv } from "@mostajs/config";
7
+ import { collectSettings } from "./registry.js";
8
+ /** Résout une spec en valeur affichable (secret → présent/absent, sans la valeur). */
9
+ export function resolveSetting(spec) {
10
+ if (spec.secret) {
11
+ const raw = getEnv(spec.key);
12
+ return { ...spec, present: !!(raw && raw.trim()) };
13
+ }
14
+ const value = getEnv(spec.key) ?? spec.default ?? "";
15
+ return { ...spec, value };
16
+ }
17
+ /** Résout des specs (ou toutes les enregistrées) et les groupe par catégorie (ordre d'apparition). */
18
+ export function resolveSettings(specs = collectSettings()) {
19
+ const groups = [];
20
+ const byCat = new Map();
21
+ for (const spec of specs) {
22
+ let g = byCat.get(spec.category);
23
+ if (!g) {
24
+ g = { category: spec.category, settings: [] };
25
+ byCat.set(spec.category, g);
26
+ groups.push(g);
27
+ }
28
+ g.settings.push(resolveSetting(spec));
29
+ }
30
+ return groups;
31
+ }
32
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,sFAAsF;AACtF,MAAM,UAAU,cAAc,CAAC,IAAiB;IAC9C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;IACrD,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,eAAe,CAAC,QAAuB,eAAe,EAAE;IACtE,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,CAAC,EAAE,CAAC;YAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;QACvG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @mostajs/appconfig/server — ré-export serveur-safe (résolution + application `.env`).
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ export * from "./index.js";
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,YAAY,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @mostajs/appconfig/server — ré-export serveur-safe (résolution + application `.env`).
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ export * from "./index.js";
6
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Specs `.env` du module @mostajs/chat (messageries de contact).
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ import type { SettingSpec } from "../types.js";
6
+ export declare const CHAT_SETTINGS: SettingSpec[];
7
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/specs/chat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,aAAa,EAAE,WAAW,EAMtC,CAAC"}
@@ -0,0 +1,9 @@
1
+ const CAT = "Chat / Messageries";
2
+ export const CHAT_SETTINGS = [
3
+ { key: "CHAT_ENABLED", label: "Activer le chat de contact", category: CAT, type: "boolean", default: "1" },
4
+ { key: "CHAT_PROVIDERS", label: "Providers (ordre)", category: CAT, type: "list", default: "wechat,whatsapp,messenger,telegram", placeholder: "wechat,whatsapp,messenger,telegram", help: "Messageries proposées." },
5
+ { key: "WECHAT_OFFICIAL_QR", label: "QR officiel WeChat", category: CAT, type: "string", placeholder: "weixin://…", help: "Contenu du QR du compte officiel." },
6
+ // Secret — envoi programmatique Telegram (Bot API). Présent/absent uniquement.
7
+ { key: "TELEGRAM_BOT_TOKEN", label: "Token Bot Telegram", category: CAT, type: "secret", secret: true, help: "Pour l'envoi RFQ réel via Telegram. Édition via .env serveur." },
8
+ ];
9
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/specs/chat.ts"],"names":[],"mappings":"AAMA,MAAM,GAAG,GAAG,oBAAoB,CAAC;AAEjC,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,4BAA4B,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE;IAC1G,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oCAAoC,EAAE,WAAW,EAAE,oCAAoC,EAAE,IAAI,EAAE,wBAAwB,EAAE;IACpN,EAAE,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,mCAAmC,EAAE;IAC/J,+EAA+E;IAC/E,EAAE,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,+DAA+D,EAAE;CAC/K,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Specs `.env` du module @mostajs/chatbot. Les clés IA sont des **secrets** (présent/absent uniquement).
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ import type { SettingSpec } from "../types.js";
6
+ export declare const CHATBOT_SETTINGS: SettingSpec[];
7
+ //# sourceMappingURL=chatbot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatbot.d.ts","sourceRoot":"","sources":["../../src/specs/chatbot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAWzC,CAAC"}
@@ -0,0 +1,14 @@
1
+ const CAT = "Chatbot";
2
+ export const CHATBOT_SETTINGS = [
3
+ { key: "CHATBOT_ENABLED", label: "Activer le chatbot", category: CAT, type: "boolean", default: "1", help: "Affiche le copilote dans l'app." },
4
+ { key: "CHATBOT_MODE", label: "Mode", category: CAT, type: "enum", choices: ["assistant", "agent"], default: "assistant", help: "assistant (Q/R) ou agent (multi-étapes, tool-use)." },
5
+ { key: "CHATBOT_BACKEND", label: "Backend", category: CAT, type: "enum", choices: ["scripted", "ai"], default: "scripted", help: "scripted (sans clé) ou ai (LLM, nécessite une clé)." },
6
+ { key: "CHATBOT_PROVIDERS", label: "Providers LLM (ordre/fallback)", category: CAT, type: "list", default: "anthropic,deepseek", placeholder: "anthropic,deepseek", help: "Liste ordonnée ; bascule au suivant en cas d'échec." },
7
+ { key: "CHATBOT_MODEL", label: "Modèle", category: CAT, type: "string", placeholder: "claude-opus-4-8", help: "Modèle par défaut du provider principal." },
8
+ { key: "CHATBOT_WEB_SEARCH", label: "Recherche web", category: CAT, type: "boolean", default: "0", help: "Autorise web_search/web_fetch (backend ai)." },
9
+ { key: "CHATBOT_ROLES", label: "Rôles autorisés", category: CAT, type: "list", placeholder: "admin,employe", help: "Vide = tous les utilisateurs connectés." },
10
+ // Secrets — jamais édités par l'UI (présent/absent), à régler dans le .env serveur (SSH).
11
+ { key: "ANTHROPIC_API_KEY", label: "Clé API Anthropic", category: CAT, type: "secret", secret: true, help: "Provider anthropic. Édition via .env serveur." },
12
+ { key: "DEEPSEEK_API_KEY", label: "Clé API DeepSeek", category: CAT, type: "secret", secret: true, help: "Provider deepseek. Édition via .env serveur." },
13
+ ];
14
+ //# sourceMappingURL=chatbot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatbot.js","sourceRoot":"","sources":["../../src/specs/chatbot.ts"],"names":[],"mappings":"AAMA,MAAM,GAAG,GAAG,SAAS,CAAC;AAEtB,MAAM,CAAC,MAAM,gBAAgB,GAAkB;IAC7C,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,iCAAiC,EAAE;IAC9I,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,oDAAoD,EAAE;IACtL,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,qDAAqD,EAAE;IACxL,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,gCAAgC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,oBAAoB,EAAE,IAAI,EAAE,qDAAqD,EAAE;IACjO,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,IAAI,EAAE,0CAA0C,EAAE;IAC1J,EAAE,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,6CAA6C,EAAE;IACxJ,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,IAAI,EAAE,yCAAyC,EAAE;IAC9J,0FAA0F;IAC1F,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,+CAA+C,EAAE;IAC5J,EAAE,GAAG,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,8CAA8C,EAAE;CAC1J,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Specs `.env` socle app : base de données, environnement, stockage.
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ import type { SettingSpec } from "../types.js";
6
+ export declare const DB_SETTINGS: SettingSpec[];
7
+ export declare const STORAGE_SETTINGS: SettingSpec[];
8
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/specs/db.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,eAAO,MAAM,WAAW,EAAE,WAAW,EAIpC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAEzC,CAAC"}
@@ -0,0 +1,9 @@
1
+ export const DB_SETTINGS = [
2
+ { key: "MOSTA_ENV", label: "Profil d'environnement", category: "Application", type: "enum", choices: ["DEV", "TEST", "PROD"], default: "PROD", help: "Cascade de configuration (préfixe des clés)." },
3
+ { key: "DB_DIALECT", label: "Dialecte base de données", category: "Base de données", type: "enum", choices: ["sqljs", "sqlite", "postgres", "mysql"], default: "sqljs", help: "Pilote ORM." },
4
+ { key: "DATABASE_URL", label: "URL / chemin base", category: "Base de données", type: "string", placeholder: "./trading.db", help: "Chemin (sqljs/sqlite) ou URL de connexion." },
5
+ ];
6
+ export const STORAGE_SETTINGS = [
7
+ { key: "STORAGE_MAX_MB", label: "Taille max upload (Mo)", category: "Stockage", type: "number", default: "200", help: "Limite par fichier." },
8
+ ];
9
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/specs/db.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,WAAW,GAAkB;IACxC,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,wBAAwB,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,8CAA8C,EAAE;IACrM,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,0BAA0B,EAAE,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE;IAC7L,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,4CAA4C,EAAE;CAClL,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAkB;IAC7C,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,wBAAwB,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE;CAC9I,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { CHATBOT_SETTINGS } from "./chatbot.js";
2
+ import { SOURCING_SETTINGS } from "./sourcing.js";
3
+ import { CHAT_SETTINGS } from "./chat.js";
4
+ import { DB_SETTINGS, STORAGE_SETTINGS } from "./db.js";
5
+ export { CHATBOT_SETTINGS, SOURCING_SETTINGS, CHAT_SETTINGS, DB_SETTINGS, STORAGE_SETTINGS };
6
+ /** Enregistre tous les packs built-in (app, db, chat, chatbot, sourcing, stockage). */
7
+ export declare function registerBuiltinSettings(): void;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/specs/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;AAE7F,uFAAuF;AACvF,wBAAgB,uBAAuB,IAAI,IAAI,CAQ9C"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @mostajs/appconfig — packs de specs built-in. `registerBuiltinSettings()` les enregistre tous ;
3
+ * l'app peut aussi en (dé)sélectionner ou enregistrer les siens via `registerSettings`.
4
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
5
+ */
6
+ import { registerSettings } from "../registry.js";
7
+ import { CHATBOT_SETTINGS } from "./chatbot.js";
8
+ import { SOURCING_SETTINGS } from "./sourcing.js";
9
+ import { CHAT_SETTINGS } from "./chat.js";
10
+ import { DB_SETTINGS, STORAGE_SETTINGS } from "./db.js";
11
+ export { CHATBOT_SETTINGS, SOURCING_SETTINGS, CHAT_SETTINGS, DB_SETTINGS, STORAGE_SETTINGS };
12
+ /** Enregistre tous les packs built-in (app, db, chat, chatbot, sourcing, stockage). */
13
+ export function registerBuiltinSettings() {
14
+ registerSettings([
15
+ ...DB_SETTINGS,
16
+ ...CHATBOT_SETTINGS,
17
+ ...SOURCING_SETTINGS,
18
+ ...CHAT_SETTINGS,
19
+ ...STORAGE_SETTINGS,
20
+ ]);
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/specs/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;AAE7F,uFAAuF;AACvF,MAAM,UAAU,uBAAuB;IACrC,gBAAgB,CAAC;QACf,GAAG,WAAW;QACd,GAAG,gBAAgB;QACnB,GAAG,iBAAiB;QACpB,GAAG,aAAa;QAChB,GAAG,gBAAgB;KACpB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Specs `.env` du module @mostajs/sourcing.
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ import type { SettingSpec } from "../types.js";
6
+ export declare const SOURCING_SETTINGS: SettingSpec[];
7
+ //# sourceMappingURL=sourcing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourcing.d.ts","sourceRoot":"","sources":["../../src/specs/sourcing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,iBAAiB,EAAE,WAAW,EAM1C,CAAC"}
@@ -0,0 +1,9 @@
1
+ const CAT = "Sourcing";
2
+ export const SOURCING_SETTINGS = [
3
+ { key: "SOURCING_CONNECTORS", label: "Connecteurs (ordre)", category: CAT, type: "list", default: "internal,web", placeholder: "internal,web", help: "Sources d'offres actives." },
4
+ { key: "SOURCING_WEIGHTS", label: "Pondérations de scoring", category: CAT, type: "string", default: "price:0.5,leadTime:0.2,moq:0.1,reliability:0.2", help: "Poids des critères (somme libre)." },
5
+ { key: "SOURCING_TOPN", label: "Top-N offres", category: CAT, type: "number", default: "5", help: "Nombre d'offres retournées." },
6
+ { key: "SOURCING_MAX_PAGES", label: "Pages web max / requête", category: CAT, type: "number", default: "8", help: "Borne coût/latence de la recherche." },
7
+ { key: "SOURCING_RFQ", label: "RFQ (envoi réel)", category: CAT, type: "boolean", default: "0", help: "Active l'envoi des demandes de devis (humain-in-the-loop conseillé)." },
8
+ ];
9
+ //# sourceMappingURL=sourcing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourcing.js","sourceRoot":"","sources":["../../src/specs/sourcing.ts"],"names":[],"mappings":"AAMA,MAAM,GAAG,GAAG,UAAU,CAAC;AAEvB,MAAM,CAAC,MAAM,iBAAiB,GAAkB;IAC9C,EAAE,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,qBAAqB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,2BAA2B,EAAE;IAClL,EAAE,GAAG,EAAE,kBAAkB,EAAE,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gDAAgD,EAAE,IAAI,EAAE,mCAAmC,EAAE;IAClM,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,6BAA6B,EAAE;IACjI,EAAE,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,qCAAqC,EAAE;IACzJ,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,sEAAsE,EAAE;CAC/K,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @mostajs/appconfig — types. Spec déclarative d'un réglage `.env`, contribuée par module (registre
3
+ * style dialecte/provider). I/O `.env` + redémarrage **injectés** (DI) — le module ne code pas en dur
4
+ * un chemin de fichier ni un process pm2.
5
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
6
+ */
7
+ export type SettingType = "string" | "number" | "boolean" | "enum" | "list" | "secret";
8
+ /** Description déclarative d'un réglage (= une variable d'environnement). */
9
+ export interface SettingSpec {
10
+ /** Nom de la variable d'environnement (ex. `CHATBOT_ENABLED`). */
11
+ key: string;
12
+ label: string;
13
+ /** Groupe d'affichage (ex. « Chatbot », « Sourcing », « Base de données »). */
14
+ category: string;
15
+ type: SettingType;
16
+ default?: string;
17
+ /** Valeurs autorisées (type `enum`). */
18
+ choices?: string[];
19
+ help?: string;
20
+ placeholder?: string;
21
+ /** Secret : la valeur n'est JAMAIS lue/écrite/renvoyée par l'UI ; seulement présent/absent. */
22
+ secret?: boolean;
23
+ }
24
+ /** Réglage résolu pour l'affichage. Pour un secret : `present` renseigné, `value` toujours absent. */
25
+ export interface ResolvedSetting extends SettingSpec {
26
+ value?: string;
27
+ present?: boolean;
28
+ }
29
+ /** Groupe de réglages résolus (par catégorie), pour l'UI. */
30
+ export interface ResolvedGroup {
31
+ category: string;
32
+ settings: ResolvedSetting[];
33
+ }
34
+ /** Modification demandée (jamais émise pour un secret). */
35
+ export interface EnvChange {
36
+ key: string;
37
+ value: string;
38
+ }
39
+ /** Erreur de validation d'un changement. */
40
+ export interface ValidationError {
41
+ key: string;
42
+ message: string;
43
+ }
44
+ /**
45
+ * I/O injectée par l'app (DI) : lecture/écriture du `.env` serveur + redémarrage. Le chemin réel et
46
+ * le process pm2 sont propres au déploiement → hors module.
47
+ */
48
+ export interface AppConfigIO {
49
+ readEnv(): Promise<string>;
50
+ writeEnv(text: string): Promise<void>;
51
+ restart?(): Promise<void>;
52
+ }
53
+ /** Rapport d'application d'un lot de changements. */
54
+ export interface ApplyReport {
55
+ ok: boolean;
56
+ applied: EnvChange[];
57
+ errors: ValidationError[];
58
+ restarted: boolean;
59
+ }
60
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvF,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,sGAAsG;AACtG,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @mostajs/appconfig — types. Spec déclarative d'un réglage `.env`, contribuée par module (registre
3
+ * style dialecte/provider). I/O `.env` + redémarrage **injectés** (DI) — le module ne code pas en dur
4
+ * un chemin de fichier ni un process pm2.
5
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @mostajs/appconfig — validation d'un changement avant écriture (type/choices/secret).
3
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+ */
5
+ import type { SettingSpec, ValidationError } from "./types.js";
6
+ /** Options de validation. `allowSecrets` autorise l'écriture SEULE de secrets (valeur non vide). */
7
+ export interface ValidateOptions {
8
+ allowSecrets?: boolean;
9
+ }
10
+ /** Valide une valeur pour une spec. Renvoie une erreur ou `null`. */
11
+ export declare function validateChange(spec: SettingSpec, value: string, opts?: ValidateOptions): ValidationError | null;
12
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAI/D,oGAAoG;AACpG,MAAM,WAAW,eAAe;IAAG,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE;AAE3D,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,eAAe,GAAG,IAAI,CA+BnH"}
@@ -0,0 +1,37 @@
1
+ const BOOL = /^(0|1|true|false|on|off|yes|no)$/i;
2
+ /** Valide une valeur pour une spec. Renvoie une erreur ou `null`. */
3
+ export function validateChange(spec, value, opts = {}) {
4
+ if (spec.secret || spec.type === "secret") {
5
+ if (!opts.allowSecrets) {
6
+ return { key: spec.key, message: "Réglage secret : non éditable via l'interface (éditer le .env serveur)." };
7
+ }
8
+ if (/[\r\n]/.test(value))
9
+ return { key: spec.key, message: "Valeur invalide : retour à la ligne interdit." };
10
+ if (value.trim() === "")
11
+ return { key: spec.key, message: "Valeur secrète vide (laisser vide pour conserver)." };
12
+ return null; // secret avec une nouvelle valeur → accepté (écriture seule)
13
+ }
14
+ if (/[\r\n]/.test(value)) {
15
+ return { key: spec.key, message: "Valeur invalide : retour à la ligne interdit." };
16
+ }
17
+ switch (spec.type) {
18
+ case "number":
19
+ if (value.trim() === "" || !Number.isFinite(Number(value))) {
20
+ return { key: spec.key, message: "Doit être un nombre." };
21
+ }
22
+ break;
23
+ case "boolean":
24
+ if (!BOOL.test(value.trim())) {
25
+ return { key: spec.key, message: "Doit être un booléen (0/1, true/false, on/off, yes/no)." };
26
+ }
27
+ break;
28
+ case "enum":
29
+ if (spec.choices && !spec.choices.includes(value)) {
30
+ return { key: spec.key, message: `Valeur hors choix autorisés : ${spec.choices.join(", ")}.` };
31
+ }
32
+ break;
33
+ // string / list : libre
34
+ }
35
+ return null;
36
+ }
37
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAMA,MAAM,IAAI,GAAG,mCAAmC,CAAC;AAKjD,qEAAqE;AACrE,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,KAAa,EAAE,OAAwB,EAAE;IACzF,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,yEAAyE,EAAE,CAAC;QAC/G,CAAC;QACD,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC;QAC7G,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,oDAAoD,EAAE,CAAC;QACjH,OAAO,IAAI,CAAC,CAAC,6DAA6D;IAC5E,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC;IACrF,CAAC;IACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC3D,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC;YAC5D,CAAC;YACD,MAAM;QACR,KAAK,SAAS;YACZ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC;YAC/F,CAAC;YACD,MAAM;QACR,KAAK,MAAM;YACT,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClD,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,iCAAiC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACjG,CAAC;YACD,MAAM;QACR,wBAAwB;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/llms.txt ADDED
@@ -0,0 +1,22 @@
1
+ # @mostajs/appconfig — llms.txt
2
+
3
+ But : administrer la configuration `.env` des modules @mostajs/* via une UI. Autosuffisant (sans @mostajs/socle), compose @mostajs/config. Ne réimplémente pas getEnv ; n'embarque pas de DB ; ne manipule jamais un secret.
4
+
5
+ API (import "@mostajs/appconfig") :
6
+ - registerSettings(specs) / collectSettings() / getSpec(key) / clearSettings() — registre (style dialecte).
7
+ - registerBuiltinSettings() + packs CHATBOT_SETTINGS / SOURCING_SETTINGS / CHAT_SETTINGS / DB_SETTINGS / STORAGE_SETTINGS.
8
+ - resolveSettings(specs?) -> ResolvedGroup[] : valeurs courantes via getEnv ; secret -> {present} sans value.
9
+ - validateChange(spec, value) -> ValidationError|null.
10
+ - upsertEnv(text, changes) / diffEnv(text, changes) / escapeEnvValue(v) — réécriture .env pure (préserve commentaires/ordre).
11
+ - applyChanges(io, changes, {restart}) -> ApplyReport : valide -> écrit -> redémarre (tout-ou-rien ; refuse secret/clé inconnue).
12
+ - Types : SettingSpec, SettingType, ResolvedSetting, ResolvedGroup, EnvChange, ValidationError, AppConfigIO, ApplyReport.
13
+
14
+ UI (import "@mostajs/appconfig/client") :
15
+ - <AppConfigForm groups={ResolvedGroup[]} action={(FormData)=>void} note? className? /> — socle-free ; secrets en présent/absent.
16
+
17
+ DI (app) : AppConfigIO { readEnv(): Promise<string>; writeEnv(text): Promise<void>; restart?(): Promise<void> }.
18
+ Le chemin du .env et le process pm2 sont fournis par l'app, jamais codés dans le module.
19
+
20
+ Règles : secrets jamais exposés/écrits par l'UI (.env serveur via SSH) ; permission app.config ; validation avant écriture.
21
+
22
+ Auteur : Dr Hamid MADANI <drmdh@msn.com> — AGPL-3.0-or-later.
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@mostajs/appconfig",
3
+ "version": "0.1.0",
4
+ "description": "Interface d'administration de la configuration .env des modules @mostajs/* : spec déclarative contribuée par module (chatbot, sourcing, chat, db…), résolution des valeurs courantes (secrets = présent/absent), édition validée et réécriture .env (I/O injectée par DI) + redémarrage. Autosuffisant (sans @mostajs/socle). Compose @mostajs/config.",
5
+ "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
+ "license": "AGPL-3.0-or-later",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./dist/server.d.ts",
18
+ "import": "./dist/server.js",
19
+ "default": "./dist/server.js"
20
+ },
21
+ "./client": {
22
+ "types": "./dist/client.d.ts",
23
+ "import": "./dist/client.js",
24
+ "default": "./dist/client.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "llms.txt",
30
+ "CHANGELOG.md",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "peerDependencies": {
35
+ "@mostajs/config": "*",
36
+ "react": ">=18"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "react": {
40
+ "optional": true
41
+ }
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.6.0",
45
+ "@types/react": "^19.0.0"
46
+ },
47
+ "keywords": [
48
+ "config",
49
+ "env",
50
+ "settings",
51
+ "admin",
52
+ "dotenv",
53
+ "no-socle",
54
+ "mostajs"
55
+ ],
56
+ "scripts": {
57
+ "build": "tsc -p tsconfig.json"
58
+ }
59
+ }