agentic-api 2.0.31 → 2.0.491
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/agents/agents.example.js +21 -22
- package/dist/src/agents/authentication.js +1 -2
- package/dist/src/agents/prompts.d.ts +5 -4
- package/dist/src/agents/prompts.js +44 -87
- package/dist/src/agents/reducer.core.d.ts +24 -2
- package/dist/src/agents/reducer.core.js +125 -35
- package/dist/src/agents/reducer.loaders.d.ts +55 -1
- package/dist/src/agents/reducer.loaders.js +114 -1
- package/dist/src/agents/reducer.types.d.ts +45 -2
- package/dist/src/agents/semantic.js +1 -2
- package/dist/src/agents/simulator.d.ts +11 -3
- package/dist/src/agents/simulator.executor.d.ts +14 -4
- package/dist/src/agents/simulator.executor.js +81 -23
- package/dist/src/agents/simulator.js +128 -42
- package/dist/src/agents/simulator.prompts.d.ts +9 -7
- package/dist/src/agents/simulator.prompts.js +66 -86
- package/dist/src/agents/simulator.types.d.ts +23 -5
- package/dist/src/agents/simulator.utils.d.ts +7 -2
- package/dist/src/agents/simulator.utils.js +31 -11
- package/dist/src/agents/system.js +1 -2
- package/dist/src/execute/helpers.d.ts +75 -0
- package/dist/src/execute/helpers.js +139 -0
- package/dist/src/execute/index.d.ts +11 -0
- package/dist/src/execute/index.js +44 -0
- package/dist/src/execute/legacy.d.ts +46 -0
- package/dist/src/execute/legacy.js +460 -0
- package/dist/src/execute/modelconfig.d.ts +19 -0
- package/dist/src/execute/modelconfig.js +56 -0
- package/dist/src/execute/responses.d.ts +55 -0
- package/dist/src/execute/responses.js +594 -0
- package/dist/src/execute/shared.d.ts +83 -0
- package/dist/src/execute/shared.js +188 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/{princing.openai.d.ts → pricing.llm.d.ts} +6 -0
- package/dist/src/pricing.llm.js +255 -0
- package/dist/src/prompts.d.ts +13 -4
- package/dist/src/prompts.js +221 -114
- package/dist/src/rag/embeddings.d.ts +36 -18
- package/dist/src/rag/embeddings.js +131 -128
- package/dist/src/rag/index.d.ts +5 -5
- package/dist/src/rag/index.js +14 -17
- package/dist/src/rag/parser.d.ts +2 -1
- package/dist/src/rag/parser.js +11 -14
- package/dist/src/rag/rag.examples.d.ts +27 -0
- package/dist/src/rag/rag.examples.js +151 -0
- package/dist/src/rag/rag.manager.d.ts +383 -0
- package/dist/src/rag/rag.manager.js +1390 -0
- package/dist/src/rag/types.d.ts +128 -12
- package/dist/src/rag/types.js +100 -1
- package/dist/src/rag/usecase.d.ts +37 -0
- package/dist/src/rag/usecase.js +96 -7
- package/dist/src/rules/git/git.e2e.helper.js +22 -2
- package/dist/src/rules/git/git.health.d.ts +61 -2
- package/dist/src/rules/git/git.health.js +333 -11
- package/dist/src/rules/git/index.d.ts +2 -2
- package/dist/src/rules/git/index.js +13 -1
- package/dist/src/rules/git/repo.d.ts +160 -0
- package/dist/src/rules/git/repo.js +777 -0
- package/dist/src/rules/git/repo.pr.js +117 -13
- package/dist/src/rules/git/repo.tools.d.ts +22 -1
- package/dist/src/rules/git/repo.tools.js +50 -1
- package/dist/src/rules/types.d.ts +27 -14
- package/dist/src/rules/utils.matter.d.ts +0 -4
- package/dist/src/rules/utils.matter.js +35 -7
- package/dist/src/scrapper.d.ts +15 -22
- package/dist/src/scrapper.js +58 -110
- package/dist/src/stategraph/index.d.ts +1 -1
- package/dist/src/stategraph/stategraph.d.ts +56 -2
- package/dist/src/stategraph/stategraph.js +134 -6
- package/dist/src/stategraph/stategraph.storage.js +8 -0
- package/dist/src/stategraph/types.d.ts +27 -0
- package/dist/src/types.d.ts +46 -9
- package/dist/src/types.js +8 -7
- package/dist/src/usecase.d.ts +11 -2
- package/dist/src/usecase.js +27 -35
- package/dist/src/utils.d.ts +32 -18
- package/dist/src/utils.js +87 -129
- package/package.json +10 -3
- package/dist/src/agents/digestor.test.d.ts +0 -1
- package/dist/src/agents/digestor.test.js +0 -45
- package/dist/src/agents/reducer.example.d.ts +0 -28
- package/dist/src/agents/reducer.example.js +0 -118
- package/dist/src/agents/reducer.process.d.ts +0 -16
- package/dist/src/agents/reducer.process.js +0 -143
- package/dist/src/agents/reducer.tools.d.ts +0 -29
- package/dist/src/agents/reducer.tools.js +0 -157
- package/dist/src/agents/simpleExample.d.ts +0 -3
- package/dist/src/agents/simpleExample.js +0 -38
- package/dist/src/agents/system-review.d.ts +0 -5
- package/dist/src/agents/system-review.js +0 -181
- package/dist/src/agents/systemReview.d.ts +0 -4
- package/dist/src/agents/systemReview.js +0 -22
- package/dist/src/execute.d.ts +0 -49
- package/dist/src/execute.js +0 -564
- package/dist/src/princing.openai.js +0 -54
- package/dist/src/rag/tools.d.ts +0 -76
- package/dist/src/rag/tools.js +0 -196
- package/dist/src/rules/user.mapper.d.ts +0 -61
- package/dist/src/rules/user.mapper.js +0 -160
- package/dist/src/rules/utils/slug.d.ts +0 -22
- package/dist/src/rules/utils/slug.js +0 -35
|
@@ -33,6 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.gitIDRegistryExists = gitIDRegistryExists;
|
|
37
|
+
exports.gitGenerateNextID = gitGenerateNextID;
|
|
38
|
+
exports.gitReloadIDRegistry = gitReloadIDRegistry;
|
|
39
|
+
exports.gitRegisterExistingID = gitRegisterExistingID;
|
|
40
|
+
exports.gitIDRegistryRename = gitIDRegistryRename;
|
|
41
|
+
exports.gitEnsureMatterID = gitEnsureMatterID;
|
|
42
|
+
exports.gitFileStrictMatter = gitFileStrictMatter;
|
|
36
43
|
exports.gitEnsureRepositoryConfiguration = gitEnsureRepositoryConfiguration;
|
|
37
44
|
exports.gitEnsureRemoteConfiguration = gitEnsureRemoteConfiguration;
|
|
38
45
|
exports.gitInit = gitInit;
|
|
@@ -46,11 +53,628 @@ exports.gitRenameFile_fs = gitRenameFile_fs;
|
|
|
46
53
|
exports.gitRenameFile = gitRenameFile;
|
|
47
54
|
exports.gitDeleteFile = gitDeleteFile;
|
|
48
55
|
exports.gitGetBranchHealth = gitGetBranchHealth;
|
|
56
|
+
exports.gitGetValidationBranchHealth = gitGetValidationBranchHealth;
|
|
49
57
|
const fs_1 = require("fs");
|
|
50
58
|
const errors_1 = require("../errors");
|
|
51
59
|
const path_1 = require("path");
|
|
52
60
|
const fs = __importStar(require("fs/promises"));
|
|
53
61
|
const repo_tools_1 = require("./repo.tools");
|
|
62
|
+
/**
|
|
63
|
+
* Service singleton pour gérer le registre d'IDs en mémoire
|
|
64
|
+
*/
|
|
65
|
+
class IDRegistryService {
|
|
66
|
+
constructor() {
|
|
67
|
+
this.registry = null;
|
|
68
|
+
this.repoPath = '';
|
|
69
|
+
this.isDirty = false;
|
|
70
|
+
}
|
|
71
|
+
static get() {
|
|
72
|
+
return IDRegistryService.instance || (IDRegistryService.instance = new IDRegistryService());
|
|
73
|
+
}
|
|
74
|
+
toString() {
|
|
75
|
+
return JSON.stringify(this.registry, null, 2);
|
|
76
|
+
}
|
|
77
|
+
clear() {
|
|
78
|
+
this.registry = null;
|
|
79
|
+
this.repoPath = '';
|
|
80
|
+
this.isDirty = false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Initialise le service avec le chemin du repository
|
|
84
|
+
*/
|
|
85
|
+
init(repoPath) {
|
|
86
|
+
// ✅ BUGFIX: Toujours mettre à jour repoPath même si le registre existe déjà
|
|
87
|
+
// Car gitReloadIDRegistry peut être appelé avec un nouveau repoPath pour un nouveau test
|
|
88
|
+
// et le repo précédent peut avoir été nettoyé
|
|
89
|
+
if (this.repoPath !== repoPath) {
|
|
90
|
+
this.repoPath = repoPath;
|
|
91
|
+
// Si le registre existe déjà, le recharger depuis le nouveau chemin
|
|
92
|
+
if (this.registry) {
|
|
93
|
+
this.registry = this.loadFromDisk();
|
|
94
|
+
this.isDirty = false;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Si le registre n'existe pas encore, le créer
|
|
99
|
+
if (!this.registry) {
|
|
100
|
+
this.registry = this.loadFromDisk();
|
|
101
|
+
this.isDirty = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Force le rechargement du registre depuis le disque
|
|
106
|
+
* Utilisé principalement pour les tests qui simulent un redémarrage
|
|
107
|
+
*/
|
|
108
|
+
forceReload() {
|
|
109
|
+
if (!this.repoPath) {
|
|
110
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
111
|
+
}
|
|
112
|
+
this.registry = this.loadFromDisk();
|
|
113
|
+
this.isDirty = false;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Charge le registre depuis le disque
|
|
117
|
+
*/
|
|
118
|
+
loadFromDisk() {
|
|
119
|
+
const registryPath = (0, path_1.join)(this.repoPath, '.git', 'with-ids.json');
|
|
120
|
+
if (!(0, fs_1.existsSync)(registryPath)) {
|
|
121
|
+
return {
|
|
122
|
+
last: 980,
|
|
123
|
+
used: [],
|
|
124
|
+
updated: new Date().toISOString(),
|
|
125
|
+
matters: {}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const data = JSON.parse((0, fs_1.readFileSync)(registryPath, 'utf8'));
|
|
130
|
+
// Migration : ajouter matters si absent (compatibilité anciens registres)
|
|
131
|
+
if (!data.matters) {
|
|
132
|
+
data.matters = {};
|
|
133
|
+
}
|
|
134
|
+
return data;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.warn('⚠️ Registre d\'IDs corrompu, création d\'un nouveau');
|
|
138
|
+
return {
|
|
139
|
+
last: 980,
|
|
140
|
+
used: [],
|
|
141
|
+
updated: new Date().toISOString(),
|
|
142
|
+
matters: {}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Sauvegarde le registre sur le disque (seulement si modifié)
|
|
148
|
+
*/
|
|
149
|
+
save() {
|
|
150
|
+
if (!this.registry || !this.isDirty) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const gitDir = (0, path_1.join)(this.repoPath, '.git');
|
|
154
|
+
const registryPath = (0, path_1.join)(gitDir, 'with-ids.json');
|
|
155
|
+
// S'assurer que le répertoire .git existe
|
|
156
|
+
if (!(0, fs_1.existsSync)(gitDir)) {
|
|
157
|
+
console.warn(`⚠️ Cannot save ID registry: .git directory not found at ${this.repoPath}`);
|
|
158
|
+
return; // Ne pas sauvegarder si le repo n'est pas initialisé
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
this.registry.updated = new Date().toISOString();
|
|
162
|
+
(0, fs_1.writeFileSync)(registryPath, JSON.stringify(this.registry, null, 2), 'utf8');
|
|
163
|
+
this.isDirty = false;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.warn(`⚠️ Failed to save ID registry:`, error);
|
|
167
|
+
// Ne pas propager l'erreur, le registre est une optimisation
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Récupère le registre (lecture seule)
|
|
172
|
+
*/
|
|
173
|
+
getRegistry() {
|
|
174
|
+
if (!this.registry) {
|
|
175
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
176
|
+
}
|
|
177
|
+
return this.registry;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Génère un nouvel ID unique
|
|
181
|
+
*/
|
|
182
|
+
generateNextID() {
|
|
183
|
+
if (!this.registry) {
|
|
184
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
185
|
+
}
|
|
186
|
+
const newID = this.registry.last + 20;
|
|
187
|
+
this.registry.last = newID;
|
|
188
|
+
this.registry.used.push(newID);
|
|
189
|
+
this.isDirty = true;
|
|
190
|
+
return newID;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Obtient l'ID d'un fichier (peu importe la branche)
|
|
194
|
+
*
|
|
195
|
+
* Un fichier a UN SEUL ID quelque soit la branche où il se trouve.
|
|
196
|
+
* Cette fonction cherche le fichier dans toutes les branches du cache.
|
|
197
|
+
*
|
|
198
|
+
* @param file Nom du fichier (ex: 'procedure.md')
|
|
199
|
+
* @returns L'ID du fichier ou undefined si le fichier n'a pas d'ID
|
|
200
|
+
*/
|
|
201
|
+
getFileID(file) {
|
|
202
|
+
if (!this.registry) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
// Chercher le fichier dans toutes les branches
|
|
206
|
+
for (const [cacheKey, matter] of Object.entries(this.registry.matters)) {
|
|
207
|
+
const [, cachedFile] = cacheKey.split(':');
|
|
208
|
+
if (cachedFile === file) {
|
|
209
|
+
return matter.id;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Vérifie si un ID appartient déjà à un fichier spécifique
|
|
216
|
+
*
|
|
217
|
+
* Un fichier garde son ID peu importe la branche, donc cette fonction
|
|
218
|
+
* cherche dans toutes les branches pour vérifier la propriété.
|
|
219
|
+
*
|
|
220
|
+
* @param id L'ID à vérifier
|
|
221
|
+
* @param file Nom du fichier
|
|
222
|
+
* @returns true si ce fichier possède cet ID
|
|
223
|
+
*/
|
|
224
|
+
isIDOwnedByFile(id, file) {
|
|
225
|
+
if (!this.registry) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
// Chercher le fichier dans toutes les branches
|
|
229
|
+
const fileID = this.getFileID(file);
|
|
230
|
+
return fileID === id;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Trouve quel fichier possède un ID donné
|
|
234
|
+
*
|
|
235
|
+
* @param id L'ID à chercher
|
|
236
|
+
* @returns Le nom du fichier qui possède cet ID, ou undefined
|
|
237
|
+
*/
|
|
238
|
+
getFileByID(id) {
|
|
239
|
+
if (!this.registry) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
// Chercher l'ID dans le cache
|
|
243
|
+
for (const [cacheKey, matter] of Object.entries(this.registry.matters)) {
|
|
244
|
+
if (matter.id === id) {
|
|
245
|
+
const [, file] = cacheKey.split(':');
|
|
246
|
+
return file;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Enregistre un ID existant
|
|
253
|
+
*
|
|
254
|
+
* @param id L'ID à enregistrer
|
|
255
|
+
* @param branch Branche du fichier (optionnel, pour vérification de propriété)
|
|
256
|
+
* @param file Nom du fichier (optionnel, pour vérification de propriété)
|
|
257
|
+
* @throws Error si l'ID est déjà utilisé par un autre fichier
|
|
258
|
+
*/
|
|
259
|
+
registerExistingID(id, branch, file) {
|
|
260
|
+
if (!this.registry) {
|
|
261
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
262
|
+
}
|
|
263
|
+
// Si l'ID est déjà utilisé
|
|
264
|
+
if (this.registry.used.includes(id)) {
|
|
265
|
+
// Vérifier si c'est le même fichier (pas un doublon)
|
|
266
|
+
// Note: isIDOwnedByFile() cherche maintenant dans TOUTES les branches
|
|
267
|
+
if (file && this.isIDOwnedByFile(id, file)) {
|
|
268
|
+
// Même fichier → OK, pas d'erreur
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// ID utilisé par un autre fichier → Erreur
|
|
272
|
+
const error = new Error(`ID ${id} est déjà utilisé`);
|
|
273
|
+
error.code = 'id_already_used';
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
// Nouvel ID → Enregistrer
|
|
277
|
+
this.registry.used.push(id);
|
|
278
|
+
if (id > this.registry.last) {
|
|
279
|
+
this.registry.last = id;
|
|
280
|
+
}
|
|
281
|
+
// ⚠️ IMPORTANT: Ne PAS créer d'entrée dans matters ici
|
|
282
|
+
// setMatterCache() DOIT être appelé AVANT registerExistingID() par l'appelant
|
|
283
|
+
// car registerExistingID() n'a pas le contexte complet (title, service)
|
|
284
|
+
// Si branch/file sont fournis mais que le cache n'existe pas, c'est une erreur de programmation
|
|
285
|
+
this.isDirty = true;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Récupère un matter depuis le cache
|
|
289
|
+
* Note: Le champ 'updated' est filtré pour ne pas être exposé à l'extérieur
|
|
290
|
+
*/
|
|
291
|
+
getMatterCache(branch, file) {
|
|
292
|
+
if (!this.registry) {
|
|
293
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
294
|
+
}
|
|
295
|
+
const cacheKey = `${branch}:${file}`;
|
|
296
|
+
const cached = this.registry.matters[cacheKey];
|
|
297
|
+
if (!cached) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
// Retourner uniquement les champs publics (sans 'updated')
|
|
301
|
+
return {
|
|
302
|
+
id: cached.id,
|
|
303
|
+
title: cached.title,
|
|
304
|
+
service: cached.service,
|
|
305
|
+
oldfile: cached.oldfile // FIXME (check if needed) ✅ Inclure oldfile temporaire si présent
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Met à jour le cache d'un matter
|
|
310
|
+
*/
|
|
311
|
+
setMatterCache(branch, file, matter) {
|
|
312
|
+
if (!this.registry) {
|
|
313
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
314
|
+
}
|
|
315
|
+
const cacheKey = `${branch}:${file}`;
|
|
316
|
+
this.registry.matters[cacheKey] = {
|
|
317
|
+
id: matter.id,
|
|
318
|
+
title: matter.title,
|
|
319
|
+
service: matter.service,
|
|
320
|
+
oldfile: matter.oldfile, // ✅ Inclure oldfile temporaire
|
|
321
|
+
updated: new Date().toISOString()
|
|
322
|
+
};
|
|
323
|
+
this.isDirty = true;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Supprime une entrée du cache
|
|
327
|
+
*/
|
|
328
|
+
deleteMatterCache(branch, file) {
|
|
329
|
+
if (!this.registry) {
|
|
330
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
331
|
+
}
|
|
332
|
+
const cacheKey = `${branch}:${file}`;
|
|
333
|
+
delete this.registry.matters[cacheKey];
|
|
334
|
+
this.isDirty = true;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Renomme une entrée dans le cache du registre
|
|
338
|
+
*
|
|
339
|
+
* Cette méthode gère le renommage d'un fichier dans le cache:
|
|
340
|
+
* - Supprime l'ancienne clé `${branch}:${oldFile}`
|
|
341
|
+
* - Crée une nouvelle clé `${branch}:${newFile}` avec les mêmes données
|
|
342
|
+
* - Préserve l'ID et les métadonnées
|
|
343
|
+
*
|
|
344
|
+
* @param oldFile Ancien nom du fichier
|
|
345
|
+
* @param newFile Nouveau nom du fichier
|
|
346
|
+
* @param branch Branche concernée
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```typescript
|
|
350
|
+
* // Après un rename Git
|
|
351
|
+
* idRegistryService.rename('old-name.md', 'new-name.md', 'rule-validation-1');
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
rename(oldFile, newFile, branch) {
|
|
355
|
+
if (!this.registry) {
|
|
356
|
+
throw new Error('IDRegistryService not initialized. Call init() first.');
|
|
357
|
+
}
|
|
358
|
+
const oldKey = `${branch}:${oldFile}`;
|
|
359
|
+
const newKey = `${branch}:${newFile}`;
|
|
360
|
+
// Récupérer le cache existant
|
|
361
|
+
const cached = this.registry.matters[oldKey];
|
|
362
|
+
if (!cached) {
|
|
363
|
+
// Pas de cache pour l'ancien fichier, rien à renommer
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// Supprimer l'ancienne clé
|
|
367
|
+
delete this.registry.matters[oldKey];
|
|
368
|
+
//
|
|
369
|
+
// Créer la nouvelle clé avec les mêmes données
|
|
370
|
+
// ⚠️ NE PAS inclure oldfile - c'est temporaire uniquement pour notification client
|
|
371
|
+
this.registry.matters[newKey] = {
|
|
372
|
+
id: cached.id,
|
|
373
|
+
title: cached.title,
|
|
374
|
+
service: cached.service,
|
|
375
|
+
updated: new Date().toISOString()
|
|
376
|
+
};
|
|
377
|
+
this.isDirty = true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
IDRegistryService.instance = null;
|
|
381
|
+
// ✅ Instance singleton du service
|
|
382
|
+
const idRegistryService = IDRegistryService.get();
|
|
383
|
+
/**
|
|
384
|
+
* Vérifie si le fichier de registre d'IDs existe dans le repository Git
|
|
385
|
+
*
|
|
386
|
+
* Cette fonction est utile pour détecter si le système d'ID natif a été initialisé
|
|
387
|
+
* dans le repository. Le fichier `with-ids.json` est créé automatiquement lors du
|
|
388
|
+
* premier appel à `gitGenerateNextID()` ou `gitEnsureMatterID()`.
|
|
389
|
+
*
|
|
390
|
+
* @param config Configuration Git optionnelle (utilise la config par défaut si non fournie)
|
|
391
|
+
* @returns `true` si le fichier `.git/with-ids.json` existe, `false` sinon
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* ```typescript
|
|
395
|
+
* const config = gitLoad({ repoPath: './my-repo' });
|
|
396
|
+
*
|
|
397
|
+
* if (gitIDRegistryExists(config)) {
|
|
398
|
+
* console.log('Le registre d\'IDs existe déjà');
|
|
399
|
+
* // On peut charger le registre existant
|
|
400
|
+
* gitReloadIDRegistry(config);
|
|
401
|
+
* } else {
|
|
402
|
+
* console.log('Première utilisation du système d\'IDs');
|
|
403
|
+
* // Le registre sera créé au premier gitGenerateNextID()
|
|
404
|
+
* }
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
function gitIDRegistryExists(config) {
|
|
408
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
409
|
+
const registryPath = (0, path_1.join)(gitConf.repoPath, '.git', 'with-ids.json');
|
|
410
|
+
return (0, fs_1.existsSync)(registryPath);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Génère un nouvel ID unique en incrémentant de +20
|
|
414
|
+
* @param config Configuration Git optionnelle
|
|
415
|
+
* @returns Un nouvel ID unique
|
|
416
|
+
*/
|
|
417
|
+
function gitGenerateNextID(config) {
|
|
418
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
419
|
+
idRegistryService.init(gitConf.repoPath);
|
|
420
|
+
const newID = idRegistryService.generateNextID();
|
|
421
|
+
// FIXME: Double save - gitGenerateNextID() et gitRegisterExistingID() appellent tous deux save()
|
|
422
|
+
// Refactorisation future: Retirer save() d'ici et le faire uniquement dans gitRegisterExistingID()
|
|
423
|
+
// quand matter est fourni (processus atomique: generate → setCache → register → save)
|
|
424
|
+
idRegistryService.save();
|
|
425
|
+
return newID;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Force le rechargement du registre d'IDs depuis le disque
|
|
429
|
+
*
|
|
430
|
+
* **Utilisé principalement pour les tests qui simulent un redémarrage du serveur.**
|
|
431
|
+
*
|
|
432
|
+
* En production, cette fonction n'est pas nécessaire car le singleton est
|
|
433
|
+
* réinitialisé à chaque redémarrage du process Node.js.
|
|
434
|
+
*
|
|
435
|
+
* @param config Configuration Git optionnelle
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```typescript
|
|
439
|
+
* // Dans un test : simuler un redémarrage
|
|
440
|
+
* await gitCreateOrEditFile(...); // Modifie le registre
|
|
441
|
+
* gitReloadIDRegistry(); // Force le rechargement depuis le disque
|
|
442
|
+
* ```
|
|
443
|
+
*/
|
|
444
|
+
function gitReloadIDRegistry(config) {
|
|
445
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
446
|
+
if (config?.reset) {
|
|
447
|
+
idRegistryService.clear();
|
|
448
|
+
}
|
|
449
|
+
idRegistryService.init(gitConf.repoPath);
|
|
450
|
+
idRegistryService.forceReload();
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Enregistre un ID existant dans le registre
|
|
454
|
+
*
|
|
455
|
+
* @param matter Le matter contenant l'ID à enregistrer
|
|
456
|
+
* @param branch Branche du fichier (optionnel, pour vérification de propriété)
|
|
457
|
+
* @param file Nom du fichier (optionnel, pour vérification de propriété)
|
|
458
|
+
* @param config Configuration Git optionnelle
|
|
459
|
+
* @throws GitOperationError si l'ID est déjà utilisé par un autre fichier
|
|
460
|
+
*
|
|
461
|
+
* **Note:** Si `branch` et `file` sont fournis, la fonction vérifie si l'ID appartient
|
|
462
|
+
* déjà au même fichier. Si oui, aucune erreur n'est levée (cas de re-scan).
|
|
463
|
+
*
|
|
464
|
+
* **Note:** `setMatterCache()` est appelé automatiquement avant l'enregistrement
|
|
465
|
+
* pour garantir la cohérence du cache.
|
|
466
|
+
*/
|
|
467
|
+
function gitRegisterExistingID(matter, branch, file, config) {
|
|
468
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
469
|
+
idRegistryService.init(gitConf.repoPath);
|
|
470
|
+
// ✅ Validation: matter doit avoir id et title
|
|
471
|
+
if (!matter.id || !matter.title) {
|
|
472
|
+
throw new errors_1.GitOperationError('Le matter est invalide (id et title requis)', 'invalid_matter');
|
|
473
|
+
}
|
|
474
|
+
// ✅ IMPORTANT: Vérifier AVANT de mettre à jour le cache
|
|
475
|
+
// Si l'ID existe déjà, vérifier qu'il appartient au MÊME fichier
|
|
476
|
+
const registry = idRegistryService.getRegistry();
|
|
477
|
+
if (registry.used.includes(matter.id)) {
|
|
478
|
+
const ownerFile = idRegistryService.getFileByID(matter.id);
|
|
479
|
+
if (ownerFile && ownerFile !== file) {
|
|
480
|
+
// ID utilisé par un AUTRE fichier → Erreur
|
|
481
|
+
const error = new Error(`ID ${matter.id} est déjà utilisé`);
|
|
482
|
+
error.code = 'id_already_used';
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
// Même fichier OU pas de fichier dans le cache → OK, continuer
|
|
486
|
+
}
|
|
487
|
+
// ✅ Mettre à jour le cache (maintenant qu'on sait que l'ID est valide)
|
|
488
|
+
if (branch && file) {
|
|
489
|
+
idRegistryService.setMatterCache(branch, file, matter);
|
|
490
|
+
}
|
|
491
|
+
// ✅ Enregistrer l'ID dans used[] si pas déjà dedans
|
|
492
|
+
idRegistryService.registerExistingID(matter.id, branch, file);
|
|
493
|
+
idRegistryService.save();
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Renomme un fichier dans le cache du registre d'IDs
|
|
497
|
+
*
|
|
498
|
+
* Cette fonction met à jour le cache lorsqu'un fichier est renommé:
|
|
499
|
+
* - Supprime l'entrée avec l'ancien nom
|
|
500
|
+
* - Crée une nouvelle entrée avec le nouveau nom
|
|
501
|
+
* - Préserve l'ID et les métadonnées
|
|
502
|
+
*
|
|
503
|
+
* @param oldFile Ancien nom du fichier
|
|
504
|
+
* @param newFile Nouveau nom du fichier
|
|
505
|
+
* @param branch Branche concernée
|
|
506
|
+
* @param config Configuration Git optionnelle
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* ```typescript
|
|
510
|
+
* // Après un rename dans Git
|
|
511
|
+
* gitIDRegistryRename('procedure-old.md', 'procedure-new.md', 'rule-validation-1');
|
|
512
|
+
* ```
|
|
513
|
+
*/
|
|
514
|
+
function gitIDRegistryRename(oldFile, newFile, branch, config) {
|
|
515
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
516
|
+
idRegistryService.init(gitConf.repoPath);
|
|
517
|
+
idRegistryService.rename(oldFile, newFile, branch);
|
|
518
|
+
idRegistryService.save();
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Valide et assure qu'un matter a un ID valide
|
|
522
|
+
*
|
|
523
|
+
* **Principe d'identité stable**: Un fichier conserve son ID à travers les branches.
|
|
524
|
+
* Cette fonction cherche d'abord si le fichier a déjà un ID dans le registre
|
|
525
|
+
* (peu importe la branche), et le réutilise pour garantir la cohérence.
|
|
526
|
+
*
|
|
527
|
+
* @param matter Le matter à valider/compléter
|
|
528
|
+
* @param config Configuration Git optionnelle
|
|
529
|
+
* @param branch Branche du fichier (optionnel, peut être undefined lors de createPullRequest)
|
|
530
|
+
* @param file Nom du fichier (requis pour chercher l'ID existant)
|
|
531
|
+
* @returns Le matter mis à jour avec un ID valide
|
|
532
|
+
*/
|
|
533
|
+
function gitEnsureMatterID(matter, config, branch, file) {
|
|
534
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
535
|
+
idRegistryService.init(gitConf.repoPath);
|
|
536
|
+
// ✅ NOUVEAU: Chercher d'abord l'ID existant du fichier (peu importe la branche)
|
|
537
|
+
const existingFileID = file ? idRegistryService.getFileID(file) : undefined;
|
|
538
|
+
if (existingFileID) {
|
|
539
|
+
// Le fichier a déjà un ID → le réutiliser pour garantir la cohérence
|
|
540
|
+
matter.id = existingFileID;
|
|
541
|
+
// Mettre à jour le cache si branch est définie (sinon ce sera fait plus tard)
|
|
542
|
+
if (branch && file && matter.title) {
|
|
543
|
+
idRegistryService.setMatterCache(branch, file, {
|
|
544
|
+
id: matter.id,
|
|
545
|
+
title: matter.title,
|
|
546
|
+
service: matter.service
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
return matter;
|
|
550
|
+
}
|
|
551
|
+
// Si matter a un ID fourni manuellement (ex: import ou test)
|
|
552
|
+
// FIXME 999 should be a constant in config
|
|
553
|
+
if (matter.id && typeof matter.id === 'number' && matter.id > 999) {
|
|
554
|
+
try {
|
|
555
|
+
// Essayer d'enregistrer cet ID
|
|
556
|
+
gitRegisterExistingID(matter, branch, file, config);
|
|
557
|
+
return matter;
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
// ID déjà utilisé par un AUTRE fichier → générer un nouveau
|
|
561
|
+
// (Si même fichier, registerExistingID ne lève pas d'erreur)
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// Générer un nouvel ID
|
|
565
|
+
matter.id = gitGenerateNextID(config);
|
|
566
|
+
// FIXME: Double save ici - gitGenerateNextID() fait save() puis gitRegisterExistingID() fait save() aussi
|
|
567
|
+
// Refactorisation future: Retirer save() de gitGenerateNextID() et le faire uniquement dans gitRegisterExistingID()
|
|
568
|
+
// quand matter est fourni (processus atomique: generate → setCache → register → save)
|
|
569
|
+
// ✅ Mettre à jour le cache avec le nouvel ID (seulement si branch est définie)
|
|
570
|
+
if (branch && file && matter.title) {
|
|
571
|
+
idRegistryService.setMatterCache(branch, file, {
|
|
572
|
+
id: matter.id,
|
|
573
|
+
title: matter.title,
|
|
574
|
+
service: matter.service
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
return matter;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Valide le format du matter (ID et title obligatoires)
|
|
581
|
+
* @param matter Le matter à valider
|
|
582
|
+
* @throws GitOperationError si le matter est invalide
|
|
583
|
+
*/
|
|
584
|
+
function validateMatter(matter) {
|
|
585
|
+
if (!matter || typeof matter !== 'object') {
|
|
586
|
+
throw new errors_1.GitOperationError('Le matter est invalide', 'invalid_matter');
|
|
587
|
+
}
|
|
588
|
+
//
|
|
589
|
+
// ✅ FIX: Vérifier que l'ID existe et est un nombre
|
|
590
|
+
if (!matter.id || typeof matter.id !== 'number') {
|
|
591
|
+
throw new errors_1.GitOperationError('Le matter doit contenir un champ "id" de type number', 'missing_id', { matter });
|
|
592
|
+
}
|
|
593
|
+
if (matter.id <= 999) {
|
|
594
|
+
throw new errors_1.GitOperationError(`L'ID ${matter.id} est réservé (doit être >= 1000)`, 'invalid_id', { id: matter.id });
|
|
595
|
+
}
|
|
596
|
+
if (!matter.title || typeof matter.title !== 'string') {
|
|
597
|
+
throw new errors_1.GitOperationError('Le matter doit contenir un champ "title" de type string', 'missing_title', { matter });
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Extraction rapide du matter depuis une chaîne de contenu (helper interne)
|
|
602
|
+
* @internal
|
|
603
|
+
*/
|
|
604
|
+
function extractMatterFromContent(content) {
|
|
605
|
+
if (!content) {
|
|
606
|
+
return { id: 0, title: undefined, service: undefined, oldfile: undefined };
|
|
607
|
+
}
|
|
608
|
+
// Regex pour extraire id (format: "id: 1234" ou "id: '1234'")
|
|
609
|
+
const idMatch = content.match(/^id:\s*['"]?(\d+)['"]?/m);
|
|
610
|
+
const id = idMatch ? parseInt(idMatch[1], 10) : undefined;
|
|
611
|
+
// Regex pour extraire title (format: "title: Mon Titre" ou "title: 'Mon Titre'")
|
|
612
|
+
const titleMatch = content.match(/^title:\s*["']?([^"'\n]+)["']?/m);
|
|
613
|
+
const title = titleMatch ? titleMatch[1].trim() : undefined;
|
|
614
|
+
const serviceMatch = content.match(/^service:\s*["']?([^"'\n]+)["']?/m);
|
|
615
|
+
const service = serviceMatch ? serviceMatch[1].trim() : undefined;
|
|
616
|
+
//
|
|
617
|
+
// ✅ Extraction de oldfile (champ temporaire pour notifier les renames)
|
|
618
|
+
const oldfileMatch = content.match(/^oldfile:\s*["']?([^"'\n]+)["']?/m);
|
|
619
|
+
const oldfile = oldfileMatch ? oldfileMatch[1].trim() : undefined;
|
|
620
|
+
return { id, title, service, oldfile };
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Lecture rapide et stricte du matter d'un fichier (id + title uniquement)
|
|
624
|
+
*
|
|
625
|
+
* **Optimisation : Utilise le cache du registre d'IDs pour éviter de lire le fichier.**
|
|
626
|
+
*
|
|
627
|
+
* Cette fonction :
|
|
628
|
+
* 1. Vérifie d'abord le cache dans `.git/with-ids.json`
|
|
629
|
+
* 2. Si absent ou invalidé, lit le fichier avec regex (pas de parsing complet)
|
|
630
|
+
* 3. Met à jour le cache automatiquement
|
|
631
|
+
*
|
|
632
|
+
* @param git Instance SimpleGit
|
|
633
|
+
* @param filePath Chemin du fichier
|
|
634
|
+
* @param branch Nom de la branche
|
|
635
|
+
* @param config Configuration Git optionnelle
|
|
636
|
+
* @returns { id, title } ou { id: undefined, title: undefined } si absent
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```typescript
|
|
640
|
+
* const matter = await gitFileStrictMatter(git, 'doc.md', 'main');
|
|
641
|
+
* console.log(matter.id); // 1234
|
|
642
|
+
* console.log(matter.title); // "Mon Document"
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
645
|
+
async function gitFileStrictMatter(git, filePath, branch, config) {
|
|
646
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
647
|
+
idRegistryService.init(gitConf.repoPath);
|
|
648
|
+
try {
|
|
649
|
+
// ✅ ÉTAPE 1: Vérifier le cache
|
|
650
|
+
const cached = idRegistryService.getMatterCache(branch, filePath);
|
|
651
|
+
if (cached) {
|
|
652
|
+
// Cache trouvé, retourner directement
|
|
653
|
+
return {
|
|
654
|
+
id: cached.id,
|
|
655
|
+
title: cached.title,
|
|
656
|
+
oldfile: cached.oldfile // ✅ Inclure oldfile temporaire
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
// ❌ ÉTAPE 2: Cache absent, lire le fichier
|
|
660
|
+
const content = await git.show([`${branch}:${filePath}`]);
|
|
661
|
+
if (!content) {
|
|
662
|
+
return { id: undefined, title: undefined, oldfile: undefined };
|
|
663
|
+
}
|
|
664
|
+
// ✅ ÉTAPE 3: Extraction rapide avec regex (sans matterParse complet)
|
|
665
|
+
const matter = extractMatterFromContent(content);
|
|
666
|
+
// ✅ ÉTAPE 4: Mettre à jour le cache
|
|
667
|
+
idRegistryService.setMatterCache(branch, filePath, matter);
|
|
668
|
+
idRegistryService.save();
|
|
669
|
+
return matter;
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
if (gitConf.verbose) {
|
|
673
|
+
console.warn(`⚠️ Erreur lecture matter de ${filePath}:`, error);
|
|
674
|
+
}
|
|
675
|
+
return { id: undefined, title: undefined, oldfile: undefined };
|
|
676
|
+
}
|
|
677
|
+
}
|
|
54
678
|
/**
|
|
55
679
|
* Vérifie et configure un repository Git existant pour s'assurer qu'il a la configuration requise
|
|
56
680
|
* @param git Instance SimpleGit
|
|
@@ -391,6 +1015,64 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
391
1015
|
let currentBranch;
|
|
392
1016
|
try {
|
|
393
1017
|
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
1018
|
+
// ===== VALIDATION DES IDs (si activée) =====
|
|
1019
|
+
if (gitConf.withID) {
|
|
1020
|
+
try {
|
|
1021
|
+
// Import dynamique pour éviter les dépendances circulaires
|
|
1022
|
+
const { matterParse, matterSerialize } = await Promise.resolve().then(() => __importStar(require('../utils.matter')));
|
|
1023
|
+
const parsed = matterParse(content);
|
|
1024
|
+
if (gitConf.strictID) {
|
|
1025
|
+
// Mode strict : valider que l'ID existe et est correct
|
|
1026
|
+
validateMatter(parsed.matter);
|
|
1027
|
+
// ✅ gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement si matter est fourni
|
|
1028
|
+
// Enregistrer l'ID existant
|
|
1029
|
+
// ✅ Passer branch (PR) et file (filePath) pour vérifier propriété
|
|
1030
|
+
gitRegisterExistingID(parsed.matter, PR, filePath, gitConf);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
// Mode permissif : générer l'ID si manquant
|
|
1034
|
+
// ✅ IMPORTANT: Ne PAS ré-assigner l'ID s'il est déjà valide
|
|
1035
|
+
// Le contenu a peut-être déjà été traité par gitEnsureMatterID() dans le caller
|
|
1036
|
+
const hasValidID = parsed.matter.id &&
|
|
1037
|
+
typeof parsed.matter.id === 'number' &&
|
|
1038
|
+
parsed.matter.id > 999;
|
|
1039
|
+
if (!hasValidID) {
|
|
1040
|
+
// Seulement si l'ID est manquant ou invalide
|
|
1041
|
+
parsed.matter = gitEnsureMatterID(parsed.matter, gitConf, PR, filePath);
|
|
1042
|
+
// Re-sérialiser avec l'ID mis à jour
|
|
1043
|
+
content = matterSerialize(parsed.content, parsed.matter);
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
// ✅ L'ID existe déjà : gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement
|
|
1047
|
+
try {
|
|
1048
|
+
gitRegisterExistingID(parsed.matter, PR, filePath, gitConf);
|
|
1049
|
+
}
|
|
1050
|
+
catch (error) {
|
|
1051
|
+
// Si l'ID est déjà utilisé par un autre fichier, générer un nouvel ID
|
|
1052
|
+
if (error.code === 'id_already_used') {
|
|
1053
|
+
// En mode non-strict, générer un nouvel ID si doublon détecté
|
|
1054
|
+
parsed.matter = gitEnsureMatterID(parsed.matter, gitConf, PR, filePath);
|
|
1055
|
+
content = matterSerialize(parsed.content, parsed.matter);
|
|
1056
|
+
// ✅ Ne pas propager l'erreur, continuer avec le nouvel ID généré
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
throw error;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
// En mode strict, propager l'erreur id_already_used
|
|
1067
|
+
// En mode permissif, cette erreur devrait déjà être gérée dans le bloc ci-dessus
|
|
1068
|
+
if (error.code === 'id_already_used' && gitConf.strictID) {
|
|
1069
|
+
throw new errors_1.GitOperationError(`Impossible de créer le fichier: ${error.message}`, 'duplicate_id', { filePath, error });
|
|
1070
|
+
}
|
|
1071
|
+
// Propager les autres erreurs de validation
|
|
1072
|
+
throw error;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
// ===== FIN VALIDATION IDs =====
|
|
394
1076
|
//
|
|
395
1077
|
// secure main and draft branches (on production)
|
|
396
1078
|
if (!gitConf?.canForce && PR == gitConf.mainBranch) {
|
|
@@ -408,6 +1090,26 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
408
1090
|
// without note the branche is not a valid PR branch
|
|
409
1091
|
const oldNote = await (0, repo_tools_1.gitReadNote)(git, PR, gitConf.gitNotes.namespace, 10);
|
|
410
1092
|
const commit = await _writeFileAndCommit(git, filePath, content, user, gitConf, `commit: ${filePath}`);
|
|
1093
|
+
// ✅ Mettre à jour le cache du matter après sauvegarde
|
|
1094
|
+
// NOTE: Le cache est TOUJOURS mis à jour, indépendamment de withID
|
|
1095
|
+
// C'est une opération de cache, pas une validation
|
|
1096
|
+
try {
|
|
1097
|
+
const { matterParse } = await Promise.resolve().then(() => __importStar(require('../utils.matter')));
|
|
1098
|
+
const parsed = matterParse(content);
|
|
1099
|
+
idRegistryService.init(gitConf.repoPath);
|
|
1100
|
+
idRegistryService.setMatterCache(PR, filePath, {
|
|
1101
|
+
id: parsed.matter.id,
|
|
1102
|
+
title: parsed.matter.title
|
|
1103
|
+
// ⚠️ Ne PAS persister oldfile dans le cache - temporaire uniquement pour client
|
|
1104
|
+
});
|
|
1105
|
+
idRegistryService.save();
|
|
1106
|
+
}
|
|
1107
|
+
catch (cacheError) {
|
|
1108
|
+
// Erreur non critique, ne pas bloquer la sauvegarde
|
|
1109
|
+
if (gitConf.verbose) {
|
|
1110
|
+
console.warn('⚠️ Erreur mise à jour cache matter:', cacheError);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
411
1113
|
if (!oldNote) {
|
|
412
1114
|
return {
|
|
413
1115
|
hash: commit.commit,
|
|
@@ -862,6 +1564,81 @@ async function gitGetBranchHealth(git, branch) {
|
|
|
862
1564
|
(health.clean || (health.modifiedFiles === 0 && health.stagedFiles === 0));
|
|
863
1565
|
return health;
|
|
864
1566
|
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Version optimisée de gitGetBranchHealth pour les branches de validation
|
|
1569
|
+
*
|
|
1570
|
+
* Cette fonction utilise une approche en cascade pour diagnostiquer rapidement
|
|
1571
|
+
* l'état d'une branche de validation sans effectuer de checkout coûteux.
|
|
1572
|
+
*
|
|
1573
|
+
* Tests effectués dans l'ordre (arrêt au premier échec) :
|
|
1574
|
+
* 1. Branche existe ? (git rev-parse --verify)
|
|
1575
|
+
* 2. Note Git présente sur HEAD ? (gitReadNote avec maxCommit=1)
|
|
1576
|
+
* 3. Note correspond au bon PR ? (note.id === prNumber)
|
|
1577
|
+
*
|
|
1578
|
+
* Avantages :
|
|
1579
|
+
* - ✅ Pas de checkout (gain 80-90% performance)
|
|
1580
|
+
* - ✅ Lecture directe du HEAD sans changer de branche
|
|
1581
|
+
* - ✅ Tests cascade (arrêt dès qu'un problème est détecté)
|
|
1582
|
+
* - ✅ Optimisé pour branches temporaires (validations)
|
|
1583
|
+
*
|
|
1584
|
+
* @param git Instance SimpleGit
|
|
1585
|
+
* @param branch Nom de la branche de validation à diagnostiquer
|
|
1586
|
+
* @param config Configuration Git optionnelle
|
|
1587
|
+
* @returns GitHealthStatus avec diagnostic optimisé
|
|
1588
|
+
*/
|
|
1589
|
+
async function gitGetValidationBranchHealth(git, branch, config) {
|
|
1590
|
+
const gitConfig = config || (0, repo_tools_1.gitLoad)();
|
|
1591
|
+
const health = {
|
|
1592
|
+
branch,
|
|
1593
|
+
exists: false,
|
|
1594
|
+
accessible: false,
|
|
1595
|
+
clean: true, // Par défaut OK pour validation (pas de working dir check)
|
|
1596
|
+
mergeInProgress: false,
|
|
1597
|
+
conflictedFiles: 0,
|
|
1598
|
+
modifiedFiles: 0,
|
|
1599
|
+
untrackedFiles: 0,
|
|
1600
|
+
stagedFiles: 0,
|
|
1601
|
+
indexReadable: true,
|
|
1602
|
+
healthy: false,
|
|
1603
|
+
issues: [],
|
|
1604
|
+
recommendations: []
|
|
1605
|
+
};
|
|
1606
|
+
try {
|
|
1607
|
+
// 1. CASCADE: Vérifier existence (très rapide, sans checkout)
|
|
1608
|
+
const branchExists = await git.raw(['rev-parse', '--verify', branch])
|
|
1609
|
+
.then(() => true)
|
|
1610
|
+
.catch(() => false);
|
|
1611
|
+
if (!branchExists) {
|
|
1612
|
+
health.issues.push(`Branche '${branch}' n'existe pas`);
|
|
1613
|
+
health.recommendations.push(`Créer la branche ou vérifier le nom`);
|
|
1614
|
+
return health;
|
|
1615
|
+
}
|
|
1616
|
+
health.exists = true;
|
|
1617
|
+
health.accessible = true; // Si rev-parse OK, branche accessible
|
|
1618
|
+
// 2. CASCADE: Vérifier note sur HEAD (rapide, sans checkout)
|
|
1619
|
+
const headCommit = await git.raw(['rev-parse', branch]).then(s => s.trim());
|
|
1620
|
+
const note = await (0, repo_tools_1.gitReadNote)(git, headCommit, gitConfig.gitNotes.namespace, 1);
|
|
1621
|
+
if (!note) {
|
|
1622
|
+
health.issues.push(`Pas de note Git sur HEAD de '${branch}'`);
|
|
1623
|
+
health.recommendations.push(`Note perdue, utiliser git-notes-list-lost.sh pour la localiser`);
|
|
1624
|
+
return health;
|
|
1625
|
+
}
|
|
1626
|
+
// 3. CASCADE: Vérifier que la note correspond au PR
|
|
1627
|
+
const prNumber = parseInt(branch.split('-').pop() || '0', 10);
|
|
1628
|
+
if (note.id !== prNumber) {
|
|
1629
|
+
health.issues.push(`Note incorrecte: ID=${note.id}, attendu PR=${prNumber}`);
|
|
1630
|
+
health.recommendations.push(`Corriger la note ou supprimer la branche`);
|
|
1631
|
+
return health;
|
|
1632
|
+
}
|
|
1633
|
+
// ✅ Tous les tests essentiels passés
|
|
1634
|
+
health.healthy = true;
|
|
1635
|
+
}
|
|
1636
|
+
catch (error) {
|
|
1637
|
+
health.issues.push(`Erreur diagnostic: ${error.message}`);
|
|
1638
|
+
health.recommendations.push(`Vérifier l'état du dépôt Git`);
|
|
1639
|
+
}
|
|
1640
|
+
return health;
|
|
1641
|
+
}
|
|
865
1642
|
/**
|
|
866
1643
|
* Fonction helper pour créer un statut unhealthy
|
|
867
1644
|
*/
|