@pactosigna/schemas 0.1.0 → 0.1.2
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/chunk-A3X6VKYK.js +141 -0
- package/dist/cli.js +1545 -0
- package/dist/document-type-patterns-JuAGFi_j.d.ts +64 -0
- package/dist/document-type-patterns.d.ts +1 -0
- package/dist/document-type-patterns.js +14 -0
- package/dist/index.d.ts +459 -223
- package/dist/index.js +968 -468
- package/package.json +11 -1
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1545 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/validate-directory.ts
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
|
|
7
|
+
// src/validate-document.ts
|
|
8
|
+
import matter from "gray-matter";
|
|
9
|
+
|
|
10
|
+
// ../shared/dist/schemas/index.js
|
|
11
|
+
import { z as z24 } from "zod";
|
|
12
|
+
|
|
13
|
+
// ../shared/dist/schemas/common-enums.js
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
var RegulatoryFrameworkSchema = z.enum([
|
|
16
|
+
"ISO_13485",
|
|
17
|
+
"IEC_62304",
|
|
18
|
+
"FDA_21_CFR_820",
|
|
19
|
+
"QMSR",
|
|
20
|
+
"EU_MDR",
|
|
21
|
+
"ISO_14971",
|
|
22
|
+
"AI_ACT"
|
|
23
|
+
]);
|
|
24
|
+
var SafetyClassSchema = z.enum(["A", "B", "C"]);
|
|
25
|
+
var DocumentTypeSchema = z.enum([
|
|
26
|
+
"user_need",
|
|
27
|
+
"requirement",
|
|
28
|
+
"architecture",
|
|
29
|
+
"detailed_design",
|
|
30
|
+
"test_protocol",
|
|
31
|
+
"test_report",
|
|
32
|
+
"sop",
|
|
33
|
+
"work_instruction",
|
|
34
|
+
"policy",
|
|
35
|
+
"usability_risk",
|
|
36
|
+
"software_risk",
|
|
37
|
+
"security_risk",
|
|
38
|
+
// Risk document types
|
|
39
|
+
"haz_soe_software",
|
|
40
|
+
"haz_soe_security",
|
|
41
|
+
"hazardous_situation",
|
|
42
|
+
"harm",
|
|
43
|
+
"hazard_category",
|
|
44
|
+
// Usability engineering (IEC 62366)
|
|
45
|
+
"usability_plan",
|
|
46
|
+
"use_specification",
|
|
47
|
+
"task_analysis",
|
|
48
|
+
"usability_evaluation",
|
|
49
|
+
"summative_evaluation",
|
|
50
|
+
// Risk management (ISO 14971)
|
|
51
|
+
"risk_management_plan",
|
|
52
|
+
// Software lifecycle (IEC 62304)
|
|
53
|
+
"software_development_plan",
|
|
54
|
+
"software_maintenance_plan",
|
|
55
|
+
"soup_register",
|
|
56
|
+
// Problem resolution (IEC 62304 §9)
|
|
57
|
+
"anomaly",
|
|
58
|
+
// Cybersecurity (IEC 81001-5-1)
|
|
59
|
+
"cybersecurity_plan",
|
|
60
|
+
"sbom",
|
|
61
|
+
// Clinical (MDR / FDA)
|
|
62
|
+
"clinical_evaluation_plan",
|
|
63
|
+
"clinical_evaluation_report",
|
|
64
|
+
// Post-market surveillance (MDR Art. 83)
|
|
65
|
+
"post_market_surveillance_plan",
|
|
66
|
+
"post_market_feedback",
|
|
67
|
+
// Labeling (MDR Annex I)
|
|
68
|
+
"labeling",
|
|
69
|
+
// Product (ISO 13485 §7.3)
|
|
70
|
+
"product_development_plan",
|
|
71
|
+
"intended_use",
|
|
72
|
+
// Release management (IEC 62304 §5.7)
|
|
73
|
+
"release_plan",
|
|
74
|
+
// Change management (ISO 13485 §7.3.5)
|
|
75
|
+
"design_review",
|
|
76
|
+
"release_notes",
|
|
77
|
+
// Supplier management (ISO 13485 §7.4)
|
|
78
|
+
"supplier",
|
|
79
|
+
// Internal audit management (ISO 13485 §8.2.2)
|
|
80
|
+
"audit_schedule",
|
|
81
|
+
"audit_report",
|
|
82
|
+
// Management review (ISO 13485 §5.6)
|
|
83
|
+
"management_review",
|
|
84
|
+
// Software test plan (IEC 62304 §5.7)
|
|
85
|
+
"software_test_plan"
|
|
86
|
+
]);
|
|
87
|
+
var DocumentStatusSchema = z.enum([
|
|
88
|
+
"draft",
|
|
89
|
+
"in_review",
|
|
90
|
+
"approved",
|
|
91
|
+
"obsolete",
|
|
92
|
+
"example"
|
|
93
|
+
]);
|
|
94
|
+
var LinkTypeSchema = z.enum([
|
|
95
|
+
"derives_from",
|
|
96
|
+
"implements",
|
|
97
|
+
"verified_by",
|
|
98
|
+
"mitigates",
|
|
99
|
+
"parent_of",
|
|
100
|
+
"related_to",
|
|
101
|
+
// New risk link types
|
|
102
|
+
"leads_to",
|
|
103
|
+
"results_in",
|
|
104
|
+
"analyzes"
|
|
105
|
+
]);
|
|
106
|
+
var AcceptabilityStatusSchema = z.enum(["acceptable", "review_required", "unacceptable"]);
|
|
107
|
+
var DepartmentRoleSchema = z.enum(["manager", "member"]);
|
|
108
|
+
|
|
109
|
+
// ../shared/dist/schemas/risk.js
|
|
110
|
+
import { z as z2 } from "zod";
|
|
111
|
+
var RiskDocumentStatusSchema = z2.enum([
|
|
112
|
+
"draft",
|
|
113
|
+
"in_review",
|
|
114
|
+
"approved",
|
|
115
|
+
"effective",
|
|
116
|
+
"archived",
|
|
117
|
+
"example"
|
|
118
|
+
]);
|
|
119
|
+
var MitigationTargetSchema = z2.enum([
|
|
120
|
+
"sequence_probability",
|
|
121
|
+
"harm_probability",
|
|
122
|
+
"severity"
|
|
123
|
+
]);
|
|
124
|
+
var RiskGapCodeSchema = z2.enum([
|
|
125
|
+
"hazard_no_situation",
|
|
126
|
+
"situation_no_harm",
|
|
127
|
+
"hazard_not_analyzed",
|
|
128
|
+
"missing_mitigation",
|
|
129
|
+
"broken_mitigation_link",
|
|
130
|
+
"control_not_approved",
|
|
131
|
+
"missing_risk_benefit",
|
|
132
|
+
"risk_missing_safety_chain",
|
|
133
|
+
"risk_broken_safety_chain",
|
|
134
|
+
"wrong_probability_dimension",
|
|
135
|
+
"missing_frontmatter",
|
|
136
|
+
"requirement_no_architecture",
|
|
137
|
+
"architecture_no_segregation",
|
|
138
|
+
"architecture_no_parent",
|
|
139
|
+
"haz_missing_category",
|
|
140
|
+
"haz_invalid_category",
|
|
141
|
+
"category_not_approved"
|
|
142
|
+
]);
|
|
143
|
+
var RiskGapSeveritySchema = z2.enum(["error", "warning"]);
|
|
144
|
+
var MitigationSchema = z2.object({
|
|
145
|
+
control: z2.string().min(1),
|
|
146
|
+
reduces: MitigationTargetSchema,
|
|
147
|
+
for_harm: z2.string().optional()
|
|
148
|
+
});
|
|
149
|
+
var HarmAssessmentSchema = z2.object({
|
|
150
|
+
harm: z2.string().min(1),
|
|
151
|
+
inherent_probability: z2.number().int().min(1).max(5).optional(),
|
|
152
|
+
inherent_exploitability: z2.number().int().min(1).max(5).optional(),
|
|
153
|
+
residual_probability: z2.number().int().min(1).max(5).optional(),
|
|
154
|
+
residual_exploitability: z2.number().int().min(1).max(5).optional(),
|
|
155
|
+
residual_severity_override: z2.number().int().min(1).max(5).optional()
|
|
156
|
+
});
|
|
157
|
+
var RiskEntryFrontmatterSchema = z2.object({
|
|
158
|
+
type: z2.enum(["software_risk", "usability_risk", "security_risk"]),
|
|
159
|
+
id: z2.string().min(1),
|
|
160
|
+
title: z2.string().min(1),
|
|
161
|
+
status: RiskDocumentStatusSchema,
|
|
162
|
+
analyzes: z2.string().min(1),
|
|
163
|
+
hazardous_situation: z2.string().min(1),
|
|
164
|
+
harm_assessments: z2.array(HarmAssessmentSchema).min(1),
|
|
165
|
+
mitigations: z2.array(MitigationSchema).optional(),
|
|
166
|
+
cvss_score: z2.number().min(0).max(10).optional(),
|
|
167
|
+
cvss_vector: z2.string().regex(/^CVSS:3\.[01]\/AV:[NALP]\/AC:[LH]\/PR:[NLH]\/UI:[NR]\/S:[UC]\/C:[NLH]\/I:[NLH]\/A:[NLH]$/).optional()
|
|
168
|
+
}).refine((data) => {
|
|
169
|
+
if (data.type === "security_risk") {
|
|
170
|
+
return data.harm_assessments.every((ha) => ha.inherent_exploitability != null);
|
|
171
|
+
}
|
|
172
|
+
return data.harm_assessments.every((ha) => ha.inherent_probability != null);
|
|
173
|
+
}, {
|
|
174
|
+
message: "Security risks must use inherent_exploitability; software/usability risks must use inherent_probability"
|
|
175
|
+
});
|
|
176
|
+
var HazardFrontmatterSchema = z2.object({
|
|
177
|
+
type: z2.enum(["haz_soe_software", "haz_soe_security"]),
|
|
178
|
+
id: z2.string().min(1),
|
|
179
|
+
title: z2.string().min(1),
|
|
180
|
+
status: RiskDocumentStatusSchema,
|
|
181
|
+
leads_to: z2.array(z2.string()).optional(),
|
|
182
|
+
// sFMEA fields
|
|
183
|
+
failure_mode: z2.string().optional(),
|
|
184
|
+
cause: z2.string().optional(),
|
|
185
|
+
detection_method: z2.string().optional(),
|
|
186
|
+
// STRIDE fields
|
|
187
|
+
threat_category: z2.string().optional(),
|
|
188
|
+
attack_vector: z2.string().optional(),
|
|
189
|
+
// Hazard category reference (HC-xxx)
|
|
190
|
+
hazard_category: z2.string().optional()
|
|
191
|
+
});
|
|
192
|
+
var HazardCategoryFrontmatterSchema = z2.object({
|
|
193
|
+
type: z2.literal("hazard_category"),
|
|
194
|
+
id: z2.string().min(1),
|
|
195
|
+
title: z2.string().min(1),
|
|
196
|
+
status: RiskDocumentStatusSchema,
|
|
197
|
+
source: z2.string().optional()
|
|
198
|
+
});
|
|
199
|
+
var HazardousSituationFrontmatterSchema = z2.object({
|
|
200
|
+
type: z2.literal("hazardous_situation"),
|
|
201
|
+
id: z2.string().min(1),
|
|
202
|
+
title: z2.string().min(1),
|
|
203
|
+
status: RiskDocumentStatusSchema,
|
|
204
|
+
results_in: z2.array(z2.string()).optional()
|
|
205
|
+
});
|
|
206
|
+
var HarmFrontmatterSchema = z2.object({
|
|
207
|
+
type: z2.literal("harm"),
|
|
208
|
+
id: z2.string().min(1),
|
|
209
|
+
title: z2.string().min(1),
|
|
210
|
+
status: RiskDocumentStatusSchema,
|
|
211
|
+
severity: z2.number().int().min(1).max(5),
|
|
212
|
+
category: z2.string().optional()
|
|
213
|
+
});
|
|
214
|
+
var RiskMatrixConfigSchema = z2.object({
|
|
215
|
+
version: z2.number(),
|
|
216
|
+
labels: z2.object({
|
|
217
|
+
severity: z2.array(z2.string()).length(5),
|
|
218
|
+
probability: z2.array(z2.string()).length(5),
|
|
219
|
+
exploitability: z2.array(z2.string()).length(5).optional()
|
|
220
|
+
}),
|
|
221
|
+
acceptability: z2.object({
|
|
222
|
+
unacceptable: z2.array(z2.tuple([z2.number(), z2.number()])),
|
|
223
|
+
review_required: z2.array(z2.tuple([z2.number(), z2.number()])).optional()
|
|
224
|
+
}),
|
|
225
|
+
overrides: z2.record(z2.object({
|
|
226
|
+
unacceptable: z2.array(z2.tuple([z2.number(), z2.number()])),
|
|
227
|
+
review_required: z2.array(z2.tuple([z2.number(), z2.number()])).optional()
|
|
228
|
+
})).optional()
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// ../shared/dist/schemas/usability.js
|
|
232
|
+
import { z as z3 } from "zod";
|
|
233
|
+
var UsabilityPlanFrontmatterSchema = z3.object({
|
|
234
|
+
type: z3.literal("usability_plan"),
|
|
235
|
+
id: z3.string().min(1),
|
|
236
|
+
title: z3.string().min(1),
|
|
237
|
+
status: RiskDocumentStatusSchema,
|
|
238
|
+
author: z3.string().optional(),
|
|
239
|
+
reviewers: z3.array(z3.string()).optional(),
|
|
240
|
+
approvers: z3.array(z3.string()).optional()
|
|
241
|
+
});
|
|
242
|
+
var UseSpecificationFrontmatterSchema = z3.object({
|
|
243
|
+
type: z3.literal("use_specification"),
|
|
244
|
+
id: z3.string().min(1),
|
|
245
|
+
title: z3.string().min(1),
|
|
246
|
+
status: RiskDocumentStatusSchema,
|
|
247
|
+
user_group: z3.string().min(1),
|
|
248
|
+
author: z3.string().optional(),
|
|
249
|
+
reviewers: z3.array(z3.string()).optional(),
|
|
250
|
+
approvers: z3.array(z3.string()).optional()
|
|
251
|
+
});
|
|
252
|
+
var TaskAnalysisFrontmatterSchema = z3.object({
|
|
253
|
+
type: z3.literal("task_analysis"),
|
|
254
|
+
id: z3.string().min(1),
|
|
255
|
+
title: z3.string().min(1),
|
|
256
|
+
status: RiskDocumentStatusSchema,
|
|
257
|
+
user_group: z3.string().min(1),
|
|
258
|
+
critical_task: z3.boolean(),
|
|
259
|
+
author: z3.string().optional(),
|
|
260
|
+
reviewers: z3.array(z3.string()).optional(),
|
|
261
|
+
approvers: z3.array(z3.string()).optional()
|
|
262
|
+
});
|
|
263
|
+
var UsabilityEvaluationFrontmatterSchema = z3.object({
|
|
264
|
+
type: z3.literal("usability_evaluation"),
|
|
265
|
+
id: z3.string().min(1),
|
|
266
|
+
title: z3.string().min(1),
|
|
267
|
+
status: RiskDocumentStatusSchema,
|
|
268
|
+
result: z3.enum(["pass", "fail", "pass_with_findings"]).optional(),
|
|
269
|
+
author: z3.string().optional(),
|
|
270
|
+
reviewers: z3.array(z3.string()).optional(),
|
|
271
|
+
approvers: z3.array(z3.string()).optional()
|
|
272
|
+
});
|
|
273
|
+
var SummativeEvaluationFrontmatterSchema = z3.object({
|
|
274
|
+
type: z3.literal("summative_evaluation"),
|
|
275
|
+
id: z3.string().min(1),
|
|
276
|
+
title: z3.string().min(1),
|
|
277
|
+
status: RiskDocumentStatusSchema,
|
|
278
|
+
result: z3.enum(["pass", "fail", "pass_with_findings"]).optional(),
|
|
279
|
+
author: z3.string().optional(),
|
|
280
|
+
reviewers: z3.array(z3.string()).optional(),
|
|
281
|
+
approvers: z3.array(z3.string()).optional()
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// ../shared/dist/schemas/risk-management-plan.js
|
|
285
|
+
import { z as z4 } from "zod";
|
|
286
|
+
var RiskManagementPlanFrontmatterSchema = z4.object({
|
|
287
|
+
type: z4.literal("risk_management_plan"),
|
|
288
|
+
id: z4.string().min(1),
|
|
289
|
+
title: z4.string().min(1),
|
|
290
|
+
status: RiskDocumentStatusSchema,
|
|
291
|
+
author: z4.string().optional(),
|
|
292
|
+
reviewers: z4.array(z4.string()).optional(),
|
|
293
|
+
approvers: z4.array(z4.string()).optional()
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ../shared/dist/schemas/software-lifecycle.js
|
|
297
|
+
import { z as z5 } from "zod";
|
|
298
|
+
var SoftwareDevelopmentPlanFrontmatterSchema = z5.object({
|
|
299
|
+
type: z5.literal("software_development_plan"),
|
|
300
|
+
id: z5.string().min(1),
|
|
301
|
+
title: z5.string().min(1),
|
|
302
|
+
status: RiskDocumentStatusSchema,
|
|
303
|
+
author: z5.string().optional(),
|
|
304
|
+
reviewers: z5.array(z5.string()).optional(),
|
|
305
|
+
approvers: z5.array(z5.string()).optional()
|
|
306
|
+
});
|
|
307
|
+
var SoftwareMaintenancePlanFrontmatterSchema = z5.object({
|
|
308
|
+
type: z5.literal("software_maintenance_plan"),
|
|
309
|
+
id: z5.string().min(1),
|
|
310
|
+
title: z5.string().min(1),
|
|
311
|
+
status: RiskDocumentStatusSchema,
|
|
312
|
+
author: z5.string().optional(),
|
|
313
|
+
reviewers: z5.array(z5.string()).optional(),
|
|
314
|
+
approvers: z5.array(z5.string()).optional()
|
|
315
|
+
});
|
|
316
|
+
var SoupRegisterFrontmatterSchema = z5.object({
|
|
317
|
+
type: z5.literal("soup_register"),
|
|
318
|
+
id: z5.string().min(1),
|
|
319
|
+
title: z5.string().min(1),
|
|
320
|
+
status: RiskDocumentStatusSchema,
|
|
321
|
+
author: z5.string().optional(),
|
|
322
|
+
reviewers: z5.array(z5.string()).optional(),
|
|
323
|
+
approvers: z5.array(z5.string()).optional()
|
|
324
|
+
});
|
|
325
|
+
var SoftwareTestPlanFrontmatterSchema = z5.object({
|
|
326
|
+
type: z5.literal("software_test_plan"),
|
|
327
|
+
id: z5.string().min(1),
|
|
328
|
+
title: z5.string().min(1),
|
|
329
|
+
status: RiskDocumentStatusSchema,
|
|
330
|
+
author: z5.string().optional(),
|
|
331
|
+
reviewers: z5.array(z5.string()).optional(),
|
|
332
|
+
approvers: z5.array(z5.string()).optional()
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ../shared/dist/schemas/architecture.js
|
|
336
|
+
import { z as z6 } from "zod";
|
|
337
|
+
var SoftwareItemTypeSchema = z6.enum(["system", "subsystem", "component", "unit"]);
|
|
338
|
+
var SegregationSchema = z6.object({
|
|
339
|
+
mechanism: z6.string().min(1),
|
|
340
|
+
rationale: z6.string().min(1)
|
|
341
|
+
});
|
|
342
|
+
var ArchitectureFrontmatterSchema = z6.object({
|
|
343
|
+
id: z6.string().min(1),
|
|
344
|
+
title: z6.string().min(1),
|
|
345
|
+
type: z6.literal("architecture"),
|
|
346
|
+
status: RiskDocumentStatusSchema,
|
|
347
|
+
software_item_type: SoftwareItemTypeSchema.optional(),
|
|
348
|
+
parent_item: z6.string().optional(),
|
|
349
|
+
safety_class: z6.enum(["A", "B", "C"]).optional(),
|
|
350
|
+
segregation: SegregationSchema.optional(),
|
|
351
|
+
author: z6.string().optional(),
|
|
352
|
+
reviewers: z6.array(z6.string()).optional(),
|
|
353
|
+
approvers: z6.array(z6.string()).optional()
|
|
354
|
+
});
|
|
355
|
+
var DetailedDesignFrontmatterSchema = z6.object({
|
|
356
|
+
id: z6.string().min(1),
|
|
357
|
+
title: z6.string().min(1),
|
|
358
|
+
type: z6.literal("detailed_design"),
|
|
359
|
+
status: RiskDocumentStatusSchema,
|
|
360
|
+
software_item_type: SoftwareItemTypeSchema.optional(),
|
|
361
|
+
parent_item: z6.string().optional(),
|
|
362
|
+
safety_class: z6.enum(["A", "B", "C"]).optional(),
|
|
363
|
+
segregation: SegregationSchema.optional(),
|
|
364
|
+
author: z6.string().optional(),
|
|
365
|
+
reviewers: z6.array(z6.string()).optional(),
|
|
366
|
+
approvers: z6.array(z6.string()).optional()
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// ../shared/dist/schemas/anomaly.js
|
|
370
|
+
import { z as z7 } from "zod";
|
|
371
|
+
var AnomalyCategorySchema = z7.enum([
|
|
372
|
+
"bug",
|
|
373
|
+
"security_vulnerability",
|
|
374
|
+
"regression",
|
|
375
|
+
"performance"
|
|
376
|
+
]);
|
|
377
|
+
var AnomalySeveritySchema = z7.enum(["critical", "major", "minor"]);
|
|
378
|
+
var AnomalyDispositionSchema = z7.enum([
|
|
379
|
+
"open",
|
|
380
|
+
"investigating",
|
|
381
|
+
"resolved",
|
|
382
|
+
"deferred",
|
|
383
|
+
"will_not_fix"
|
|
384
|
+
]);
|
|
385
|
+
var AnomalyFrontmatterSchema = z7.object({
|
|
386
|
+
type: z7.literal("anomaly"),
|
|
387
|
+
id: z7.string().min(1),
|
|
388
|
+
title: z7.string().min(1),
|
|
389
|
+
status: RiskDocumentStatusSchema,
|
|
390
|
+
category: AnomalyCategorySchema,
|
|
391
|
+
severity: AnomalySeveritySchema,
|
|
392
|
+
disposition: AnomalyDispositionSchema,
|
|
393
|
+
affected_version: z7.string().optional(),
|
|
394
|
+
author: z7.string().optional(),
|
|
395
|
+
reviewers: z7.array(z7.string()).optional(),
|
|
396
|
+
approvers: z7.array(z7.string()).optional()
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// ../shared/dist/schemas/cybersecurity.js
|
|
400
|
+
import { z as z8 } from "zod";
|
|
401
|
+
var CybersecurityPlanFrontmatterSchema = z8.object({
|
|
402
|
+
type: z8.literal("cybersecurity_plan"),
|
|
403
|
+
id: z8.string().min(1),
|
|
404
|
+
title: z8.string().min(1),
|
|
405
|
+
status: RiskDocumentStatusSchema,
|
|
406
|
+
author: z8.string().optional(),
|
|
407
|
+
reviewers: z8.array(z8.string()).optional(),
|
|
408
|
+
approvers: z8.array(z8.string()).optional()
|
|
409
|
+
});
|
|
410
|
+
var SbomFrontmatterSchema = z8.object({
|
|
411
|
+
type: z8.literal("sbom"),
|
|
412
|
+
id: z8.string().min(1),
|
|
413
|
+
title: z8.string().min(1),
|
|
414
|
+
status: RiskDocumentStatusSchema,
|
|
415
|
+
author: z8.string().optional(),
|
|
416
|
+
reviewers: z8.array(z8.string()).optional(),
|
|
417
|
+
approvers: z8.array(z8.string()).optional()
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// ../shared/dist/schemas/clinical.js
|
|
421
|
+
import { z as z9 } from "zod";
|
|
422
|
+
var ClinicalEvaluationPlanFrontmatterSchema = z9.object({
|
|
423
|
+
type: z9.literal("clinical_evaluation_plan"),
|
|
424
|
+
id: z9.string().min(1),
|
|
425
|
+
title: z9.string().min(1),
|
|
426
|
+
status: RiskDocumentStatusSchema,
|
|
427
|
+
author: z9.string().optional(),
|
|
428
|
+
reviewers: z9.array(z9.string()).optional(),
|
|
429
|
+
approvers: z9.array(z9.string()).optional()
|
|
430
|
+
});
|
|
431
|
+
var ClinicalEvaluationReportFrontmatterSchema = z9.object({
|
|
432
|
+
type: z9.literal("clinical_evaluation_report"),
|
|
433
|
+
id: z9.string().min(1),
|
|
434
|
+
title: z9.string().min(1),
|
|
435
|
+
status: RiskDocumentStatusSchema,
|
|
436
|
+
author: z9.string().optional(),
|
|
437
|
+
reviewers: z9.array(z9.string()).optional(),
|
|
438
|
+
approvers: z9.array(z9.string()).optional()
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ../shared/dist/schemas/post-market.js
|
|
442
|
+
import { z as z10 } from "zod";
|
|
443
|
+
var PostMarketSurveillancePlanFrontmatterSchema = z10.object({
|
|
444
|
+
type: z10.literal("post_market_surveillance_plan"),
|
|
445
|
+
id: z10.string().min(1),
|
|
446
|
+
title: z10.string().min(1),
|
|
447
|
+
status: RiskDocumentStatusSchema,
|
|
448
|
+
author: z10.string().optional(),
|
|
449
|
+
reviewers: z10.array(z10.string()).optional(),
|
|
450
|
+
approvers: z10.array(z10.string()).optional()
|
|
451
|
+
});
|
|
452
|
+
var PostMarketFeedbackCategorySchema = z10.enum([
|
|
453
|
+
"complaint",
|
|
454
|
+
"field_observation",
|
|
455
|
+
"clinical_followup",
|
|
456
|
+
"trend_report"
|
|
457
|
+
]);
|
|
458
|
+
var PostMarketFeedbackSeveritySchema = z10.enum(["low", "medium", "high", "critical"]);
|
|
459
|
+
var PostMarketFeedbackFrontmatterSchema = z10.object({
|
|
460
|
+
type: z10.literal("post_market_feedback"),
|
|
461
|
+
id: z10.string().min(1),
|
|
462
|
+
title: z10.string().min(1),
|
|
463
|
+
status: RiskDocumentStatusSchema,
|
|
464
|
+
category: PostMarketFeedbackCategorySchema,
|
|
465
|
+
severity: PostMarketFeedbackSeveritySchema,
|
|
466
|
+
device: z10.string().optional(),
|
|
467
|
+
reporting_period: z10.string().optional(),
|
|
468
|
+
author: z10.string().optional(),
|
|
469
|
+
reviewers: z10.array(z10.string()).optional(),
|
|
470
|
+
approvers: z10.array(z10.string()).optional()
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// ../shared/dist/schemas/labeling.js
|
|
474
|
+
import { z as z11 } from "zod";
|
|
475
|
+
var LabelingFrontmatterSchema = z11.object({
|
|
476
|
+
type: z11.literal("labeling"),
|
|
477
|
+
id: z11.string().min(1),
|
|
478
|
+
title: z11.string().min(1),
|
|
479
|
+
status: RiskDocumentStatusSchema,
|
|
480
|
+
label_type: z11.enum(["ifu", "product_label", "packaging_label"]).optional(),
|
|
481
|
+
author: z11.string().optional(),
|
|
482
|
+
reviewers: z11.array(z11.string()).optional(),
|
|
483
|
+
approvers: z11.array(z11.string()).optional()
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// ../shared/dist/schemas/product.js
|
|
487
|
+
import { z as z12 } from "zod";
|
|
488
|
+
var ProductDevelopmentPlanFrontmatterSchema = z12.object({
|
|
489
|
+
type: z12.literal("product_development_plan"),
|
|
490
|
+
id: z12.string().min(1),
|
|
491
|
+
title: z12.string().min(1),
|
|
492
|
+
status: RiskDocumentStatusSchema,
|
|
493
|
+
author: z12.string().optional(),
|
|
494
|
+
reviewers: z12.array(z12.string()).optional(),
|
|
495
|
+
approvers: z12.array(z12.string()).optional()
|
|
496
|
+
});
|
|
497
|
+
var IntendedUseFrontmatterSchema = z12.object({
|
|
498
|
+
type: z12.literal("intended_use"),
|
|
499
|
+
id: z12.string().min(1),
|
|
500
|
+
title: z12.string().min(1),
|
|
501
|
+
status: RiskDocumentStatusSchema,
|
|
502
|
+
author: z12.string().optional(),
|
|
503
|
+
reviewers: z12.array(z12.string()).optional(),
|
|
504
|
+
approvers: z12.array(z12.string()).optional()
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// ../shared/dist/schemas/user-need.js
|
|
508
|
+
import { z as z13 } from "zod";
|
|
509
|
+
var UserNeedPrioritySchema = z13.enum(["must_have", "should_have", "nice_to_have"]);
|
|
510
|
+
var UserNeedFrontmatterSchema = z13.object({
|
|
511
|
+
id: z13.string().min(1),
|
|
512
|
+
title: z13.string().min(1),
|
|
513
|
+
status: RiskDocumentStatusSchema,
|
|
514
|
+
/** Validated if present — ensures frontmatter doesn't misidentify the document type */
|
|
515
|
+
type: z13.literal("user_need").optional(),
|
|
516
|
+
/** The user role or stakeholder (e.g., "Quality Manager", "Developer") */
|
|
517
|
+
stakeholder: z13.string().optional(),
|
|
518
|
+
/** MoSCoW priority classification */
|
|
519
|
+
priority: UserNeedPrioritySchema.optional(),
|
|
520
|
+
/** Where this need originated (e.g., "ISO 13485 §7.3", "user interview") */
|
|
521
|
+
source: z13.string().optional(),
|
|
522
|
+
/** IDs of product requirements derived from this need */
|
|
523
|
+
derives: z13.array(z13.string()).optional(),
|
|
524
|
+
author: z13.string().optional(),
|
|
525
|
+
reviewers: z13.array(z13.string()).optional(),
|
|
526
|
+
approvers: z13.array(z13.string()).optional()
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// ../shared/dist/schemas/requirement.js
|
|
530
|
+
import { z as z14 } from "zod";
|
|
531
|
+
var RequirementTypeSchema = z14.enum([
|
|
532
|
+
"functional",
|
|
533
|
+
"interface",
|
|
534
|
+
"performance",
|
|
535
|
+
"security",
|
|
536
|
+
"usability",
|
|
537
|
+
"safety",
|
|
538
|
+
"regulatory"
|
|
539
|
+
]);
|
|
540
|
+
var RequirementFormatSchema = z14.enum(["standard", "user_story"]);
|
|
541
|
+
var RequirementFulfillmentTypeSchema = z14.enum([
|
|
542
|
+
"software",
|
|
543
|
+
"labeling",
|
|
544
|
+
"clinical",
|
|
545
|
+
"usability",
|
|
546
|
+
"regulatory_doc",
|
|
547
|
+
"process"
|
|
548
|
+
]);
|
|
549
|
+
var RequirementFrontmatterSchema = z14.object({
|
|
550
|
+
id: z14.string().min(1),
|
|
551
|
+
title: z14.string().min(1),
|
|
552
|
+
status: RiskDocumentStatusSchema,
|
|
553
|
+
/** IEC 62304 §5.2.2 — requirement classification (required for SRS, optional for PRS) */
|
|
554
|
+
req_type: RequirementTypeSchema.optional(),
|
|
555
|
+
/** Authoring convention — controls which required sections are checked */
|
|
556
|
+
format: RequirementFormatSchema.optional(),
|
|
557
|
+
/** ISO 13485 §7.3.3 — how this PRS design input is fulfilled (PRS only, defaults to 'software') */
|
|
558
|
+
fulfillment_type: RequirementFulfillmentTypeSchema.optional(),
|
|
559
|
+
author: z14.string().optional(),
|
|
560
|
+
reviewers: z14.array(z14.string()).optional(),
|
|
561
|
+
approvers: z14.array(z14.string()).optional(),
|
|
562
|
+
/** Upstream traceability — IDs of documents this requirement traces from (e.g., UN for PRS, PRS for SRS) */
|
|
563
|
+
traces_from: z14.array(z14.string()).optional(),
|
|
564
|
+
/** Downstream traceability — IDs of documents this requirement traces to (e.g., SRS for PRS) */
|
|
565
|
+
traces_to: z14.array(z14.string()).optional()
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// ../shared/dist/schemas/test-documents.js
|
|
569
|
+
import { z as z15 } from "zod";
|
|
570
|
+
var TestProtocolFrontmatterSchema = z15.object({
|
|
571
|
+
type: z15.literal("test_protocol"),
|
|
572
|
+
id: z15.string().min(1),
|
|
573
|
+
title: z15.string().min(1),
|
|
574
|
+
status: RiskDocumentStatusSchema,
|
|
575
|
+
author: z15.string().optional(),
|
|
576
|
+
reviewers: z15.array(z15.string()).optional(),
|
|
577
|
+
approvers: z15.array(z15.string()).optional()
|
|
578
|
+
});
|
|
579
|
+
var TestPhaseSchema = z15.enum(["verification", "production"]);
|
|
580
|
+
var TestReportFrontmatterSchema = z15.object({
|
|
581
|
+
type: z15.literal("test_report"),
|
|
582
|
+
id: z15.string().min(1),
|
|
583
|
+
title: z15.string().min(1),
|
|
584
|
+
status: RiskDocumentStatusSchema,
|
|
585
|
+
author: z15.string().optional(),
|
|
586
|
+
reviewers: z15.array(z15.string()).optional(),
|
|
587
|
+
approvers: z15.array(z15.string()).optional(),
|
|
588
|
+
release_version: z15.string().optional(),
|
|
589
|
+
test_phase: TestPhaseSchema.optional()
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// ../shared/dist/schemas/repo-config.js
|
|
593
|
+
import { z as z16 } from "zod";
|
|
594
|
+
var ReleaseReviewConfigSchema = z16.object({
|
|
595
|
+
required_departments: z16.array(z16.string().min(1)).min(1),
|
|
596
|
+
final_approver: z16.string().min(1)
|
|
597
|
+
});
|
|
598
|
+
var RepoConfigSchema = z16.object({
|
|
599
|
+
device: z16.object({
|
|
600
|
+
name: z16.string().min(1),
|
|
601
|
+
safety_class: z16.enum(["A", "B", "C"]),
|
|
602
|
+
classification: z16.object({
|
|
603
|
+
eu: z16.string().optional(),
|
|
604
|
+
us: z16.string().optional()
|
|
605
|
+
}).optional(),
|
|
606
|
+
udi_device_identifier: z16.string().optional()
|
|
607
|
+
}),
|
|
608
|
+
release_review: ReleaseReviewConfigSchema.optional()
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// ../shared/dist/schemas/qms-device.js
|
|
612
|
+
import { z as z17 } from "zod";
|
|
613
|
+
var OwnerTypeSchema = z17.enum(["qms", "device"]);
|
|
614
|
+
var DocumentSnapshotSchema = z17.object({
|
|
615
|
+
documentId: z17.string(),
|
|
616
|
+
commitSha: z17.string(),
|
|
617
|
+
documentPath: z17.string(),
|
|
618
|
+
documentTitle: z17.string(),
|
|
619
|
+
documentType: z17.string(),
|
|
620
|
+
previousReleasedCommitSha: z17.string().optional(),
|
|
621
|
+
changeType: z17.enum(["added", "modified", "unchanged"]),
|
|
622
|
+
changeDescription: z17.string().optional()
|
|
623
|
+
});
|
|
624
|
+
var QmsDocumentTypeSchema = z17.enum([
|
|
625
|
+
"sop",
|
|
626
|
+
"policy",
|
|
627
|
+
"work_instruction",
|
|
628
|
+
"supplier",
|
|
629
|
+
"audit_schedule",
|
|
630
|
+
"audit_report",
|
|
631
|
+
"management_review"
|
|
632
|
+
]);
|
|
633
|
+
var DeviceDocumentTypeSchema = z17.enum([
|
|
634
|
+
"user_need",
|
|
635
|
+
"requirement",
|
|
636
|
+
"architecture",
|
|
637
|
+
"detailed_design",
|
|
638
|
+
"test_protocol",
|
|
639
|
+
"test_report",
|
|
640
|
+
"usability_risk",
|
|
641
|
+
"software_risk",
|
|
642
|
+
"security_risk",
|
|
643
|
+
"hazardous_situation",
|
|
644
|
+
"harm",
|
|
645
|
+
"haz_soe_software",
|
|
646
|
+
"haz_soe_security",
|
|
647
|
+
"hazard_category",
|
|
648
|
+
// Usability engineering (IEC 62366)
|
|
649
|
+
"usability_plan",
|
|
650
|
+
"use_specification",
|
|
651
|
+
"task_analysis",
|
|
652
|
+
"usability_evaluation",
|
|
653
|
+
"summative_evaluation",
|
|
654
|
+
// Risk management (ISO 14971)
|
|
655
|
+
"risk_management_plan",
|
|
656
|
+
// Software lifecycle (IEC 62304)
|
|
657
|
+
"software_development_plan",
|
|
658
|
+
"software_maintenance_plan",
|
|
659
|
+
"soup_register",
|
|
660
|
+
// Problem resolution (IEC 62304 §9)
|
|
661
|
+
"anomaly",
|
|
662
|
+
// Cybersecurity (IEC 81001-5-1)
|
|
663
|
+
"cybersecurity_plan",
|
|
664
|
+
"sbom",
|
|
665
|
+
// Clinical (MDR / FDA)
|
|
666
|
+
"clinical_evaluation_plan",
|
|
667
|
+
"clinical_evaluation_report",
|
|
668
|
+
// Post-market surveillance (MDR Art. 83)
|
|
669
|
+
"post_market_surveillance_plan",
|
|
670
|
+
"post_market_feedback",
|
|
671
|
+
// Labeling (MDR Annex I)
|
|
672
|
+
"labeling",
|
|
673
|
+
// Product (ISO 13485 §7.3)
|
|
674
|
+
"product_development_plan",
|
|
675
|
+
"intended_use",
|
|
676
|
+
// Release management (IEC 62304 §5.7)
|
|
677
|
+
"release_plan",
|
|
678
|
+
// Change management (ISO 13485 §7.3.5)
|
|
679
|
+
"design_review",
|
|
680
|
+
"release_notes",
|
|
681
|
+
// Software test plan (IEC 62304 §5.7)
|
|
682
|
+
"software_test_plan"
|
|
683
|
+
]);
|
|
684
|
+
|
|
685
|
+
// ../shared/dist/schemas/change-management.js
|
|
686
|
+
import { z as z18 } from "zod";
|
|
687
|
+
var ReleasePlanFrontmatterSchema = z18.object({
|
|
688
|
+
id: z18.string().min(1),
|
|
689
|
+
title: z18.string().min(1),
|
|
690
|
+
type: z18.literal("release_plan").optional(),
|
|
691
|
+
status: z18.string().optional(),
|
|
692
|
+
author: z18.string().optional(),
|
|
693
|
+
reviewers: z18.array(z18.string()).optional(),
|
|
694
|
+
approvers: z18.array(z18.string()).optional(),
|
|
695
|
+
version: z18.string().optional(),
|
|
696
|
+
target_date: z18.string().optional()
|
|
697
|
+
});
|
|
698
|
+
var SuspectedLinkDispositionSchema = z18.enum(["included_in_release", "not_impacted"]);
|
|
699
|
+
var SuspectedLinkNeighborSchema = z18.object({
|
|
700
|
+
document: z18.string().min(1),
|
|
701
|
+
direction: z18.enum(["upstream", "downstream"]),
|
|
702
|
+
disposition: SuspectedLinkDispositionSchema,
|
|
703
|
+
/** Required when disposition is 'not_impacted' */
|
|
704
|
+
rationale: z18.string().min(1).optional()
|
|
705
|
+
});
|
|
706
|
+
var SuspectedLinkGroupSchema = z18.object({
|
|
707
|
+
triggered_by: z18.string().min(1),
|
|
708
|
+
neighbors: z18.array(SuspectedLinkNeighborSchema).min(1)
|
|
709
|
+
});
|
|
710
|
+
var DesignReviewFrontmatterSchema = z18.object({
|
|
711
|
+
id: z18.string().min(1),
|
|
712
|
+
title: z18.string().min(1),
|
|
713
|
+
type: z18.literal("design_review").optional(),
|
|
714
|
+
status: z18.string().optional(),
|
|
715
|
+
author: z18.string().optional(),
|
|
716
|
+
reviewers: z18.array(z18.string()).optional(),
|
|
717
|
+
approvers: z18.array(z18.string()).optional(),
|
|
718
|
+
/** Optional link to a Release Plan document (e.g. "RP-001") */
|
|
719
|
+
release_plan: z18.string().optional(),
|
|
720
|
+
/** Acknowledged suspected links — one-up/one-down neighbor analysis */
|
|
721
|
+
suspected_links: z18.array(SuspectedLinkGroupSchema).optional()
|
|
722
|
+
});
|
|
723
|
+
var ReleaseNotesAudienceSchema = z18.enum(["customer", "technical"]);
|
|
724
|
+
var ReleaseNotesFrontmatterSchema = z18.object({
|
|
725
|
+
id: z18.string().min(1),
|
|
726
|
+
title: z18.string().min(1),
|
|
727
|
+
type: z18.literal("release_notes").optional(),
|
|
728
|
+
status: RiskDocumentStatusSchema,
|
|
729
|
+
author: z18.string().optional(),
|
|
730
|
+
reviewers: z18.array(z18.string()).optional(),
|
|
731
|
+
approvers: z18.array(z18.string()).optional(),
|
|
732
|
+
audience: ReleaseNotesAudienceSchema,
|
|
733
|
+
release_version: z18.string().min(1)
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
// ../shared/dist/schemas/retention-policy.js
|
|
737
|
+
import { z as z19 } from "zod";
|
|
738
|
+
var RetentionPolicySchema = z19.object({
|
|
739
|
+
/** Default retention period in years for all record types */
|
|
740
|
+
defaultPeriodYears: z19.number().int().min(1).max(30).default(10),
|
|
741
|
+
/**
|
|
742
|
+
* Optional per-record-type overrides (e.g., { "release": 15, "signature": 20 }).
|
|
743
|
+
* Keys are record type identifiers; values are retention periods in years.
|
|
744
|
+
*/
|
|
745
|
+
byRecordType: z19.record(z19.string(), z19.number().int().min(1).max(30)).optional()
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
// ../shared/dist/schemas/audit.js
|
|
749
|
+
import { z as z20 } from "zod";
|
|
750
|
+
var AuditFindingClassificationSchema = z20.enum(["observation", "minor_nc", "major_nc"]);
|
|
751
|
+
var AuditStatusSchema = z20.enum(["planned", "in_progress", "completed", "cancelled"]);
|
|
752
|
+
var PlannedAuditEntrySchema = z20.object({
|
|
753
|
+
audit_id: z20.string().min(1),
|
|
754
|
+
process_area: z20.string().min(1),
|
|
755
|
+
clause: z20.string().optional(),
|
|
756
|
+
planned_date: z20.string().min(1),
|
|
757
|
+
auditor: z20.string().min(1),
|
|
758
|
+
status: AuditStatusSchema
|
|
759
|
+
});
|
|
760
|
+
var AuditScheduleFrontmatterSchema = z20.object({
|
|
761
|
+
id: z20.string().min(1),
|
|
762
|
+
title: z20.string().min(1),
|
|
763
|
+
type: z20.literal("audit_schedule").optional(),
|
|
764
|
+
status: z20.string().optional(),
|
|
765
|
+
author: z20.string().optional(),
|
|
766
|
+
reviewers: z20.array(z20.string()).optional(),
|
|
767
|
+
approvers: z20.array(z20.string()).optional(),
|
|
768
|
+
cycle_year: z20.number().int().min(2e3).max(2100),
|
|
769
|
+
audits: z20.array(PlannedAuditEntrySchema).min(1)
|
|
770
|
+
});
|
|
771
|
+
var AuditFindingSchema = z20.object({
|
|
772
|
+
finding_id: z20.string().min(1),
|
|
773
|
+
classification: AuditFindingClassificationSchema,
|
|
774
|
+
description: z20.string().min(1),
|
|
775
|
+
capa_id: z20.string().optional()
|
|
776
|
+
});
|
|
777
|
+
var AuditReportFrontmatterSchema = z20.object({
|
|
778
|
+
id: z20.string().min(1),
|
|
779
|
+
title: z20.string().min(1),
|
|
780
|
+
type: z20.literal("audit_report").optional(),
|
|
781
|
+
status: z20.string().optional(),
|
|
782
|
+
author: z20.string().optional(),
|
|
783
|
+
reviewers: z20.array(z20.string()).optional(),
|
|
784
|
+
approvers: z20.array(z20.string()).optional(),
|
|
785
|
+
audit_date: z20.string().min(1),
|
|
786
|
+
audit_id: z20.string().optional(),
|
|
787
|
+
process_area: z20.string().min(1),
|
|
788
|
+
clause: z20.string().optional(),
|
|
789
|
+
auditor: z20.string().min(1),
|
|
790
|
+
findings: z20.array(AuditFindingSchema),
|
|
791
|
+
findings_major: z20.number().int().min(0).optional(),
|
|
792
|
+
findings_minor: z20.number().int().min(0).optional(),
|
|
793
|
+
findings_observations: z20.number().int().min(0).optional()
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// ../shared/dist/schemas/management-review.js
|
|
797
|
+
import { z as z21 } from "zod";
|
|
798
|
+
var ManagementReviewAttendeeSchema = z21.object({
|
|
799
|
+
name: z21.string().min(1),
|
|
800
|
+
role: z21.string().min(1)
|
|
801
|
+
});
|
|
802
|
+
var ManagementReviewFrontmatterSchema = z21.object({
|
|
803
|
+
id: z21.string().min(1),
|
|
804
|
+
title: z21.string().min(1),
|
|
805
|
+
type: z21.literal("management_review").optional(),
|
|
806
|
+
version: z21.string().optional(),
|
|
807
|
+
status: z21.enum(["draft", "scheduled", "completed"]).optional(),
|
|
808
|
+
review_date: z21.string().min(1),
|
|
809
|
+
review_period: z21.object({
|
|
810
|
+
from: z21.string().min(1),
|
|
811
|
+
to: z21.string().min(1)
|
|
812
|
+
}),
|
|
813
|
+
attendees: z21.array(ManagementReviewAttendeeSchema).min(1),
|
|
814
|
+
data_snapshot_date: z21.string().optional(),
|
|
815
|
+
next_review_date: z21.string().optional()
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// ../shared/dist/schemas/frameworks.js
|
|
819
|
+
import { z as z22 } from "zod";
|
|
820
|
+
var FrameworkScopeSchema = z22.enum(["qms", "device", "both"]);
|
|
821
|
+
var MappingRelationshipSchema = z22.enum(["equivalent", "partial", "related", "supersedes"]);
|
|
822
|
+
var FrameworkDefinitionSeedSchema = z22.object({
|
|
823
|
+
id: RegulatoryFrameworkSchema,
|
|
824
|
+
name: z22.string().min(1),
|
|
825
|
+
version: z22.string().min(1),
|
|
826
|
+
scope: FrameworkScopeSchema,
|
|
827
|
+
url: z22.string().url().nullable(),
|
|
828
|
+
active: z22.boolean().default(true)
|
|
829
|
+
});
|
|
830
|
+
var ClauseBase = z22.object({
|
|
831
|
+
clauseNumber: z22.string().min(1),
|
|
832
|
+
title: z22.string().min(1)
|
|
833
|
+
});
|
|
834
|
+
function buildClauseSchema(remainingDepth) {
|
|
835
|
+
if (remainingDepth <= 0) {
|
|
836
|
+
return ClauseBase.strict();
|
|
837
|
+
}
|
|
838
|
+
return ClauseBase.extend({
|
|
839
|
+
children: z22.array(z22.lazy(() => buildClauseSchema(remainingDepth - 1))).optional()
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
var FrameworkClauseSeedSchema = buildClauseSchema(5);
|
|
843
|
+
var FrameworkMappingSeedSchema = z22.object({
|
|
844
|
+
sourceClause: z22.string().min(1),
|
|
845
|
+
targetFramework: RegulatoryFrameworkSchema,
|
|
846
|
+
targetClause: z22.string().min(1),
|
|
847
|
+
relationship: MappingRelationshipSchema,
|
|
848
|
+
notes: z22.string().nullable().default(null)
|
|
849
|
+
});
|
|
850
|
+
var MappingFileSeedSchema = z22.object({
|
|
851
|
+
sourceFramework: RegulatoryFrameworkSchema,
|
|
852
|
+
mappings: z22.array(FrameworkMappingSeedSchema)
|
|
853
|
+
});
|
|
854
|
+
var EvidenceSourceSchema = z22.enum(["documents", "training_records"]);
|
|
855
|
+
var EvidenceRuleConditionsSeedSchema = z22.object({
|
|
856
|
+
evidenceSource: EvidenceSourceSchema.optional(),
|
|
857
|
+
documentType: DocumentTypeSchema.optional(),
|
|
858
|
+
linkType: LinkTypeSchema.optional(),
|
|
859
|
+
status: z22.string().optional(),
|
|
860
|
+
minCount: z22.number().int().positive().optional()
|
|
861
|
+
});
|
|
862
|
+
var EvidenceRuleSeedSchema = z22.object({
|
|
863
|
+
clauseId: z22.string().min(1),
|
|
864
|
+
description: z22.string().min(1),
|
|
865
|
+
conditions: EvidenceRuleConditionsSeedSchema,
|
|
866
|
+
weight: z22.number().min(0).max(1)
|
|
867
|
+
});
|
|
868
|
+
var EvidenceRulesFileSeedSchema = z22.object({
|
|
869
|
+
frameworkId: RegulatoryFrameworkSchema,
|
|
870
|
+
rules: z22.array(EvidenceRuleSeedSchema)
|
|
871
|
+
});
|
|
872
|
+
var FrameworkFileSeedSchema = FrameworkDefinitionSeedSchema.extend({
|
|
873
|
+
clauses: z22.array(FrameworkClauseSeedSchema)
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
// ../shared/dist/schemas/legal.js
|
|
877
|
+
import { z as z23 } from "zod";
|
|
878
|
+
var LegalDocumentTypeSchema = z23.enum(["tos", "privacy-policy", "billing-terms"]);
|
|
879
|
+
var LegalDocumentVersionSchema = z23.string().regex(/^\d{4}-\d{2}-\d{2}$/, {
|
|
880
|
+
message: "Version must be in YYYY-MM-DD format"
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// ../shared/dist/schemas/index.js
|
|
884
|
+
var GateOverrideCategorySchema = z24.enum([
|
|
885
|
+
"unverified_requirements",
|
|
886
|
+
"unanalyzed_requirements",
|
|
887
|
+
"error_risk_gaps",
|
|
888
|
+
"missing_risk_management_plan_reference",
|
|
889
|
+
"missing_plan_risk_management",
|
|
890
|
+
"missing_plan_software_development",
|
|
891
|
+
"missing_plan_cybersecurity",
|
|
892
|
+
"missing_plan_usability",
|
|
893
|
+
"missing_plan_clinical_evaluation",
|
|
894
|
+
"missing_plan_post_market_surveillance",
|
|
895
|
+
"missing_artifact_soup_register",
|
|
896
|
+
"missing_artifact_sbom",
|
|
897
|
+
"missing_artifact_summative_evaluation",
|
|
898
|
+
"missing_deployment_references",
|
|
899
|
+
"missing_smoke_test_evidence"
|
|
900
|
+
]);
|
|
901
|
+
var MemberRoleSchema = z24.enum(["admin", "member"]);
|
|
902
|
+
var MemberStatusSchema = z24.enum(["active", "suspended", "removed"]);
|
|
903
|
+
var EmailAddressSchema = z24.object({
|
|
904
|
+
value: z24.string().email(),
|
|
905
|
+
verified: z24.boolean()
|
|
906
|
+
});
|
|
907
|
+
var ReviewerRuleSchema = z24.object({
|
|
908
|
+
documentType: DocumentTypeSchema,
|
|
909
|
+
requiredDepartments: z24.array(z24.string()),
|
|
910
|
+
finalApproverDepartment: z24.string()
|
|
911
|
+
});
|
|
912
|
+
var SyncStatusSchema = z24.enum(["active", "paused", "error"]);
|
|
913
|
+
var AuditActionSchema = z24.enum([
|
|
914
|
+
// Organization
|
|
915
|
+
"organization.created",
|
|
916
|
+
"organization.settings_updated",
|
|
917
|
+
"organization.checklist_template_updated",
|
|
918
|
+
// Members
|
|
919
|
+
"member.invited",
|
|
920
|
+
"member.invite_declined",
|
|
921
|
+
"member.joined",
|
|
922
|
+
"member.role_changed",
|
|
923
|
+
"member.removed",
|
|
924
|
+
"member.password_reset_forced",
|
|
925
|
+
// Repositories
|
|
926
|
+
"repository.connected",
|
|
927
|
+
"repository.disconnected",
|
|
928
|
+
"repository.sync_triggered",
|
|
929
|
+
"repository.updated",
|
|
930
|
+
"repository.assigned",
|
|
931
|
+
"repository.unassigned",
|
|
932
|
+
// Documents
|
|
933
|
+
"document.synced",
|
|
934
|
+
"document.removed",
|
|
935
|
+
"document.viewed",
|
|
936
|
+
// Releases
|
|
937
|
+
"release.created",
|
|
938
|
+
"release.updated",
|
|
939
|
+
"release.deleted",
|
|
940
|
+
"release.withdrawn",
|
|
941
|
+
"release.submitted_for_review",
|
|
942
|
+
"release.signature_added",
|
|
943
|
+
"release.approved",
|
|
944
|
+
"release.published",
|
|
945
|
+
"release.checklist_updated",
|
|
946
|
+
"release.signing_obligations_rederived",
|
|
947
|
+
"release.gate_override_added",
|
|
948
|
+
"release.gate_override_removed",
|
|
949
|
+
"release.gate_override_invalidated",
|
|
950
|
+
"release.viewed",
|
|
951
|
+
// Signatures
|
|
952
|
+
"signature.created",
|
|
953
|
+
"signature.attempt_failed",
|
|
954
|
+
"signature.lockout_triggered",
|
|
955
|
+
// Signing Requests
|
|
956
|
+
"signing_request.created",
|
|
957
|
+
"signing_request.sent",
|
|
958
|
+
"signing_request.viewed",
|
|
959
|
+
"signing_request.signed",
|
|
960
|
+
"signing_request.declined",
|
|
961
|
+
"signing_request.cancelled",
|
|
962
|
+
"signing_request.expired",
|
|
963
|
+
"signing_request.reminded",
|
|
964
|
+
"signing_request.document_accessed",
|
|
965
|
+
// Training
|
|
966
|
+
"training.assigned",
|
|
967
|
+
"training.completed",
|
|
968
|
+
"training.started",
|
|
969
|
+
"training.settings_updated",
|
|
970
|
+
"training.quiz_submitted",
|
|
971
|
+
"training.quiz_passed",
|
|
972
|
+
"training.quiz_failed",
|
|
973
|
+
"training.overdue_notified",
|
|
974
|
+
"training.acknowledged",
|
|
975
|
+
"training.signature_added",
|
|
976
|
+
"training.instructor_signoff",
|
|
977
|
+
"training.task_created",
|
|
978
|
+
"training.viewed",
|
|
979
|
+
// QMS
|
|
980
|
+
"qms.created",
|
|
981
|
+
"qms.updated",
|
|
982
|
+
"qms.deleted",
|
|
983
|
+
// Devices
|
|
984
|
+
"device.created",
|
|
985
|
+
"device.updated",
|
|
986
|
+
"device.deleted",
|
|
987
|
+
// DCOs
|
|
988
|
+
"dco.created",
|
|
989
|
+
"dco.updated",
|
|
990
|
+
"dco.deleted",
|
|
991
|
+
"dco.submitted",
|
|
992
|
+
"dco.obligation_fulfilled",
|
|
993
|
+
"dco.approved",
|
|
994
|
+
"dco.effective",
|
|
995
|
+
// Design Reviews
|
|
996
|
+
"design_review.created",
|
|
997
|
+
"design_review.updated",
|
|
998
|
+
"design_review.deleted",
|
|
999
|
+
"design_review.submitted",
|
|
1000
|
+
"design_review.signature_added",
|
|
1001
|
+
"design_review.signed",
|
|
1002
|
+
"design_review.cancelled",
|
|
1003
|
+
// CAPAs
|
|
1004
|
+
"capa.created",
|
|
1005
|
+
"capa.updated",
|
|
1006
|
+
"capa.deleted",
|
|
1007
|
+
"capa.investigation_started",
|
|
1008
|
+
"capa.implementation_started",
|
|
1009
|
+
"capa.verification_started",
|
|
1010
|
+
"capa.closed",
|
|
1011
|
+
"capa.cancelled",
|
|
1012
|
+
"capa.action_added",
|
|
1013
|
+
"capa.action_updated",
|
|
1014
|
+
"capa.action_removed",
|
|
1015
|
+
// Complaints
|
|
1016
|
+
"complaint.created",
|
|
1017
|
+
"complaint.updated",
|
|
1018
|
+
"complaint.deleted",
|
|
1019
|
+
"complaint.investigation_started",
|
|
1020
|
+
"complaint.resolved",
|
|
1021
|
+
"complaint.closed",
|
|
1022
|
+
"complaint.cancelled",
|
|
1023
|
+
"complaint.escalated_to_capa",
|
|
1024
|
+
// Nonconformances
|
|
1025
|
+
"nc.created",
|
|
1026
|
+
"nc.updated",
|
|
1027
|
+
"nc.deleted",
|
|
1028
|
+
"nc.investigation_started",
|
|
1029
|
+
"nc.disposition_set",
|
|
1030
|
+
"nc.concession_approved",
|
|
1031
|
+
"nc.closed",
|
|
1032
|
+
"nc.cancelled",
|
|
1033
|
+
"nc.escalated_to_capa",
|
|
1034
|
+
// Actions
|
|
1035
|
+
"action.created",
|
|
1036
|
+
"action.updated",
|
|
1037
|
+
"action.completed",
|
|
1038
|
+
"action.cancelled",
|
|
1039
|
+
"action.deleted",
|
|
1040
|
+
// Legal
|
|
1041
|
+
"billing_terms.accepted",
|
|
1042
|
+
"legal_document.accepted",
|
|
1043
|
+
// Billing
|
|
1044
|
+
"billing.checkout_created",
|
|
1045
|
+
"billing.portal_accessed",
|
|
1046
|
+
"billing.subscription_activated",
|
|
1047
|
+
"billing.subscription_updated",
|
|
1048
|
+
"billing.subscription_canceled",
|
|
1049
|
+
// API Keys
|
|
1050
|
+
"api_key.created",
|
|
1051
|
+
"api_key.revoked",
|
|
1052
|
+
// Risk Management
|
|
1053
|
+
"risk.viewed",
|
|
1054
|
+
// Compliance
|
|
1055
|
+
"compliance_package.requested",
|
|
1056
|
+
"compliance_package.downloaded",
|
|
1057
|
+
"validation_package.downloaded",
|
|
1058
|
+
// ERSD
|
|
1059
|
+
"ersd.created",
|
|
1060
|
+
"ersd.auto_seeded",
|
|
1061
|
+
// Quality Objectives
|
|
1062
|
+
"quality_objective.created",
|
|
1063
|
+
"quality_objective.updated",
|
|
1064
|
+
"quality_objective.value_recorded",
|
|
1065
|
+
"quality_objective.deactivated",
|
|
1066
|
+
// Quality Reviews
|
|
1067
|
+
"quality_review.created",
|
|
1068
|
+
"quality_review.updated",
|
|
1069
|
+
"quality_review.started",
|
|
1070
|
+
"quality_review.completed",
|
|
1071
|
+
"quality_review.cancelled",
|
|
1072
|
+
// Review Types
|
|
1073
|
+
"review_type.created",
|
|
1074
|
+
"review_type.updated",
|
|
1075
|
+
// Audit
|
|
1076
|
+
"audit_log.exported"
|
|
1077
|
+
]);
|
|
1078
|
+
var ResourceTypeSchema = z24.enum([
|
|
1079
|
+
"organization",
|
|
1080
|
+
"member",
|
|
1081
|
+
"invite",
|
|
1082
|
+
"repository",
|
|
1083
|
+
"document",
|
|
1084
|
+
"release",
|
|
1085
|
+
"signature",
|
|
1086
|
+
"training",
|
|
1087
|
+
"training_task",
|
|
1088
|
+
"training_settings",
|
|
1089
|
+
"qms",
|
|
1090
|
+
"device",
|
|
1091
|
+
"dco",
|
|
1092
|
+
"design_review",
|
|
1093
|
+
"signing_request",
|
|
1094
|
+
"capa",
|
|
1095
|
+
"complaint",
|
|
1096
|
+
"nonconformance",
|
|
1097
|
+
"action",
|
|
1098
|
+
"legal_acceptance",
|
|
1099
|
+
"subscription",
|
|
1100
|
+
"api_key",
|
|
1101
|
+
"compliance_package",
|
|
1102
|
+
"validation_package",
|
|
1103
|
+
"ersd",
|
|
1104
|
+
"qualityObjective",
|
|
1105
|
+
"qualityReview",
|
|
1106
|
+
"reviewType",
|
|
1107
|
+
"audit_log",
|
|
1108
|
+
"risk_matrix"
|
|
1109
|
+
]);
|
|
1110
|
+
var ReleaseTypeSchema = z24.enum(["qms", "device", "combined"]);
|
|
1111
|
+
var ReleaseStatusSchema = z24.enum(["draft", "in_review", "approved", "published"]);
|
|
1112
|
+
var SignatureTypeSchema = z24.enum(["author", "dept_reviewer", "final_approver", "trainee"]);
|
|
1113
|
+
var ChangeTypeSchema = z24.enum(["added", "modified", "deleted"]);
|
|
1114
|
+
var SigningRequestStatusSchema = z24.enum([
|
|
1115
|
+
"sent",
|
|
1116
|
+
"partially_signed",
|
|
1117
|
+
"completed",
|
|
1118
|
+
"expired",
|
|
1119
|
+
"cancelled"
|
|
1120
|
+
]);
|
|
1121
|
+
var SignerStatusSchema = z24.enum(["pending", "viewed", "signed", "declined"]);
|
|
1122
|
+
var FieldTypeSchema = z24.enum(["signature", "initials", "date"]);
|
|
1123
|
+
|
|
1124
|
+
// ../shared/dist/constants/risk-matrix.js
|
|
1125
|
+
var RISK_DOCUMENT_TYPES = [
|
|
1126
|
+
"software_risk",
|
|
1127
|
+
"usability_risk",
|
|
1128
|
+
"security_risk"
|
|
1129
|
+
];
|
|
1130
|
+
var HAZARD_DOCUMENT_TYPES = ["haz_soe_software", "haz_soe_security"];
|
|
1131
|
+
var ALL_RISK_RELATED_DOCUMENT_TYPES = [
|
|
1132
|
+
...RISK_DOCUMENT_TYPES,
|
|
1133
|
+
...HAZARD_DOCUMENT_TYPES,
|
|
1134
|
+
"hazardous_situation",
|
|
1135
|
+
"harm"
|
|
1136
|
+
];
|
|
1137
|
+
|
|
1138
|
+
// ../shared/dist/constants/index.js
|
|
1139
|
+
var REQUIRED_SECTIONS = {
|
|
1140
|
+
user_need: ["Purpose", "Stakeholder", "User Needs"],
|
|
1141
|
+
architecture: ["Purpose"],
|
|
1142
|
+
release_plan: ["Scope", "Applicable Plans", "Release-Specific Criteria", "Known Anomalies"],
|
|
1143
|
+
design_review: ["Review Scope", "Attendees", "Findings", "Actions", "Conclusion"],
|
|
1144
|
+
release_notes: ["Changes", "Known Issues"],
|
|
1145
|
+
audit_schedule: ["Scope", "Audit Criteria"],
|
|
1146
|
+
audit_report: ["Scope", "Methodology", "Findings", "Conclusion"],
|
|
1147
|
+
management_review: ["Review Inputs", "Review Outputs", "Action Items", "Decisions"],
|
|
1148
|
+
hazard_category: ["Description", "Examples", "Applicable Standards"]
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
// ../shared/dist/validators/permissions.js
|
|
1152
|
+
import { z as z25 } from "zod";
|
|
1153
|
+
var MemberPermissionsSchema = z25.object({
|
|
1154
|
+
canSign: z25.boolean(),
|
|
1155
|
+
canApprove: z25.boolean(),
|
|
1156
|
+
canCreateRelease: z25.boolean(),
|
|
1157
|
+
canPublishRelease: z25.boolean(),
|
|
1158
|
+
canManageMembers: z25.boolean(),
|
|
1159
|
+
canViewAuditLog: z25.boolean(),
|
|
1160
|
+
canExport: z25.boolean()
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
// src/schema-map.ts
|
|
1164
|
+
var SCHEMA_MAP = {
|
|
1165
|
+
// Risk entries
|
|
1166
|
+
software_risk: RiskEntryFrontmatterSchema,
|
|
1167
|
+
usability_risk: RiskEntryFrontmatterSchema,
|
|
1168
|
+
security_risk: RiskEntryFrontmatterSchema,
|
|
1169
|
+
// Hazard / harm documents
|
|
1170
|
+
haz_soe_software: HazardFrontmatterSchema,
|
|
1171
|
+
haz_soe_security: HazardFrontmatterSchema,
|
|
1172
|
+
hazardous_situation: HazardousSituationFrontmatterSchema,
|
|
1173
|
+
harm: HarmFrontmatterSchema,
|
|
1174
|
+
hazard_category: HazardCategoryFrontmatterSchema,
|
|
1175
|
+
// Problem resolution (IEC 62304 §9)
|
|
1176
|
+
anomaly: AnomalyFrontmatterSchema,
|
|
1177
|
+
// Usability engineering (IEC 62366)
|
|
1178
|
+
usability_plan: UsabilityPlanFrontmatterSchema,
|
|
1179
|
+
use_specification: UseSpecificationFrontmatterSchema,
|
|
1180
|
+
task_analysis: TaskAnalysisFrontmatterSchema,
|
|
1181
|
+
usability_evaluation: UsabilityEvaluationFrontmatterSchema,
|
|
1182
|
+
summative_evaluation: SummativeEvaluationFrontmatterSchema,
|
|
1183
|
+
// Risk management (ISO 14971)
|
|
1184
|
+
risk_management_plan: RiskManagementPlanFrontmatterSchema,
|
|
1185
|
+
// Software lifecycle (IEC 62304)
|
|
1186
|
+
software_development_plan: SoftwareDevelopmentPlanFrontmatterSchema,
|
|
1187
|
+
software_maintenance_plan: SoftwareMaintenancePlanFrontmatterSchema,
|
|
1188
|
+
software_test_plan: SoftwareTestPlanFrontmatterSchema,
|
|
1189
|
+
soup_register: SoupRegisterFrontmatterSchema,
|
|
1190
|
+
// Cybersecurity (IEC 81001-5-1)
|
|
1191
|
+
cybersecurity_plan: CybersecurityPlanFrontmatterSchema,
|
|
1192
|
+
sbom: SbomFrontmatterSchema,
|
|
1193
|
+
// Clinical (MDR / FDA)
|
|
1194
|
+
clinical_evaluation_plan: ClinicalEvaluationPlanFrontmatterSchema,
|
|
1195
|
+
clinical_evaluation_report: ClinicalEvaluationReportFrontmatterSchema,
|
|
1196
|
+
// Post-market surveillance (MDR Art. 83)
|
|
1197
|
+
post_market_surveillance_plan: PostMarketSurveillancePlanFrontmatterSchema,
|
|
1198
|
+
post_market_feedback: PostMarketFeedbackFrontmatterSchema,
|
|
1199
|
+
// Labeling (MDR Annex I)
|
|
1200
|
+
labeling: LabelingFrontmatterSchema,
|
|
1201
|
+
// Product (ISO 13485 §7.3)
|
|
1202
|
+
product_development_plan: ProductDevelopmentPlanFrontmatterSchema,
|
|
1203
|
+
intended_use: IntendedUseFrontmatterSchema,
|
|
1204
|
+
// User needs (ISO 13485 §7.3.2)
|
|
1205
|
+
user_need: UserNeedFrontmatterSchema,
|
|
1206
|
+
// Requirements (IEC 62304 §5.2.2, ISO 13485 §7.3.3)
|
|
1207
|
+
requirement: RequirementFrontmatterSchema,
|
|
1208
|
+
// Test documents
|
|
1209
|
+
test_protocol: TestProtocolFrontmatterSchema,
|
|
1210
|
+
test_report: TestReportFrontmatterSchema,
|
|
1211
|
+
// Change management (ISO 13485 §7.3.5)
|
|
1212
|
+
release_plan: ReleasePlanFrontmatterSchema,
|
|
1213
|
+
design_review: DesignReviewFrontmatterSchema,
|
|
1214
|
+
release_notes: ReleaseNotesFrontmatterSchema,
|
|
1215
|
+
// Internal audit management (ISO 13485 §8.2.2)
|
|
1216
|
+
audit_schedule: AuditScheduleFrontmatterSchema,
|
|
1217
|
+
audit_report: AuditReportFrontmatterSchema,
|
|
1218
|
+
// Management review (ISO 13485 §5.6)
|
|
1219
|
+
management_review: ManagementReviewFrontmatterSchema
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
// src/document-type-patterns.ts
|
|
1223
|
+
var FOLDER_RULES = {
|
|
1224
|
+
// Product (ISO 13485 §7.3)
|
|
1225
|
+
"docs/product": {
|
|
1226
|
+
prefixes: ["PDP-", "IU-"],
|
|
1227
|
+
type: ["product_development_plan", "intended_use"]
|
|
1228
|
+
},
|
|
1229
|
+
"docs/product/requirements": { prefixes: ["PRS-"], type: "requirement", category: "product" },
|
|
1230
|
+
"docs/product/user-needs": { prefixes: ["UN-"], type: "user_need" },
|
|
1231
|
+
// Software requirements (moved under software discipline)
|
|
1232
|
+
"docs/software/requirements": { prefixes: ["SRS-"], type: "requirement", category: "software" },
|
|
1233
|
+
// Design (IEC 62304 — under software discipline)
|
|
1234
|
+
"docs/software/architecture": { prefixes: ["HLD-", "SAD-", "DEV-PLAN-"], type: "architecture" },
|
|
1235
|
+
"docs/software/design": { prefixes: ["SDD-", "DDD-"], type: "detailed_design" },
|
|
1236
|
+
// Test (protocols + reports)
|
|
1237
|
+
"docs/test/protocols": { prefixes: ["TP-"], type: "test_protocol" },
|
|
1238
|
+
"docs/test/reports": { prefixes: ["TR-"], type: "test_report" },
|
|
1239
|
+
// Risk management — ISO 14971 (umbrella)
|
|
1240
|
+
"docs/risk/plans": { prefixes: ["RMP-"], type: "risk_management_plan" },
|
|
1241
|
+
"docs/risk/situations": { prefixes: ["HS-"], type: "hazardous_situation" },
|
|
1242
|
+
"docs/risk/harms": { prefixes: ["HARM-"], type: "harm" },
|
|
1243
|
+
"docs/risk/hazard-categories": { prefixes: ["HC-"], type: "hazard_category" },
|
|
1244
|
+
// Software lifecycle — IEC 62304
|
|
1245
|
+
"docs/software/plans": {
|
|
1246
|
+
prefixes: ["DEV-PLAN-", "MAINT-PLAN-", "SDP-", "STP-"],
|
|
1247
|
+
type: [
|
|
1248
|
+
"software_development_plan",
|
|
1249
|
+
"software_maintenance_plan",
|
|
1250
|
+
"software_development_plan",
|
|
1251
|
+
"software_test_plan"
|
|
1252
|
+
]
|
|
1253
|
+
},
|
|
1254
|
+
"docs/software/risks": {
|
|
1255
|
+
prefixes: ["HAZ-SW-", "RISK-SW-"],
|
|
1256
|
+
type: ["haz_soe_software", "software_risk"]
|
|
1257
|
+
},
|
|
1258
|
+
"docs/software/soup": { prefixes: ["SOUP-"], type: "soup_register" },
|
|
1259
|
+
// Problem resolution — IEC 62304 §9
|
|
1260
|
+
"docs/software/anomalies": { prefixes: ["ANOM-"], type: "anomaly" },
|
|
1261
|
+
// Cybersecurity — IEC 81001-5-1
|
|
1262
|
+
"docs/cybersecurity/plans": { prefixes: ["CSPLAN-"], type: "cybersecurity_plan" },
|
|
1263
|
+
"docs/cybersecurity/risks": {
|
|
1264
|
+
prefixes: ["HAZ-SEC-", "RISK-SEC-"],
|
|
1265
|
+
type: ["haz_soe_security", "security_risk"]
|
|
1266
|
+
},
|
|
1267
|
+
"docs/cybersecurity/sbom": { prefixes: ["SBOM-"], type: "sbom" },
|
|
1268
|
+
// Usability engineering — IEC 62366
|
|
1269
|
+
"docs/usability": { prefixes: ["UEP-"], type: "usability_plan" },
|
|
1270
|
+
"docs/usability/use-specifications": { prefixes: ["US-"], type: "use_specification" },
|
|
1271
|
+
"docs/usability/task-analyses": { prefixes: ["TA-"], type: "task_analysis" },
|
|
1272
|
+
"docs/usability/evaluations": {
|
|
1273
|
+
prefixes: ["UE-", "SE-"],
|
|
1274
|
+
type: ["usability_evaluation", "summative_evaluation"]
|
|
1275
|
+
},
|
|
1276
|
+
"docs/usability/risks": { prefixes: ["RISK-US-"], type: "usability_risk" },
|
|
1277
|
+
// Clinical — MDR / FDA
|
|
1278
|
+
"docs/clinical": {
|
|
1279
|
+
prefixes: ["CEP-", "CER-"],
|
|
1280
|
+
type: ["clinical_evaluation_plan", "clinical_evaluation_report"]
|
|
1281
|
+
},
|
|
1282
|
+
// Post-market surveillance — MDR Art. 83
|
|
1283
|
+
"docs/post-market": { prefixes: ["PMS-"], type: "post_market_surveillance_plan" },
|
|
1284
|
+
"docs/post-market/feedback": { prefixes: ["PMF-"], type: "post_market_feedback" },
|
|
1285
|
+
// Change management — ISO 13485 §7.3.5
|
|
1286
|
+
"docs/change/drm": { prefixes: ["DRM-"], type: "design_review" },
|
|
1287
|
+
"docs/change/release-plans": { prefixes: ["RP-"], type: "release_plan" },
|
|
1288
|
+
"docs/change/release-notes": { prefixes: ["RN-"], type: "release_notes" },
|
|
1289
|
+
// Labeling — MDR Annex I
|
|
1290
|
+
"docs/labeling": { prefixes: ["LBL-"], type: "labeling" },
|
|
1291
|
+
// QMS documents
|
|
1292
|
+
"docs/policies": { prefixes: ["POL-"], type: "policy" },
|
|
1293
|
+
"docs/sops": { prefixes: ["SOP-"], type: "sop" },
|
|
1294
|
+
"docs/work-instructions": { prefixes: ["WI-"], type: "work_instruction" },
|
|
1295
|
+
// Internal audit management (ISO 13485 §8.2.2)
|
|
1296
|
+
"docs/audits/schedules": { prefixes: ["AUD-SCH-"], type: "audit_schedule" },
|
|
1297
|
+
"docs/audits/reports": { prefixes: ["AUD-RPT-"], type: "audit_report" },
|
|
1298
|
+
// Management review (ISO 13485 §5.6)
|
|
1299
|
+
"docs/management-reviews": { prefixes: ["MR-"], type: "management_review" },
|
|
1300
|
+
// Supplier management (ISO 13485 §7.4)
|
|
1301
|
+
"docs/suppliers": { prefixes: ["SUP-"], type: "supplier" }
|
|
1302
|
+
};
|
|
1303
|
+
var JSON_SBOM_FOLDERS = ["docs/cybersecurity/sbom"];
|
|
1304
|
+
var NON_REGULATED_FOLDERS = ["docs/compliance", "docs/user-guide", "docs/legal"];
|
|
1305
|
+
function normalizePath(path2) {
|
|
1306
|
+
return path2.toLowerCase().replace(/\\/g, "/");
|
|
1307
|
+
}
|
|
1308
|
+
function extractFolder(filePath) {
|
|
1309
|
+
const normalized = normalizePath(filePath);
|
|
1310
|
+
const parts = normalized.split("/");
|
|
1311
|
+
parts.pop();
|
|
1312
|
+
return parts.join("/");
|
|
1313
|
+
}
|
|
1314
|
+
function findFolderRule(filePath) {
|
|
1315
|
+
const folder = extractFolder(filePath);
|
|
1316
|
+
if (FOLDER_RULES[folder]) {
|
|
1317
|
+
return { rule: FOLDER_RULES[folder], folder };
|
|
1318
|
+
}
|
|
1319
|
+
const parts = folder.split("/");
|
|
1320
|
+
while (parts.length > 0) {
|
|
1321
|
+
const partial = parts.join("/");
|
|
1322
|
+
if (FOLDER_RULES[partial]) {
|
|
1323
|
+
return { rule: FOLDER_RULES[partial], folder: partial };
|
|
1324
|
+
}
|
|
1325
|
+
parts.pop();
|
|
1326
|
+
}
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
function inferDocumentType(filePath, documentId) {
|
|
1330
|
+
const match = findFolderRule(filePath);
|
|
1331
|
+
if (!match) {
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
const { rule } = match;
|
|
1335
|
+
if (typeof rule.type === "string") {
|
|
1336
|
+
return {
|
|
1337
|
+
docType: rule.type,
|
|
1338
|
+
category: rule.category
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
const prefixIndex = rule.prefixes.findIndex(
|
|
1342
|
+
(prefix) => documentId.toUpperCase().startsWith(prefix.toUpperCase())
|
|
1343
|
+
);
|
|
1344
|
+
if (prefixIndex === -1) {
|
|
1345
|
+
return {
|
|
1346
|
+
docType: rule.type[0],
|
|
1347
|
+
category: rule.category
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
return {
|
|
1351
|
+
docType: rule.type[prefixIndex],
|
|
1352
|
+
category: rule.category
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// src/validate-document.ts
|
|
1357
|
+
function validateDocument(content, filePath) {
|
|
1358
|
+
const errors = [];
|
|
1359
|
+
const warnings = [];
|
|
1360
|
+
let documentId = null;
|
|
1361
|
+
let docType = null;
|
|
1362
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
1363
|
+
let frontmatter;
|
|
1364
|
+
let body;
|
|
1365
|
+
try {
|
|
1366
|
+
const parsed = matter(content);
|
|
1367
|
+
if (!parsed.data || Object.keys(parsed.data).length === 0) {
|
|
1368
|
+
if (!content.trimStart().startsWith("---")) {
|
|
1369
|
+
return {
|
|
1370
|
+
path: normalizedPath,
|
|
1371
|
+
documentId: null,
|
|
1372
|
+
docType: null,
|
|
1373
|
+
errors: [{ code: "missing_frontmatter", message: "File has no YAML frontmatter block" }],
|
|
1374
|
+
warnings: []
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
frontmatter = parsed.data;
|
|
1379
|
+
body = parsed.content;
|
|
1380
|
+
} catch {
|
|
1381
|
+
return {
|
|
1382
|
+
path: normalizedPath,
|
|
1383
|
+
documentId: null,
|
|
1384
|
+
docType: null,
|
|
1385
|
+
errors: [{ code: "missing_frontmatter", message: "Failed to parse YAML frontmatter" }],
|
|
1386
|
+
warnings: []
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
if (!frontmatter.id || typeof frontmatter.id !== "string" || frontmatter.id.trim() === "") {
|
|
1390
|
+
return {
|
|
1391
|
+
path: normalizedPath,
|
|
1392
|
+
documentId: null,
|
|
1393
|
+
docType: null,
|
|
1394
|
+
errors: [{ code: "missing_id", message: "Frontmatter exists but lacks `id` field" }],
|
|
1395
|
+
warnings: []
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
documentId = frontmatter.id;
|
|
1399
|
+
const ruleMatch = findFolderRule(normalizedPath);
|
|
1400
|
+
if (!ruleMatch) {
|
|
1401
|
+
return {
|
|
1402
|
+
path: normalizedPath,
|
|
1403
|
+
documentId,
|
|
1404
|
+
docType: null,
|
|
1405
|
+
errors: [
|
|
1406
|
+
{
|
|
1407
|
+
code: "unknown_type",
|
|
1408
|
+
message: `File is in a folder not matched by any FOLDER_RULES entry: ${normalizedPath}`
|
|
1409
|
+
}
|
|
1410
|
+
],
|
|
1411
|
+
warnings: []
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
const { rule } = ruleMatch;
|
|
1415
|
+
const prefixMatches = rule.prefixes.some(
|
|
1416
|
+
(prefix) => documentId.toUpperCase().startsWith(prefix.toUpperCase())
|
|
1417
|
+
);
|
|
1418
|
+
if (!prefixMatches) {
|
|
1419
|
+
errors.push({
|
|
1420
|
+
code: "invalid_prefix",
|
|
1421
|
+
message: `ID "${documentId}" does not match expected prefixes for this folder: ${rule.prefixes.join(", ")}`
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
const typeResult = inferDocumentType(normalizedPath, documentId);
|
|
1425
|
+
if (typeResult) {
|
|
1426
|
+
docType = typeResult.docType;
|
|
1427
|
+
}
|
|
1428
|
+
if (docType) {
|
|
1429
|
+
const schema = SCHEMA_MAP[docType];
|
|
1430
|
+
if (schema) {
|
|
1431
|
+
const parseResult = schema.safeParse(frontmatter);
|
|
1432
|
+
if (!parseResult.success) {
|
|
1433
|
+
for (const issue of parseResult.error.issues) {
|
|
1434
|
+
errors.push({
|
|
1435
|
+
code: "invalid_frontmatter",
|
|
1436
|
+
message: issue.message,
|
|
1437
|
+
field: issue.path.join(".")
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
if (docType) {
|
|
1444
|
+
let requiredSections = [];
|
|
1445
|
+
if (docType === "requirement") {
|
|
1446
|
+
const format = frontmatter.format;
|
|
1447
|
+
if (format === "user_story") {
|
|
1448
|
+
requiredSections = ["User Story", "Acceptance Criteria"];
|
|
1449
|
+
} else {
|
|
1450
|
+
requiredSections = ["Requirements", "Verification Criteria"];
|
|
1451
|
+
}
|
|
1452
|
+
} else {
|
|
1453
|
+
const sections = REQUIRED_SECTIONS[docType];
|
|
1454
|
+
if (sections) {
|
|
1455
|
+
requiredSections = sections;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
const h2Headings = body.split("\n").filter((line) => line.startsWith("## ")).map((line) => line.slice(3).trim().toLowerCase());
|
|
1459
|
+
for (const required of requiredSections) {
|
|
1460
|
+
const found = h2Headings.some((h) => h.includes(required.toLowerCase()));
|
|
1461
|
+
if (!found) {
|
|
1462
|
+
warnings.push({
|
|
1463
|
+
code: "missing_section",
|
|
1464
|
+
message: `Missing required section: ${required}`
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return { path: normalizedPath, documentId, docType, errors, warnings };
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// src/validate-directory.ts
|
|
1473
|
+
function validateDirectory(dirPath, options) {
|
|
1474
|
+
const absoluteDir = path.resolve(dirPath);
|
|
1475
|
+
const repoRoot = path.basename(absoluteDir) === "docs" ? path.dirname(absoluteDir) : absoluteDir;
|
|
1476
|
+
const results = [];
|
|
1477
|
+
const orphanFolders = [];
|
|
1478
|
+
const strayFiles = [];
|
|
1479
|
+
const seenFolders = /* @__PURE__ */ new Set();
|
|
1480
|
+
const excludeList = options?.exclude ?? [];
|
|
1481
|
+
function walk(dir2) {
|
|
1482
|
+
let entries;
|
|
1483
|
+
try {
|
|
1484
|
+
entries = fs.readdirSync(dir2, { withFileTypes: true });
|
|
1485
|
+
} catch {
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
for (const entry of entries) {
|
|
1489
|
+
const fullPath = path.join(dir2, entry.name);
|
|
1490
|
+
try {
|
|
1491
|
+
if (fs.lstatSync(fullPath).isSymbolicLink()) continue;
|
|
1492
|
+
} catch {
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1495
|
+
if (entry.isDirectory()) {
|
|
1496
|
+
walk(fullPath);
|
|
1497
|
+
} else if (entry.isFile()) {
|
|
1498
|
+
const repoRelative = path.relative(repoRoot, fullPath).replace(/\\/g, "/");
|
|
1499
|
+
if (excludeList.some((ex) => repoRelative.startsWith(ex))) continue;
|
|
1500
|
+
if (NON_REGULATED_FOLDERS.some((nr) => repoRelative.startsWith(nr))) continue;
|
|
1501
|
+
const folder = path.dirname(repoRelative).replace(/\\/g, "/");
|
|
1502
|
+
seenFolders.add(folder);
|
|
1503
|
+
if (entry.name.endsWith(".md")) {
|
|
1504
|
+
if (entry.name.toLowerCase() === "readme.md") continue;
|
|
1505
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
1506
|
+
results.push(validateDocument(content, repoRelative));
|
|
1507
|
+
} else {
|
|
1508
|
+
const folderRule = findFolderRule(repoRelative);
|
|
1509
|
+
if (folderRule) {
|
|
1510
|
+
const isJsonInSbomFolder = entry.name.endsWith(".json") && JSON_SBOM_FOLDERS.some((sbomFolder) => repoRelative.startsWith(sbomFolder));
|
|
1511
|
+
if (!isJsonInSbomFolder) {
|
|
1512
|
+
strayFiles.push(repoRelative);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
walk(absoluteDir);
|
|
1520
|
+
for (const folder of seenFolders) {
|
|
1521
|
+
if (folder.startsWith("docs/") || folder === "docs") {
|
|
1522
|
+
const rule = findFolderRule(folder + "/dummy.md");
|
|
1523
|
+
if (!rule && folder !== "docs") {
|
|
1524
|
+
orphanFolders.push(folder);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
const failed = results.filter((r) => r.errors.length > 0).length;
|
|
1529
|
+
const withWarnings = results.filter((r) => r.warnings.length > 0).length;
|
|
1530
|
+
const summary = {
|
|
1531
|
+
total: results.length,
|
|
1532
|
+
passed: results.length - failed,
|
|
1533
|
+
failed,
|
|
1534
|
+
warnings: withWarnings
|
|
1535
|
+
};
|
|
1536
|
+
return { results, summary, orphanFolders, strayFiles };
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// src/cli.ts
|
|
1540
|
+
var args = process.argv.slice(2);
|
|
1541
|
+
var dir = args.find((a) => !a.startsWith("-")) ?? "docs/";
|
|
1542
|
+
var exclude = args.filter((a) => a.startsWith("--exclude=")).map((a) => a.slice(a.indexOf("=") + 1));
|
|
1543
|
+
var result = validateDirectory(dir, { exclude });
|
|
1544
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1545
|
+
process.exit(result.summary.failed > 0 ? 1 : 0);
|