@nodefony/mongoose 10.0.0-alpha.1

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 (51) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +97 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
  5. package/dist/index.js +90 -0
  6. package/dist/nodefony/config/config.js +57 -0
  7. package/dist/nodefony/config/defineModuleConfig.js +60 -0
  8. package/dist/nodefony/entity/sessionEntity.js +63 -0
  9. package/dist/nodefony/entity/tokenEntity.js +184 -0
  10. package/dist/nodefony/entity/userEntity.js +106 -0
  11. package/dist/nodefony/entity/webAuthnCredentialEntity.js +97 -0
  12. package/dist/nodefony/entity/webhookEndpointEntity.js +109 -0
  13. package/dist/nodefony/interfaces/IMongooseConfig.js +1 -0
  14. package/dist/nodefony/interfaces/index.js +1 -0
  15. package/dist/nodefony/registerStores.js +89 -0
  16. package/dist/nodefony/service/MongooseService.js +95 -0
  17. package/dist/nodefony/src/MongooseTokenStore.js +237 -0
  18. package/dist/nodefony/src/MongooseUserRepository.js +205 -0
  19. package/dist/nodefony/src/MongooseWebAuthnCredentialStore.js +144 -0
  20. package/dist/nodefony/src/MongooseWebhookStore.js +181 -0
  21. package/dist/nodefony/src/SessionStorage.js +241 -0
  22. package/dist/nodefony/src/mongoOrder.js +49 -0
  23. package/dist/nodefony/src/orm-core/MongooseOrm.js +440 -0
  24. package/dist/nodefony/src/orm-core/MongooseRepository.js +300 -0
  25. package/dist/nodefony/src/orm-core/MongooseTransaction.js +53 -0
  26. package/dist/nodefony/src/orm-core/index.js +4 -0
  27. package/dist/types/index.d.ts +74 -0
  28. package/dist/types/nodefony/config/config.d.ts +21 -0
  29. package/dist/types/nodefony/config/defineModuleConfig.d.ts +26 -0
  30. package/dist/types/nodefony/entity/sessionEntity.d.ts +42 -0
  31. package/dist/types/nodefony/entity/tokenEntity.d.ts +59 -0
  32. package/dist/types/nodefony/entity/userEntity.d.ts +54 -0
  33. package/dist/types/nodefony/entity/webAuthnCredentialEntity.d.ts +61 -0
  34. package/dist/types/nodefony/entity/webhookEndpointEntity.d.ts +62 -0
  35. package/dist/types/nodefony/interfaces/IMongooseConfig.d.ts +17 -0
  36. package/dist/types/nodefony/interfaces/index.d.ts +1 -0
  37. package/dist/types/nodefony/registerStores.d.ts +37 -0
  38. package/dist/types/nodefony/service/MongooseService.d.ts +48 -0
  39. package/dist/types/nodefony/src/MongooseTokenStore.d.ts +126 -0
  40. package/dist/types/nodefony/src/MongooseUserRepository.d.ts +82 -0
  41. package/dist/types/nodefony/src/MongooseWebAuthnCredentialStore.d.ts +52 -0
  42. package/dist/types/nodefony/src/MongooseWebhookStore.d.ts +72 -0
  43. package/dist/types/nodefony/src/SessionStorage.d.ts +63 -0
  44. package/dist/types/nodefony/src/mongoOrder.d.ts +40 -0
  45. package/dist/types/nodefony/src/orm-core/MongooseOrm.d.ts +132 -0
  46. package/dist/types/nodefony/src/orm-core/MongooseRepository.d.ts +51 -0
  47. package/dist/types/nodefony/src/orm-core/MongooseTransaction.d.ts +37 -0
  48. package/dist/types/nodefony/src/orm-core/index.d.ts +9 -0
  49. package/docs/configuration.md +776 -0
  50. package/docs/index.md +881 -0
  51. package/package.json +97 -0
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
1
+ import config, { mongooseConfigSchema } from "./nodefony/config/config.js";
2
+ import { defineMongooseConfig, mongooseConfigJsonSchema } from "./nodefony/config/defineModuleConfig.js";
3
+ import { MongooseRepository } from "./nodefony/src/orm-core/MongooseRepository.js";
4
+ import { MongooseTransaction } from "./nodefony/src/orm-core/MongooseTransaction.js";
5
+ import { MongooseOrm } from "./nodefony/src/orm-core/MongooseOrm.js";
6
+ import MongooseService from "./nodefony/service/MongooseService.js";
7
+ import "./nodefony/src/orm-core/index.js";
8
+ import { TOKEN_ENTITY_NAMES, accessTokenSchema, createTokenEntities, deniedJtiSchema, registerTokenEntities, subjectRevocationSchema } from "./nodefony/entity/tokenEntity.js";
9
+ import { WEBAUTHN_CREDENTIAL_ENTITY, createWebAuthnCredentialEntity, registerWebAuthnCredentialEntity, webAuthnCredentialSchema } from "./nodefony/entity/webAuthnCredentialEntity.js";
10
+ import { WEBHOOK_ENDPOINT_ENTITY, createWebhookEndpointEntity, registerWebhookEndpointEntity, webhookEndpointSchema } from "./nodefony/entity/webhookEndpointEntity.js";
11
+ import { MongooseTokenStore } from "./nodefony/src/MongooseTokenStore.js";
12
+ import { MongooseWebAuthnCredentialStore } from "./nodefony/src/MongooseWebAuthnCredentialStore.js";
13
+ import { MongooseWebhookStore } from "./nodefony/src/MongooseWebhookStore.js";
14
+ import { FRAMEWORK_CONNECTOR, registerMongooseFrameworkStores } from "./nodefony/registerStores.js";
15
+ import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
16
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
17
+ import sessionEntity_default, { SESSION_CONNECTOR, sessionSchema as schema } from "./nodefony/entity/sessionEntity.js";
18
+ import SessionStorage from "./nodefony/src/SessionStorage.js";
19
+ import { createUserEntity, registerUserEntity, userSchema } from "./nodefony/entity/userEntity.js";
20
+ import { MongooseUserRepository } from "./nodefony/src/MongooseUserRepository.js";
21
+ import mongoose from "mongoose";
22
+ import { Kernel, Module, registerErrorAdapter, services } from "nodefony";
23
+ import { wireOrmAdminPlane } from "@nodefony/orm-core";
24
+ import { registerUserStore } from "@nodefony/user";
25
+ //#region index.ts
26
+ /**
27
+ * `@nodefony/mongoose` — module Mongoose ORM (driver NoSQL) sur `@nodefony/orm-core`.
28
+ *
29
+ * **Module bootable** : enregistré dans le manifeste `modules`, son
30
+ * {@link MongooseService} connecte au boot un {@link MongooseOrm} par connecteur
31
+ * configuré. Refonte 2026-06-08 (Ph.2 virage ORM) : ne dérive plus de l'`Orm`
32
+ * legacy du core — le service `extends Service` et orchestre des adapters
33
+ * orm-core autonomes (modèle `DrizzleService`). Le core ne connaît plus l'ORM.
34
+ *
35
+ * Config = source de vérité Zod (`nodefony/config/config.ts`), validée au boot
36
+ * via {@link defineMongooseConfig} (style `@nodefony/redis`/`@nodefony/realtime`).
37
+ */
38
+ let Mongoose = class Mongoose extends Module {
39
+ /**
40
+ * Module **optionnel** (driver NoSQL externe, opt-in) : un échec de son boot
41
+ * (Mongo injoignable) ne tue jamais le process — le store de session dégrade
42
+ * gracieusement (`#repo()` → null). Résilience cloud-native (l'orchestrateur
43
+ * relèvera Mongo). Convention-frère `@nodefony/redis`.
44
+ */
45
+ static critical = false;
46
+ constructor(kernel) {
47
+ super("mongoose", kernel, import.meta.url, config);
48
+ }
49
+ /** JSON Schema de la config mongoose → data plane admin (config riche Studio). */
50
+ configSchema() {
51
+ return mongooseConfigJsonSchema();
52
+ }
53
+ /**
54
+ * Valide la config (défauts + `module.options` + surcharge env) au boot via
55
+ * `defineMongooseConfig`, et l'expose au container sous `mongooseConfig` pour
56
+ * que le `MongooseService` la consomme sans redupliquer la validation. Plante
57
+ * propre avec messages clairs si la config est invalide (convention Zod).
58
+ */
59
+ async onKernelRegister() {
60
+ const validated = defineMongooseConfig(this.options ?? {});
61
+ this.options = validated;
62
+ registerUserStore("mongoose");
63
+ if (validated.frameworkEntities !== false) {
64
+ const report = registerMongooseFrameworkStores();
65
+ if (report.appOwned.length) this.log(`schéma framework : entités déjà enregistrées par l'app [${report.appOwned.join(", ")}] — auto-register respecte l'app`, "DEBUG");
66
+ if (report.registered.length) this.log(`schéma framework déclaré sur "${FRAMEWORK_CONNECTOR}" : [${report.registered.join(", ")}]`, "DEBUG");
67
+ }
68
+ return this;
69
+ }
70
+ /**
71
+ * Monte le data plane ORM (`/nodefony/orm/api/*` + providers santé/flux) via
72
+ * {@link wireOrmAdminPlane} — branchement GLOBAL et idempotent factorisé en
73
+ * orm-core (C5), identique à Drizzle. Avant la factorisation, ce wiring était
74
+ * déclenché par le seul module Drizzle → une app Mongoose-only avait un Studio
75
+ * ORM muet ; chaque driver l'invoque désormais. En plus, enregistre l'adapter
76
+ * d'erreurs Mongoose (spécifique au driver, hors plan d'administration).
77
+ */
78
+ async onKernelBoot() {
79
+ wireOrmAdminPlane(this.kernel);
80
+ registerErrorAdapter("mongoose", {
81
+ isError: (e) => e instanceof mongoose.Error,
82
+ errorToString: (e) => String(e?.message ?? e)
83
+ });
84
+ return this;
85
+ }
86
+ };
87
+ Mongoose = __decorate([services([MongooseService]), __decorateMetadata("design:paramtypes", [typeof Kernel === "undefined" ? Object : Kernel])], Mongoose);
88
+ var mongoose_default = Mongoose;
89
+ //#endregion
90
+ export { FRAMEWORK_CONNECTOR, MongooseOrm, MongooseRepository, MongooseService, MongooseTokenStore, MongooseTransaction, MongooseUserRepository, MongooseWebAuthnCredentialStore, MongooseWebhookStore, SESSION_CONNECTOR, sessionEntity_default as SessionEntity, SessionStorage, TOKEN_ENTITY_NAMES, WEBAUTHN_CREDENTIAL_ENTITY, WEBHOOK_ENDPOINT_ENTITY, accessTokenSchema, createTokenEntities, createUserEntity, createWebAuthnCredentialEntity, createWebhookEndpointEntity, mongoose_default as default, defineMongooseConfig, deniedJtiSchema, mongoose, mongooseConfigJsonSchema, mongooseConfigSchema, registerMongooseFrameworkStores, registerTokenEntities, registerUserEntity, registerWebAuthnCredentialEntity, registerWebhookEndpointEntity, schema as sessionSchema, subjectRevocationSchema, userSchema, webAuthnCredentialSchema, webhookEndpointSchema };
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ //#region nodefony/config/config.ts
3
+ /**
4
+ * @nodefony/mongoose — CONFIGURATION DU MODULE (schéma Zod = source unique).
5
+ *
6
+ * ⭐ TL;DR : CE SCHÉMA EST LA CONFIG. Chaque `.default(...)` = la valeur d'usine ;
7
+ * changer un défaut du module = ÉDITER ICI (et nulle part ailleurs). L'app, elle,
8
+ * surcharge via `use("@nodefony/...", { … })` dans SON `nodefony.config.ts`.
9
+ *
10
+ * RÈGLE D'OR (ADR-0006) : ce fichier porte le **schéma Zod commenté** (type +
11
+ * validation + défaut + doc) ET matérialise les défauts via `parse({})`. Aucune
12
+ * valeur n'est re-tapée ailleurs. Le builder (`defineMongooseConfig`) et les types
13
+ * (`interfaces/IMongooseConfig.ts`) importent le schéma D'ICI (nœud bas : ce fichier
14
+ * n'importe que `zod` → pas de cycle).
15
+ *
16
+ * Le type TS est dérivé via `z.infer<>` ({@link MongooseConfig}), et la config est
17
+ * validée au boot du Module class (hook `onKernelRegister`, via le builder
18
+ * {@link defineMongooseConfig}) → plante propre avec messages clairs si la config
19
+ * est invalide, plutôt qu'un `undefined.x` silencieux en runtime.
20
+ *
21
+ * Convention figée (cf `feedback_config_validation_zod` + audit config ORM
22
+ * 2026-06), alignée sur `@nodefony/drizzle` (adapter SQL frère, référence).
23
+ *
24
+ * ⚠️ ENV : ce schéma reste **PUR** (aucune lecture `process.env` ici, sinon il
25
+ * devient non déterministe et non sérialisable en JSON Schema). La surcharge par
26
+ * variables d'environnement (`MONGODB_URI`, `NF_MONGODB_DEBUG`) est appliquée dans
27
+ * {@link defineMongooseConfig}, APRÈS le parse.
28
+ *
29
+ * SURCHARGE (précédence croissante — cf ADR-0006) :
30
+ * • App (typé) : `use("@nodefony/mongoose", { debug: true, connectors: { … } })` ;
31
+ * • Par environnement : `MONGODB_URI` · `NF_MONGODB_DEBUG` (appliqués dans
32
+ * `defineMongooseConfig`).
33
+ *
34
+ * ⚠️ NE PAS éditer les défauts matérialisés en bas de fichier : modifier les
35
+ * `.default(...)` du schéma. La validation + le merge env finaux sont faits dans
36
+ * `index.ts` au hook `onKernelRegister` via `defineMongooseConfig`.
37
+ */
38
+ const connectorSchema = z.strictObject({
39
+ uri: z.string().min(1).optional().describe("URI de connexion complète (`mongodb://…` ou `mongodb+srv://…`). Si fournie, elle PRIME sur `host`/`port`/`dbname`. Pratique pour les PaaS / Atlas. ⚠️ Zero Trust : un secret (user:pass) NE doit JAMAIS être committé — passer par l'env. Pour la poser par variable : `MONGODB_URI` (ou l'infra `NF_DATABASE_URL` de famille mongo). `NF__MONGOOSE__CONNECTORS__<NOM>__URI` ne marche QUE si la clé `uri` existe déjà dans la config de l'application : la surcharge par chemin remplace une valeur, elle n'en crée pas (elle avertit sinon)."),
40
+ host: z.string().min(1).default("localhost").describe("Hôte du serveur MongoDB. Défaut `localhost` (jamais d'hôte d'infra hardcodé). Ignoré si `uri` est fournie. Reco prod : hôte managé + TLS."),
41
+ port: z.number().int().min(1).max(65535).default(27017).describe("Port TCP du serveur. Défaut 27017. Ignoré si `uri` fournie."),
42
+ dbname: z.string().min(1).default("nodefony").describe("Nom de la base. Défaut `nodefony`. Ignoré si `uri` fournie."),
43
+ autoIndex: z.boolean().optional().describe("Construire les index déclarés au démarrage (défaut mongoose : true). Mongoose recommande `false` en production : la construction bloque les opérations sur une grosse collection. ⚠️ À `false`, un index manquant N'EST PAS créé — il est seulement CONSTATÉ et journalisé en CRITIC ; le poser devient un geste d'exploitation. Prime sur une clé `autoIndex` écrite dans `options` (canal typé > fourre-tout)."),
44
+ options: z.record(z.string(), z.unknown()).optional().describe("Options de connexion Mongoose (`ConnectOptions` : `user`/`pass`/`maxPoolSize`/`serverSelectionTimeoutMS`/`socketTimeoutMS`…). NON re-modélisées ici (validées par Mongoose lui-même). ⚠️ Les credentials (`user`/`pass`) doivent venir de l'env, jamais du dépôt.")
45
+ }).describe("Définition d'une connexion Mongoose nommée.");
46
+ const mongooseConfigSchema = z.strictObject({
47
+ debug: z.boolean().default(false).describe("Active la trace Mongoose des requêtes (`mongoose.set('debug')`). Défaut false. Mettre true en dev pour voir chaque opération. Surchargé par l'env `NF_MONGODB_DEBUG` (1/true). Reco prod : false."),
48
+ connectors: z.record(z.string(), connectorSchema).default(() => ({ nodefony: connectorSchema.parse({}) })).describe("Connexions indexées par nom (= clé dans le `ormRegistry`). Défaut : un connecteur `nodefony` sur `localhost:27017/nodefony`. Le nom `nodefony` (≠ `default` de Drizzle) évite toute collision d'entité dans le `entityRegistry` (process-wide) si les deux ORM cohabitent."),
49
+ frameworkEntities: z.boolean().default(true).describe("Déclare le schéma framework sur le connecteur `nodefony` (tokens, webauthn, webhooks — modèles compilés au connect) et rend les stores correspondants sélectionnables par nom (`mongoose`). Couverture partielle assumée : PAS d'audit ni d'idempotence mongoose. `false` = module data-only.")
50
+ }).describe("Configuration de @nodefony/mongoose.");
51
+ /**
52
+ * Défauts du module, matérialisés depuis le schéma (source unique). Toujours
53
+ * valides par construction ; passés au `super(..., config)` du Module class.
54
+ */
55
+ const config = mongooseConfigSchema.parse({});
56
+ //#endregion
57
+ export { config as default, mongooseConfigSchema };
@@ -0,0 +1,60 @@
1
+ import { mongooseConfigSchema } from "./config.js";
2
+ import { parseModuleConfig, resolveInfra } from "nodefony";
3
+ import { z } from "zod";
4
+ //#region nodefony/config/defineModuleConfig.ts
5
+ /**
6
+ * Applique la surcharge par variables d'environnement APRÈS le parse Zod.
7
+ *
8
+ * Le schéma reste pur (déterministe, sérialisable en JSON Schema) ; l'env est une
9
+ * couche explicite par-dessus. Précédence : env > config app > défauts.
10
+ *
11
+ * - `MONGODB_URI`, sinon infra database `NF_DATABASE_URL`/`DATABASE_URL` de
12
+ * famille mongo (`mongodb://`/`mongodb+srv://` — une URL SQL est IGNORÉE ici,
13
+ * elle appartient à `@nodefony/drizzle`) → `uri` du connecteur primaire
14
+ * (`nodefony`, sinon le premier). ⚠️ La place pour le secret de connexion
15
+ * (user:pass) — JAMAIS dans le dépôt.
16
+ * - `NF_MONGODB_DEBUG` (1/true) → `debug`.
17
+ */
18
+ function applyEnvOverrides(config) {
19
+ const env = process.env;
20
+ const database = resolveInfra(env).database;
21
+ const uri = env.MONGODB_URI ?? (database && database.family === "mongo" ? database.url : void 0);
22
+ if (uri) {
23
+ const target = config.connectors.nodefony ? "nodefony" : Object.keys(config.connectors)[0];
24
+ if (target && config.connectors[target]) config.connectors[target].uri = uri;
25
+ }
26
+ if (env.NF_MONGODB_DEBUG === "1" || env.NF_MONGODB_DEBUG === "true") config.debug = true;
27
+ return config;
28
+ }
29
+ /**
30
+ * Builder type-safe de la configuration de `@nodefony/mongoose`.
31
+ *
32
+ * ⭐ TL;DR : MACHINERIE DE BOOT — on n'édite (presque) jamais ce fichier. Même
33
+ * pattern que `nodefony.config.ts` ↔ `defineConfig()` du core : `config.ts` PORTE
34
+ * la config (schéma + défauts), `define<X>Config()` la VALIDE au boot (parse +
35
+ * env + freeze) et publie le JSON Schema Studio.
36
+ *
37
+ * Principes (alignés sur `defineRedisConfig` / `defineRealtimeConfig`) :
38
+ * - **Source unique** : `./config.ts` (Zod). Le builder VALIDE, applique l'ENV,
39
+ * puis GÈLE — il ne dévie jamais du schéma.
40
+ * - **Auto-documenté** : chaque champ Zod porte un `.describe()` →
41
+ * {@link mongooseConfigJsonSchema} produit un JSON Schema qu'un formulaire Studio
42
+ * (futur) consommera pour générer son UI d'édition.
43
+ *
44
+ * @param config - configuration brute (sections omises = défauts sûrs).
45
+ * @returns config validée, surchargée par l'env, et gelée.
46
+ * @throws ZodError si la config est invalide.
47
+ */
48
+ function defineMongooseConfig(config = {}) {
49
+ const parsed = parseModuleConfig(mongooseConfigSchema, config, "@nodefony/mongoose");
50
+ return Object.freeze(applyEnvOverrides(parsed));
51
+ }
52
+ /**
53
+ * JSON Schema introspectable de la config Mongoose — destiné au formulaire
54
+ * d'édition Studio (futur) et à la documentation générée.
55
+ */
56
+ function mongooseConfigJsonSchema() {
57
+ return z.toJSONSchema(mongooseConfigSchema);
58
+ }
59
+ //#endregion
60
+ export { defineMongooseConfig, mongooseConfigJsonSchema };
@@ -0,0 +1,63 @@
1
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
2
+ import { entity } from "@nodefony/orm-core";
3
+ //#region nodefony/entity/sessionEntity.ts
4
+ /** ORM cible du stockage de session (connecteur par défaut du module). */
5
+ const SESSION_CONNECTOR = "nodefony";
6
+ /**
7
+ * Schéma Mongoose de stockage des sessions (compilé par `MongooseOrm` au boot).
8
+ *
9
+ * Équivalent portable de l'entité session legacy : mêmes champs logiques
10
+ * (`session_id` PK applicative, sacs `Attributes`/`flashBag`/`metaBag`,
11
+ * `user`). Les horodatages sont des **nombres** (ms epoch), comme l'adapter
12
+ * Drizzle, pour que `SessionStorage` reste strictement portable entre les ORM
13
+ * (cutoff GC = `updatedAt < now - ttl`, opérateur riche `$lt` natif Mongo).
14
+ */
15
+ const schema = {
16
+ session_id: {
17
+ type: String,
18
+ index: true,
19
+ unique: true
20
+ },
21
+ Attributes: {
22
+ type: Object,
23
+ default: {}
24
+ },
25
+ flashBag: {
26
+ type: Object,
27
+ default: {}
28
+ },
29
+ metaBag: {
30
+ type: Object,
31
+ default: {}
32
+ },
33
+ user: {
34
+ type: String,
35
+ default: null
36
+ },
37
+ createdAt: { type: Number },
38
+ updatedAt: { type: Number }
39
+ };
40
+ /**
41
+ * Entité session enregistrée dans le `entityRegistry` pour le connecteur
42
+ * `nodefony` — `MongooseOrm` compile le modèle à la connexion (au boot).
43
+ *
44
+ * ⚠️ **`frameworkEntities: false` ne la coupe PAS.** Ce commutateur gouverne les
45
+ * entités déclarées dans le flux de boot (tokens, webauthn, webhooks) ; la
46
+ * session, elle, s'enregistre à l'**import** de ce fichier — le décorateur
47
+ * `@entity` s'exécute au chargement du barrel, avant que la moindre config soit
48
+ * lue. Même chose pour son store (`SessionStorage.ts`, `registerStorage`).
49
+ *
50
+ * Conséquence pratique : couper `frameworkEntities` retire les autres entités,
51
+ * pas la collection `session`. Pour ne pas avoir de session Mongo du tout, il
52
+ * faut ne pas router le store session vers mongoose (`session.store`), pas
53
+ * baisser ce drapeau.
54
+ */
55
+ let SessionEntity = class SessionEntity {};
56
+ SessionEntity = __decorate([entity({
57
+ connector: SESSION_CONNECTOR,
58
+ name: "session",
59
+ schema
60
+ })], SessionEntity);
61
+ var sessionEntity_default = SessionEntity;
62
+ //#endregion
63
+ export { SESSION_CONNECTOR, sessionEntity_default as default, schema as sessionSchema };
@@ -0,0 +1,184 @@
1
+ import { entityRegistry } from "@nodefony/orm-core";
2
+ //#region nodefony/entity/tokenEntity.ts
3
+ /**
4
+ * Schémas Mongoose du **store de jetons** `@nodefony/security` (pendant
5
+ * documentaire des tables Drizzle) — implémentation NoSQL d'`ITokenStore` (PAT,
6
+ * refresh, denylist `jti`, seuil de révocation en masse).
7
+ *
8
+ * ⚠️ **`_id` = clé naturelle (String), PAS un ObjectId auto** : le contrat
9
+ * `IRepository` traduit le critère `{ id }` en `{ _id }` (cf `MongooseRepository`).
10
+ * Comme l'`id` d'un jeton est un **jti fourni par l'appelant** (pas généré par
11
+ * Mongo), on force `_id: String` → le jti EST la clé primaire (gratuit : unique +
12
+ * éligible à un TTL index natif). Le virtuel `id` (activé par `MongooseOrm`,
13
+ * `toObject:{virtuals:true}`) renvoie `String(_id)` = le jti.
14
+ *
15
+ * ⚠️ **Horodatages = `Number` (epoch ms), `timestamps:false`** : `IAccessTokenRecord`
16
+ * porte des `number` (`Date.now()`) et l'appelant fournit `createdAt` → pas de
17
+ * gestion auto Mongoose. Le `gc()` applicatif reste portable (`$lte` exclut les
18
+ * `null` par type bracketing Mongo, comme Drizzle exclut `NULL`).
19
+ */
20
+ const accessTokenSchema = {
21
+ _id: { type: String },
22
+ kind: {
23
+ type: String,
24
+ required: true
25
+ },
26
+ name: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ prefix: {
31
+ type: String,
32
+ default: null
33
+ },
34
+ subjectId: {
35
+ type: String,
36
+ required: true,
37
+ index: true
38
+ },
39
+ subjectType: {
40
+ type: String,
41
+ required: true
42
+ },
43
+ tenantId: {
44
+ type: String,
45
+ default: null
46
+ },
47
+ scopes: {
48
+ type: [String],
49
+ default: []
50
+ },
51
+ audience: {
52
+ type: [String],
53
+ default: []
54
+ },
55
+ resources: {
56
+ type: Object,
57
+ default: null
58
+ },
59
+ secretHash: {
60
+ type: String,
61
+ required: true,
62
+ unique: true,
63
+ index: true
64
+ },
65
+ hashAlg: {
66
+ type: String,
67
+ required: true
68
+ },
69
+ clientId: {
70
+ type: String,
71
+ default: null
72
+ },
73
+ cnf: {
74
+ type: String,
75
+ default: null
76
+ },
77
+ family: {
78
+ type: String,
79
+ default: null,
80
+ index: true
81
+ },
82
+ replacedBy: {
83
+ type: String,
84
+ default: null
85
+ },
86
+ createdAt: {
87
+ type: Number,
88
+ required: true
89
+ },
90
+ expiresAt: {
91
+ type: Number,
92
+ default: null
93
+ },
94
+ lastUsedAt: {
95
+ type: Number,
96
+ default: null
97
+ },
98
+ lastUsedIp: {
99
+ type: String,
100
+ default: null
101
+ },
102
+ lastUsedUserAgent: {
103
+ type: String,
104
+ default: null
105
+ },
106
+ revokedAt: {
107
+ type: Number,
108
+ default: null
109
+ },
110
+ revokedReason: {
111
+ type: String,
112
+ default: null
113
+ },
114
+ metadata: {
115
+ type: Object,
116
+ default: {}
117
+ }
118
+ };
119
+ /** Denylist des access tokens (`jti` = `_id`) révoqués avant leur `exp`. */
120
+ const deniedJtiSchema = {
121
+ _id: { type: String },
122
+ expiresAt: {
123
+ type: Number,
124
+ required: true
125
+ }
126
+ };
127
+ /** Seuil de révocation en masse par porteur (`subjectId` = `_id`). */
128
+ const subjectRevocationSchema = {
129
+ _id: { type: String },
130
+ invalidBefore: {
131
+ type: Number,
132
+ required: true
133
+ }
134
+ };
135
+ /** Noms logiques des entités du store (clés de lookup `getRepository`). */
136
+ const TOKEN_ENTITY_NAMES = {
137
+ records: "access_token",
138
+ denied: "denied_jti",
139
+ revocations: "subject_revocation"
140
+ };
141
+ /**
142
+ * Construit les descripteurs d'entités du store de jetons pour un ORM nommé.
143
+ *
144
+ * Le `connector` est **dynamique** (nom du connecteur de l'app, ex. `"nodefony"`) : les
145
+ * schémas sont statiques mais leur liaison à un ORM dépend de la config → pas
146
+ * d'`@entity` figé (parité `createUserEntity`). `timestamps:false` (l'appelant
147
+ * gère `createdAt`). À enregistrer **avant** `orm.connect()`.
148
+ *
149
+ * @param orm - clé de l'ORM cible dans le `ormRegistry`.
150
+ * @returns les trois descripteurs {@link IEntity} (records / denylist / seuils).
151
+ */
152
+ function createTokenEntities(connector) {
153
+ return [
154
+ {
155
+ connector,
156
+ name: TOKEN_ENTITY_NAMES.records,
157
+ module: "security",
158
+ schema: accessTokenSchema
159
+ },
160
+ {
161
+ connector,
162
+ name: TOKEN_ENTITY_NAMES.denied,
163
+ module: "security",
164
+ schema: deniedJtiSchema
165
+ },
166
+ {
167
+ connector,
168
+ name: TOKEN_ENTITY_NAMES.revocations,
169
+ module: "security",
170
+ schema: subjectRevocationSchema
171
+ }
172
+ ];
173
+ }
174
+ /**
175
+ * Enregistre les entités du store de jetons dans le `entityRegistry` pour un ORM
176
+ * donné. À appeler **avant** `orm.connect()` (le modèle est compilé au connect).
177
+ *
178
+ * @param connector - nom de la connexion cible (clé du registre).
179
+ */
180
+ function registerTokenEntities(connector) {
181
+ for (const entity of createTokenEntities(connector)) entityRegistry.register(entity);
182
+ }
183
+ //#endregion
184
+ export { TOKEN_ENTITY_NAMES, accessTokenSchema, createTokenEntities, deniedJtiSchema, registerTokenEntities, subjectRevocationSchema };
@@ -0,0 +1,106 @@
1
+ import { entityRegistry } from "@nodefony/orm-core";
2
+ import { USER_COLUMNS } from "@nodefony/user";
3
+ //#region nodefony/entity/userEntity.ts
4
+ /**
5
+ * Traduction d'un type LOGIQUE du contrat utilisateur vers un type Mongoose.
6
+ *
7
+ * C'est le seul endroit du module document qui connaisse ce vocabulaire : le
8
+ * contrat dit ce que la donnée EST, ce fichier dit comment un document la range.
9
+ * `object[]` reste un `Array` LIBRE — pas un sous-schéma : c'est ce qui permet
10
+ * d'accueillir un nouveau fournisseur d'identité sans migration.
11
+ */
12
+ const TYPE_BY_COLUMN = {
13
+ uuid: String,
14
+ string: String,
15
+ "string[]": [String],
16
+ boolean: Boolean,
17
+ object: Object,
18
+ "object[]": Array,
19
+ date: Date
20
+ };
21
+ /**
22
+ * Construit la définition Mongoose d'une colonne du contrat utilisateur.
23
+ *
24
+ * Un défaut structuré est passé en FABRIQUE, jamais en valeur : Mongoose
25
+ * partagerait sinon le même tableau entre tous les documents. Une colonne
26
+ * facultative reçoit `null` explicite plutôt que rien, pour qu'un document
27
+ * ancien et un document neuf se lisent pareil. Une colonne obligatoire QUI A un
28
+ * défaut n'est pas `required` : la valeur ne peut pas manquer, l'exiger de
29
+ * l'appelant n'ajouterait qu'un refus.
30
+ */
31
+ function toFieldDefinition(column) {
32
+ const base = {
33
+ type: TYPE_BY_COLUMN[column.type],
34
+ ...column.nullable || column.makeDefault ? {} : { required: true },
35
+ ...column.unique ? {
36
+ unique: true,
37
+ index: true
38
+ } : {}
39
+ };
40
+ if (column.makeDefault) return {
41
+ ...base,
42
+ default: column.makeDefault
43
+ };
44
+ if (column.nullable) return {
45
+ ...base,
46
+ default: null
47
+ };
48
+ return base;
49
+ }
50
+ /**
51
+ * Schéma Mongoose de l'utilisateur Nodefony — implémentation NoSQL du contrat
52
+ * `@nodefony/user`, **dérivée** de `USER_COLUMNS` (pendant documentaire de
53
+ * `userTable`).
54
+ *
55
+ * Rien n'est recopié : noms, types logiques, défauts et unicité viennent du
56
+ * contrat, et `tests/unit/userContractParity.test.ts` refuse le contraire.
57
+ *
58
+ * Deux origines du contrat ne sont pas déclarées ici, et c'est voulu : la clé
59
+ * primaire est `_id` (ObjectId), servie au contrat `id: string` par le
60
+ * **virtuel `id`** activé à la sérialisation par `MongooseOrm`
61
+ * (`toObject/toJSON: { virtuals: true }`) ; les horodatages sont gérés par
62
+ * l'option `timestamps: true` du descripteur. Le moteur les fournit — les
63
+ * redéclarer les mettrait en concurrence avec lui.
64
+ */
65
+ const userSchema = Object.fromEntries(USER_COLUMNS.filter((column) => column.origin === "column").map((column) => [column.name, toFieldDefinition(column)]));
66
+ /**
67
+ * Les colonnes du contrat qu'un schéma DOCUMENT porte EN PROPRE.
68
+ *
69
+ * Dérivée de {@link userSchema}, jamais recopiée : ce que le framework exige de
70
+ * l'entité d'une application est exactement ce qu'il produit pour la sienne. La
71
+ * clé (`_id` + virtuel `id`) et les horodatages (option `timestamps`) restent
72
+ * donc hors de cette liste — les exiger comme chemins refuserait une entité
73
+ * parfaitement correcte, et un refus faux apprend à passer outre les refus.
74
+ */
75
+ const DOCUMENT_USER_COLUMNS = USER_COLUMNS.filter((column) => Object.prototype.hasOwnProperty.call(userSchema, column.name));
76
+ /**
77
+ * Construit le descripteur d'entité `User` Mongoose pour un ORM nommé.
78
+ *
79
+ * Le `connector` est **dynamique** (nom du connecteur de l'app, ex. `"nodefony"`) : le
80
+ * schéma est statique mais sa liaison à un ORM dépend de la config → pas d'`@entity`
81
+ * figé (parité avec `createUserEntity` Drizzle). À enregistrer **avant**
82
+ * `orm.connect()` (le modèle est compilé à la connexion par `MongooseOrm`).
83
+ *
84
+ * @param orm - clé de l'ORM cible dans le `ormRegistry`.
85
+ * @returns descripteur {@link IEntity} (`name: "User"`, `timestamps: true`).
86
+ */
87
+ function createUserEntity(connector) {
88
+ return {
89
+ connector,
90
+ name: "User",
91
+ module: "user",
92
+ schema: userSchema,
93
+ timestamps: true
94
+ };
95
+ }
96
+ /**
97
+ * Enregistre l'entité `User` Mongoose dans le `entityRegistry` pour un ORM donné.
98
+ * À appeler **avant** `orm.connect()` (le modèle est compilé au connect).
99
+ *
100
+ * @param connector - nom de la connexion cible (clé du registre).
101
+ */
102
+ function registerUserEntity(connector) {
103
+ entityRegistry.register(createUserEntity(connector));
104
+ }
105
+ //#endregion
106
+ export { DOCUMENT_USER_COLUMNS, createUserEntity, registerUserEntity, userSchema };
@@ -0,0 +1,97 @@
1
+ import { entityRegistry } from "@nodefony/orm-core";
2
+ //#region nodefony/entity/webAuthnCredentialEntity.ts
3
+ /**
4
+ * Schéma Mongoose du **store de credentials WebAuthn** `@nodefony/security`
5
+ * (pendant documentaire de la table Drizzle) — implémentation NoSQL d'
6
+ * `IWebAuthnCredentialStore` (passkeys).
7
+ *
8
+ * ⚠️ **`_id` = clé naturelle (String), PAS un ObjectId auto** : le contrat
9
+ * `IRepository` traduit le critère `{ id }` en `{ _id }` (cf `MongooseRepository`).
10
+ * Comme l'`id` d'un credential est un **identifiant base64url fourni par
11
+ * l'authenticator** (pas généré par Mongo), on force `_id: String` → le
12
+ * credentialId EST la clé primaire. Le virtuel `id` (activé par `MongooseOrm`,
13
+ * `toObject:{virtuals:true}`) renvoie `String(_id)` = le credentialId.
14
+ *
15
+ * ⚠️ **Horodatages = `Number` (epoch ms), `timestamps:false`** : `IWebAuthnCredential`
16
+ * porte des `number` (`Date.now()`) et l'appelant fournit `createdAt`.
17
+ *
18
+ * Le store ne lit/écrit JAMAIS `IWebAuthnCredential` directement : il traduit via
19
+ * {@link WebAuthnCredentialRow} (forme plate, `nickname: string | null`) — le
20
+ * contrat porte un `nickname?` optionnel et des champs `readonly`, que la
21
+ * frontière de persistance normalise.
22
+ */
23
+ const webAuthnCredentialSchema = {
24
+ _id: { type: String },
25
+ userId: {
26
+ type: String,
27
+ required: true,
28
+ index: true
29
+ },
30
+ publicKey: {
31
+ type: String,
32
+ required: true
33
+ },
34
+ signCount: {
35
+ type: Number,
36
+ required: true
37
+ },
38
+ transports: {
39
+ type: [String],
40
+ default: []
41
+ },
42
+ backupEligible: {
43
+ type: Boolean,
44
+ required: true
45
+ },
46
+ backupState: {
47
+ type: Boolean,
48
+ required: true
49
+ },
50
+ uvInitialized: {
51
+ type: Boolean,
52
+ required: true
53
+ },
54
+ nickname: {
55
+ type: String,
56
+ default: null
57
+ },
58
+ createdAt: {
59
+ type: Number,
60
+ required: true
61
+ },
62
+ lastUsedAt: {
63
+ type: Number,
64
+ default: null
65
+ }
66
+ };
67
+ /** Nom logique de l'entité (clé de lookup `getRepository`). */
68
+ const WEBAUTHN_CREDENTIAL_ENTITY = "webauthn_credential";
69
+ /**
70
+ * Construit le descripteur d'entité du store de credentials pour un ORM nommé.
71
+ *
72
+ * Le `connector` est **dynamique** (nom du connecteur de l'app, ex. `"nodefony"`) : le
73
+ * schéma est statique mais sa liaison à un ORM dépend de la config → pas d'`@entity`
74
+ * figé (parité `createTokenEntities`). `timestamps:false`. À enregistrer **avant**
75
+ * `orm.connect()`.
76
+ *
77
+ * @param orm - clé de l'ORM cible dans le `ormRegistry`.
78
+ */
79
+ function createWebAuthnCredentialEntity(connector) {
80
+ return {
81
+ connector,
82
+ name: WEBAUTHN_CREDENTIAL_ENTITY,
83
+ module: "security",
84
+ schema: webAuthnCredentialSchema
85
+ };
86
+ }
87
+ /**
88
+ * Enregistre l'entité du store de credentials dans le `entityRegistry` pour un
89
+ * ORM donné. À appeler **avant** `orm.connect()` (le modèle est compilé au connect).
90
+ *
91
+ * @param connector - nom de la connexion cible (clé du registre).
92
+ */
93
+ function registerWebAuthnCredentialEntity(connector) {
94
+ entityRegistry.register(createWebAuthnCredentialEntity(connector));
95
+ }
96
+ //#endregion
97
+ export { WEBAUTHN_CREDENTIAL_ENTITY, createWebAuthnCredentialEntity, registerWebAuthnCredentialEntity, webAuthnCredentialSchema };