@piecemaker-legal/piecemaker 2.0.9 → 2.0.11
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/assets/FindReplaceDialog-KU5TEIBJ-BucOHLdm.js +1 -0
- package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-D5UqbelV.js +1 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-8yEhLigd.js +144 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
- package/dist/assets/ImagePositionDialog-HEWYR6Q3-wGIBYxCm.js +1 -0
- package/dist/assets/ImagePropertiesDialog-RAB47C6M-D86PckZ2.js +1 -0
- package/dist/assets/PageSetupDialog-AIGIXO3E-DLETP-7v.js +1 -0
- package/dist/assets/SplitCellDialog-IFBKYCD6-CwdQBXeb.js +1 -0
- package/dist/assets/TablePropertiesDialog-TMLKGOOW-j2nA7KJs.js +1 -0
- package/dist/assets/WatermarkDialog-KRL7WMIS-XFJ9gTkC.js +1 -0
- package/dist/assets/{abnfDiagram-VCTEODGH-WrsSAHOe.js → abnfDiagram-VCTEODGH-DiCrND_Y.js} +1 -1
- package/dist/assets/arc-DOjgbMYz.js +1 -0
- package/dist/assets/architectureDiagram-5GKGNRK7-brzIFVhl.js +36 -0
- package/dist/assets/{blockDiagram-I7D4REHJ-zGMnF7Ox.js → blockDiagram-I7D4REHJ-C8JhTjQQ.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-ClWw3bqt.js → c4Diagram-7LVT6UL2-jfYxccyk.js} +1 -1
- package/dist/assets/channel-Dajzu2W1.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-C-9ICPHv.js → chunk-2Q5K7J3B-CrlEShp9.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-Bawy3teT.js → chunk-5VM5RSS4-DzPlUHGq.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-DQ375RQO.js → chunk-F27PBJKO-Cp9cpeWU.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-CflNF2f6.js → chunk-IMKFNOWR-BifUldEN.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CIXQv0gc.js → chunk-JWPE2WC7-ggPEIkWu.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-Cza5j6Qw.js → chunk-POPQ4Y6H-3h_nayRX.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-GQ_VvWZR.js → chunk-SVP7TREG-CH29aG6J.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-BEmU16W5.js → chunk-TICWLB2K-PluJUM5k.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-C1KpkLYH.js → chunk-XXDRQBXY-DXU39Vwg.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-dDEP_giv.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-dDEP_giv.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-CZE48TRG.js → cose-bilkent-JH36ORCC-Dz6roYrN.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-L-tx1eC-.js → cynefin-OW5HDTMX-BLzrpSit.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-Coa8oJwR.js → cynefinDiagram-5FMLGOSQ-kfxnERDZ.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-Bu_3Y75K.js → dagre-GXQ25YYZ-DP57bp-N.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-CFiiV4es.js → diagram-S7CK7UJ4-DAlsrQVW.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-CCXheGAA.js → diagram-UQ7AKVKN-By5yrMer.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B70m4lr_.js → diagram-VSXAHHWV-Dx89kryN.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-CHkiMOVA.js → diagram-VX7I27RA-B9fARitZ.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-_awpc4Bs.js → diagram-Z3DM3KII-DC15CEH1.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CZzjH86H.js → ebnfDiagram-PWID7BFC-BKQzghGF.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-9DgiHZg9.js → erDiagram-RLTQ6QDP-CPY6RM7G.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-DHY_503b.js → flowDiagram-HODETNUW-4dL4GJcf.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-DPiiYZ_l.js → ganttDiagram-EL5Y4UJY-D4-cVcfb.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-Ddd_evm7.js → gitGraphDiagram-WWUBYQGX-BMeVIEiX.js} +1 -1
- package/dist/assets/{index-tibHq7Qp.js → index-DNtFDOni.js} +140 -140
- package/dist/assets/index-Dc9wNuS4.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-BPRNhfwb.js → infoDiagram-27XIBGKW-D5qMSUEU.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BXx6aqkq.js → ishikawaDiagram-5VMMS53U-BDnyKRHm.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-BK8TZM29.js → journeyDiagram-3NMN7TZE-dGz5ZGzq.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-CHioRmxz.js → kanban-definition-UXKFOSKX-D7UAz7o_.js} +6 -6
- package/dist/assets/layout-DKrmEGrz.js +1 -0
- package/dist/assets/{linear-CTEhUcKG.js → linear-CrTdMY-p.js} +1 -1
- package/dist/assets/{mermaid.core-DYUb_UUB.js → mermaid.core-CXQOT5G6.js} +7 -7
- package/dist/assets/{mindmap-definition-YA3MSWOX-D7ODZ4oS.js → mindmap-definition-YA3MSWOX-VgEiYfay.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-8J-_2_kw.js → pegDiagram-XKGWAZYB-9IqT8gGx.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-ebg0yfMi.js → pieDiagram-E7YTZNPT-D98pRE-t.js} +1 -1
- package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-BoWkDiFb.js → quadrantDiagram-AXDQQJYC-b9PwkYzL.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BRJ77ldz.js → railroadDiagram-O6MQD6OU-Dz87hsPE.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-Z0JJgktA.js → requirementDiagram-BXWQKSXE-CkcV73dK.js} +1 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-UBN31Yrs.js +40 -0
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-BPAjtXwp.js → sequenceDiagram-WJ2MYXX4-CpQxd5Za.js} +1 -1
- package/dist/assets/{server.browser-BzI1jrZw.js → server.browser-BY3bL-IX.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BGZPCzM4.js → sizeCapture-INFHLROL-mCsFmyzw.js} +1 -1
- package/dist/assets/stateDiagram-D77RDMKH-B0MTBN-3.js +1 -0
- package/dist/assets/stateDiagram-v2-MP3YSRHH-DzBPN39W.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-DvuB15qF.js → swimlanes-42K2YHIH-Dzydjm0e.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-BXQpbUMD.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-ZIziYCIF.js → timeline-definition-24CTP7MA-Dz0toOLu.js} +3 -3
- package/dist/assets/{vendor-codemirror-CjW0lHR5.js → vendor-codemirror-CN-sJePf.js} +2 -2
- package/dist/assets/{vendor-react-D809iwXR.js → vendor-react-B22wqN3J.js} +5 -5
- package/dist/assets/{vennDiagram-4TSXK5OY-C5_B9gWa.js → vennDiagram-4TSXK5OY-DOWp7dkf.js} +4 -4
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DMlH9Afw.js → wardleyDiagram-VM6X3IG4-CUxsztUB.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-BldhJt7i.js → xychartDiagram-S5SC5T6Z-DS96vNED.js} +1 -1
- package/dist/index.html +4 -4
- package/dist/sw.js +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js +4 -53
- package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js +2 -3
- package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js +30 -0
- package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js +24 -0
- package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js.map +1 -0
- package/dist-server/server/piecemaker/agent-instructions.js +148 -0
- package/dist-server/server/piecemaker/agent-instructions.js.map +1 -0
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js +2 -3
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
- package/dist-server/server/piecemaker/docx-document.js +50 -0
- package/dist-server/server/piecemaker/docx-document.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +13 -2
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/model-discovery/codex-catalog.js +46 -0
- package/dist-server/server/piecemaker/model-discovery/codex-catalog.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/index.js +42 -0
- package/dist-server/server/piecemaker/model-discovery/index.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js +19 -0
- package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js +61 -0
- package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js.map +1 -0
- package/dist-server/server/piecemaker/project-registry.js +29 -0
- package/dist-server/server/piecemaker/project-registry.js.map +1 -0
- package/package.json +12 -1
- package/scripts/piecemaker/cli/disable-anonymizer.mjs +19 -0
- package/scripts/piecemaker/cli/lib/config.mjs +0 -1
- package/scripts/piecemaker/cli/lib/plugins.mjs +1 -0
- package/scripts/piecemaker/cli/lib/services.mjs +1 -10
- package/scripts/piecemaker/cli/piecemaker.mjs +0 -3
- package/server/modules/providers/list/mistral/mistral-models.provider.ts +4 -53
- package/server/modules/providers/list/mistral/mistral-runtime.provider.ts +2 -3
- package/server/modules/providers/list/mistral/mistral-vibe-models.ts +43 -0
- package/server/modules/providers/tests/mistral-vibe-models.test.ts +27 -0
- package/server/piecemaker/agent-instructions.ts +148 -0
- package/server/piecemaker/anonymizer/client-config.cjs +92 -92
- package/server/piecemaker/anonymizer/dictionary.cjs +34 -3
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +9 -16
- package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +1 -1
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +251 -268
- package/server/piecemaker/anonymizer/hudsucker-smoke.cjs +343 -0
- package/server/piecemaker/anonymizer/lifecycle.ts +2 -3
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +13 -4
- package/server/piecemaker/anonymizer/rewriter-worker.cjs +85 -0
- package/server/piecemaker/anonymizer/service.cjs +109 -179
- package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +12 -1
- package/server/piecemaker/docx-document.ts +54 -0
- package/server/piecemaker/harness/decisions.cjs +1 -1
- package/server/piecemaker/harness/index.cjs +2 -3
- package/server/piecemaker/harness/verification.cjs +1 -1
- package/server/piecemaker/index.ts +15 -3
- package/server/piecemaker/model-discovery/codex-catalog.ts +67 -0
- package/server/piecemaker/model-discovery/index.ts +69 -0
- package/server/piecemaker/model-discovery/mistral-catalog.ts +36 -0
- package/server/piecemaker/model-discovery/stdio-rpc.ts +79 -0
- package/server/piecemaker/project-registry.ts +34 -0
- package/server/piecemaker/router.cjs +6 -8
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +31 -77
- package/server/piecemaker/vendor/installer/lib/codex-skills.mjs +2 -84
- package/server/piecemaker/vendor/installer/lib/service.mjs +4 -24
- package/server/piecemaker/vendor/installer/steps/02-dependances-node.mjs +5 -5
- package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +10 -21
- package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -11
- package/server/piecemaker/vendor/installer/steps/09-codex-plugin.mjs +6 -36
- package/server/piecemaker/vendor/installer/steps/13-garde-secrets.mjs +30 -12
- package/server/piecemaker/vendor/piecemaker-plugin/README.md +2 -9
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +5 -5
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/classify-ai-documents.mjs +8 -8
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +2 -2
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +4 -6
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folder-structure.cjs +2 -90
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +71 -38
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +8 -38
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +82 -61
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +189 -41
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +4 -55
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
- package/server/piecemaker/vendor/piecemaker-plugin/skills/ajout-de-fonctionnalites/SKILL.md +4 -18
- package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +14 -148
- package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -42
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +0 -73
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +0 -7
- package/server/piecemaker/vendor/websocket-server/stamping-routes.cjs +3 -12
- package/server/piecemaker/vendor/websocket-server/workspace-paths.cjs +6 -6
- package/dist/assets/arc-DAA6wH-C.js +0 -1
- package/dist/assets/architectureDiagram-5GKGNRK7-BGNHr7ef.js +0 -36
- package/dist/assets/channel-BAQyfMll.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-BNGCuvrR.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-BNGCuvrR.js +0 -1
- package/dist/assets/index-WwknP0l4.css +0 -1
- package/dist/assets/layout-DEnkyfTY.js +0 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-4PW5HTOP.js +0 -40
- package/dist/assets/stateDiagram-D77RDMKH-o-vEcKwz.js +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-BsuDaKkN.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-DcCAyKlT.js +0 -8
- package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +0 -78
- package/scripts/piecemaker/hooks/proxy-guard.mjs +0 -251
- package/server/piecemaker/anonymizer/providers.cjs +0 -120
- package/server/piecemaker/vendor/installer/steps/08-telegram.mjs +0 -482
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/central-mapping.cjs +0 -0
- package/server/piecemaker/vendor/websocket-server/telegram-admin.cjs +0 -602
|
@@ -7,7 +7,6 @@ const CASE_FOLDER_STRUCTURE_VERSION = 1;
|
|
|
7
7
|
const CASE_FOLDER_STRUCTURE_RELATIVE = '.piecemaker/case-folder-structure.json';
|
|
8
8
|
|
|
9
9
|
const DEFAULT_CASE_FOLDER_STRUCTURE = Object.freeze({
|
|
10
|
-
administrative: '00_ADMINISTRATIF_ET_FACTURATION',
|
|
11
10
|
correspondence: '01_CORRESPONDANCE',
|
|
12
11
|
correspondenceClient: '01_Client',
|
|
13
12
|
correspondenceOpposingCounsel: '02_Avocats_adverses',
|
|
@@ -65,7 +64,6 @@ function normalizeCaseFolderStructure(value = {}) {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
ensureUniqueSiblingNames(structure, [
|
|
68
|
-
'administrative',
|
|
69
67
|
'correspondence',
|
|
70
68
|
'dataRoom',
|
|
71
69
|
'drafts',
|
|
@@ -107,9 +105,8 @@ function readStructureManifest(caseRoot) {
|
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* des dossiers déjà existants.
|
|
108
|
+
* Seuls les anciens dossiers portent encore un manifeste d'arborescence : il
|
|
109
|
+
* continue de router leur Markdown. Sans manifeste, le dossier n'est pas géré.
|
|
113
110
|
*/
|
|
114
111
|
function readCaseFolderStructure(caseRoot, config = {}) {
|
|
115
112
|
const manifest = readStructureManifest(caseRoot);
|
|
@@ -119,78 +116,6 @@ function readCaseFolderStructure(caseRoot, config = {}) {
|
|
|
119
116
|
};
|
|
120
117
|
}
|
|
121
118
|
|
|
122
|
-
function caseFolderDirectoriesFromStructure(structure) {
|
|
123
|
-
return [
|
|
124
|
-
structure.administrative,
|
|
125
|
-
structure.correspondence,
|
|
126
|
-
path.join(structure.correspondence, structure.correspondenceMarkdown),
|
|
127
|
-
path.join(structure.correspondence, structure.correspondenceClient),
|
|
128
|
-
path.join(structure.correspondence, structure.correspondenceOpposingCounsel),
|
|
129
|
-
path.join(structure.correspondence, structure.correspondenceThirdParties),
|
|
130
|
-
structure.dataRoom,
|
|
131
|
-
path.join(structure.dataRoom, structure.dataRoomMarkdown),
|
|
132
|
-
structure.drafts,
|
|
133
|
-
structure.notesAndResearch,
|
|
134
|
-
structure.procedure,
|
|
135
|
-
];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Chemins relatifs, dans l'ordre lisible de l'arborescence documentée. */
|
|
139
|
-
function caseFolderDirectories(config = {}) {
|
|
140
|
-
return caseFolderDirectoriesFromStructure(configuredCaseFolderStructure(config));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function writeStructureManifest(file, structure) {
|
|
144
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
145
|
-
const temporary = `${file}.piecemaker-${process.pid}.tmp`;
|
|
146
|
-
fs.writeFileSync(temporary, `${JSON.stringify({
|
|
147
|
-
version: CASE_FOLDER_STRUCTURE_VERSION,
|
|
148
|
-
structure,
|
|
149
|
-
}, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
150
|
-
fs.renameSync(temporary, file);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Crée uniquement les dossiers manquants. Aucun fichier ni dossier existant
|
|
155
|
-
* n'est renommé ou remplacé quand l'utilisateur modifie les noms par défaut.
|
|
156
|
-
*/
|
|
157
|
-
function ensureCaseFolderStructure(caseRoot, config = {}) {
|
|
158
|
-
const requested = String(caseRoot || '').trim();
|
|
159
|
-
if (!requested || !path.isAbsolute(requested)) throw new Error('Racine du dossier juridique invalide.');
|
|
160
|
-
const root = path.resolve(requested);
|
|
161
|
-
let rootStat;
|
|
162
|
-
try {
|
|
163
|
-
rootStat = fs.statSync(root);
|
|
164
|
-
} catch {
|
|
165
|
-
throw new Error('Racine du dossier juridique introuvable.');
|
|
166
|
-
}
|
|
167
|
-
if (!rootStat.isDirectory()) throw new Error('Racine du dossier juridique invalide.');
|
|
168
|
-
|
|
169
|
-
const created = [];
|
|
170
|
-
const manifest = readStructureManifest(root);
|
|
171
|
-
const structure = manifest.structure || configuredCaseFolderStructure(config);
|
|
172
|
-
const directories = caseFolderDirectoriesFromStructure(structure);
|
|
173
|
-
for (const relative of directories) {
|
|
174
|
-
const target = path.join(root, relative);
|
|
175
|
-
if (fs.existsSync(target)) {
|
|
176
|
-
if (!fs.statSync(target).isDirectory()) {
|
|
177
|
-
throw new Error(`L’emplacement « ${relative} » existe mais n’est pas un dossier.`);
|
|
178
|
-
}
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
fs.mkdirSync(target, { recursive: true });
|
|
182
|
-
created.push(relative.split(path.sep).join('/'));
|
|
183
|
-
}
|
|
184
|
-
if (!manifest.exists) writeStructureManifest(manifest.file, structure);
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
file: manifest.file,
|
|
188
|
-
names: structure,
|
|
189
|
-
directories: directories.map((relative) => relative.split(path.sep).join('/')),
|
|
190
|
-
created,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
119
|
function relativeSegments(relativePath) {
|
|
195
120
|
const value = String(relativePath || '').replaceAll('\\', '/').replace(/^\.\//, '');
|
|
196
121
|
if (!value || value.startsWith('/') || value.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {
|
|
@@ -278,20 +203,7 @@ function structuredMarkdownCounterpart(absolutePath, caseRoot, config = {}) {
|
|
|
278
203
|
}
|
|
279
204
|
|
|
280
205
|
module.exports = {
|
|
281
|
-
CASE_FOLDER_KEYS,
|
|
282
|
-
CASE_FOLDER_STRUCTURE_KEY,
|
|
283
|
-
CASE_FOLDER_STRUCTURE_RELATIVE,
|
|
284
|
-
DEFAULT_CASE_FOLDER_STRUCTURE,
|
|
285
|
-
caseConversionOutputDirectory,
|
|
286
|
-
caseFolderDirectories,
|
|
287
|
-
caseFolderStructureFile,
|
|
288
|
-
classifyCaseFolderPath,
|
|
289
206
|
classifyRelativeCaseFolderPath,
|
|
290
|
-
configuredCaseFolderStructure,
|
|
291
|
-
ensureCaseFolderStructure,
|
|
292
|
-
isCaseGeneratedPath,
|
|
293
|
-
normalizeCaseFolderStructure,
|
|
294
207
|
readCaseFolderStructure,
|
|
295
208
|
structuredMarkdownCounterpart,
|
|
296
|
-
validateCaseFolderName,
|
|
297
209
|
};
|
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Legal-case folders: every CloudCLI project, as published by the PieceMaker server. */
|
|
2
2
|
const fs = require('node:fs');
|
|
3
|
+
const os = require('node:os');
|
|
3
4
|
const path = require('node:path');
|
|
4
5
|
|
|
6
|
+
const PROJECTS_FILE = 'projects.json';
|
|
7
|
+
|
|
8
|
+
function piecemakerHome() {
|
|
9
|
+
return process.env.PIECEMAKER_HOME || path.join(os.homedir(), '.piecemaker');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function projectsFile() {
|
|
13
|
+
return path.join(piecemakerHome(), PROJECTS_FILE);
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
function realDirectory(value) {
|
|
6
17
|
const requested = String(value || '').trim();
|
|
7
18
|
if (!requested || !path.isAbsolute(requested)) return null;
|
|
@@ -13,23 +24,57 @@ function realDirectory(value) {
|
|
|
13
24
|
}
|
|
14
25
|
}
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
function registeredCaseFolders(config) {
|
|
18
|
-
const folders = Array.isArray(config?.caseFolders) ? config.caseFolders : [];
|
|
19
|
-
return [...new Set(folders.map(realDirectory).filter(Boolean))];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function isInsideOrEqual(root, candidate) {
|
|
27
|
+
function isInsideOrEqualPath(root, candidate) {
|
|
23
28
|
const relative = path.relative(root, candidate);
|
|
24
29
|
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
function excludedRoots() {
|
|
33
|
+
return [os.tmpdir(), '/tmp', '/private/tmp', '/var/folders', '/private/var/folders']
|
|
34
|
+
.map(realDirectory)
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isEligibleProjectFolder(folder) {
|
|
39
|
+
const home = realDirectory(os.homedir());
|
|
40
|
+
if (home && isInsideOrEqualPath(folder, home)) return false;
|
|
41
|
+
return !excludedRoots().some((root) => isInsideOrEqualPath(root, folder));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readProjectSources() {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(fs.readFileSync(projectsFile(), 'utf8'));
|
|
47
|
+
return parsed?.sources && typeof parsed.sources === 'object' && !Array.isArray(parsed.sources) ? parsed.sources : {};
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Absolute, existing and de-duplicated project folders, across every publishing server. */
|
|
54
|
+
function registeredProjectFolders() {
|
|
55
|
+
const folders = Object.values(readProjectSources()).flatMap((list) => (Array.isArray(list) ? list : []));
|
|
56
|
+
return [...new Set(folders.map(realDirectory).filter(Boolean))].filter(isEligibleProjectFolder);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function publishProjectSource(sourceId, folders) {
|
|
60
|
+
const file = projectsFile();
|
|
61
|
+
const sources = readProjectSources();
|
|
62
|
+
if (folders === null) delete sources[sourceId];
|
|
63
|
+
else sources[sourceId] = [...new Set(folders.map((folder) => path.resolve(String(folder))))].sort();
|
|
64
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
65
|
+
const temporaryFile = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
66
|
+
try {
|
|
67
|
+
fs.writeFileSync(temporaryFile, `${JSON.stringify({ version: 1, sources }, null, 2)}\n`, 'utf8');
|
|
68
|
+
fs.renameSync(temporaryFile, file);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
fs.rmSync(temporaryFile, { force: true });
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveTarget(target) {
|
|
31
76
|
try {
|
|
32
|
-
|
|
77
|
+
return fs.realpathSync(path.resolve(String(target)));
|
|
33
78
|
} catch {
|
|
34
79
|
// Write/Edit may target a file that does not exist yet. Resolve its nearest
|
|
35
80
|
// existing parent, then append the missing tail without following links.
|
|
@@ -37,22 +82,24 @@ function locateRegisteredCase(folders, target) {
|
|
|
37
82
|
const tail = [];
|
|
38
83
|
while (path.dirname(current) !== current) {
|
|
39
84
|
try {
|
|
40
|
-
|
|
41
|
-
break;
|
|
85
|
+
return path.join(fs.realpathSync(current), ...tail);
|
|
42
86
|
} catch {
|
|
43
87
|
tail.unshift(path.basename(current));
|
|
44
88
|
current = path.dirname(current);
|
|
45
89
|
}
|
|
46
90
|
}
|
|
47
|
-
|
|
91
|
+
return null;
|
|
48
92
|
}
|
|
93
|
+
}
|
|
49
94
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
95
|
+
/** Locate a target inside one of the project folders; a nested project wins over its parent. */
|
|
96
|
+
function locateProjectCase(target) {
|
|
97
|
+
if (!target) return null;
|
|
98
|
+
const absolute = resolveTarget(target);
|
|
99
|
+
if (!absolute) return null;
|
|
100
|
+
const roots = registeredProjectFolders().sort((a, b) => b.length - a.length);
|
|
54
101
|
for (const caseRoot of roots) {
|
|
55
|
-
if (!
|
|
102
|
+
if (!isInsideOrEqualPath(caseRoot, absolute)) continue;
|
|
56
103
|
return {
|
|
57
104
|
casesRoot: path.dirname(caseRoot),
|
|
58
105
|
caseName: path.basename(caseRoot),
|
|
@@ -65,22 +112,8 @@ function locateRegisteredCase(folders, target) {
|
|
|
65
112
|
return null;
|
|
66
113
|
}
|
|
67
114
|
|
|
68
|
-
/**
|
|
69
|
-
* Locate a target inside one of the explicitly registered legal-case folders.
|
|
70
|
-
* There is no longer a workspace-root fallback: a matter is protected only once
|
|
71
|
-
* it is explicitly registered, never by mere location under a common root.
|
|
72
|
-
*/
|
|
73
|
-
function locateConfiguredCase(config, target) {
|
|
74
|
-
return locateRegisteredCase(registeredCaseFolders(config), target);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function configuredWatchPaths(config) {
|
|
78
|
-
return registeredCaseFolders(config);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
115
|
module.exports = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
registeredCaseFolders,
|
|
116
|
+
locateProjectCase,
|
|
117
|
+
publishProjectSource,
|
|
118
|
+
registeredProjectFolders,
|
|
86
119
|
};
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Mapping d'anonymisation d'un dossier juridique — implémentation unique.
|
|
3
3
|
*
|
|
4
4
|
* Le pipeline admin (`websocket-server/originals-pipeline.cjs`), le serveur
|
|
5
|
-
* Word, l'historique et les vues juridiques la partagent.
|
|
6
|
-
* mapping central produit à partir de ces mappings de dossier.
|
|
5
|
+
* Word, l'historique et les vues juridiques la partagent.
|
|
7
6
|
*
|
|
8
7
|
* Deux sens, jamais symétriques dans leur usage :
|
|
9
8
|
* - `applyMapping` entité → code, sur tout ce que l'IA s'apprête à lire ;
|
|
@@ -16,11 +15,11 @@
|
|
|
16
15
|
const fs = require('node:fs');
|
|
17
16
|
const path = require('node:path');
|
|
18
17
|
|
|
19
|
-
const {
|
|
20
|
-
const {
|
|
18
|
+
const { WORKSPACE_SUBDIR } = require('./protection.cjs');
|
|
19
|
+
const { locateProjectCase } = require('./case-folders.cjs');
|
|
21
20
|
const { isInstitutionalEntity } = require('./institutional-terms.cjs');
|
|
22
21
|
// Le moteur de substitution est extrait dans un module autonome : il est aussi
|
|
23
|
-
// requis par le
|
|
22
|
+
// requis par le proxy PII (`anonymizer/dictionary.cjs`). Une seule
|
|
24
23
|
// implémentation, ré-exportée ici pour les appelants historiques.
|
|
25
24
|
const {
|
|
26
25
|
applyMapping,
|
|
@@ -286,40 +285,13 @@ function readCaseMapping(caseRoot) {
|
|
|
286
285
|
};
|
|
287
286
|
}
|
|
288
287
|
|
|
289
|
-
/**
|
|
290
|
-
* Un mapping d'anonymisation exploitable existe-t-il pour ce dossier ? C'est la
|
|
291
|
-
* condition de la garantie « anonymisé avant envoi » : sans lui, le proxy n'a
|
|
292
|
-
* rien à coder et une surface lisible partirait en clair. `protect-originals`
|
|
293
|
-
* s'en sert pour refuser la lecture d'un dossier PieceMaker pas encore anonymisé.
|
|
294
|
-
* On s'appuie sur `readCaseMapping` (et non sur la seule présence du fichier) pour
|
|
295
|
-
* qu'un `mapping_default.json` illisible compte comme absent — c'est exactement ce
|
|
296
|
-
* que voit la reconstruction du mapping central.
|
|
297
|
-
*/
|
|
298
|
-
function caseHasMapping(caseRoot) {
|
|
299
|
-
return readCaseMapping(caseRoot).exists;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
288
|
// La substitution (`applyMapping` / `revertMapping`) vit dans `substitution.cjs`
|
|
303
289
|
// et est importée en tête de fichier. Elle est ré-exportée ci-dessous pour les
|
|
304
290
|
// appelants historiques de `mapping.cjs`.
|
|
305
291
|
|
|
306
|
-
/**
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
* Telegram). Retourne `null` hors dossier juridique ou sans mapping utilisable :
|
|
310
|
-
* les hooks n'ont alors rien à faire.
|
|
311
|
-
*/
|
|
312
|
-
function resolveCaseMapping(casesRoot, hint) {
|
|
313
|
-
const located = locateCase(casesRoot, hint);
|
|
314
|
-
if (!located) return null;
|
|
315
|
-
const mapping = readCaseMapping(located.caseRoot);
|
|
316
|
-
if (!mapping.exists) return null;
|
|
317
|
-
return { caseRoot: located.caseRoot, caseName: located.caseName, ...mapping };
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/** Resolve a mapping from the explicit folder registry plus legacy workspace. */
|
|
321
|
-
function resolveConfiguredCaseMapping(config, hint) {
|
|
322
|
-
const located = locateConfiguredCase(config, hint);
|
|
292
|
+
/** Resolve the mapping of the project folder containing the hint. */
|
|
293
|
+
function resolveProjectCaseMapping(hint) {
|
|
294
|
+
const located = locateProjectCase(hint);
|
|
323
295
|
if (!located) return null;
|
|
324
296
|
const mapping = readCaseMapping(located.caseRoot);
|
|
325
297
|
if (!mapping.exists) return null;
|
|
@@ -330,15 +302,13 @@ module.exports = {
|
|
|
330
302
|
applyMapping,
|
|
331
303
|
buildEntityRegex,
|
|
332
304
|
byDescendingEntityLength,
|
|
333
|
-
caseHasMapping,
|
|
334
305
|
caseMappingFile,
|
|
335
306
|
escapeWithVariants,
|
|
336
307
|
normalizeMappingDocument,
|
|
337
308
|
normalizeProcedureInfo,
|
|
338
309
|
readCaseMapping,
|
|
339
310
|
readJsonFile,
|
|
340
|
-
|
|
341
|
-
resolveCaseMapping,
|
|
311
|
+
resolveProjectCaseMapping,
|
|
342
312
|
resolveMappedPath,
|
|
343
313
|
revertMapping,
|
|
344
314
|
sortedMapping,
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* L'ancienne règle protégeait le contenu d'un sous-dossier « Pièces
|
|
5
5
|
* originales ». Elle ne protégeait donc rien dans un dossier organisé
|
|
6
|
-
* autrement, ce qui est le cas courant : les PDF
|
|
7
|
-
*
|
|
6
|
+
* autrement, ce qui est le cas courant : les PDF y côtoient le Markdown qu'on
|
|
7
|
+
* en a tiré. La protection est désormais une propriété du fichier,
|
|
8
8
|
* décidée dans l'administration.
|
|
9
9
|
*
|
|
10
10
|
* **Protégés par défaut : les PDF et les images** (`PROTECTED_EXTENSIONS`), qui
|
|
@@ -81,7 +81,7 @@ const PROTECTED_EXTENSIONS = new Set([
|
|
|
81
81
|
* celles rangées sous `ignored` et les variantes d'`extracted_data` ressortiraient
|
|
82
82
|
* telles quelles. Le seul traitement correct est le refus.
|
|
83
83
|
*/
|
|
84
|
-
const FORBIDDEN_JSON_PATTERNS = [/^mapping.*\.json$/i, /_sensitive_map\.json$/i
|
|
84
|
+
const FORBIDDEN_JSON_PATTERNS = [/^mapping.*\.json$/i, /_sensitive_map\.json$/i];
|
|
85
85
|
|
|
86
86
|
/** Vrai pour un mapping de dossier ou un scan PII, où qu'il soit rangé. */
|
|
87
87
|
function isMappingFile(filePath) {
|
|
@@ -172,37 +172,6 @@ function relativeKey(absolutePath, caseRoot) {
|
|
|
172
172
|
return relative.split(path.sep).join('/');
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
/**
|
|
176
|
-
* Situe un chemin dans la racine PieceMaker : chaque enfant direct de cette
|
|
177
|
-
* racine est un dossier juridique indépendant. Retourne `null` hors racine, ce
|
|
178
|
-
* qui est le cas de la grande majorité des appels d'outil — c'est le chemin
|
|
179
|
-
* rapide des hooks.
|
|
180
|
-
*/
|
|
181
|
-
function locateCase(casesRoot, target) {
|
|
182
|
-
if (!casesRoot || !target) return null;
|
|
183
|
-
let root;
|
|
184
|
-
try {
|
|
185
|
-
root = fs.realpathSync(path.resolve(String(casesRoot)));
|
|
186
|
-
} catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
// Les deux côtés doivent être résolus : sur macOS `/var/…` est un lien vers
|
|
190
|
-
// `/private/var/…`, si bien qu'une racine résolue et une cible qui ne l'est
|
|
191
|
-
// pas ne se recouvrent jamais — le dossier passait alors pour hors racine et
|
|
192
|
-
// rien n'était protégé.
|
|
193
|
-
const absolute = realpathOrParent(path.resolve(String(target)));
|
|
194
|
-
if (absolute === root || !absolute.startsWith(`${root}${path.sep}`)) return null;
|
|
195
|
-
const [caseName, ...rest] = path.relative(root, absolute).split(path.sep);
|
|
196
|
-
if (!caseName || caseName.startsWith('.')) return null;
|
|
197
|
-
const caseRoot = path.join(root, caseName);
|
|
198
|
-
try {
|
|
199
|
-
if (!fs.statSync(caseRoot).isDirectory()) return null;
|
|
200
|
-
} catch {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
return { casesRoot: root, caseName, caseRoot, absolute, relative: rest.join('/') };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
175
|
/** Normalise une liste de chemins relatifs en Set de clés POSIX. */
|
|
207
176
|
function normalizeKeySet(list) {
|
|
208
177
|
return new Set(
|
|
@@ -244,26 +213,76 @@ function readProtection(caseRoot) {
|
|
|
244
213
|
* que `unprotected` — l'initialisation d'un dossier — n'efface pas les
|
|
245
214
|
* ressources déjà déclarées.
|
|
246
215
|
*/
|
|
247
|
-
function writeProtection(caseRoot,
|
|
216
|
+
function writeProtection(caseRoot, lists = {}) {
|
|
217
|
+
return mutateProtection(caseRoot, () => lists);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const PROTECTION_LOCK_TIMEOUT_MS = 3000;
|
|
221
|
+
const PROTECTION_LOCK_STALE_MS = 10000;
|
|
222
|
+
const PROTECTION_LOCK_RETRY_MS = 25;
|
|
223
|
+
|
|
224
|
+
function sleepSync(milliseconds) {
|
|
225
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function acquireProtectionLock(lockFile) {
|
|
229
|
+
const deadline = Date.now() + PROTECTION_LOCK_TIMEOUT_MS;
|
|
230
|
+
for (;;) {
|
|
231
|
+
try {
|
|
232
|
+
fs.closeSync(fs.openSync(lockFile, 'wx'));
|
|
233
|
+
return;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (error.code !== 'EEXIST') throw error;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
if (Date.now() - fs.statSync(lockFile).mtimeMs > PROTECTION_LOCK_STALE_MS) {
|
|
239
|
+
fs.rmSync(lockFile, { force: true });
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (Date.now() > deadline) throw new Error(`Le fichier de protection est verrouillé : ${lockFile}`);
|
|
246
|
+
sleepSync(PROTECTION_LOCK_RETRY_MS);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function mutateProtection(caseRoot, mutate) {
|
|
248
251
|
const file = protectionFile(caseRoot);
|
|
249
|
-
const
|
|
250
|
-
const clean = (list, fallback) => [...new Set(
|
|
251
|
-
(Array.isArray(list) ? list : [...fallback])
|
|
252
|
-
.map((entry) => String(entry || '').replaceAll('\\', '/').trim())
|
|
253
|
-
.filter((entry) => entry && !entry.startsWith('../') && !path.isAbsolute(entry))
|
|
254
|
-
)].sort((a, b) => a.localeCompare(b, 'fr'));
|
|
255
|
-
// Une pièce ne peut être à la fois « espace de travail » et « ressource » :
|
|
256
|
-
// `resources` a priorité, on la retire donc de `unprotected`.
|
|
257
|
-
const resourcesList = clean(resources, existing.resources);
|
|
258
|
-
const resourceSet = new Set(resourcesList);
|
|
259
|
-
const unprotectedList = clean(unprotected, existing.unprotected).filter((key) => !resourceSet.has(key));
|
|
252
|
+
const lockFile = `${file}.lock`;
|
|
260
253
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
254
|
+
acquireProtectionLock(lockFile);
|
|
255
|
+
try {
|
|
256
|
+
const existing = readProtection(caseRoot);
|
|
257
|
+
const next = mutate(existing);
|
|
258
|
+
if (!next) return { file, unprotected: existing.unprotected, resources: existing.resources };
|
|
259
|
+
const { unprotected, resources } = next;
|
|
260
|
+
const clean = (list, fallback) => [...new Set(
|
|
261
|
+
(Array.isArray(list) ? list : [...fallback])
|
|
262
|
+
.map((entry) => String(entry || '').replaceAll('\\', '/').trim())
|
|
263
|
+
.filter((entry) => entry && !entry.startsWith('../') && !path.isAbsolute(entry))
|
|
264
|
+
)].sort((a, b) => a.localeCompare(b, 'fr'));
|
|
265
|
+
// Une pièce ne peut être à la fois « espace de travail » et « ressource » :
|
|
266
|
+
// `resources` a priorité, on la retire donc de `unprotected`.
|
|
267
|
+
const resourcesList = clean(resources, existing.resources);
|
|
268
|
+
const resourceSet = new Set(resourcesList);
|
|
269
|
+
const unprotectedList = clean(unprotected, existing.unprotected).filter((key) => !resourceSet.has(key));
|
|
270
|
+
const temporaryFile = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
271
|
+
try {
|
|
272
|
+
fs.writeFileSync(
|
|
273
|
+
temporaryFile,
|
|
274
|
+
`${JSON.stringify({ version: 1, unprotected: unprotectedList, resources: resourcesList }, null, 2)}\n`,
|
|
275
|
+
'utf8'
|
|
276
|
+
);
|
|
277
|
+
fs.renameSync(temporaryFile, file);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
fs.rmSync(temporaryFile, { force: true });
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
return { file, unprotected: new Set(unprotectedList), resources: resourceSet };
|
|
283
|
+
} finally {
|
|
284
|
+
fs.rmSync(lockFile, { force: true });
|
|
285
|
+
}
|
|
267
286
|
}
|
|
268
287
|
|
|
269
288
|
/** Vrai pour un chemin recevable dans une des deux listes d'exceptions. */
|
|
@@ -367,9 +386,9 @@ function markdownCounterpart(absolutePath, caseRoot) {
|
|
|
367
386
|
* de naître au coffre-fort, elle ne pourrait plus se relire.
|
|
368
387
|
*
|
|
369
388
|
* Ne fait rien, et retourne pourquoi, quand le chemin n'est pas une pièce
|
|
370
|
-
* classable : hors dossier,
|
|
371
|
-
* scan PII (jamais déclassés), espace de travail
|
|
372
|
-
* inscrite. `resources` est prioritaire : une ressource n'est jamais
|
|
389
|
+
* classable : hors dossier, extension jamais protégée (seuls PDF et images le
|
|
390
|
+
* sont), dotfile, mapping ou scan PII (jamais déclassés), espace de travail
|
|
391
|
+
* OOXML implicite, ou clé déjà inscrite. `resources` est prioritaire : une ressource n'est jamais
|
|
373
392
|
* rétrogradée en espace de travail. Idempotent, ne lève jamais.
|
|
374
393
|
*/
|
|
375
394
|
function classifyAsWorkspace(absolutePath, caseRoot) {
|
|
@@ -378,12 +397,15 @@ function classifyAsWorkspace(absolutePath, caseRoot) {
|
|
|
378
397
|
if (isMappingFile(absolutePath)) return { classified: false, key: null, reason: 'mapping' };
|
|
379
398
|
const key = exceptionKey(absolutePath, caseRoot);
|
|
380
399
|
if (!key) return { classified: false, key: null, reason: 'non-classable' };
|
|
400
|
+
if (!PROTECTED_EXTENSIONS.has(path.extname(key).toLowerCase())) return { classified: false, key, reason: 'jamais protégée' };
|
|
381
401
|
if (isOoxmlWorkspacePath(absolutePath, caseRoot)) return { classified: false, key, reason: 'ooxml' };
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
402
|
+
let reason = null;
|
|
403
|
+
mutateProtection(caseRoot, ({ unprotected, resources }) => {
|
|
404
|
+
if (resources.has(key)) reason = 'ressource';
|
|
405
|
+
else if (unprotected.has(key)) reason = 'déjà classée';
|
|
406
|
+
return reason ? null : { unprotected: [...unprotected, key] };
|
|
407
|
+
});
|
|
408
|
+
return { classified: !reason, key, reason };
|
|
387
409
|
} catch (error) {
|
|
388
410
|
return { classified: false, key: null, reason: error?.message || 'erreur' };
|
|
389
411
|
}
|
|
@@ -395,7 +417,6 @@ module.exports = {
|
|
|
395
417
|
exceptionKey,
|
|
396
418
|
isMappingFile,
|
|
397
419
|
isSecretFile,
|
|
398
|
-
locateCase,
|
|
399
420
|
isProtectedFile,
|
|
400
421
|
isProtectionBypassed,
|
|
401
422
|
isOoxmlWorkspacePath,
|