@nodefony/orm-core 10.0.0-alpha.9 → 10.0.0-beta.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.
@@ -1,3 +1,4 @@
1
+ import { omitUndefined } from "./omitUndefined.js";
1
2
  import { paginate } from "./paginate.js";
2
3
  import { Service } from "nodefony";
3
4
  //#region nodefony/src/AbstractCrudService.ts
@@ -115,12 +116,15 @@ var AbstractCrudService = class extends Service {
115
116
  /**
116
117
  * Crée une entité : `beforeCreate` → persistance → `afterCreate` → `onCreated`.
117
118
  *
119
+ * Une clé qui vaut `undefined` vaut clé absente : elle est retirée avant la
120
+ * persistance (cf `omitUndefined`), quel que soit l'adaptateur.
121
+ *
118
122
  * @param data - champs de l'entité à créer.
119
123
  * @returns l'entité persistée. Émet `onCreated`.
120
124
  */
121
125
  async create(data) {
122
126
  const prepared = await this.beforeCreate(data);
123
- const entity = await this.repository.create(prepared);
127
+ const entity = await this.repository.create(omitUndefined(prepared));
124
128
  await this.afterCreate(entity);
125
129
  this.fire("onCreated", entity);
126
130
  return entity;
@@ -139,12 +143,12 @@ var AbstractCrudService = class extends Service {
139
143
  * on la remontera dans un service le jour où un usage métier la réclame.
140
144
  *
141
145
  * @param criteria - filtre de sélection (champ inconnu → `UnknownCriteriaField`).
142
- * @param data - champs à modifier.
146
+ * @param data - champs à modifier ; une clé `undefined` n'est pas touchée.
143
147
  * @returns l'entité mise à jour, ou `null` si aucune ne correspond (pas d'event).
144
148
  */
145
149
  async updateOne(criteria, data) {
146
150
  const prepared = await this.beforeUpdate(criteria, data);
147
- const updated = await this.repository.updateOne(criteria, prepared);
151
+ const updated = await this.repository.updateOne(criteria, omitUndefined(prepared));
148
152
  if (updated !== null) {
149
153
  await this.afterUpdate(updated);
150
154
  this.fire("onUpdated", updated);
@@ -51,7 +51,7 @@ var ConnectionMonitor = class {
51
51
  #stats = null;
52
52
  /** Crée/retourne les stats mutables d'un connecteur (alloue à la demande). */
53
53
  #ensure(name) {
54
- if (this.#stats === null) this.#stats = /* @__PURE__ */ new Map();
54
+ this.#stats ??= /* @__PURE__ */ new Map();
55
55
  let s = this.#stats.get(name);
56
56
  if (s === void 0) {
57
57
  s = {
@@ -80,7 +80,7 @@ var ConnectionMonitor = class {
80
80
  */
81
81
  recordPing(name, ms) {
82
82
  const s = this.#ensure(name);
83
- if (s.latencies === null) s.latencies = [];
83
+ s.latencies ??= [];
84
84
  s.latencies.push(ms);
85
85
  if (s.latencies.length > MAX_LATENCY_SAMPLES) s.latencies.shift();
86
86
  }
@@ -139,7 +139,7 @@ var ConnectionMonitor = class {
139
139
  }
140
140
  /** Ajoute un événement en tête de la chronologie (allouée au 1ᵉʳ, bornée). */
141
141
  #pushEvent(s, e) {
142
- if (s.events === null) s.events = [];
142
+ s.events ??= [];
143
143
  s.events.unshift(e);
144
144
  if (s.events.length > MAX_EVENTS) s.events.length = MAX_EVENTS;
145
145
  }
@@ -157,7 +157,7 @@ var ConnectionMonitor = class {
157
157
  ts: Date.now()
158
158
  };
159
159
  s.lastError = err;
160
- if (s.recentErrors === null) s.recentErrors = [];
160
+ s.recentErrors ??= [];
161
161
  s.recentErrors.unshift(err);
162
162
  if (s.recentErrors.length > MAX_RECENT_ERRORS) s.recentErrors.length = MAX_RECENT_ERRORS;
163
163
  }
@@ -172,9 +172,10 @@ var ConnectionMonitor = class {
172
172
  if (s === void 0) return EMPTY_CORE;
173
173
  const lat = s.latencies;
174
174
  let latency = EMPTY_LATENCY;
175
- if (lat && lat.length) {
176
- let min = lat[0];
177
- let max = lat[0];
175
+ const last = lat?.at(-1);
176
+ if (lat && last !== void 0) {
177
+ let min = last;
178
+ let max = last;
178
179
  let sum = 0;
179
180
  for (const v of lat) {
180
181
  if (v < min) min = v;
@@ -182,7 +183,7 @@ var ConnectionMonitor = class {
182
183
  sum += v;
183
184
  }
184
185
  latency = {
185
- last: lat[lat.length - 1],
186
+ last,
186
187
  min: Math.round(min * 100) / 100,
187
188
  avg: Math.round(sum / lat.length * 100) / 100,
188
189
  max: Math.round(max * 100) / 100,
@@ -20,7 +20,7 @@ var EntityRegistry = class {
20
20
  * @throws si cette entité est déjà enregistrée sur le même connecteur.
21
21
  */
22
22
  register(entity) {
23
- if (this.#entities === null) this.#entities = Object.create(null);
23
+ this.#entities ??= Object.create(null);
24
24
  const store = this.#entities;
25
25
  let bucket = store[entity.name];
26
26
  if (bucket === void 0) {
@@ -49,7 +49,10 @@ var EntityRegistry = class {
49
49
  }
50
50
  const connectors = Object.keys(bucket);
51
51
  if (connectors.length > 1) throw new Error(`EntityRegistry: entity "${name}" exists on multiple connectors (${connectors.join(", ")}); specify one.`);
52
- return bucket[connectors[0]];
52
+ const only = connectors[0];
53
+ const entity = only === void 0 ? void 0 : bucket[only];
54
+ if (entity === void 0) throw new Error(`EntityRegistry: no entity registered under "${name}".`);
55
+ return entity;
53
56
  }
54
57
  /**
55
58
  * Indique si une entité (optionnellement sur un connecteur précis) est enregistrée.
@@ -72,7 +75,10 @@ var EntityRegistry = class {
72
75
  const out = [];
73
76
  for (const name in this.#entities) {
74
77
  const bucket = this.#entities[name];
75
- for (const connector in bucket) out.push(bucket[connector]);
78
+ for (const connector in bucket) {
79
+ const entity = bucket[connector];
80
+ if (entity !== void 0) out.push(entity);
81
+ }
76
82
  }
77
83
  return out;
78
84
  }
@@ -194,7 +194,7 @@ var Orm = class extends Service {
194
194
  const timer = setInterval(() => {
195
195
  this.#beat();
196
196
  }, this.heartbeatMs);
197
- timer.unref?.();
197
+ timer.unref();
198
198
  this.#heartbeat = timer;
199
199
  }
200
200
  /**
@@ -264,7 +264,7 @@ var Orm = class extends Service {
264
264
  watchdog = setTimeout(() => {
265
265
  rejeter(/* @__PURE__ */ new Error(`aucune réponse en ${this.heartbeatTimeoutMs} ms`));
266
266
  }, this.heartbeatTimeoutMs);
267
- watchdog.unref?.();
267
+ watchdog.unref();
268
268
  })]);
269
269
  this.connectionRestored();
270
270
  } catch (e) {
@@ -349,8 +349,8 @@ function jsonSchemaType(nativeType) {
349
349
  const t = nativeType.toLowerCase();
350
350
  if (/\b(serial|bigint|smallint|tinyint|mediumint|int|integer)\b/.test(t)) return { type: "integer" };
351
351
  if (/(real|float|double|decimal|numeric|number)/.test(t)) return { type: "number" };
352
- if (/bool/.test(t)) return { type: "boolean" };
353
- if (/json/.test(t)) return { type: "object" };
352
+ if (t.includes("bool")) return { type: "boolean" };
353
+ if (t.includes("json")) return { type: "object" };
354
354
  if (/(date|time|timestamp)/.test(t)) return {
355
355
  type: "string",
356
356
  format: "date-time"
@@ -422,7 +422,7 @@ function createOrmAdminApi() {
422
422
  path: "entity/{name}",
423
423
  summary: "Une entité (colonnes + relations) — ?connector= si homonymes",
424
424
  handler: (request) => {
425
- const name = request.params.name ?? "";
425
+ const name = request.params.name;
426
426
  const connector = oneParam(request, "connector");
427
427
  try {
428
428
  const found = entityRegistry.list().find((e) => e.name === name && (!connector || e.connector === connector));
@@ -22,7 +22,7 @@ var OrmRegistry = class {
22
22
  * @throws si un ORM du même nom est déjà enregistré (erreur de configuration).
23
23
  */
24
24
  register(name, orm) {
25
- if (this.#orms === null) this.#orms = /* @__PURE__ */ new Map();
25
+ this.#orms ??= /* @__PURE__ */ new Map();
26
26
  if (this.#orms.has(name)) throw new Error(`OrmRegistry: an ORM named "${name}" is already registered.`);
27
27
  this.#orms.set(name, orm);
28
28
  }
@@ -51,7 +51,7 @@ var QueryFlowMonitor = class {
51
51
  }
52
52
  /** Crée/retourne les stats mutables d'un connecteur (alloue à la demande). */
53
53
  #ensure(connector) {
54
- if (this.#stats === null) this.#stats = /* @__PURE__ */ new Map();
54
+ this.#stats ??= /* @__PURE__ */ new Map();
55
55
  let s = this.#stats.get(connector);
56
56
  if (s === void 0) {
57
57
  s = {
@@ -85,7 +85,7 @@ var QueryFlowMonitor = class {
85
85
  s.ewmaMs = s.ewmaMs === null ? durationMs : EWMA_ALPHA * durationMs + .8 * s.ewmaMs;
86
86
  if (durationMs >= this.slowMs) {
87
87
  s.slowTotal += 1;
88
- if (s.slow === null) s.slow = [];
88
+ s.slow ??= [];
89
89
  s.slow.unshift({
90
90
  ts: Date.now(),
91
91
  durationMs,
@@ -57,7 +57,7 @@ async function withConnectDeadline(work, what, ms = CONNECT_TIMEOUT_MS) {
57
57
  timer = setTimeout(() => {
58
58
  reject(/* @__PURE__ */ new Error(`délai dépassé (${ms} ms) en attendant ${what} — le serveur a accepté la connexion sans répondre, ou n'a jamais répondu. Vérifier que le service visé est bien celui qui écoute, et qu'il est en état de servir.`));
59
59
  }, ms);
60
- timer.unref?.();
60
+ timer.unref();
61
61
  })]);
62
62
  } finally {
63
63
  if (timer !== void 0) clearTimeout(timer);
@@ -124,8 +124,8 @@ function escapeLikeTerm(text) {
124
124
  function likePatternToRegExp(pattern) {
125
125
  let source = "";
126
126
  for (let i = 0; i < pattern.length; i++) {
127
- const c = pattern[i];
128
- if (c === "\\" && i + 1 < pattern.length) source += escapeRegExpChar(pattern[++i]);
127
+ const c = pattern.charAt(i);
128
+ if (c === "\\" && i + 1 < pattern.length) source += escapeRegExpChar(pattern.charAt(++i));
129
129
  else if (c === "%") source += ".*";
130
130
  else if (c === "_") source += ".";
131
131
  else source += escapeRegExpChar(c);
@@ -169,7 +169,8 @@ function searchCriteria(q, fields) {
169
169
  const term = q?.trim();
170
170
  if (!term || fields.length === 0) return null;
171
171
  const likePattern = `${escapeLikeTerm(term)}%`;
172
- if (fields.length === 1) return { [fields[0]]: { $like: likePattern } };
172
+ const only = fields.length === 1 ? fields[0] : void 0;
173
+ if (only !== void 0) return { [only]: { $like: likePattern } };
173
174
  return { $or: fields.map((f) => ({ [f]: { $like: likePattern } })) };
174
175
  }
175
176
  //#endregion
@@ -0,0 +1,30 @@
1
+ //#region nodefony/src/omitUndefined.ts
2
+ /**
3
+ * Retire les clés qui valent `undefined` d'une donnée d'écriture partielle.
4
+ *
5
+ * `undefined` et clé absente veulent dire « ne pas toucher » : Drizzle ignore
6
+ * déjà une valeur `undefined`, Mongoose (≥ 6) la retire d'une mise à jour. La
7
+ * règle est posée ICI, une fois, au seuil de la persistance, au lieu d'être
8
+ * un comportement propre à chaque adaptateur.
9
+ *
10
+ * Rend l'objet reçu TEL QUEL quand aucune clé ne vaut `undefined` — le cas
11
+ * courant d'un corps JSON, qui ne peut pas en porter : aucune allocation.
12
+ *
13
+ * @param data - données d'écriture, clés `undefined` admises
14
+ * @returns les mêmes données, sans aucune clé `undefined`
15
+ */
16
+ function omitUndefined(data) {
17
+ const source = data;
18
+ for (const key of Object.keys(source)) {
19
+ if (source[key] !== void 0) continue;
20
+ const out = {};
21
+ for (const k of Object.keys(source)) {
22
+ const value = source[k];
23
+ if (value !== void 0) out[k] = value;
24
+ }
25
+ return out;
26
+ }
27
+ return data;
28
+ }
29
+ //#endregion
30
+ export { omitUndefined };
@@ -53,7 +53,7 @@ export interface IEntity<S = unknown, M = unknown> {
53
53
  * **Optionnel** : sert au regroupement dans le graphe canonique / ERD Studio.
54
54
  * Non renseigné → entité non rattachée (groupe « — » côté UI).
55
55
  */
56
- readonly module?: string;
56
+ readonly module?: string | undefined;
57
57
  /**
58
58
  * **Classification** de l'entité (ex. domaine fonctionnel `"facturation"`,
59
59
  * `"comptabilité"`) — axe de regroupement **distinct** du `module`
@@ -61,7 +61,7 @@ export interface IEntity<S = unknown, M = unknown> {
61
61
  * base** (centaines de tables d'un même module) : l'ERD groupe/filtre par
62
62
  * `domain`. **Optionnel** ; non renseigné → retombe sur `module`.
63
63
  */
64
- readonly domain?: string;
64
+ readonly domain?: string | undefined;
65
65
  /** Définition de schéma propre au driver (forme libre). */
66
66
  readonly schema: S;
67
67
  /** Modèle compilé natif, renseigné après connexion de l'ORM. */
@@ -72,13 +72,13 @@ export interface IEntity<S = unknown, M = unknown> {
72
72
  * schema-as-code (Drizzle) les expriment en colonnes → ce flag y est sans effet.
73
73
  * **Optionnel**, défaut `false`.
74
74
  */
75
- readonly timestamps?: boolean;
75
+ readonly timestamps?: boolean | undefined;
76
76
  /** Relations déclarées vers d'autres entités (par nom logique). */
77
- readonly relations?: ReadonlyArray<IEntityRelation>;
77
+ readonly relations?: ReadonlyArray<IEntityRelation> | undefined;
78
78
  /**
79
79
  * Index composites, appliqués par l'adapter qui les exprime hors schéma
80
80
  * (Mongoose `schema.index()`). Les ORM schema-as-code (Drizzle) les déclarent
81
81
  * dans leur table → ce champ y est sans effet. **Optionnel**.
82
82
  */
83
- readonly indexes?: ReadonlyArray<IEntityIndex>;
83
+ readonly indexes?: ReadonlyArray<IEntityIndex> | undefined;
84
84
  }
@@ -26,7 +26,7 @@ export interface ISlowQuery {
26
26
  /** Connecteur ORM (clé du registre). */
27
27
  connector: string;
28
28
  /** SQL paramétré et redacté (absent si l'adapter ne sait pas l'extraire). */
29
- sql?: string;
29
+ sql?: string | undefined;
30
30
  }
31
31
  /**
32
32
  * Compteurs de flux d'UN connecteur ORM. Cumuls depuis le boot (le débit/s se
@@ -32,7 +32,7 @@ export interface IRelationInfo {
32
32
  /** Champ portant la relation côté entité courante. */
33
33
  field: string;
34
34
  /** Clé étrangère (explicite ou dérivée déterministe). */
35
- foreignKey?: string;
35
+ foreignKey?: string | undefined;
36
36
  }
37
37
  /** Nœud du graphe : une entité avec ses colonnes et ses relations. */
38
38
  export interface IEntityGraphNode {
@@ -66,11 +66,11 @@ export interface IConnectionInfo {
66
66
  * `host:port/base` (serveur) ou URI redactée. **Jamais de credential** (mot de
67
67
  * passe retiré côté adapter).
68
68
  */
69
- target?: string;
69
+ target?: string | undefined;
70
70
  /** Version du moteur/base (ex. SQLite `3.45.1`), si l'adapter peut l'obtenir. */
71
- version?: string;
71
+ version?: string | undefined;
72
72
  /** Version de la lib ORM elle-même (ex. drizzle-orm `0.44.x`), si connue. */
73
- ormVersion?: string;
73
+ ormVersion?: string | undefined;
74
74
  }
75
75
  /** Résumé d'un ORM/connecteur enregistré. */
76
76
  export interface IOrmSummary {
@@ -93,7 +93,7 @@ export interface IOrmSummary {
93
93
  /** Nombre d'entités rattachées à cet ORM. */
94
94
  entityCount: number;
95
95
  /** Connexion sous-jacente (driver + cible), si l'adapter l'expose. */
96
- connection?: IConnectionInfo;
96
+ connection?: IConnectionInfo | undefined;
97
97
  }
98
98
  /** Erreur de connexion horodatée (message **redacté** — jamais de credential). */
99
99
  export interface IConnectionError {
@@ -112,7 +112,7 @@ export interface IConnectionEvent {
112
112
  /** Horodatage epoch ms. */
113
113
  ts: number;
114
114
  /** Cause de la perte (credential déjà retiré) — absente pour une reprise. */
115
- reason?: string;
115
+ reason?: string | undefined;
116
116
  }
117
117
  /**
118
118
  * Le mécanisme de résilience d'un connecteur, TEL QU'IL TOURNE dans ce process.
@@ -153,11 +153,11 @@ export interface IConnectionHealth {
153
153
  /** Base/driver (`sqlite`, `mongodb`…). */
154
154
  driver: string;
155
155
  /** Cible lisible (chemin relatif / host:port), jamais de credential. */
156
- target?: string;
156
+ target?: string | undefined;
157
157
  /** Version moteur. */
158
- version?: string;
158
+ version?: string | undefined;
159
159
  /** Version lib ORM. */
160
- ormVersion?: string;
160
+ ormVersion?: string | undefined;
161
161
  /** État courant (`isConnected`). */
162
162
  connected: boolean;
163
163
  /** Connecté depuis (epoch ms), `null` si jamais connecté. */
@@ -177,7 +177,7 @@ export interface IConnectionHealth {
177
177
  /** Pertes et reprises récentes (ring borné, plus récentes d'abord). */
178
178
  events: IConnectionEvent[];
179
179
  /** Mécanisme de détection et de reprise, si l'ORM le décrit. */
180
- resilience?: IOrmResilience;
180
+ resilience?: IOrmResilience | undefined;
181
181
  /** Nombre total d'erreurs enregistrées (connexion + ping). */
182
182
  errorCount: number;
183
183
  /** Dernière erreur, `null` si aucune. */
@@ -195,11 +195,11 @@ export interface IConnectionHealth {
195
195
  /** Fenêtre glissante de latence (min/moy/max sur les N derniers pings). */
196
196
  latency: ILatencyWindow;
197
197
  /** Sonde de stockage (driver), si l'adapter l'expose. */
198
- storage?: IOrmStorageProbe;
198
+ storage?: IOrmStorageProbe | undefined;
199
199
  /** Sonde de pool de connexions (driver), si l'adapter l'expose. */
200
- pool?: IOrmPoolProbe;
200
+ pool?: IOrmPoolProbe | undefined;
201
201
  /** Métriques driver libres (clé→valeur). */
202
- extra?: Record<string, string | number | boolean>;
202
+ extra?: Record<string, string | number | boolean> | undefined;
203
203
  }
204
204
  /** Graphe complet du modèle de données — réponse de `/nodefony/orm/api/graph`. */
205
205
  export interface IOrmGraph {
@@ -32,15 +32,15 @@ export interface ILatencyWindow {
32
32
  */
33
33
  export interface IOrmStorageProbe {
34
34
  /** Taille de la base (octets) — `pages × pageSize` (SQLite). */
35
- sizeBytes?: number;
35
+ sizeBytes?: number | undefined;
36
36
  /** Nombre de pages allouées. */
37
- pages?: number;
37
+ pages?: number | undefined;
38
38
  /** Taille d'une page (octets). */
39
- pageSize?: number;
39
+ pageSize?: number | undefined;
40
40
  /** Mode de journalisation (SQLite : `wal`, `delete`, `memory`…). */
41
- journalMode?: string;
41
+ journalMode?: string | undefined;
42
42
  /** Pages libres (fragmentation / espace récupérable au VACUUM). */
43
- freePages?: number;
43
+ freePages?: number | undefined;
44
44
  }
45
45
  /**
46
46
  * Sonde de pool de connexions — pertinente pour les bases serveur (Mongo,
@@ -48,13 +48,13 @@ export interface IOrmStorageProbe {
48
48
  */
49
49
  export interface IOrmPoolProbe {
50
50
  /** Taille max configurée du pool. */
51
- size?: number;
51
+ size?: number | undefined;
52
52
  /** Connexions disponibles (idle). */
53
- available?: number;
53
+ available?: number | undefined;
54
54
  /** Connexions en cours d'utilisation. */
55
- borrowed?: number;
55
+ borrowed?: number | undefined;
56
56
  /** Demandes en attente d'une connexion. */
57
- pending?: number;
57
+ pending?: number | undefined;
58
58
  }
59
59
  /**
60
60
  * Sonde profonde driver-spécifique retournée par {@link IOrm.probe}. Tous les
@@ -20,5 +20,5 @@ export type { IPage, IPageQuery } from "nodefony";
20
20
  */
21
21
  export interface PageQuery<T = unknown> extends IPageQuery {
22
22
  /** Filtre typé optionnel appliqué avant la pagination (toutes les lignes si omis). */
23
- criteria?: Criteria<T>;
23
+ criteria?: Criteria<T> | undefined;
24
24
  }
@@ -23,23 +23,23 @@ export type OrmCriteria = Record<string, unknown>;
23
23
  */
24
24
  export interface FieldOperators<V> {
25
25
  /** Égalité stricte (équivalent à passer la valeur nue). */
26
- $eq?: V;
26
+ $eq?: V | undefined;
27
27
  /** Différent de. */
28
- $ne?: V;
28
+ $ne?: V | undefined;
29
29
  /** Strictement supérieur à. */
30
- $gt?: V;
30
+ $gt?: V | undefined;
31
31
  /** Supérieur ou égal à. */
32
- $gte?: V;
32
+ $gte?: V | undefined;
33
33
  /** Strictement inférieur à. */
34
- $lt?: V;
34
+ $lt?: V | undefined;
35
35
  /** Inférieur ou égal à. */
36
- $lte?: V;
36
+ $lte?: V | undefined;
37
37
  /** Appartient à l'ensemble. */
38
- $in?: readonly V[];
38
+ $in?: readonly V[] | undefined;
39
39
  /** N'appartient pas à l'ensemble. */
40
- $nin?: readonly V[];
40
+ $nin?: readonly V[] | undefined;
41
41
  /** Motif SQL `LIKE` (`%`/`_`) — pertinent pour les champs texte uniquement. */
42
- $like?: string;
42
+ $like?: string | undefined;
43
43
  /**
44
44
  * Teste l'**absence de valeur** : `true` → `IS NULL`, `false` → `IS NOT NULL`
45
45
  * (Mongo : `$eq`/`$ne null`, qui couvre aussi le champ absent).
@@ -60,7 +60,7 @@ export interface FieldOperators<V> {
60
60
  * // les PAT qui ont une expiration
61
61
  * repo.find({ expiresAt: { $null: false } });
62
62
  */
63
- $null?: boolean;
63
+ $null?: boolean | undefined;
64
64
  }
65
65
  /**
66
66
  * Opérateurs d'**écriture** applicables à un champ dans le `update` d'un
@@ -154,7 +154,7 @@ export type Criteria<T> = {
154
154
  * // les clés encore utilisables
155
155
  * repo.count({ revokedAt: null, $or: [{ expiresAt: null }, { expiresAt: { $gt: now } }] });
156
156
  */
157
- $or?: ReadonlyArray<Criteria<T>>;
157
+ $or?: ReadonlyArray<Criteria<T>> | undefined;
158
158
  } & OrmCriteria;
159
159
  /**
160
160
  * Options de lecture (`find`/`findOne`) portables cross-ORM.
@@ -181,7 +181,7 @@ export interface RepositoryReadOptions {
181
181
  * la frontière qui la reçoit (`parsePageQuery` accepte `champ:sens` en casse
182
182
  * libre), jamais au repository.
183
183
  */
184
- order?: Array<[string, "ASC" | "DESC"]>;
184
+ order?: Array<[string, "ASC" | "DESC"]> | undefined;
185
185
  }
186
186
  /**
187
187
  * Contrat CRUD minimal exposé par un repository, indépendant de l'ORM sous-jacent.
@@ -1,4 +1,5 @@
1
1
  import { Service } from "nodefony";
2
+ import type { PartialInput } from "nodefony";
2
3
  import type { IRepository, Criteria, RepositoryReadOptions, IPage, PageQuery } from "../interfaces/index.js";
3
4
  import type { ServiceWiring } from "./serviceWiring.js";
4
5
  /**
@@ -102,10 +103,13 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
102
103
  /**
103
104
  * Crée une entité : `beforeCreate` → persistance → `afterCreate` → `onCreated`.
104
105
  *
106
+ * Une clé qui vaut `undefined` vaut clé absente : elle est retirée avant la
107
+ * persistance (cf `omitUndefined`), quel que soit l'adaptateur.
108
+ *
105
109
  * @param data - champs de l'entité à créer.
106
110
  * @returns l'entité persistée. Émet `onCreated`.
107
111
  */
108
- create(data: Partial<T>): Promise<T>;
112
+ create(data: PartialInput<T>): Promise<T>;
109
113
  /**
110
114
  * Met à jour **au plus une** entité : `beforeUpdate` → persistance atomique
111
115
  * → `afterUpdate` → `onUpdated`.
@@ -120,10 +124,10 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
120
124
  * on la remontera dans un service le jour où un usage métier la réclame.
121
125
  *
122
126
  * @param criteria - filtre de sélection (champ inconnu → `UnknownCriteriaField`).
123
- * @param data - champs à modifier.
127
+ * @param data - champs à modifier ; une clé `undefined` n'est pas touchée.
124
128
  * @returns l'entité mise à jour, ou `null` si aucune ne correspond (pas d'event).
125
129
  */
126
- updateOne(criteria: Criteria<T>, data: Partial<T>): Promise<T | null>;
130
+ updateOne(criteria: Criteria<T>, data: PartialInput<T>): Promise<T | null>;
127
131
  /**
128
132
  * Supprime : `beforeDelete` → persistance → `afterDelete` → `onDeleted`.
129
133
  *
@@ -138,7 +142,7 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
138
142
  * @param data - données entrantes.
139
143
  * @returns les données préparées à persister.
140
144
  */
141
- protected beforeCreate(data: Partial<T>): Partial<T> | Promise<Partial<T>>;
145
+ protected beforeCreate(data: PartialInput<T>): PartialInput<T> | Promise<PartialInput<T>>;
142
146
  /** Effet de bord après création (ex. provisionner une ressource liée). */
143
147
  protected afterCreate(_entity: T): void | Promise<void>;
144
148
  /**
@@ -148,7 +152,7 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
148
152
  * @param data - données entrantes.
149
153
  * @returns les données préparées à persister.
150
154
  */
151
- protected beforeUpdate(_criteria: Criteria<T>, data: Partial<T>): Partial<T> | Promise<Partial<T>>;
155
+ protected beforeUpdate(_criteria: Criteria<T>, data: PartialInput<T>): PartialInput<T> | Promise<PartialInput<T>>;
152
156
  /** Effet de bord après mise à jour. */
153
157
  protected afterUpdate(_entity: T): void | Promise<void>;
154
158
  /** Garde/validation avant suppression (ex. interdire la suppression du dernier admin). */
@@ -25,7 +25,7 @@ export declare abstract class Entity<S = unknown, M = unknown> implements IEntit
25
25
  /** Modèle compilé natif, renseigné après connexion de l'ORM. */
26
26
  model?: M;
27
27
  /** Relations déclarées vers d'autres entités (par nom logique). */
28
- readonly relations?: ReadonlyArray<IEntityRelation>;
28
+ readonly relations?: ReadonlyArray<IEntityRelation> | undefined;
29
29
  /**
30
30
  * Construit la définition de schéma propre au driver.
31
31
  *
@@ -21,7 +21,7 @@ export interface EntityOptions<S = unknown> {
21
21
  */
22
22
  domain?: string;
23
23
  /** Schéma natif du driver (schéma Mongoose, schéma Drizzle...). */
24
- schema?: S;
24
+ schema?: S | undefined;
25
25
  /** Relations déclaratives vers d'autres entités. */
26
26
  relations?: ReadonlyArray<IEntityRelation>;
27
27
  }
@@ -17,13 +17,13 @@ export interface EntityMetadata<S = unknown> {
17
17
  /** Connexion nommée cible (clé du `ormRegistry`). */
18
18
  readonly connector: string;
19
19
  /** Module Nodefony propriétaire (regroupement graphe/ERD), si fourni. */
20
- readonly module?: string;
20
+ readonly module?: string | undefined;
21
21
  /** Classification (domaine fonctionnel), axe de regroupement distinct du module. */
22
- readonly domain?: string;
22
+ readonly domain?: string | undefined;
23
23
  /** Schéma natif du driver (forme libre), si fourni au décorateur. */
24
- readonly schema?: S;
24
+ readonly schema?: S | undefined;
25
25
  /** Relations déclaratives vers d'autres entités. */
26
- readonly relations?: ReadonlyArray<IEntityRelation>;
26
+ readonly relations?: ReadonlyArray<IEntityRelation> | undefined;
27
27
  /** Constructeur décoré (pour introspection / résolution lazy par le driver). */
28
28
  readonly target: DecoratedClass;
29
29
  }
@@ -36,7 +36,7 @@ export interface RepositoryMetadata {
36
36
  /** Nom logique de l'entité gérée par ce repository. */
37
37
  readonly entity: string;
38
38
  /** Connecteur cible (lève l'ambiguïté si l'entité existe sur plusieurs connexions). */
39
- readonly connector?: string;
39
+ readonly connector?: string | undefined;
40
40
  /** Constructeur décoré. */
41
41
  readonly target: DecoratedClass;
42
42
  }
@@ -0,0 +1,16 @@
1
+ import type { PartialInput } from "nodefony";
2
+ /**
3
+ * Retire les clés qui valent `undefined` d'une donnée d'écriture partielle.
4
+ *
5
+ * `undefined` et clé absente veulent dire « ne pas toucher » : Drizzle ignore
6
+ * déjà une valeur `undefined`, Mongoose (≥ 6) la retire d'une mise à jour. La
7
+ * règle est posée ICI, une fois, au seuil de la persistance, au lieu d'être
8
+ * un comportement propre à chaque adaptateur.
9
+ *
10
+ * Rend l'objet reçu TEL QUEL quand aucune clé ne vaut `undefined` — le cas
11
+ * courant d'un corps JSON, qui ne peut pas en porter : aucune allocation.
12
+ *
13
+ * @param data - données d'écriture, clés `undefined` admises
14
+ * @returns les mêmes données, sans aucune clé `undefined`
15
+ */
16
+ export declare function omitUndefined<T>(data: PartialInput<T>): Partial<T>;
package/docs/index.md CHANGED
@@ -177,7 +177,7 @@ garantit que la dépendance va bien du concret vers l'abstrait, et jamais l'inve
177
177
  > [!NOTE]
178
178
  > `orm-core` n'est **pas un module bootable** : pas de classe `Module`, rien à mettre dans
179
179
  > `modules: [...]`. C'est une bibliothèque pure. Les modules, ce sont les **drivers** — ce sont eux
180
- > qui s'enregistrent dans `ormRegistry` (`OrmRegistry.ts:88`) à leur démarrage.
180
+ > qui s'enregistrent dans `ormRegistry` (`OrmRegistry.ts:86`) à leur démarrage.
181
181
 
182
182
  ## La vision Nodefony
183
183
 
@@ -377,13 +377,13 @@ sequenceDiagram
377
377
  Note over K: onServersReady → récap de boot
378
378
  ```
379
379
 
380
- **`entityRegistry`** (`EntityRegistry.ts:147`) indexe les entités à **deux** niveaux — nom puis
380
+ **`entityRegistry`** (`EntityRegistry.ts:145`) indexe les entités à **deux** niveaux — nom puis
381
381
  connecteur — parce qu'une même entité logique (`User`) peut vivre sur plusieurs connexions. Demander
382
382
  `get("User")` sans préciser le connecteur alors que deux le portent **lève** plutôt que de deviner
383
383
  (`EntityRegistry.get()`, `EntityRegistry.ts:54`). Le stockage est un `Object.create(null)` alloué
384
384
  **au premier enregistrement** : une application sans base ne paie rien.
385
385
 
386
- **`ormRegistry`** (`OrmRegistry.ts:88`) associe un nom de connexion à son instance `IOrm`. Un doublon
386
+ **`ormRegistry`** (`OrmRegistry.ts:86`) associe un nom de connexion à son instance `IOrm`. Un doublon
387
387
  de nom **lève** (`OrmRegistry.register()`, `OrmRegistry.ts:26`) : deux connexions homonymes seraient
388
388
  un bug silencieux, jamais une intention.
389
389
 
@@ -394,7 +394,7 @@ l'ordre des écouteurs, la table existe ou non. `entities()` s'accroche à `onRe
394
394
  comportement avec `@controllers`, qui lui reste à `onBoot`.
395
395
 
396
396
  **`Orm.connect()`** (`Orm.ts:118`) est une **template method** : elle mesure la latence, alimente le
397
- moniteur de connexion, puis émet `onOrmReady`. Un adapter surcharge `onConnect()` (`Orm.ts:74`), et
397
+ moniteur de connexion, puis émet `onOrmReady`. Un adapter surcharge `onConnect()` (`Orm.ts:363`), et
398
398
  **jamais** `connect()` — sinon l'événement et l'instrumentation disparaissent.
399
399
 
400
400
  ## 🧰 Les contrats — la surface publique
@@ -420,20 +420,22 @@ lieu d'échouer : `describeEntity()` (`IOrm.ts:78`, colonnes pour l'ERD), `descr
420
420
  `IRepository<T>` (`IRepository.ts:197`) est la seule surface que ton métier devrait connaître. Les
421
421
  verbes se choisissent sur **la garantie** qu'ils apportent, pas sur leur nom.
422
422
 
423
- | Verbe | Ce qu'il garantit | Ancre |
424
- | --------------------- | -------------------------------------------------------------------- | ---------------------------- |
425
- | `find` / `findOne` | lecture filtrée + eager-load + tri + bornes | `IRepository.ts:230` |
426
- | `count` / `exists` | compter, ou juste savoir s'il y en a un (sans charger de colonne) | `IRepository.ts:395`, `:335` |
427
- | `create` | insertion d'une ligne, rend la version persistée (id, défauts) | `IRepository.ts:240` |
428
- | `createMany` | N lignes en **une** requête — seed, import, ingestion par lots | `IRepository.ts:252` |
429
- | `updateOne` | met à jour **au plus une** ligne, **atomiquement**, et la rend | `IRepository.ts:269` |
430
- | `updateMany` | met à jour toutes les lignes du critère, rend le **nombre** | `IRepository.ts:312` |
431
- | `upsert` | insère **ou** met à jour sur conflit de clé, en **une** instruction | `IRepository.ts:296` |
432
- | `increment` | `SET f = f + ?` atomique — compteurs, quotas, rate-limit | `IRepository.ts:287` |
433
- | `delete` | supprime tout ce qui matche, rend le nombre | `IRepository.ts:336` |
434
- | `deleteOne` | supprime **au plus une** ligne, rend un booléen | `IRepository.ts:345` |
435
- | `findOneAndDelete` | supprime **et rend** la ligne — file de jobs, outbox, `pop` atomique | `IRepository.ts:355` |
436
- | `withTransaction(tx)` | une **vue** du repository liée à une transaction | `IRepository.ts:406` |
423
+ | Verbe | Ce qu'il garantit | Ancre |
424
+ | --------------------- | ------------------------------------------------------------------------- | -------------------- |
425
+ | `find` / `findOne` | lecture filtrée + eager-load + tri + bornes | `IRepository.ts:230` |
426
+ | `count` | compter les entités qui correspondent au critère | `IRepository.ts:362` |
427
+ | `exists` | savoir s'il y en a au moins un, sans charger de colonne | `IRepository.ts:395` |
428
+ | `countDistinct` | compter les valeurs DISTINCTES et non nulles d'un champ, sans les charger | `IRepository.ts:381` |
429
+ | `create` | insertion d'une ligne, rend la version persistée (id, défauts) | `IRepository.ts:240` |
430
+ | `createMany` | N lignes en **une** requête — seed, import, ingestion par lots | `IRepository.ts:252` |
431
+ | `updateOne` | met à jour **au plus une** ligne, **atomiquement**, et la rend | `IRepository.ts:269` |
432
+ | `updateMany` | met à jour toutes les lignes du critère, rend le **nombre** | `IRepository.ts:312` |
433
+ | `upsert` | insère **ou** met à jour sur conflit de clé, en **une** instruction | `IRepository.ts:296` |
434
+ | `increment` | `SET f = f + ?` atomique — compteurs, quotas, rate-limit | `IRepository.ts:287` |
435
+ | `delete` | supprime tout ce qui matche, rend le nombre | `IRepository.ts:336` |
436
+ | `deleteOne` | supprime **au plus une** ligne, rend un booléen | `IRepository.ts:345` |
437
+ | `findOneAndDelete` | supprime **et rend** la ligne — file de jobs, outbox, `pop` atomique | `IRepository.ts:355` |
438
+ | `withTransaction(tx)` | une **vue** du repository liée à une transaction | `IRepository.ts:406` |
437
439
 
438
440
  > [!IMPORTANT]
439
441
  > `updateOne` est atomique **par construction** : une seule requête (`UPDATE … RETURNING` en SQL,
@@ -608,8 +610,8 @@ d'administration : elle ne charge qu'une page, quelle que soit la taille de la t
608
610
 
609
611
  Deux drivers implémentent les contrats. Le contrat `IRepository` est tenu **en entier** par les
610
612
  deux : les quinze verbes existent des deux côtés — par exemple l'upsert, avec
611
- `DrizzleRepository.upsert()` (`DrizzleRepository.ts:879`) et `MongooseRepository.upsert()`
612
- (`MongooseRepository.ts:405`).
613
+ `DrizzleRepository.upsert()` (`DrizzleRepository.ts:866`) et `MongooseRepository.upsert()`
614
+ (`MongooseRepository.ts:422`).
613
615
 
614
616
  | Capacité | `@nodefony/drizzle` | `@nodefony/mongoose` |
615
617
  | -------------------------------------- | ----------------------------------- | -------------------------------------------- |
@@ -618,12 +620,12 @@ deux : les quinze verbes existent des deux côtés — par exemple l'upsert, ave
618
620
  | Eager-load `{ relations }` | oui | oui (`populate`) |
619
621
  | Transactions | oui | oui (replica set requis par Mongo) |
620
622
  | Savepoints (rollback partiel) | oui | non — refusés (`SavepointNotSupportedError`) |
621
- | Colonnes pour l'ERD (`describeEntity`) | oui (`DrizzleOrm.ts:1933`) | oui (`MongooseOrm.ts:649`) |
623
+ | Colonnes pour l'ERD (`describeEntity`) | oui (`DrizzleOrm.ts:1971`) | oui (`MongooseOrm.ts:649`) |
622
624
  | Sonde de flux (requêtes/s, lentes) | oui — alimente `queryFlowMonitor` | non câblée |
623
- | Sonde profonde (`probe`) | oui (`DrizzleOrm.ts:1835`) | oui (`MongooseOrm.ts:617`) |
625
+ | Sonde profonde (`probe`) | oui (`DrizzleOrm.ts:1873`) | oui (`MongooseOrm.ts:617`) |
624
626
 
625
- **Les « stores » du framework, eux, ne sont pas alignés — et c'est un choix.** Un adapter déclare ce
626
- qu'il porte dans son `package.json`, clé `nodefony.stores` :
627
+ **Les « stores » du framework** : chaque adapter déclare ce qu'il porte dans son `package.json`, clé
628
+ `nodefony.stores`. Les deux backends durables portent les huit :
627
629
 
628
630
  | Store | drizzle | mongoose |
629
631
  | ------------- | ------- | -------- |
@@ -632,16 +634,15 @@ qu'il porte dans son `package.json`, clé `nodefony.stores` :
632
634
  | `tokens` | ✅ | ✅ |
633
635
  | `passkeys` | ✅ | ✅ |
634
636
  | `webhooks` | ✅ | ✅ |
635
- | `totp` | ✅ | — |
636
- | `audit` | ✅ | — |
637
- | `idempotency` | ✅ | — |
637
+ | `totp` | ✅ | ✅ |
638
+ | `audit` | ✅ | ✅ |
639
+ | `idempotency` | ✅ | ✅ |
638
640
 
639
641
  > [!NOTE]
640
- > La couverture est **adaptée à la nature de chaque backend**, ce n'est pas une parité SQL × NoSQL à
641
- > atteindre. Un store d'idempotence veut une contrainte d'unicité et un `ON CONFLICT` — le terrain du
642
- > SQL. Lire ce tableau comme « mongoose est incomplet » serait un contresens : il porte exactement ce
643
- > qu'un déploiement Mongo attend de lui. La liste fait autorité côté code
644
- > (`@nodefony/drizzle/package.json`, clé `nodefony.stores`) — elle n'est pas un commentaire.
642
+ > La couverture est **adaptée à la nature de chaque backend** : c'est `@nodefony/redis`, un cache,
643
+ > qui s'arrête à quatre stores (`session`, `tokens`, `passkeys`, `idempotency`) — il ne garde ni
644
+ > comptes ni journal. La liste fait autorité côté code (`nodefony.stores` du `package.json` de chaque
645
+ > adapter) — elle n'est pas un commentaire.
645
646
 
646
647
  ## 🧩 Extension — brancher son propre driver
647
648
 
@@ -678,7 +679,7 @@ première connexion, reconnexions, erreurs récentes, et une fenêtre de latence
678
679
  (`ConnectionMonitor.recordPing()`, `ConnectionMonitor.ts:136`). Il est alimenté par `Orm.connect()`
679
680
  sans que l'adapter ait à y penser.
680
681
 
681
- **`queryFlowMonitor`** (`QueryFlowMonitor.ts:146`) suit le **débit** : total de requêtes, latence
682
+ **`queryFlowMonitor`** (`QueryFlowMonitor.ts:144`) suit le **débit** : total de requêtes, latence
682
683
  moyenne et EWMA, pire latence, et un anneau borné à vingt requêtes lentes. Trois propriétés le
683
684
  rendent sûr en production :
684
685
 
@@ -740,7 +741,7 @@ deux, et pas de course).
740
741
  | `updateOne` rend `null` alors que la ligne a bien changé | ancien réflexe `UPDATE` + relecture (le critère porte sur le champ modifié) | utiliser `updateOne`, atomique par construction (`IRepository.ts:269`) |
741
742
  | Un `upsert` écrase une valeur qui devait progresser | le `DO UPDATE` est inconditionnel (contrainte MySQL) | poser la condition **dans** la valeur : `{ seuil: { $max: v } }` (`IRepository.ts:94`) |
742
743
  | Un objet de critère est pris pour une égalité (colonne JSON) | comportement **voulu** : une valeur n'est un filtre que si **toutes** ses clés sont des opérateurs | c'est la protection ; pour filtrer dedans, passer au natif (`criteria.ts:42`) |
743
- | `onOrmReady` ne part plus après un ajout dans l'adapter | `connect()` a été surchargé | surcharger `onConnect()` (`Orm.ts:74`), jamais `connect()` |
744
+ | `onOrmReady` ne part plus après un ajout dans l'adapter | `connect()` a été surchargé | surcharger `onConnect()` (`Orm.ts:363`), jamais `connect()` |
744
745
  | Une entité déclarée dans un module reste invisible | le module embarque sa **propre copie** du registre (singleton dédoublé) | externaliser `@nodefony/orm-core` dans le `rolldown.config.ts` du module |
745
746
  | Rien dans `flow` alors que la base travaille | la sonde est éteinte hors développement, ou le driver n'a pas de tap | `NF_ORM_FLOW=1` (`ormWiring.ts:96`) ; le tap n'est câblé que côté Drizzle |
746
747
 
@@ -347,20 +347,22 @@ que l'import plus la ligne du décorateur.
347
347
  Le repository porte **quinze verbes**, pas cinq. Ils se choisissent sur la **garantie** qu'ils
348
348
  apportent, jamais sur leur nom.
349
349
 
350
- | Verbe | Ce qu'il garantit | Ancre |
351
- | --------------------- | ------------------------------------------------------------------ | ---------------------------- |
352
- | `find` / `findOne` | lecture filtrée, avec tri, bornes et eager-load | `IRepository.ts:230`, `:192` |
353
- | `create` | insère une ligne et rend sa version persistée (id, défauts) | `IRepository.ts:240` |
354
- | `createMany` | N lignes en **une** requête — seed, import, ingestion par lots | `IRepository.ts:252` |
355
- | `updateOne` | modifie **au plus une** ligne, **atomiquement**, et la rend | `IRepository.ts:269` |
356
- | `updateMany` | modifie toutes les lignes du critère, rend le **nombre** | `IRepository.ts:312` |
357
- | `upsert` | insère **ou** met à jour sur conflit de clé, en une instruction | `IRepository.ts:296` |
358
- | `increment` | `SET f = f + ?` atomique — compteurs, quotas, limitation de débit | `IRepository.ts:287` |
359
- | `delete` | supprime tout ce qui matche, rend le nombre | `IRepository.ts:336` |
360
- | `deleteOne` | supprime **au plus une** ligne, rend un booléen | `IRepository.ts:345` |
361
- | `findOneAndDelete` | supprime **et rend** la ligne — file de jobs, `pop` atomique | `IRepository.ts:355` |
362
- | `count` / `exists` | compter, ou juste savoir s'il y en a une (sans charger de colonne) | `IRepository.ts:395`, `:335` |
363
- | `withTransaction(tx)` | une **vue** du repository liée à une transaction | `IRepository.ts:406` |
350
+ | Verbe | Ce qu'il garantit | Ancre |
351
+ | --------------------- | ------------------------------------------------------------------------- | ---------------------------- |
352
+ | `find` / `findOne` | lecture filtrée, avec tri, bornes et eager-load | `IRepository.ts:230`, `:192` |
353
+ | `create` | insère une ligne et rend sa version persistée (id, défauts) | `IRepository.ts:240` |
354
+ | `createMany` | N lignes en **une** requête — seed, import, ingestion par lots | `IRepository.ts:252` |
355
+ | `updateOne` | modifie **au plus une** ligne, **atomiquement**, et la rend | `IRepository.ts:269` |
356
+ | `updateMany` | modifie toutes les lignes du critère, rend le **nombre** | `IRepository.ts:312` |
357
+ | `upsert` | insère **ou** met à jour sur conflit de clé, en une instruction | `IRepository.ts:296` |
358
+ | `increment` | `SET f = f + ?` atomique — compteurs, quotas, limitation de débit | `IRepository.ts:287` |
359
+ | `delete` | supprime tout ce qui matche, rend le nombre | `IRepository.ts:336` |
360
+ | `deleteOne` | supprime **au plus une** ligne, rend un booléen | `IRepository.ts:345` |
361
+ | `findOneAndDelete` | supprime **et rend** la ligne — file de jobs, `pop` atomique | `IRepository.ts:355` |
362
+ | `count` | compter les entités qui correspondent au critère | `IRepository.ts:362` |
363
+ | `exists` | savoir s'il y en a au moins une, sans charger de colonne | `IRepository.ts:395` |
364
+ | `countDistinct` | compter les valeurs DISTINCTES et non nulles d'un champ, sans les charger | `IRepository.ts:381` |
365
+ | `withTransaction(tx)` | une **vue** du repository liée à une transaction | `IRepository.ts:406` |
364
366
 
365
367
  > [!IMPORTANT]
366
368
  > **Il n'existe pas de méthode `update()`.** Le choix est explicite et il est intentionnel :
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodefony/orm-core",
3
- "version": "10.0.0-alpha.9",
3
+ "version": "10.0.0-beta.1",
4
4
  "description": "Un contrat de dépôt de données pour Nodefony, plusieurs moteurs : interfaces, registre et classes de base pour le support multi-ORM",
5
5
  "contributors": [],
6
6
  "type": "module",
@@ -37,12 +37,12 @@
37
37
  "esm"
38
38
  ],
39
39
  "peerDependencies": {
40
- "nodefony": "^10.0.0-alpha.9"
40
+ "nodefony": "^10.0.0-beta.1"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/node": "26.6.2",
44
- "nodefony": "^10.0.0-alpha.9",
45
- "vitest": "5.0.1"
43
+ "@types/node": "26.6.3",
44
+ "nodefony": "^10.0.0-beta.1",
45
+ "vitest": "5.0.2"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",