@svashevchenko/ez-know 0.1.0
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/README.md +91 -0
- package/dist/cli.js +4709 -0
- package/dist/cli.js.map +7 -0
- package/dist/guides/business-rules.md +47 -0
- package/dist/guides/capabilities.md +41 -0
- package/dist/guides/conflicts.md +16 -0
- package/dist/guides/constraints.md +35 -0
- package/dist/guides/domains.md +17 -0
- package/dist/guides/entities.md +44 -0
- package/dist/guides/evidence.md +16 -0
- package/dist/guides/features.md +43 -0
- package/dist/guides/questions.md +17 -0
- package/dist/guides/rationales.md +17 -0
- package/dist/guides/relationships.md +19 -0
- package/dist/public/assets/index-BvKMODjW.js +331 -0
- package/dist/public/assets/index-CzQmD36n.css +1 -0
- package/dist/public/index.html +17 -0
- package/dist/reference/ez-know-extract-knowledge-from-code/SKILL.md +339 -0
- package/dist/reference/ez-know-update-knowledge-base/SKILL.md +312 -0
- package/dist/rest/server.js +3237 -0
- package/dist/rest/server.js.map +7 -0
- package/dist/server.js +3357 -0
- package/dist/server.js.map +7 -0
- package/package.json +73 -0
|
@@ -0,0 +1,3237 @@
|
|
|
1
|
+
// src/rest-server.ts
|
|
2
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
// packages/rest/src/app.ts
|
|
6
|
+
import { access } from "node:fs/promises";
|
|
7
|
+
import { resolve as resolve5 } from "node:path";
|
|
8
|
+
import fastifyStatic from "@fastify/static";
|
|
9
|
+
import Fastify from "fastify";
|
|
10
|
+
|
|
11
|
+
// packages/core/src/knowledge/schema.ts
|
|
12
|
+
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
|
|
13
|
+
import { z } from "zod/v4";
|
|
14
|
+
extendZodWithOpenApi(z);
|
|
15
|
+
var confidenceSchema = z.object({
|
|
16
|
+
score: z.number().min(0).max(1),
|
|
17
|
+
level: z.enum(["Low", "Medium", "High"]),
|
|
18
|
+
reason: z.string().optional()
|
|
19
|
+
});
|
|
20
|
+
var certaintySchema = z.enum([
|
|
21
|
+
"confirmed",
|
|
22
|
+
"direct_evidence",
|
|
23
|
+
"inferred",
|
|
24
|
+
"weak_inference",
|
|
25
|
+
"unknown"
|
|
26
|
+
]);
|
|
27
|
+
var withConfidenceSchema = z.object({
|
|
28
|
+
certainty: certaintySchema,
|
|
29
|
+
confidence: confidenceSchema,
|
|
30
|
+
evidence_ids: z.array(z.string()),
|
|
31
|
+
questions: z.array(z.string())
|
|
32
|
+
});
|
|
33
|
+
var evidenceSchema = z.object({
|
|
34
|
+
id: z.string(),
|
|
35
|
+
source_type: z.enum([
|
|
36
|
+
"code",
|
|
37
|
+
"test",
|
|
38
|
+
"ui_text",
|
|
39
|
+
"translation",
|
|
40
|
+
"api",
|
|
41
|
+
"route",
|
|
42
|
+
"schema",
|
|
43
|
+
"comment",
|
|
44
|
+
"documentation",
|
|
45
|
+
"repowise_search",
|
|
46
|
+
"human",
|
|
47
|
+
"increment_description",
|
|
48
|
+
"git_diff"
|
|
49
|
+
]),
|
|
50
|
+
description: z.string(),
|
|
51
|
+
location: z.object({
|
|
52
|
+
path: z.string().optional(),
|
|
53
|
+
symbol: z.string().optional(),
|
|
54
|
+
route: z.string().optional(),
|
|
55
|
+
api: z.string().optional(),
|
|
56
|
+
query: z.string().optional()
|
|
57
|
+
}).optional(),
|
|
58
|
+
supports: z.array(z.string()),
|
|
59
|
+
excerpt: z.string().optional(),
|
|
60
|
+
confidence: confidenceSchema
|
|
61
|
+
});
|
|
62
|
+
var domainSchema = withConfidenceSchema.extend({
|
|
63
|
+
id: z.string(),
|
|
64
|
+
name: z.string(),
|
|
65
|
+
purpose: z.string(),
|
|
66
|
+
why_it_exists: z.string().optional(),
|
|
67
|
+
entity_ids: z.array(z.string()),
|
|
68
|
+
capability_ids: z.array(z.string())
|
|
69
|
+
});
|
|
70
|
+
var entityTechnicalRepresentationSchema = z.object({
|
|
71
|
+
kind: z.enum([
|
|
72
|
+
"type",
|
|
73
|
+
"api",
|
|
74
|
+
"route",
|
|
75
|
+
"database",
|
|
76
|
+
"ui_label",
|
|
77
|
+
"translation",
|
|
78
|
+
"unknown"
|
|
79
|
+
]),
|
|
80
|
+
name: z.string(),
|
|
81
|
+
location_hint: z.string().optional()
|
|
82
|
+
});
|
|
83
|
+
var entitySchema = withConfidenceSchema.extend({
|
|
84
|
+
id: z.string(),
|
|
85
|
+
name: z.string(),
|
|
86
|
+
business_definition: z.string(),
|
|
87
|
+
description: z.string(),
|
|
88
|
+
purpose: z.string().optional(),
|
|
89
|
+
aliases: z.array(z.string()),
|
|
90
|
+
ambiguous_terms: z.array(z.string()),
|
|
91
|
+
technical_representations: z.array(entityTechnicalRepresentationSchema),
|
|
92
|
+
domain_ids: z.array(z.string()),
|
|
93
|
+
state_values: z.array(z.string()).optional(),
|
|
94
|
+
relationship_ids: z.array(z.string()),
|
|
95
|
+
capability_ids: z.array(z.string()),
|
|
96
|
+
feature_ids: z.array(z.string()),
|
|
97
|
+
rule_ids: z.array(z.string())
|
|
98
|
+
});
|
|
99
|
+
var relationshipSchema = withConfidenceSchema.extend({
|
|
100
|
+
id: z.string(),
|
|
101
|
+
from_entity_id: z.string(),
|
|
102
|
+
to_entity_id: z.string(),
|
|
103
|
+
relationship_type: z.enum([
|
|
104
|
+
"has_many",
|
|
105
|
+
"has_one",
|
|
106
|
+
"belongs_to",
|
|
107
|
+
"depends_on",
|
|
108
|
+
"references",
|
|
109
|
+
"transitions_to",
|
|
110
|
+
"unknown"
|
|
111
|
+
]),
|
|
112
|
+
description: z.string()
|
|
113
|
+
});
|
|
114
|
+
var capabilitySchema = withConfidenceSchema.extend({
|
|
115
|
+
id: z.string(),
|
|
116
|
+
name: z.string(),
|
|
117
|
+
description: z.string(),
|
|
118
|
+
why_it_exists: z.string().optional(),
|
|
119
|
+
domain_ids: z.array(z.string()),
|
|
120
|
+
entity_ids: z.array(z.string()),
|
|
121
|
+
parent_capability_ids: z.array(z.string()),
|
|
122
|
+
child_capability_ids: z.array(z.string()),
|
|
123
|
+
feature_ids: z.array(z.string())
|
|
124
|
+
});
|
|
125
|
+
var featureSchema = withConfidenceSchema.extend({
|
|
126
|
+
id: z.string(),
|
|
127
|
+
name: z.string(),
|
|
128
|
+
description: z.string(),
|
|
129
|
+
purpose: z.string().optional(),
|
|
130
|
+
why_it_exists: z.string().optional(),
|
|
131
|
+
domain_ids: z.array(z.string()),
|
|
132
|
+
capability_ids: z.array(z.string()),
|
|
133
|
+
entity_ids: z.array(z.string()),
|
|
134
|
+
rule_ids: z.array(z.string()),
|
|
135
|
+
constraint_ids: z.array(z.string())
|
|
136
|
+
});
|
|
137
|
+
var ruleConditionSchema = z.object({
|
|
138
|
+
subject: z.string(),
|
|
139
|
+
field: z.string().optional(),
|
|
140
|
+
operator: z.enum([
|
|
141
|
+
"equals",
|
|
142
|
+
"not_equals",
|
|
143
|
+
"contains",
|
|
144
|
+
"not_contains",
|
|
145
|
+
"exists",
|
|
146
|
+
"not_exists",
|
|
147
|
+
"greater_than",
|
|
148
|
+
"less_than",
|
|
149
|
+
"succeeds",
|
|
150
|
+
"fails",
|
|
151
|
+
"unknown"
|
|
152
|
+
]),
|
|
153
|
+
value: z.unknown().optional()
|
|
154
|
+
});
|
|
155
|
+
var ruleTriggerSchema = z.object({
|
|
156
|
+
actor: z.string(),
|
|
157
|
+
action: z.string(),
|
|
158
|
+
subject: z.string().optional(),
|
|
159
|
+
field: z.string().optional(),
|
|
160
|
+
operator: z.enum([
|
|
161
|
+
"equals",
|
|
162
|
+
"not_equals",
|
|
163
|
+
"contains",
|
|
164
|
+
"not_contains",
|
|
165
|
+
"exists",
|
|
166
|
+
"not_exists",
|
|
167
|
+
"greater_than",
|
|
168
|
+
"less_than",
|
|
169
|
+
"succeeds",
|
|
170
|
+
"fails",
|
|
171
|
+
"unknown"
|
|
172
|
+
]).optional(),
|
|
173
|
+
value: z.unknown().optional()
|
|
174
|
+
});
|
|
175
|
+
var ruleEffectSchema = z.object({
|
|
176
|
+
effect: z.enum([
|
|
177
|
+
"allow_action",
|
|
178
|
+
"disallow_action",
|
|
179
|
+
"hide_action",
|
|
180
|
+
"disable_action",
|
|
181
|
+
"require_action",
|
|
182
|
+
"show_message",
|
|
183
|
+
"change_state",
|
|
184
|
+
"calculate_value",
|
|
185
|
+
"unknown"
|
|
186
|
+
]),
|
|
187
|
+
action: z.string().optional(),
|
|
188
|
+
message: z.string().optional(),
|
|
189
|
+
target_state: z.string().optional(),
|
|
190
|
+
value: z.unknown().optional()
|
|
191
|
+
});
|
|
192
|
+
var businessRuleSchema = withConfidenceSchema.extend({
|
|
193
|
+
id: z.string(),
|
|
194
|
+
name: z.string(),
|
|
195
|
+
statement: z.string(),
|
|
196
|
+
rule_type: z.enum([
|
|
197
|
+
"validation",
|
|
198
|
+
"permission",
|
|
199
|
+
"state_transition",
|
|
200
|
+
"visibility",
|
|
201
|
+
"workflow",
|
|
202
|
+
"calculation",
|
|
203
|
+
"integration",
|
|
204
|
+
"integration_behavior",
|
|
205
|
+
"data_consistency",
|
|
206
|
+
"acceptance_criterion",
|
|
207
|
+
"unknown"
|
|
208
|
+
]),
|
|
209
|
+
behavior_type: z.enum([
|
|
210
|
+
"allow",
|
|
211
|
+
"disallow",
|
|
212
|
+
"require",
|
|
213
|
+
"calculate",
|
|
214
|
+
"show",
|
|
215
|
+
"hide",
|
|
216
|
+
"transition",
|
|
217
|
+
"notify",
|
|
218
|
+
"block",
|
|
219
|
+
"default",
|
|
220
|
+
"unknown"
|
|
221
|
+
]),
|
|
222
|
+
applies_to: z.object({
|
|
223
|
+
domain_ids: z.array(z.string()).optional(),
|
|
224
|
+
entity_ids: z.array(z.string()).optional(),
|
|
225
|
+
capability_ids: z.array(z.string()).optional(),
|
|
226
|
+
feature_ids: z.array(z.string()).optional()
|
|
227
|
+
}),
|
|
228
|
+
condition: z.object({
|
|
229
|
+
given: z.array(ruleConditionSchema),
|
|
230
|
+
when: z.array(ruleTriggerSchema),
|
|
231
|
+
then: z.array(ruleEffectSchema)
|
|
232
|
+
}).optional(),
|
|
233
|
+
enforcement: z.object({
|
|
234
|
+
level: z.enum([
|
|
235
|
+
"ui",
|
|
236
|
+
"api",
|
|
237
|
+
"backend",
|
|
238
|
+
"database",
|
|
239
|
+
"external_system",
|
|
240
|
+
"unknown"
|
|
241
|
+
]),
|
|
242
|
+
description: z.string().optional()
|
|
243
|
+
}).optional(),
|
|
244
|
+
related_constraint_ids: z.array(z.string()),
|
|
245
|
+
rationale_ids: z.array(z.string())
|
|
246
|
+
});
|
|
247
|
+
var constraintSchema = withConfidenceSchema.extend({
|
|
248
|
+
id: z.string(),
|
|
249
|
+
name: z.string(),
|
|
250
|
+
description: z.string(),
|
|
251
|
+
constraint_type: z.enum([
|
|
252
|
+
"business",
|
|
253
|
+
"technical",
|
|
254
|
+
"regulatory",
|
|
255
|
+
"compliance",
|
|
256
|
+
"integration",
|
|
257
|
+
"legacy",
|
|
258
|
+
"architecture",
|
|
259
|
+
"infrastructure",
|
|
260
|
+
"performance",
|
|
261
|
+
"security",
|
|
262
|
+
"operational",
|
|
263
|
+
"data_model",
|
|
264
|
+
"unknown"
|
|
265
|
+
]),
|
|
266
|
+
cause: z.object({
|
|
267
|
+
source: z.string(),
|
|
268
|
+
source_type: z.enum([
|
|
269
|
+
"external_system",
|
|
270
|
+
"database",
|
|
271
|
+
"framework",
|
|
272
|
+
"infrastructure",
|
|
273
|
+
"architecture",
|
|
274
|
+
"policy",
|
|
275
|
+
"regulation",
|
|
276
|
+
"business_process",
|
|
277
|
+
"legacy_code",
|
|
278
|
+
"unknown"
|
|
279
|
+
]),
|
|
280
|
+
description: z.string()
|
|
281
|
+
}).optional(),
|
|
282
|
+
impact: z.object({
|
|
283
|
+
affected_change_types: z.array(z.string()),
|
|
284
|
+
severity: z.enum(["low", "medium", "high", "critical", "unknown"]),
|
|
285
|
+
description: z.string()
|
|
286
|
+
}).optional(),
|
|
287
|
+
applies_to: z.object({
|
|
288
|
+
domain_ids: z.array(z.string()).optional(),
|
|
289
|
+
entity_ids: z.array(z.string()).optional(),
|
|
290
|
+
capability_ids: z.array(z.string()).optional(),
|
|
291
|
+
feature_ids: z.array(z.string()).optional(),
|
|
292
|
+
business_rule_ids: z.array(z.string()).optional()
|
|
293
|
+
}),
|
|
294
|
+
workaround: z.object({
|
|
295
|
+
exists: z.enum(["yes", "no", "unknown"]),
|
|
296
|
+
description: z.string().optional()
|
|
297
|
+
}).optional(),
|
|
298
|
+
removability: z.object({
|
|
299
|
+
can_be_removed_by_refactor: z.enum(["yes", "no", "partially", "unknown"]),
|
|
300
|
+
requires: z.array(z.string()),
|
|
301
|
+
risk: z.enum(["low", "medium", "high", "critical", "unknown"])
|
|
302
|
+
}).optional()
|
|
303
|
+
});
|
|
304
|
+
var rationaleSchema = withConfidenceSchema.extend({
|
|
305
|
+
id: z.string(),
|
|
306
|
+
statement: z.string(),
|
|
307
|
+
explains: z.array(z.string())
|
|
308
|
+
});
|
|
309
|
+
var questionSchema = z.object({
|
|
310
|
+
id: z.string(),
|
|
311
|
+
question: z.string(),
|
|
312
|
+
category: z.enum([
|
|
313
|
+
"rationale",
|
|
314
|
+
"business_rule",
|
|
315
|
+
"constraint",
|
|
316
|
+
"terminology",
|
|
317
|
+
"domain_boundary",
|
|
318
|
+
"lifecycle",
|
|
319
|
+
"permission",
|
|
320
|
+
"legacy",
|
|
321
|
+
"ownership",
|
|
322
|
+
"enforcement"
|
|
323
|
+
]),
|
|
324
|
+
reason: z.string(),
|
|
325
|
+
impact: z.string().optional(),
|
|
326
|
+
related_ids: z.array(z.string()),
|
|
327
|
+
evidence_ids: z.array(z.string()),
|
|
328
|
+
priority: z.enum(["low", "medium", "high"]),
|
|
329
|
+
status: z.enum(["open", "answered", "obsolete"]),
|
|
330
|
+
answer: z.string().nullable().optional(),
|
|
331
|
+
answered_by: z.string().nullable().optional(),
|
|
332
|
+
answered_at: z.string().nullable().optional()
|
|
333
|
+
});
|
|
334
|
+
var conflictSchema = z.object({
|
|
335
|
+
id: z.string(),
|
|
336
|
+
type: z.enum([
|
|
337
|
+
"domain_boundary",
|
|
338
|
+
"terminology",
|
|
339
|
+
"rule_conflict",
|
|
340
|
+
"rationale_conflict",
|
|
341
|
+
"entity_duplicate",
|
|
342
|
+
"relationship_conflict"
|
|
343
|
+
]),
|
|
344
|
+
description: z.string(),
|
|
345
|
+
related_ids: z.array(z.string()),
|
|
346
|
+
evidence_ids: z.array(z.string()),
|
|
347
|
+
status: z.enum(["needs_human_input", "resolved", "obsolete"]),
|
|
348
|
+
questions: z.array(z.string()),
|
|
349
|
+
resolution: z.string().optional(),
|
|
350
|
+
resolved_by_evidence_id: z.string().optional()
|
|
351
|
+
});
|
|
352
|
+
var knowledgeCollectionSchemas = {
|
|
353
|
+
domains: z.array(domainSchema),
|
|
354
|
+
entities: z.array(entitySchema),
|
|
355
|
+
relationships: z.array(relationshipSchema),
|
|
356
|
+
capabilities: z.array(capabilitySchema),
|
|
357
|
+
features: z.array(featureSchema),
|
|
358
|
+
business_rules: z.array(businessRuleSchema),
|
|
359
|
+
constraints: z.array(constraintSchema),
|
|
360
|
+
rationales: z.array(rationaleSchema),
|
|
361
|
+
evidence: z.array(evidenceSchema),
|
|
362
|
+
questions: z.array(questionSchema),
|
|
363
|
+
conflicts: z.array(conflictSchema)
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// packages/core/src/knowledge/extraction-registry.ts
|
|
367
|
+
var KNOWLEDGE_EXTRACTION_TYPE_REGISTRY = {
|
|
368
|
+
domains: {
|
|
369
|
+
collection: "domains",
|
|
370
|
+
title: "Domains",
|
|
371
|
+
storageFile: "domain.json",
|
|
372
|
+
idConvention: {
|
|
373
|
+
pattern: "domain.<domain-slug>",
|
|
374
|
+
prefix: "domain",
|
|
375
|
+
example: "domain.orders"
|
|
376
|
+
},
|
|
377
|
+
schemaName: "Domain",
|
|
378
|
+
schema: domainSchema,
|
|
379
|
+
guideFilePath: "packages/core/src/knowledge/guides/domains.md",
|
|
380
|
+
aliases: []
|
|
381
|
+
},
|
|
382
|
+
entities: {
|
|
383
|
+
collection: "entities",
|
|
384
|
+
title: "Entities",
|
|
385
|
+
storageFile: "entities.json",
|
|
386
|
+
idConvention: {
|
|
387
|
+
pattern: "entity.<domain-slug>.<entity-slug>",
|
|
388
|
+
prefix: "entity",
|
|
389
|
+
example: "entity.orders.order"
|
|
390
|
+
},
|
|
391
|
+
schemaName: "Entity",
|
|
392
|
+
schema: entitySchema,
|
|
393
|
+
guideFilePath: "packages/core/src/knowledge/guides/entities.md",
|
|
394
|
+
aliases: []
|
|
395
|
+
},
|
|
396
|
+
relationships: {
|
|
397
|
+
collection: "relationships",
|
|
398
|
+
title: "Relationships",
|
|
399
|
+
storageFile: "relationships.json",
|
|
400
|
+
idConvention: {
|
|
401
|
+
pattern: "rel.<domain-slug>.<relationship-slug>",
|
|
402
|
+
prefix: "rel",
|
|
403
|
+
example: "rel.orders.order-has-items"
|
|
404
|
+
},
|
|
405
|
+
schemaName: "EntityRelationship",
|
|
406
|
+
schema: relationshipSchema,
|
|
407
|
+
guideFilePath: "packages/core/src/knowledge/guides/relationships.md",
|
|
408
|
+
aliases: ["entity-relationships"]
|
|
409
|
+
},
|
|
410
|
+
capabilities: {
|
|
411
|
+
collection: "capabilities",
|
|
412
|
+
title: "Capabilities",
|
|
413
|
+
storageFile: "capabilities.json",
|
|
414
|
+
idConvention: {
|
|
415
|
+
pattern: "cap.<domain-slug>.<capability-slug>",
|
|
416
|
+
prefix: "cap",
|
|
417
|
+
example: "cap.orders.order-lifecycle"
|
|
418
|
+
},
|
|
419
|
+
schemaName: "Capability",
|
|
420
|
+
schema: capabilitySchema,
|
|
421
|
+
guideFilePath: "packages/core/src/knowledge/guides/capabilities.md",
|
|
422
|
+
aliases: []
|
|
423
|
+
},
|
|
424
|
+
features: {
|
|
425
|
+
collection: "features",
|
|
426
|
+
title: "Features",
|
|
427
|
+
storageFile: "features.json",
|
|
428
|
+
idConvention: {
|
|
429
|
+
pattern: "feature.<domain-slug>.<feature-slug>",
|
|
430
|
+
prefix: "feature",
|
|
431
|
+
example: "feature.orders.cancel-order"
|
|
432
|
+
},
|
|
433
|
+
schemaName: "Feature",
|
|
434
|
+
schema: featureSchema,
|
|
435
|
+
guideFilePath: "packages/core/src/knowledge/guides/features.md",
|
|
436
|
+
aliases: []
|
|
437
|
+
},
|
|
438
|
+
business_rules: {
|
|
439
|
+
collection: "business_rules",
|
|
440
|
+
title: "Business Rules",
|
|
441
|
+
storageFile: "business-rules.json",
|
|
442
|
+
idConvention: {
|
|
443
|
+
pattern: "rule.<domain-slug>.<rule-slug>",
|
|
444
|
+
prefix: "rule",
|
|
445
|
+
example: "rule.orders.no-direct-cancel-paid-order"
|
|
446
|
+
},
|
|
447
|
+
schemaName: "BusinessRule",
|
|
448
|
+
schema: businessRuleSchema,
|
|
449
|
+
guideFilePath: "packages/core/src/knowledge/guides/business-rules.md",
|
|
450
|
+
aliases: ["business-rules"]
|
|
451
|
+
},
|
|
452
|
+
constraints: {
|
|
453
|
+
collection: "constraints",
|
|
454
|
+
title: "Constraints",
|
|
455
|
+
storageFile: "constraints.json",
|
|
456
|
+
idConvention: {
|
|
457
|
+
pattern: "constraint.<domain-slug>.<constraint-slug>",
|
|
458
|
+
prefix: "constraint",
|
|
459
|
+
example: "constraint.orders.erp-sync-one-way"
|
|
460
|
+
},
|
|
461
|
+
schemaName: "Constraint",
|
|
462
|
+
schema: constraintSchema,
|
|
463
|
+
guideFilePath: "packages/core/src/knowledge/guides/constraints.md",
|
|
464
|
+
aliases: []
|
|
465
|
+
},
|
|
466
|
+
rationales: {
|
|
467
|
+
collection: "rationales",
|
|
468
|
+
title: "Rationales",
|
|
469
|
+
storageFile: "rationales.json",
|
|
470
|
+
idConvention: {
|
|
471
|
+
pattern: "why.<domain-slug>.<rationale-slug>",
|
|
472
|
+
prefix: "why",
|
|
473
|
+
example: "why.orders.refund-required-for-paid-order"
|
|
474
|
+
},
|
|
475
|
+
schemaName: "Rationale",
|
|
476
|
+
schema: rationaleSchema,
|
|
477
|
+
guideFilePath: "packages/core/src/knowledge/guides/rationales.md",
|
|
478
|
+
aliases: []
|
|
479
|
+
},
|
|
480
|
+
evidence: {
|
|
481
|
+
collection: "evidence",
|
|
482
|
+
title: "Evidence",
|
|
483
|
+
storageFile: "evidence.json",
|
|
484
|
+
idConvention: {
|
|
485
|
+
pattern: "ev.<domain-slug>.<evidence-slug>",
|
|
486
|
+
prefix: "ev",
|
|
487
|
+
example: "ev.orders.cancel-disabled-paid"
|
|
488
|
+
},
|
|
489
|
+
schemaName: "Evidence",
|
|
490
|
+
schema: evidenceSchema,
|
|
491
|
+
guideFilePath: "packages/core/src/knowledge/guides/evidence.md",
|
|
492
|
+
aliases: []
|
|
493
|
+
},
|
|
494
|
+
questions: {
|
|
495
|
+
collection: "questions",
|
|
496
|
+
title: "Questions",
|
|
497
|
+
storageFile: "questions.json",
|
|
498
|
+
idConvention: {
|
|
499
|
+
pattern: "q.<domain-slug>.<question-slug>",
|
|
500
|
+
prefix: "q",
|
|
501
|
+
example: "q.orders.paid-cancel-rationale"
|
|
502
|
+
},
|
|
503
|
+
schemaName: "Question",
|
|
504
|
+
schema: questionSchema,
|
|
505
|
+
guideFilePath: "packages/core/src/knowledge/guides/questions.md",
|
|
506
|
+
aliases: []
|
|
507
|
+
},
|
|
508
|
+
conflicts: {
|
|
509
|
+
collection: "conflicts",
|
|
510
|
+
title: "Conflicts",
|
|
511
|
+
storageFile: "conflicts.json",
|
|
512
|
+
idConvention: {
|
|
513
|
+
pattern: "conflict.<domain-slug>.<conflict-slug>",
|
|
514
|
+
prefix: "conflict",
|
|
515
|
+
example: "conflict.invoice-domain"
|
|
516
|
+
},
|
|
517
|
+
schemaName: "Conflict",
|
|
518
|
+
schema: conflictSchema,
|
|
519
|
+
guideFilePath: "packages/core/src/knowledge/guides/conflicts.md",
|
|
520
|
+
aliases: []
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
var KNOWLEDGE_EXTRACTION_TYPES = Object.values(
|
|
524
|
+
KNOWLEDGE_EXTRACTION_TYPE_REGISTRY
|
|
525
|
+
);
|
|
526
|
+
var KNOWLEDGE_EXTRACTION_TYPE_LOOKUP = new Map(
|
|
527
|
+
KNOWLEDGE_EXTRACTION_TYPES.flatMap((definition) => [
|
|
528
|
+
[definition.collection, definition],
|
|
529
|
+
...definition.aliases.map((alias) => [alias, definition])
|
|
530
|
+
])
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
// packages/core/src/knowledge/extraction-resources.ts
|
|
534
|
+
import { z as z2 } from "zod/v4";
|
|
535
|
+
|
|
536
|
+
// packages/core/src/knowledge/loader.ts
|
|
537
|
+
import { existsSync } from "node:fs";
|
|
538
|
+
import { readFile } from "node:fs/promises";
|
|
539
|
+
import { basename, join } from "node:path";
|
|
540
|
+
import { z as z3 } from "zod/v4";
|
|
541
|
+
|
|
542
|
+
// packages/core/src/knowledge/root.ts
|
|
543
|
+
import { resolve } from "node:path";
|
|
544
|
+
function resolveKnowledgeRootFromEnv(envValue, cwd = process.cwd()) {
|
|
545
|
+
return envValue ? resolve(cwd, envValue) : resolve(cwd, "ez-know");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// packages/core/src/knowledge/loader.ts
|
|
549
|
+
var COLLECTION_SCHEMAS = {
|
|
550
|
+
domains: z3.array(domainSchema),
|
|
551
|
+
entities: z3.array(entitySchema),
|
|
552
|
+
relationships: z3.array(relationshipSchema),
|
|
553
|
+
capabilities: z3.array(capabilitySchema),
|
|
554
|
+
features: z3.array(featureSchema),
|
|
555
|
+
business_rules: z3.array(businessRuleSchema),
|
|
556
|
+
constraints: z3.array(constraintSchema),
|
|
557
|
+
rationales: z3.array(rationaleSchema),
|
|
558
|
+
evidence: z3.array(evidenceSchema),
|
|
559
|
+
questions: z3.array(questionSchema),
|
|
560
|
+
conflicts: z3.array(conflictSchema)
|
|
561
|
+
};
|
|
562
|
+
var COLLECTION_DEFINITIONS = KNOWLEDGE_EXTRACTION_TYPE_REGISTRY;
|
|
563
|
+
var KnowledgeStoreLoadError = class extends Error {
|
|
564
|
+
diagnostics;
|
|
565
|
+
constructor(message, diagnostics) {
|
|
566
|
+
super(message);
|
|
567
|
+
this.name = "KnowledgeStoreLoadError";
|
|
568
|
+
this.diagnostics = diagnostics;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
function resolveKnowledgeRoot(envValue, cwd = process.cwd()) {
|
|
572
|
+
return resolveKnowledgeRootFromEnv(envValue, cwd);
|
|
573
|
+
}
|
|
574
|
+
async function discoverDomainFolders(rootDir) {
|
|
575
|
+
const { readdir: readdir2 } = await import("node:fs/promises");
|
|
576
|
+
const discovered = [];
|
|
577
|
+
const entries = await readdir2(rootDir, { withFileTypes: true });
|
|
578
|
+
for (const entry of entries) {
|
|
579
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
580
|
+
discovered.push(join(rootDir, entry.name));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
discovered.sort((left, right) => left.localeCompare(right));
|
|
584
|
+
return discovered;
|
|
585
|
+
}
|
|
586
|
+
function readDiagnostic(kind, path, message, id) {
|
|
587
|
+
return {
|
|
588
|
+
kind,
|
|
589
|
+
path,
|
|
590
|
+
id,
|
|
591
|
+
message
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
async function readCanonicalFile(filePath, schema) {
|
|
595
|
+
let raw;
|
|
596
|
+
try {
|
|
597
|
+
raw = await readFile(filePath, "utf8");
|
|
598
|
+
} catch (error) {
|
|
599
|
+
throw new KnowledgeStoreLoadError(
|
|
600
|
+
`Failed to read canonical knowledge file: ${filePath}`,
|
|
601
|
+
[
|
|
602
|
+
readDiagnostic(
|
|
603
|
+
"missing_domain_file",
|
|
604
|
+
filePath,
|
|
605
|
+
`Unable to read canonical file: ${filePath}`
|
|
606
|
+
)
|
|
607
|
+
]
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
let parsed;
|
|
611
|
+
try {
|
|
612
|
+
parsed = JSON.parse(raw);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
throw new KnowledgeStoreLoadError(
|
|
615
|
+
`Invalid JSON in canonical knowledge file: ${filePath}`,
|
|
616
|
+
[readDiagnostic("invalid_json", filePath, `Invalid JSON in ${filePath}`)]
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
return schema.parse(parsed);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
throw new KnowledgeStoreLoadError(
|
|
623
|
+
`Invalid knowledge schema in canonical file: ${filePath}`,
|
|
624
|
+
[
|
|
625
|
+
readDiagnostic(
|
|
626
|
+
"invalid_shape",
|
|
627
|
+
filePath,
|
|
628
|
+
`Invalid canonical file shape in ${filePath}`
|
|
629
|
+
)
|
|
630
|
+
]
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function validateIdPrefix(collection, id, domainSlug) {
|
|
635
|
+
const definition = COLLECTION_DEFINITIONS[collection];
|
|
636
|
+
if (collection === "domains") {
|
|
637
|
+
const expectedId = `domain.${domainSlug}`;
|
|
638
|
+
if (id !== expectedId) {
|
|
639
|
+
return `Expected ${id} to equal ${expectedId}`;
|
|
640
|
+
}
|
|
641
|
+
return void 0;
|
|
642
|
+
}
|
|
643
|
+
const expectedPrefix = `${definition.idConvention.prefix}.${domainSlug}.`;
|
|
644
|
+
if (!id.startsWith(expectedPrefix)) {
|
|
645
|
+
return `Expected ${id} to start with ${expectedPrefix}`;
|
|
646
|
+
}
|
|
647
|
+
return void 0;
|
|
648
|
+
}
|
|
649
|
+
function getDomainId(domainSlug) {
|
|
650
|
+
return `domain.${domainSlug}`;
|
|
651
|
+
}
|
|
652
|
+
function emptyStore(rootDir) {
|
|
653
|
+
return {
|
|
654
|
+
rootDir,
|
|
655
|
+
sourceFiles: [],
|
|
656
|
+
domains: [],
|
|
657
|
+
entities: [],
|
|
658
|
+
relationships: [],
|
|
659
|
+
capabilities: [],
|
|
660
|
+
features: [],
|
|
661
|
+
business_rules: [],
|
|
662
|
+
constraints: [],
|
|
663
|
+
rationales: [],
|
|
664
|
+
evidence: [],
|
|
665
|
+
questions: [],
|
|
666
|
+
conflicts: [],
|
|
667
|
+
byCollection: {
|
|
668
|
+
domains: /* @__PURE__ */ new Map(),
|
|
669
|
+
entities: /* @__PURE__ */ new Map(),
|
|
670
|
+
relationships: /* @__PURE__ */ new Map(),
|
|
671
|
+
capabilities: /* @__PURE__ */ new Map(),
|
|
672
|
+
features: /* @__PURE__ */ new Map(),
|
|
673
|
+
business_rules: /* @__PURE__ */ new Map(),
|
|
674
|
+
constraints: /* @__PURE__ */ new Map(),
|
|
675
|
+
rationales: /* @__PURE__ */ new Map(),
|
|
676
|
+
evidence: /* @__PURE__ */ new Map(),
|
|
677
|
+
questions: /* @__PURE__ */ new Map(),
|
|
678
|
+
conflicts: /* @__PURE__ */ new Map()
|
|
679
|
+
},
|
|
680
|
+
byId: /* @__PURE__ */ new Map(),
|
|
681
|
+
reverseReferences: /* @__PURE__ */ new Map(),
|
|
682
|
+
diagnostics: []
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function addRecord(store, collection, record, diagnostics) {
|
|
686
|
+
if (store.byId.has(record.id)) {
|
|
687
|
+
diagnostics.push(
|
|
688
|
+
readDiagnostic(
|
|
689
|
+
"duplicate_id",
|
|
690
|
+
void 0,
|
|
691
|
+
`Duplicate knowledge id detected: ${record.id}`,
|
|
692
|
+
record.id
|
|
693
|
+
)
|
|
694
|
+
);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
store.byId.set(record.id, record);
|
|
698
|
+
store.byCollection[collection].set(record.id, record);
|
|
699
|
+
store[collection].push(record);
|
|
700
|
+
}
|
|
701
|
+
function validateFolderRecordIds(collection, records, domainSlug, diagnostics) {
|
|
702
|
+
for (const record of records) {
|
|
703
|
+
const message = validateIdPrefix(collection, record.id, domainSlug);
|
|
704
|
+
if (message) {
|
|
705
|
+
diagnostics.push(
|
|
706
|
+
readDiagnostic("invalid_domain_id", void 0, message, record.id)
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
function collectReferenceIds(record) {
|
|
712
|
+
if ("relationship_type" in record) {
|
|
713
|
+
return [
|
|
714
|
+
record.from_entity_id,
|
|
715
|
+
record.to_entity_id
|
|
716
|
+
];
|
|
717
|
+
}
|
|
718
|
+
if ("business_definition" in record) {
|
|
719
|
+
const entity = record;
|
|
720
|
+
return [
|
|
721
|
+
...entity.domain_ids,
|
|
722
|
+
...entity.relationship_ids,
|
|
723
|
+
...entity.capability_ids,
|
|
724
|
+
...entity.feature_ids,
|
|
725
|
+
...entity.rule_ids
|
|
726
|
+
];
|
|
727
|
+
}
|
|
728
|
+
if ("behavior_type" in record) {
|
|
729
|
+
const rule = record;
|
|
730
|
+
return [
|
|
731
|
+
...rule.applies_to.domain_ids ?? [],
|
|
732
|
+
...rule.applies_to.entity_ids ?? [],
|
|
733
|
+
...rule.applies_to.capability_ids ?? [],
|
|
734
|
+
...rule.applies_to.feature_ids ?? [],
|
|
735
|
+
...rule.related_constraint_ids,
|
|
736
|
+
...rule.rationale_ids
|
|
737
|
+
];
|
|
738
|
+
}
|
|
739
|
+
if ("constraint_type" in record) {
|
|
740
|
+
const constraint = record;
|
|
741
|
+
return [
|
|
742
|
+
...constraint.applies_to.domain_ids ?? [],
|
|
743
|
+
...constraint.applies_to.entity_ids ?? [],
|
|
744
|
+
...constraint.applies_to.capability_ids ?? [],
|
|
745
|
+
...constraint.applies_to.feature_ids ?? [],
|
|
746
|
+
...constraint.applies_to.business_rule_ids ?? []
|
|
747
|
+
];
|
|
748
|
+
}
|
|
749
|
+
if ("parent_capability_ids" in record) {
|
|
750
|
+
const capability = record;
|
|
751
|
+
return [
|
|
752
|
+
...capability.domain_ids,
|
|
753
|
+
...capability.entity_ids,
|
|
754
|
+
...capability.parent_capability_ids,
|
|
755
|
+
...capability.child_capability_ids,
|
|
756
|
+
...capability.feature_ids
|
|
757
|
+
];
|
|
758
|
+
}
|
|
759
|
+
if ("rule_ids" in record && "constraint_ids" in record) {
|
|
760
|
+
const feature = record;
|
|
761
|
+
return [
|
|
762
|
+
...feature.domain_ids,
|
|
763
|
+
...feature.capability_ids,
|
|
764
|
+
...feature.entity_ids,
|
|
765
|
+
...feature.rule_ids,
|
|
766
|
+
...feature.constraint_ids
|
|
767
|
+
];
|
|
768
|
+
}
|
|
769
|
+
if ("entity_ids" in record && "capability_ids" in record && "why_it_exists" in record && !("business_definition" in record) && !("parent_capability_ids" in record) && !("constraint_ids" in record) && !("rule_ids" in record)) {
|
|
770
|
+
const domain = record;
|
|
771
|
+
return [...domain.entity_ids, ...domain.capability_ids];
|
|
772
|
+
}
|
|
773
|
+
if ("explains" in record) {
|
|
774
|
+
return record.explains;
|
|
775
|
+
}
|
|
776
|
+
if ("source_type" in record) {
|
|
777
|
+
return record.supports;
|
|
778
|
+
}
|
|
779
|
+
if ("priority" in record) {
|
|
780
|
+
const question = record;
|
|
781
|
+
return [...question.related_ids, ...question.evidence_ids];
|
|
782
|
+
}
|
|
783
|
+
const conflict = record;
|
|
784
|
+
return [
|
|
785
|
+
...conflict.related_ids,
|
|
786
|
+
...conflict.evidence_ids,
|
|
787
|
+
...conflict.questions
|
|
788
|
+
];
|
|
789
|
+
}
|
|
790
|
+
function buildReverseReferences(store) {
|
|
791
|
+
for (const record of store.byId.values()) {
|
|
792
|
+
const references = collectReferenceIds(record);
|
|
793
|
+
for (const referenceId of references) {
|
|
794
|
+
const reverse = store.reverseReferences.get(referenceId) ?? [];
|
|
795
|
+
reverse.push(record.id);
|
|
796
|
+
store.reverseReferences.set(referenceId, reverse);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function validateReferences(store, diagnostics) {
|
|
801
|
+
for (const record of store.byId.values()) {
|
|
802
|
+
for (const referenceId of collectReferenceIds(record)) {
|
|
803
|
+
if (!store.byId.has(referenceId)) {
|
|
804
|
+
diagnostics.push(
|
|
805
|
+
readDiagnostic(
|
|
806
|
+
"invalid_reference",
|
|
807
|
+
void 0,
|
|
808
|
+
`Unknown reference id "${referenceId}" from record "${record.id}"`,
|
|
809
|
+
record.id
|
|
810
|
+
)
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function finalizeStore(store, diagnostics) {
|
|
817
|
+
store.diagnostics = diagnostics;
|
|
818
|
+
if (diagnostics.length > 0) {
|
|
819
|
+
throw new KnowledgeStoreLoadError(
|
|
820
|
+
diagnostics.map((diagnostic) => diagnostic.message).join("; "),
|
|
821
|
+
diagnostics
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
return store;
|
|
825
|
+
}
|
|
826
|
+
async function loadKnowledgeStoreFromRoot(rootDir) {
|
|
827
|
+
if (!existsSync(rootDir)) {
|
|
828
|
+
throw new KnowledgeStoreLoadError(
|
|
829
|
+
`Knowledge root does not exist: ${rootDir}`,
|
|
830
|
+
[
|
|
831
|
+
readDiagnostic(
|
|
832
|
+
"missing_root",
|
|
833
|
+
rootDir,
|
|
834
|
+
`Knowledge root does not exist: ${rootDir}`
|
|
835
|
+
)
|
|
836
|
+
]
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
const diagnostics = [];
|
|
840
|
+
const store = emptyStore(rootDir);
|
|
841
|
+
const domainFolders = await discoverDomainFolders(rootDir);
|
|
842
|
+
const sourceFiles = [];
|
|
843
|
+
for (const domainFolder of domainFolders) {
|
|
844
|
+
const domainSlug = basename(domainFolder);
|
|
845
|
+
const domainFile = join(
|
|
846
|
+
domainFolder,
|
|
847
|
+
COLLECTION_DEFINITIONS.domains.storageFile
|
|
848
|
+
);
|
|
849
|
+
if (!existsSync(domainFile)) {
|
|
850
|
+
diagnostics.push(
|
|
851
|
+
readDiagnostic(
|
|
852
|
+
"missing_domain_file",
|
|
853
|
+
domainFile,
|
|
854
|
+
`Missing canonical domain file: ${domainFile}`
|
|
855
|
+
)
|
|
856
|
+
);
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
const domain = await readCanonicalFile(
|
|
860
|
+
domainFile,
|
|
861
|
+
domainSchema
|
|
862
|
+
);
|
|
863
|
+
sourceFiles.push(domainFile);
|
|
864
|
+
validateFolderRecordIds("domains", [domain], domainSlug, diagnostics);
|
|
865
|
+
const domainId = getDomainId(domainSlug);
|
|
866
|
+
if (domain.id !== domainId) {
|
|
867
|
+
diagnostics.push(
|
|
868
|
+
readDiagnostic(
|
|
869
|
+
"invalid_domain_id",
|
|
870
|
+
domainFile,
|
|
871
|
+
`Domain id "${domain.id}" must match folder slug "${domainSlug}"`,
|
|
872
|
+
domain.id
|
|
873
|
+
)
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
addRecord(store, "domains", domain, diagnostics);
|
|
877
|
+
for (const collection of Object.keys(
|
|
878
|
+
COLLECTION_DEFINITIONS
|
|
879
|
+
)) {
|
|
880
|
+
if (collection === "domains") {
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
const fileName = COLLECTION_DEFINITIONS[collection].storageFile;
|
|
884
|
+
const filePath = join(domainFolder, fileName);
|
|
885
|
+
if (!existsSync(filePath)) {
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
const parsed = await readCanonicalFile(
|
|
889
|
+
filePath,
|
|
890
|
+
COLLECTION_SCHEMAS[collection]
|
|
891
|
+
);
|
|
892
|
+
sourceFiles.push(filePath);
|
|
893
|
+
validateFolderRecordIds(
|
|
894
|
+
collection,
|
|
895
|
+
parsed,
|
|
896
|
+
domainSlug,
|
|
897
|
+
diagnostics
|
|
898
|
+
);
|
|
899
|
+
for (const record of parsed) {
|
|
900
|
+
addRecord(store, collection, record, diagnostics);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
store.sourceFiles = sourceFiles.sort(
|
|
905
|
+
(left, right) => left.localeCompare(right)
|
|
906
|
+
);
|
|
907
|
+
buildReverseReferences(store);
|
|
908
|
+
validateReferences(store, diagnostics);
|
|
909
|
+
return finalizeStore(store, diagnostics);
|
|
910
|
+
}
|
|
911
|
+
async function loadKnowledgeStoreFromEnv(envValue, cwd = process.cwd()) {
|
|
912
|
+
const rootDir = resolveKnowledgeRoot(envValue, cwd);
|
|
913
|
+
if (!rootDir || !existsSync(rootDir)) {
|
|
914
|
+
return void 0;
|
|
915
|
+
}
|
|
916
|
+
return loadKnowledgeStoreFromRoot(rootDir);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// packages/core/src/knowledge/patch/storage.ts
|
|
920
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
921
|
+
import { mkdir, readFile as readFile2, readdir, writeFile } from "node:fs/promises";
|
|
922
|
+
import { isAbsolute, join as join3, normalize, resolve as resolve4, sep } from "node:path";
|
|
923
|
+
import "zod/v4";
|
|
924
|
+
|
|
925
|
+
// packages/core/src/knowledge/patch/constants.ts
|
|
926
|
+
var KNOWLEDGE_PATCH_SCHEMA_VERSION = 1;
|
|
927
|
+
var KNOWLEDGE_PATCH_FOLDER_NAME = ".patches";
|
|
928
|
+
var SUPPORTED_OPERATION_TYPES = Object.keys(
|
|
929
|
+
KNOWLEDGE_EXTRACTION_TYPE_REGISTRY
|
|
930
|
+
);
|
|
931
|
+
var supportedOperationTypeSet = new Set(
|
|
932
|
+
SUPPORTED_OPERATION_TYPES
|
|
933
|
+
);
|
|
934
|
+
|
|
935
|
+
// packages/core/src/knowledge/patch/paths.ts
|
|
936
|
+
import { join as join2 } from "node:path";
|
|
937
|
+
function getKnowledgeRoot(envValue, cwd = process.cwd()) {
|
|
938
|
+
return resolveKnowledgeRootFromEnv(envValue, cwd);
|
|
939
|
+
}
|
|
940
|
+
function resolveKnowledgePatchRoot(envValue, cwd = process.cwd()) {
|
|
941
|
+
return join2(getKnowledgeRoot(envValue, cwd), KNOWLEDGE_PATCH_FOLDER_NAME);
|
|
942
|
+
}
|
|
943
|
+
function resolveKnowledgePatchDirectory(envValue, patchId, cwd = process.cwd()) {
|
|
944
|
+
return join2(resolveKnowledgePatchRoot(envValue, cwd), patchId);
|
|
945
|
+
}
|
|
946
|
+
function patchMetadataPath(patchDir) {
|
|
947
|
+
return join2(patchDir, "patch.json");
|
|
948
|
+
}
|
|
949
|
+
function patchOperationsPath(patchDir) {
|
|
950
|
+
return join2(patchDir, "operations.json");
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// packages/core/src/knowledge/patch/schemas.ts
|
|
954
|
+
import { z as z4 } from "zod/v4";
|
|
955
|
+
var patchStatusSchema = z4.enum(["draft", "closed"]);
|
|
956
|
+
var canonicalOperationTypeSchema = z4.string().trim().min(1).superRefine((value, ctx) => {
|
|
957
|
+
if (!supportedOperationTypeSet.has(value)) {
|
|
958
|
+
ctx.addIssue({
|
|
959
|
+
code: "custom",
|
|
960
|
+
message: `Unsupported patch operation type "${value}". Supported types: ${SUPPORTED_OPERATION_TYPES.join(
|
|
961
|
+
", "
|
|
962
|
+
)}`
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}).transform((value) => value);
|
|
966
|
+
var knowledgePatchMetadataSchema = z4.object({
|
|
967
|
+
id: z4.string().min(1),
|
|
968
|
+
title: z4.string().trim().min(1),
|
|
969
|
+
description: z4.string().trim().min(1).optional(),
|
|
970
|
+
status: patchStatusSchema,
|
|
971
|
+
rootFolder: z4.string().trim().min(1),
|
|
972
|
+
omittedFiles: z4.array(
|
|
973
|
+
z4.object({
|
|
974
|
+
path: z4.string().trim().min(1),
|
|
975
|
+
reason: z4.string().trim().min(1)
|
|
976
|
+
})
|
|
977
|
+
),
|
|
978
|
+
schemaVersion: z4.literal(KNOWLEDGE_PATCH_SCHEMA_VERSION),
|
|
979
|
+
createdAt: z4.string().datetime({ offset: true }),
|
|
980
|
+
updatedAt: z4.string().datetime({ offset: true }),
|
|
981
|
+
closedAt: z4.string().datetime({ offset: true }).optional()
|
|
982
|
+
});
|
|
983
|
+
var operationBaseSchema = z4.object({
|
|
984
|
+
id: z4.string().min(1),
|
|
985
|
+
type: canonicalOperationTypeSchema,
|
|
986
|
+
domain: z4.string().trim().min(1)
|
|
987
|
+
});
|
|
988
|
+
var knowledgePatchCreateOperationSchema = operationBaseSchema.extend({
|
|
989
|
+
kind: z4.literal("create"),
|
|
990
|
+
payload: z4.record(z4.string(), z4.unknown())
|
|
991
|
+
});
|
|
992
|
+
var knowledgePatchUpdateOperationSchema = operationBaseSchema.extend({
|
|
993
|
+
kind: z4.literal("update"),
|
|
994
|
+
targetId: z4.string().min(1),
|
|
995
|
+
payload: z4.record(z4.string(), z4.unknown())
|
|
996
|
+
});
|
|
997
|
+
var knowledgePatchDeleteOperationSchema = operationBaseSchema.extend({
|
|
998
|
+
kind: z4.literal("delete"),
|
|
999
|
+
targetId: z4.string().min(1),
|
|
1000
|
+
reason: z4.string().trim().min(1)
|
|
1001
|
+
});
|
|
1002
|
+
var knowledgePatchOperationSchema = z4.discriminatedUnion("kind", [
|
|
1003
|
+
knowledgePatchCreateOperationSchema,
|
|
1004
|
+
knowledgePatchUpdateOperationSchema,
|
|
1005
|
+
knowledgePatchDeleteOperationSchema
|
|
1006
|
+
]);
|
|
1007
|
+
var knowledgePatchOperationsEnvelopeSchema = z4.object({
|
|
1008
|
+
schemaVersion: z4.literal(KNOWLEDGE_PATCH_SCHEMA_VERSION),
|
|
1009
|
+
operations: z4.array(knowledgePatchOperationSchema)
|
|
1010
|
+
});
|
|
1011
|
+
var knowledgePatchDiagnosticSchema = z4.object({
|
|
1012
|
+
severity: z4.enum(["error", "critical"]),
|
|
1013
|
+
code: z4.string(),
|
|
1014
|
+
message: z4.string(),
|
|
1015
|
+
patchId: z4.string().optional(),
|
|
1016
|
+
operationId: z4.string().optional(),
|
|
1017
|
+
targetId: z4.string().optional(),
|
|
1018
|
+
path: z4.array(z4.union([z4.string(), z4.number()])).optional(),
|
|
1019
|
+
supportedTypes: z4.array(z4.string()).optional()
|
|
1020
|
+
});
|
|
1021
|
+
var knowledgePatchValidationResultSchema = z4.object({
|
|
1022
|
+
valid: z4.boolean(),
|
|
1023
|
+
fingerprint: z4.string().optional(),
|
|
1024
|
+
diagnostics: z4.array(knowledgePatchDiagnosticSchema),
|
|
1025
|
+
operationCount: z4.number().int().nonnegative(),
|
|
1026
|
+
appliedOperationIds: z4.array(z4.string()),
|
|
1027
|
+
affectedFiles: z4.array(z4.string())
|
|
1028
|
+
});
|
|
1029
|
+
var knowledgePatchApplyApprovalSchema = z4.object({
|
|
1030
|
+
approved: z4.boolean(),
|
|
1031
|
+
validationFingerprint: z4.string().min(1)
|
|
1032
|
+
});
|
|
1033
|
+
var createPatchInputSchema = z4.object({
|
|
1034
|
+
title: z4.string().trim().min(1),
|
|
1035
|
+
description: z4.string().trim().min(1).optional(),
|
|
1036
|
+
rootFolder: z4.string().trim().min(1),
|
|
1037
|
+
omittedFiles: z4.array(
|
|
1038
|
+
z4.object({
|
|
1039
|
+
path: z4.string().trim().min(1),
|
|
1040
|
+
reason: z4.string().trim().min(1)
|
|
1041
|
+
})
|
|
1042
|
+
).optional()
|
|
1043
|
+
});
|
|
1044
|
+
var addOperationInputSchema = z4.discriminatedUnion("kind", [
|
|
1045
|
+
knowledgePatchCreateOperationSchema.omit({ id: true }).extend({
|
|
1046
|
+
id: z4.string().min(1).optional()
|
|
1047
|
+
}),
|
|
1048
|
+
knowledgePatchUpdateOperationSchema.omit({ id: true }).extend({
|
|
1049
|
+
id: z4.string().min(1).optional()
|
|
1050
|
+
}),
|
|
1051
|
+
knowledgePatchDeleteOperationSchema.omit({ id: true }).extend({
|
|
1052
|
+
id: z4.string().min(1).optional()
|
|
1053
|
+
})
|
|
1054
|
+
]);
|
|
1055
|
+
var updateOperationInputSchema = z4.object({
|
|
1056
|
+
operationId: z4.string().min(1),
|
|
1057
|
+
operation: addOperationInputSchema
|
|
1058
|
+
});
|
|
1059
|
+
var updatePatchMetadataInputSchema = z4.object({
|
|
1060
|
+
rootFolder: z4.string().trim().min(1).optional(),
|
|
1061
|
+
omittedFiles: z4.array(
|
|
1062
|
+
z4.object({
|
|
1063
|
+
path: z4.string().trim().min(1),
|
|
1064
|
+
reason: z4.string().trim().min(1)
|
|
1065
|
+
})
|
|
1066
|
+
)
|
|
1067
|
+
});
|
|
1068
|
+
var knowledgePatchToolInputSchema = z4.discriminatedUnion("action", [
|
|
1069
|
+
z4.object({
|
|
1070
|
+
action: z4.literal("create"),
|
|
1071
|
+
patch: createPatchInputSchema
|
|
1072
|
+
}),
|
|
1073
|
+
z4.object({
|
|
1074
|
+
action: z4.literal("list")
|
|
1075
|
+
}),
|
|
1076
|
+
z4.object({
|
|
1077
|
+
action: z4.literal("get"),
|
|
1078
|
+
patchId: z4.string().min(1)
|
|
1079
|
+
}),
|
|
1080
|
+
z4.object({
|
|
1081
|
+
action: z4.literal("add_operation"),
|
|
1082
|
+
patchId: z4.string().min(1),
|
|
1083
|
+
operation: addOperationInputSchema
|
|
1084
|
+
}),
|
|
1085
|
+
z4.object({
|
|
1086
|
+
action: z4.literal("update_operation"),
|
|
1087
|
+
patchId: z4.string().min(1),
|
|
1088
|
+
update: updateOperationInputSchema
|
|
1089
|
+
}),
|
|
1090
|
+
z4.object({
|
|
1091
|
+
action: z4.literal("update_metadata"),
|
|
1092
|
+
patchId: z4.string().min(1),
|
|
1093
|
+
metadata: updatePatchMetadataInputSchema
|
|
1094
|
+
}),
|
|
1095
|
+
z4.object({
|
|
1096
|
+
action: z4.literal("delete_operation"),
|
|
1097
|
+
patchId: z4.string().min(1),
|
|
1098
|
+
operationId: z4.string().min(1)
|
|
1099
|
+
}),
|
|
1100
|
+
z4.object({
|
|
1101
|
+
action: z4.literal("validate"),
|
|
1102
|
+
patchId: z4.string().min(1)
|
|
1103
|
+
}),
|
|
1104
|
+
z4.object({
|
|
1105
|
+
action: z4.literal("close"),
|
|
1106
|
+
patchId: z4.string().min(1)
|
|
1107
|
+
})
|
|
1108
|
+
]);
|
|
1109
|
+
var knowledgePatchApplyToolInputSchema = z4.object({
|
|
1110
|
+
patchId: z4.string().min(1),
|
|
1111
|
+
approval: knowledgePatchApplyApprovalSchema
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
// packages/core/src/knowledge/patch/utils.ts
|
|
1115
|
+
function buildDiagnostic(diagnostic) {
|
|
1116
|
+
return knowledgePatchDiagnosticSchema.parse(diagnostic);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// packages/core/src/knowledge/patch/storage.ts
|
|
1120
|
+
async function readJsonFile(filePath, schema) {
|
|
1121
|
+
const raw = await readFile2(filePath, "utf8");
|
|
1122
|
+
const parsed = JSON.parse(raw);
|
|
1123
|
+
return schema.parse(parsed);
|
|
1124
|
+
}
|
|
1125
|
+
async function loadPatchStateByDir(patchDir) {
|
|
1126
|
+
const patch = await readJsonFile(
|
|
1127
|
+
patchMetadataPath(patchDir),
|
|
1128
|
+
knowledgePatchMetadataSchema
|
|
1129
|
+
);
|
|
1130
|
+
const envelope = await readJsonFile(
|
|
1131
|
+
patchOperationsPath(patchDir),
|
|
1132
|
+
knowledgePatchOperationsEnvelopeSchema
|
|
1133
|
+
);
|
|
1134
|
+
return {
|
|
1135
|
+
patch,
|
|
1136
|
+
operations: envelope.operations
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
async function readKnowledgePatch(envValue, patchId, cwd = process.cwd()) {
|
|
1140
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1141
|
+
return loadPatchStateByDir(patchDir);
|
|
1142
|
+
}
|
|
1143
|
+
async function listKnowledgePatches(envValue, cwd = process.cwd()) {
|
|
1144
|
+
const patchRoot = resolveKnowledgePatchRoot(envValue, cwd);
|
|
1145
|
+
if (!existsSync2(patchRoot)) {
|
|
1146
|
+
return [];
|
|
1147
|
+
}
|
|
1148
|
+
const entries = await readdir(patchRoot, { withFileTypes: true });
|
|
1149
|
+
const summaries = [];
|
|
1150
|
+
for (const entry of entries) {
|
|
1151
|
+
if (!entry.isDirectory()) {
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const patchDir = join3(patchRoot, entry.name);
|
|
1155
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1156
|
+
summaries.push({
|
|
1157
|
+
id: state.patch.id,
|
|
1158
|
+
status: state.patch.status,
|
|
1159
|
+
title: state.patch.title,
|
|
1160
|
+
description: state.patch.description,
|
|
1161
|
+
rootFolder: state.patch.rootFolder,
|
|
1162
|
+
omittedFiles: state.patch.omittedFiles,
|
|
1163
|
+
operationCount: state.operations.length,
|
|
1164
|
+
createdAt: state.patch.createdAt,
|
|
1165
|
+
updatedAt: state.patch.updatedAt
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
summaries.sort(
|
|
1169
|
+
(left, right) => left.createdAt === right.createdAt ? left.id.localeCompare(right.id) : left.createdAt.localeCompare(right.createdAt)
|
|
1170
|
+
);
|
|
1171
|
+
return summaries;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// packages/core/src/knowledge/patch/simulation.ts
|
|
1175
|
+
function cloneStoreState(store) {
|
|
1176
|
+
const byCollection = Object.fromEntries(
|
|
1177
|
+
SUPPORTED_OPERATION_TYPES.map((collection) => [
|
|
1178
|
+
collection,
|
|
1179
|
+
new Map(store.byCollection[collection])
|
|
1180
|
+
])
|
|
1181
|
+
);
|
|
1182
|
+
return {
|
|
1183
|
+
byCollection,
|
|
1184
|
+
byId: new Map(store.byId)
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
function validateRecordIdForDomain(type, id, domain) {
|
|
1188
|
+
const definition = KNOWLEDGE_EXTRACTION_TYPE_REGISTRY[type];
|
|
1189
|
+
if (type === "domains") {
|
|
1190
|
+
const expectedId = `domain.${domain}`;
|
|
1191
|
+
return id === expectedId ? void 0 : `Expected domain record id "${id}" to equal "${expectedId}".`;
|
|
1192
|
+
}
|
|
1193
|
+
const expectedPrefix = `${definition.idConvention.prefix}.${domain}.`;
|
|
1194
|
+
return id.startsWith(expectedPrefix) ? void 0 : `Expected record id "${id}" to start with "${expectedPrefix}".`;
|
|
1195
|
+
}
|
|
1196
|
+
function collectReferenceIds2(record) {
|
|
1197
|
+
if ("relationship_type" in record) {
|
|
1198
|
+
return [
|
|
1199
|
+
record.from_entity_id,
|
|
1200
|
+
record.to_entity_id
|
|
1201
|
+
];
|
|
1202
|
+
}
|
|
1203
|
+
if ("business_definition" in record) {
|
|
1204
|
+
const entity = record;
|
|
1205
|
+
return [
|
|
1206
|
+
...entity.domain_ids,
|
|
1207
|
+
...entity.relationship_ids,
|
|
1208
|
+
...entity.capability_ids,
|
|
1209
|
+
...entity.feature_ids,
|
|
1210
|
+
...entity.rule_ids
|
|
1211
|
+
];
|
|
1212
|
+
}
|
|
1213
|
+
if ("behavior_type" in record) {
|
|
1214
|
+
const rule = record;
|
|
1215
|
+
return [
|
|
1216
|
+
...rule.applies_to.domain_ids ?? [],
|
|
1217
|
+
...rule.applies_to.entity_ids ?? [],
|
|
1218
|
+
...rule.applies_to.capability_ids ?? [],
|
|
1219
|
+
...rule.applies_to.feature_ids ?? [],
|
|
1220
|
+
...rule.related_constraint_ids,
|
|
1221
|
+
...rule.rationale_ids
|
|
1222
|
+
];
|
|
1223
|
+
}
|
|
1224
|
+
if ("constraint_type" in record) {
|
|
1225
|
+
const constraint = record;
|
|
1226
|
+
return [
|
|
1227
|
+
...constraint.applies_to.domain_ids ?? [],
|
|
1228
|
+
...constraint.applies_to.entity_ids ?? [],
|
|
1229
|
+
...constraint.applies_to.capability_ids ?? [],
|
|
1230
|
+
...constraint.applies_to.feature_ids ?? [],
|
|
1231
|
+
...constraint.applies_to.business_rule_ids ?? []
|
|
1232
|
+
];
|
|
1233
|
+
}
|
|
1234
|
+
if ("parent_capability_ids" in record) {
|
|
1235
|
+
const capability = record;
|
|
1236
|
+
return [
|
|
1237
|
+
...capability.domain_ids,
|
|
1238
|
+
...capability.entity_ids,
|
|
1239
|
+
...capability.parent_capability_ids,
|
|
1240
|
+
...capability.child_capability_ids,
|
|
1241
|
+
...capability.feature_ids
|
|
1242
|
+
];
|
|
1243
|
+
}
|
|
1244
|
+
if ("rule_ids" in record && "constraint_ids" in record) {
|
|
1245
|
+
const feature = record;
|
|
1246
|
+
return [
|
|
1247
|
+
...feature.domain_ids,
|
|
1248
|
+
...feature.capability_ids,
|
|
1249
|
+
...feature.entity_ids,
|
|
1250
|
+
...feature.rule_ids,
|
|
1251
|
+
...feature.constraint_ids
|
|
1252
|
+
];
|
|
1253
|
+
}
|
|
1254
|
+
if ("entity_ids" in record && "capability_ids" in record && "why_it_exists" in record && !("business_definition" in record) && !("parent_capability_ids" in record) && !("constraint_ids" in record) && !("rule_ids" in record)) {
|
|
1255
|
+
const domain = record;
|
|
1256
|
+
return [...domain.entity_ids, ...domain.capability_ids];
|
|
1257
|
+
}
|
|
1258
|
+
if ("explains" in record) {
|
|
1259
|
+
return record.explains;
|
|
1260
|
+
}
|
|
1261
|
+
if ("source_type" in record) {
|
|
1262
|
+
return record.supports;
|
|
1263
|
+
}
|
|
1264
|
+
if ("priority" in record) {
|
|
1265
|
+
const question = record;
|
|
1266
|
+
return [...question.related_ids, ...question.evidence_ids];
|
|
1267
|
+
}
|
|
1268
|
+
const conflict = record;
|
|
1269
|
+
return [
|
|
1270
|
+
...conflict.related_ids,
|
|
1271
|
+
...conflict.evidence_ids,
|
|
1272
|
+
...conflict.questions,
|
|
1273
|
+
...conflict.resolved_by_evidence_id ? [conflict.resolved_by_evidence_id] : []
|
|
1274
|
+
];
|
|
1275
|
+
}
|
|
1276
|
+
function parsePatchRecord(operation, diagnostics) {
|
|
1277
|
+
const definition = KNOWLEDGE_EXTRACTION_TYPE_REGISTRY[operation.type];
|
|
1278
|
+
if (operation.kind === "delete") {
|
|
1279
|
+
return void 0;
|
|
1280
|
+
}
|
|
1281
|
+
const parsed = definition.schema.safeParse(operation.payload);
|
|
1282
|
+
if (!parsed.success) {
|
|
1283
|
+
diagnostics.push(
|
|
1284
|
+
buildDiagnostic({
|
|
1285
|
+
severity: "error",
|
|
1286
|
+
code: "invalid_payload",
|
|
1287
|
+
message: `Operation "${operation.id}" payload is invalid for type "${operation.type}".`,
|
|
1288
|
+
operationId: operation.id,
|
|
1289
|
+
path: ["operations", operation.id, "payload"]
|
|
1290
|
+
})
|
|
1291
|
+
);
|
|
1292
|
+
return void 0;
|
|
1293
|
+
}
|
|
1294
|
+
return parsed.data;
|
|
1295
|
+
}
|
|
1296
|
+
function simulateKnowledgePatch(store, operations, patchId) {
|
|
1297
|
+
const state = cloneStoreState(store);
|
|
1298
|
+
const diagnostics = [];
|
|
1299
|
+
const deleteTargets = [];
|
|
1300
|
+
const appliedOperationIds = [];
|
|
1301
|
+
for (const operation of operations) {
|
|
1302
|
+
if (!supportedOperationTypeSet.has(operation.type)) {
|
|
1303
|
+
diagnostics.push(
|
|
1304
|
+
buildDiagnostic({
|
|
1305
|
+
severity: "error",
|
|
1306
|
+
code: "invalid_type",
|
|
1307
|
+
message: `Unsupported patch operation type "${operation.type}".`,
|
|
1308
|
+
patchId,
|
|
1309
|
+
operationId: operation.id,
|
|
1310
|
+
supportedTypes: SUPPORTED_OPERATION_TYPES
|
|
1311
|
+
})
|
|
1312
|
+
);
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
if (operation.kind === "delete") {
|
|
1316
|
+
const existing = state.byCollection[operation.type].get(
|
|
1317
|
+
operation.targetId
|
|
1318
|
+
);
|
|
1319
|
+
if (!existing) {
|
|
1320
|
+
diagnostics.push(
|
|
1321
|
+
buildDiagnostic({
|
|
1322
|
+
severity: "error",
|
|
1323
|
+
code: "missing_target",
|
|
1324
|
+
message: `Delete target "${operation.targetId}" does not exist in "${operation.type}".`,
|
|
1325
|
+
patchId,
|
|
1326
|
+
operationId: operation.id,
|
|
1327
|
+
targetId: operation.targetId
|
|
1328
|
+
})
|
|
1329
|
+
);
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
state.byCollection[operation.type].delete(operation.targetId);
|
|
1333
|
+
state.byId.delete(operation.targetId);
|
|
1334
|
+
deleteTargets.push(operation.targetId);
|
|
1335
|
+
appliedOperationIds.push(operation.id);
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
const parsedRecord = parsePatchRecord(operation, diagnostics);
|
|
1339
|
+
if (!parsedRecord) {
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
const idMessage = validateRecordIdForDomain(
|
|
1343
|
+
operation.type,
|
|
1344
|
+
parsedRecord.id,
|
|
1345
|
+
operation.domain
|
|
1346
|
+
);
|
|
1347
|
+
if (idMessage) {
|
|
1348
|
+
diagnostics.push(
|
|
1349
|
+
buildDiagnostic({
|
|
1350
|
+
severity: "error",
|
|
1351
|
+
code: "invalid_id_convention",
|
|
1352
|
+
message: idMessage,
|
|
1353
|
+
patchId,
|
|
1354
|
+
operationId: operation.id,
|
|
1355
|
+
targetId: parsedRecord.id
|
|
1356
|
+
})
|
|
1357
|
+
);
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
if (operation.kind === "create" && state.byId.has(parsedRecord.id)) {
|
|
1361
|
+
diagnostics.push(
|
|
1362
|
+
buildDiagnostic({
|
|
1363
|
+
severity: "error",
|
|
1364
|
+
code: "duplicate_id",
|
|
1365
|
+
message: `Create operation would duplicate existing id "${parsedRecord.id}".`,
|
|
1366
|
+
patchId,
|
|
1367
|
+
operationId: operation.id,
|
|
1368
|
+
targetId: parsedRecord.id
|
|
1369
|
+
})
|
|
1370
|
+
);
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (operation.kind === "update") {
|
|
1374
|
+
if (parsedRecord.id !== operation.targetId) {
|
|
1375
|
+
diagnostics.push(
|
|
1376
|
+
buildDiagnostic({
|
|
1377
|
+
severity: "error",
|
|
1378
|
+
code: "update_target_mismatch",
|
|
1379
|
+
message: `Update payload id "${parsedRecord.id}" must match target "${operation.targetId}".`,
|
|
1380
|
+
patchId,
|
|
1381
|
+
operationId: operation.id,
|
|
1382
|
+
targetId: operation.targetId
|
|
1383
|
+
})
|
|
1384
|
+
);
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
if (!state.byCollection[operation.type].has(operation.targetId)) {
|
|
1388
|
+
diagnostics.push(
|
|
1389
|
+
buildDiagnostic({
|
|
1390
|
+
severity: "error",
|
|
1391
|
+
code: "missing_target",
|
|
1392
|
+
message: `Update target "${operation.targetId}" does not exist in "${operation.type}".`,
|
|
1393
|
+
patchId,
|
|
1394
|
+
operationId: operation.id,
|
|
1395
|
+
targetId: operation.targetId
|
|
1396
|
+
})
|
|
1397
|
+
);
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
state.byCollection[operation.type].set(parsedRecord.id, parsedRecord);
|
|
1402
|
+
state.byId.set(parsedRecord.id, parsedRecord);
|
|
1403
|
+
appliedOperationIds.push(operation.id);
|
|
1404
|
+
}
|
|
1405
|
+
for (const record of state.byId.values()) {
|
|
1406
|
+
for (const referenceId of collectReferenceIds2(record)) {
|
|
1407
|
+
if (!state.byId.has(referenceId)) {
|
|
1408
|
+
diagnostics.push(
|
|
1409
|
+
buildDiagnostic({
|
|
1410
|
+
severity: "error",
|
|
1411
|
+
code: "invalid_reference",
|
|
1412
|
+
message: `Record "${record.id}" references unknown id "${referenceId}".`,
|
|
1413
|
+
patchId,
|
|
1414
|
+
targetId: record.id
|
|
1415
|
+
})
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
const inboundReferenceMap = /* @__PURE__ */ new Map();
|
|
1421
|
+
for (const record of state.byId.values()) {
|
|
1422
|
+
for (const referenceId of collectReferenceIds2(record)) {
|
|
1423
|
+
const inbound = inboundReferenceMap.get(referenceId) ?? [];
|
|
1424
|
+
inbound.push(record.id);
|
|
1425
|
+
inboundReferenceMap.set(referenceId, inbound);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
for (const deleteTarget of deleteTargets) {
|
|
1429
|
+
const inbound = inboundReferenceMap.get(deleteTarget) ?? [];
|
|
1430
|
+
if (inbound.length > 0) {
|
|
1431
|
+
diagnostics.push(
|
|
1432
|
+
buildDiagnostic({
|
|
1433
|
+
severity: "error",
|
|
1434
|
+
code: "delete_has_inbound_references",
|
|
1435
|
+
message: `Delete target "${deleteTarget}" still has inbound references from: ${inbound.join(
|
|
1436
|
+
", "
|
|
1437
|
+
)}`,
|
|
1438
|
+
patchId,
|
|
1439
|
+
targetId: deleteTarget
|
|
1440
|
+
})
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return {
|
|
1445
|
+
state,
|
|
1446
|
+
diagnostics,
|
|
1447
|
+
appliedOperationIds,
|
|
1448
|
+
deleteTargets
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// packages/core/src/knowledge/effective-state.ts
|
|
1453
|
+
function toSimulationStore(rootDir, sourceFiles, state) {
|
|
1454
|
+
const store = {
|
|
1455
|
+
rootDir,
|
|
1456
|
+
sourceFiles,
|
|
1457
|
+
domains: [...state.byCollection.domains.values()],
|
|
1458
|
+
entities: [...state.byCollection.entities.values()],
|
|
1459
|
+
relationships: [...state.byCollection.relationships.values()],
|
|
1460
|
+
capabilities: [...state.byCollection.capabilities.values()],
|
|
1461
|
+
features: [...state.byCollection.features.values()],
|
|
1462
|
+
business_rules: [...state.byCollection.business_rules.values()],
|
|
1463
|
+
constraints: [...state.byCollection.constraints.values()],
|
|
1464
|
+
rationales: [...state.byCollection.rationales.values()],
|
|
1465
|
+
evidence: [...state.byCollection.evidence.values()],
|
|
1466
|
+
questions: [...state.byCollection.questions.values()],
|
|
1467
|
+
conflicts: [...state.byCollection.conflicts.values()],
|
|
1468
|
+
byCollection: state.byCollection,
|
|
1469
|
+
byId: state.byId,
|
|
1470
|
+
reverseReferences: /* @__PURE__ */ new Map(),
|
|
1471
|
+
diagnostics: []
|
|
1472
|
+
};
|
|
1473
|
+
for (const record of store.byId.values()) {
|
|
1474
|
+
for (const referenceId of collectReferenceIds2(record)) {
|
|
1475
|
+
const reverse = store.reverseReferences.get(referenceId) ?? [];
|
|
1476
|
+
reverse.push(record.id);
|
|
1477
|
+
store.reverseReferences.set(referenceId, reverse);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return store;
|
|
1481
|
+
}
|
|
1482
|
+
function selectOrderedPatches(patches, options) {
|
|
1483
|
+
const visiblePatches = options.includeAllStatuses ? patches : patches.filter((patch) => patch.status === "draft");
|
|
1484
|
+
if (!options.selectedPatchId) {
|
|
1485
|
+
return visiblePatches;
|
|
1486
|
+
}
|
|
1487
|
+
const selectedIndex = visiblePatches.findIndex(
|
|
1488
|
+
(patch) => patch.id === options.selectedPatchId
|
|
1489
|
+
);
|
|
1490
|
+
if (selectedIndex === -1) {
|
|
1491
|
+
throw new Error(`Patch "${options.selectedPatchId}" was not found.`);
|
|
1492
|
+
}
|
|
1493
|
+
const endIndex = options.includeSelectedPatch === false ? selectedIndex : selectedIndex + 1;
|
|
1494
|
+
return visiblePatches.slice(0, endIndex);
|
|
1495
|
+
}
|
|
1496
|
+
async function composeEffectiveKnowledgeState(store, envValue, options = {}, cwd = process.cwd()) {
|
|
1497
|
+
const orderedPatches = selectOrderedPatches(
|
|
1498
|
+
await listKnowledgePatches(envValue, cwd),
|
|
1499
|
+
options
|
|
1500
|
+
);
|
|
1501
|
+
let currentState = store;
|
|
1502
|
+
const provenance = /* @__PURE__ */ new Map();
|
|
1503
|
+
const diagnostics = [];
|
|
1504
|
+
for (const patch of orderedPatches) {
|
|
1505
|
+
const patchState = await readKnowledgePatch(envValue, patch.id, cwd);
|
|
1506
|
+
const simulation = simulateKnowledgePatch(
|
|
1507
|
+
currentState,
|
|
1508
|
+
patchState.operations,
|
|
1509
|
+
patch.id
|
|
1510
|
+
);
|
|
1511
|
+
diagnostics.push(...simulation.diagnostics);
|
|
1512
|
+
for (const operationId of simulation.appliedOperationIds) {
|
|
1513
|
+
const operation = patchState.operations.find(
|
|
1514
|
+
(entry) => entry.id === operationId
|
|
1515
|
+
);
|
|
1516
|
+
if (!operation || operation.kind === "delete") {
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
const recordId = operation.kind === "update" ? operation.targetId : operation.payload.id;
|
|
1520
|
+
const existing = provenance.get(recordId);
|
|
1521
|
+
const patchIds = new Set(existing?.patchIds ?? []);
|
|
1522
|
+
patchIds.add(patch.id);
|
|
1523
|
+
provenance.set(recordId, {
|
|
1524
|
+
patchIds: [...patchIds],
|
|
1525
|
+
patched: true
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
currentState = toSimulationStore(
|
|
1529
|
+
store.rootDir,
|
|
1530
|
+
store.sourceFiles,
|
|
1531
|
+
simulation.state
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
return {
|
|
1535
|
+
store: currentState,
|
|
1536
|
+
patches: orderedPatches,
|
|
1537
|
+
patchIds: orderedPatches.map((patch) => patch.id),
|
|
1538
|
+
provenance,
|
|
1539
|
+
diagnostics
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
async function loadEffectiveKnowledgeStoreFromRoot(rootDir, options = {}) {
|
|
1543
|
+
const store = await loadKnowledgeStoreFromRoot(rootDir);
|
|
1544
|
+
return composeEffectiveKnowledgeState(store, rootDir, options);
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
// packages/core/src/knowledge/graphql.ts
|
|
1548
|
+
import {
|
|
1549
|
+
GraphQLBoolean,
|
|
1550
|
+
GraphQLFloat,
|
|
1551
|
+
GraphQLID,
|
|
1552
|
+
GraphQLList,
|
|
1553
|
+
GraphQLObjectType,
|
|
1554
|
+
GraphQLSchema,
|
|
1555
|
+
GraphQLString,
|
|
1556
|
+
graphql
|
|
1557
|
+
} from "graphql";
|
|
1558
|
+
function stringifyValue(value) {
|
|
1559
|
+
if (value === void 0 || value === null) {
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1563
|
+
}
|
|
1564
|
+
var confidenceType = new GraphQLObjectType({
|
|
1565
|
+
name: "Confidence",
|
|
1566
|
+
fields: {
|
|
1567
|
+
score: { type: GraphQLFloat },
|
|
1568
|
+
level: { type: GraphQLString },
|
|
1569
|
+
reason: { type: GraphQLString }
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
var locationType = new GraphQLObjectType({
|
|
1573
|
+
name: "EvidenceLocation",
|
|
1574
|
+
fields: {
|
|
1575
|
+
path: { type: GraphQLString },
|
|
1576
|
+
symbol: { type: GraphQLString },
|
|
1577
|
+
route: { type: GraphQLString },
|
|
1578
|
+
api: { type: GraphQLString },
|
|
1579
|
+
query: { type: GraphQLString }
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
var evidenceType = new GraphQLObjectType({
|
|
1583
|
+
name: "Evidence",
|
|
1584
|
+
fields: () => ({
|
|
1585
|
+
id: { type: GraphQLID },
|
|
1586
|
+
source_type: { type: GraphQLString },
|
|
1587
|
+
description: { type: GraphQLString },
|
|
1588
|
+
location: { type: locationType },
|
|
1589
|
+
supports: { type: new GraphQLList(GraphQLID) },
|
|
1590
|
+
excerpt: { type: GraphQLString },
|
|
1591
|
+
confidence: { type: confidenceType }
|
|
1592
|
+
})
|
|
1593
|
+
});
|
|
1594
|
+
var entityTechnicalRepresentationType = new GraphQLObjectType({
|
|
1595
|
+
name: "EntityTechnicalRepresentation",
|
|
1596
|
+
fields: {
|
|
1597
|
+
kind: { type: GraphQLString },
|
|
1598
|
+
name: { type: GraphQLString },
|
|
1599
|
+
location_hint: { type: GraphQLString }
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
var entityType = new GraphQLObjectType({
|
|
1603
|
+
name: "Entity",
|
|
1604
|
+
fields: () => ({
|
|
1605
|
+
id: { type: GraphQLID },
|
|
1606
|
+
name: { type: GraphQLString },
|
|
1607
|
+
business_definition: { type: GraphQLString },
|
|
1608
|
+
description: { type: GraphQLString },
|
|
1609
|
+
purpose: { type: GraphQLString },
|
|
1610
|
+
aliases: { type: new GraphQLList(GraphQLString) },
|
|
1611
|
+
ambiguous_terms: { type: new GraphQLList(GraphQLString) },
|
|
1612
|
+
technical_representations: {
|
|
1613
|
+
type: new GraphQLList(entityTechnicalRepresentationType)
|
|
1614
|
+
},
|
|
1615
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1616
|
+
state_values: { type: new GraphQLList(GraphQLString) },
|
|
1617
|
+
relationship_ids: { type: new GraphQLList(GraphQLID) },
|
|
1618
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1619
|
+
feature_ids: { type: new GraphQLList(GraphQLID) },
|
|
1620
|
+
rule_ids: { type: new GraphQLList(GraphQLID) },
|
|
1621
|
+
domains: {
|
|
1622
|
+
type: new GraphQLList(domainType),
|
|
1623
|
+
resolve: (entity, _args, context) => resolveByIds(context, "domains", entity.domain_ids)
|
|
1624
|
+
},
|
|
1625
|
+
relationships: {
|
|
1626
|
+
type: new GraphQLList(entityRelationshipType),
|
|
1627
|
+
resolve: (entity, _args, context) => resolveByIds(context, "relationships", entity.relationship_ids)
|
|
1628
|
+
},
|
|
1629
|
+
capabilities: {
|
|
1630
|
+
type: new GraphQLList(capabilityType),
|
|
1631
|
+
resolve: (entity, _args, context) => resolveByIds(context, "capabilities", entity.capability_ids)
|
|
1632
|
+
},
|
|
1633
|
+
features: {
|
|
1634
|
+
type: new GraphQLList(featureType),
|
|
1635
|
+
resolve: (entity, _args, context) => resolveByIds(context, "features", entity.feature_ids)
|
|
1636
|
+
},
|
|
1637
|
+
business_rules: {
|
|
1638
|
+
type: new GraphQLList(businessRuleType),
|
|
1639
|
+
resolve: (entity, _args, context) => resolveByIds(context, "business_rules", entity.rule_ids)
|
|
1640
|
+
},
|
|
1641
|
+
certainty: { type: GraphQLString },
|
|
1642
|
+
confidence: { type: confidenceType }
|
|
1643
|
+
})
|
|
1644
|
+
});
|
|
1645
|
+
var ruleConditionType = new GraphQLObjectType({
|
|
1646
|
+
name: "RuleCondition",
|
|
1647
|
+
fields: {
|
|
1648
|
+
subject: { type: GraphQLString },
|
|
1649
|
+
field: { type: GraphQLString },
|
|
1650
|
+
operator: { type: GraphQLString },
|
|
1651
|
+
value: {
|
|
1652
|
+
type: GraphQLString,
|
|
1653
|
+
resolve: (condition) => stringifyValue(condition.value)
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
var ruleTriggerType = new GraphQLObjectType({
|
|
1658
|
+
name: "RuleTrigger",
|
|
1659
|
+
fields: {
|
|
1660
|
+
actor: { type: GraphQLString },
|
|
1661
|
+
action: { type: GraphQLString },
|
|
1662
|
+
subject: { type: GraphQLString },
|
|
1663
|
+
field: { type: GraphQLString },
|
|
1664
|
+
operator: { type: GraphQLString },
|
|
1665
|
+
value: {
|
|
1666
|
+
type: GraphQLString,
|
|
1667
|
+
resolve: (trigger) => stringifyValue(trigger.value)
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
var ruleEffectType = new GraphQLObjectType({
|
|
1672
|
+
name: "RuleEffect",
|
|
1673
|
+
fields: {
|
|
1674
|
+
effect: { type: GraphQLString },
|
|
1675
|
+
action: { type: GraphQLString },
|
|
1676
|
+
message: { type: GraphQLString },
|
|
1677
|
+
target_state: { type: GraphQLString },
|
|
1678
|
+
value: {
|
|
1679
|
+
type: GraphQLString,
|
|
1680
|
+
resolve: (effect) => stringifyValue(effect.value)
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1684
|
+
var businessRuleConditionType = new GraphQLObjectType({
|
|
1685
|
+
name: "BusinessRuleCondition",
|
|
1686
|
+
fields: {
|
|
1687
|
+
given: { type: new GraphQLList(ruleConditionType) },
|
|
1688
|
+
when: { type: new GraphQLList(ruleTriggerType) },
|
|
1689
|
+
then: { type: new GraphQLList(ruleEffectType) }
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
var businessRuleAppliesToType = new GraphQLObjectType({
|
|
1693
|
+
name: "BusinessRuleAppliesTo",
|
|
1694
|
+
fields: {
|
|
1695
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1696
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
1697
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1698
|
+
feature_ids: { type: new GraphQLList(GraphQLID) }
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
var businessRuleEnforcementType = new GraphQLObjectType({
|
|
1702
|
+
name: "BusinessRuleEnforcement",
|
|
1703
|
+
fields: {
|
|
1704
|
+
level: { type: GraphQLString },
|
|
1705
|
+
description: { type: GraphQLString }
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
var businessRuleType = new GraphQLObjectType({
|
|
1709
|
+
name: "BusinessRule",
|
|
1710
|
+
fields: () => ({
|
|
1711
|
+
id: { type: GraphQLID },
|
|
1712
|
+
name: { type: GraphQLString },
|
|
1713
|
+
statement: { type: GraphQLString },
|
|
1714
|
+
rule_type: { type: GraphQLString },
|
|
1715
|
+
behavior_type: { type: GraphQLString },
|
|
1716
|
+
applies_to: { type: businessRuleAppliesToType },
|
|
1717
|
+
condition: { type: businessRuleConditionType },
|
|
1718
|
+
enforcement: { type: businessRuleEnforcementType },
|
|
1719
|
+
related_constraint_ids: { type: new GraphQLList(GraphQLID) },
|
|
1720
|
+
rationale_ids: { type: new GraphQLList(GraphQLID) },
|
|
1721
|
+
certainty: { type: GraphQLString },
|
|
1722
|
+
confidence: { type: confidenceType }
|
|
1723
|
+
})
|
|
1724
|
+
});
|
|
1725
|
+
var constraintCauseType = new GraphQLObjectType({
|
|
1726
|
+
name: "ConstraintCause",
|
|
1727
|
+
fields: {
|
|
1728
|
+
source: { type: GraphQLString },
|
|
1729
|
+
source_type: { type: GraphQLString },
|
|
1730
|
+
description: { type: GraphQLString }
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
var constraintImpactType = new GraphQLObjectType({
|
|
1734
|
+
name: "ConstraintImpact",
|
|
1735
|
+
fields: {
|
|
1736
|
+
affected_change_types: { type: new GraphQLList(GraphQLString) },
|
|
1737
|
+
severity: { type: GraphQLString },
|
|
1738
|
+
description: { type: GraphQLString }
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
var constraintAppliesToType = new GraphQLObjectType({
|
|
1742
|
+
name: "ConstraintAppliesTo",
|
|
1743
|
+
fields: {
|
|
1744
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1745
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
1746
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1747
|
+
feature_ids: { type: new GraphQLList(GraphQLID) },
|
|
1748
|
+
business_rule_ids: { type: new GraphQLList(GraphQLID) }
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
var constraintWorkaroundType = new GraphQLObjectType({
|
|
1752
|
+
name: "ConstraintWorkaround",
|
|
1753
|
+
fields: {
|
|
1754
|
+
exists: { type: GraphQLString },
|
|
1755
|
+
description: { type: GraphQLString }
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
var constraintRemovabilityType = new GraphQLObjectType({
|
|
1759
|
+
name: "ConstraintRemovability",
|
|
1760
|
+
fields: {
|
|
1761
|
+
can_be_removed_by_refactor: { type: GraphQLString },
|
|
1762
|
+
requires: { type: new GraphQLList(GraphQLString) },
|
|
1763
|
+
risk: { type: GraphQLString }
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
var constraintType = new GraphQLObjectType({
|
|
1767
|
+
name: "Constraint",
|
|
1768
|
+
fields: () => ({
|
|
1769
|
+
id: { type: GraphQLID },
|
|
1770
|
+
name: { type: GraphQLString },
|
|
1771
|
+
description: { type: GraphQLString },
|
|
1772
|
+
constraint_type: { type: GraphQLString },
|
|
1773
|
+
cause: { type: constraintCauseType },
|
|
1774
|
+
impact: { type: constraintImpactType },
|
|
1775
|
+
applies_to: { type: constraintAppliesToType },
|
|
1776
|
+
workaround: { type: constraintWorkaroundType },
|
|
1777
|
+
removability: { type: constraintRemovabilityType },
|
|
1778
|
+
certainty: { type: GraphQLString },
|
|
1779
|
+
confidence: { type: confidenceType }
|
|
1780
|
+
})
|
|
1781
|
+
});
|
|
1782
|
+
var rationaleType = new GraphQLObjectType({
|
|
1783
|
+
name: "Rationale",
|
|
1784
|
+
fields: () => ({
|
|
1785
|
+
id: { type: GraphQLID },
|
|
1786
|
+
statement: { type: GraphQLString },
|
|
1787
|
+
explains: { type: new GraphQLList(GraphQLID) },
|
|
1788
|
+
certainty: { type: GraphQLString },
|
|
1789
|
+
confidence: { type: confidenceType }
|
|
1790
|
+
})
|
|
1791
|
+
});
|
|
1792
|
+
var questionType = new GraphQLObjectType({
|
|
1793
|
+
name: "Question",
|
|
1794
|
+
fields: () => ({
|
|
1795
|
+
id: { type: GraphQLID },
|
|
1796
|
+
question: { type: GraphQLString },
|
|
1797
|
+
category: { type: GraphQLString },
|
|
1798
|
+
reason: { type: GraphQLString },
|
|
1799
|
+
impact: { type: GraphQLString },
|
|
1800
|
+
related_ids: { type: new GraphQLList(GraphQLID) },
|
|
1801
|
+
evidence_ids: { type: new GraphQLList(GraphQLID) },
|
|
1802
|
+
priority: { type: GraphQLString },
|
|
1803
|
+
status: { type: GraphQLString },
|
|
1804
|
+
answer: { type: GraphQLString },
|
|
1805
|
+
answered_by: { type: GraphQLString },
|
|
1806
|
+
answered_at: { type: GraphQLString }
|
|
1807
|
+
})
|
|
1808
|
+
});
|
|
1809
|
+
var conflictType = new GraphQLObjectType({
|
|
1810
|
+
name: "Conflict",
|
|
1811
|
+
fields: () => ({
|
|
1812
|
+
id: { type: GraphQLID },
|
|
1813
|
+
type: { type: GraphQLString },
|
|
1814
|
+
description: { type: GraphQLString },
|
|
1815
|
+
related_ids: { type: new GraphQLList(GraphQLID) },
|
|
1816
|
+
evidence_ids: { type: new GraphQLList(GraphQLID) },
|
|
1817
|
+
status: { type: GraphQLString },
|
|
1818
|
+
questions: { type: new GraphQLList(GraphQLID) },
|
|
1819
|
+
resolution: { type: GraphQLString },
|
|
1820
|
+
resolved_by_evidence_id: { type: GraphQLID }
|
|
1821
|
+
})
|
|
1822
|
+
});
|
|
1823
|
+
var domainType = new GraphQLObjectType({
|
|
1824
|
+
name: "Domain",
|
|
1825
|
+
fields: () => ({
|
|
1826
|
+
id: { type: GraphQLID },
|
|
1827
|
+
name: { type: GraphQLString },
|
|
1828
|
+
purpose: { type: GraphQLString },
|
|
1829
|
+
why_it_exists: { type: GraphQLString },
|
|
1830
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
1831
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1832
|
+
entities: {
|
|
1833
|
+
type: new GraphQLList(entityType),
|
|
1834
|
+
resolve: (domain, _args, context) => resolveByIds(context, "entities", domain.entity_ids)
|
|
1835
|
+
},
|
|
1836
|
+
capabilities: {
|
|
1837
|
+
type: new GraphQLList(capabilityType),
|
|
1838
|
+
resolve: (domain, _args, context) => resolveByIds(context, "capabilities", domain.capability_ids)
|
|
1839
|
+
},
|
|
1840
|
+
certainty: { type: GraphQLString },
|
|
1841
|
+
confidence: { type: confidenceType }
|
|
1842
|
+
})
|
|
1843
|
+
});
|
|
1844
|
+
var entityRelationshipType = new GraphQLObjectType({
|
|
1845
|
+
name: "EntityRelationship",
|
|
1846
|
+
fields: () => ({
|
|
1847
|
+
id: { type: GraphQLID },
|
|
1848
|
+
from_entity_id: { type: GraphQLID },
|
|
1849
|
+
to_entity_id: { type: GraphQLID },
|
|
1850
|
+
relationship_type: { type: GraphQLString },
|
|
1851
|
+
description: { type: GraphQLString },
|
|
1852
|
+
from_entity: {
|
|
1853
|
+
type: entityType,
|
|
1854
|
+
resolve: (relationship, _args, context) => resolveByIds(context, "entities", [relationship.from_entity_id])[0] ?? null
|
|
1855
|
+
},
|
|
1856
|
+
to_entity: {
|
|
1857
|
+
type: entityType,
|
|
1858
|
+
resolve: (relationship, _args, context) => resolveByIds(context, "entities", [relationship.to_entity_id])[0] ?? null
|
|
1859
|
+
},
|
|
1860
|
+
certainty: { type: GraphQLString },
|
|
1861
|
+
confidence: { type: confidenceType }
|
|
1862
|
+
})
|
|
1863
|
+
});
|
|
1864
|
+
var capabilityType = new GraphQLObjectType({
|
|
1865
|
+
name: "Capability",
|
|
1866
|
+
fields: () => ({
|
|
1867
|
+
id: { type: GraphQLID },
|
|
1868
|
+
name: { type: GraphQLString },
|
|
1869
|
+
description: { type: GraphQLString },
|
|
1870
|
+
why_it_exists: { type: GraphQLString },
|
|
1871
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1872
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
1873
|
+
parent_capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1874
|
+
child_capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1875
|
+
feature_ids: { type: new GraphQLList(GraphQLID) },
|
|
1876
|
+
domains: {
|
|
1877
|
+
type: new GraphQLList(domainType),
|
|
1878
|
+
resolve: (capability, _args, context) => resolveByIds(context, "domains", capability.domain_ids)
|
|
1879
|
+
},
|
|
1880
|
+
entities: {
|
|
1881
|
+
type: new GraphQLList(entityType),
|
|
1882
|
+
resolve: (capability, _args, context) => resolveByIds(context, "entities", capability.entity_ids)
|
|
1883
|
+
},
|
|
1884
|
+
parent_capabilities: {
|
|
1885
|
+
type: new GraphQLList(capabilityType),
|
|
1886
|
+
resolve: (capability, _args, context) => resolveByIds(context, "capabilities", capability.parent_capability_ids)
|
|
1887
|
+
},
|
|
1888
|
+
child_capabilities: {
|
|
1889
|
+
type: new GraphQLList(capabilityType),
|
|
1890
|
+
resolve: (capability, _args, context) => resolveByIds(context, "capabilities", capability.child_capability_ids)
|
|
1891
|
+
},
|
|
1892
|
+
features: {
|
|
1893
|
+
type: new GraphQLList(featureType),
|
|
1894
|
+
resolve: (capability, _args, context) => resolveByIds(context, "features", capability.feature_ids)
|
|
1895
|
+
},
|
|
1896
|
+
certainty: { type: GraphQLString },
|
|
1897
|
+
confidence: { type: confidenceType }
|
|
1898
|
+
})
|
|
1899
|
+
});
|
|
1900
|
+
var featureType = new GraphQLObjectType({
|
|
1901
|
+
name: "Feature",
|
|
1902
|
+
fields: () => ({
|
|
1903
|
+
id: { type: GraphQLID },
|
|
1904
|
+
name: { type: GraphQLString },
|
|
1905
|
+
description: { type: GraphQLString },
|
|
1906
|
+
purpose: { type: GraphQLString },
|
|
1907
|
+
why_it_exists: { type: GraphQLString },
|
|
1908
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1909
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1910
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
1911
|
+
rule_ids: { type: new GraphQLList(GraphQLID) },
|
|
1912
|
+
constraint_ids: { type: new GraphQLList(GraphQLID) },
|
|
1913
|
+
domains: {
|
|
1914
|
+
type: new GraphQLList(domainType),
|
|
1915
|
+
resolve: (feature, _args, context) => resolveByIds(context, "domains", feature.domain_ids)
|
|
1916
|
+
},
|
|
1917
|
+
capabilities: {
|
|
1918
|
+
type: new GraphQLList(capabilityType),
|
|
1919
|
+
resolve: (feature, _args, context) => resolveByIds(context, "capabilities", feature.capability_ids)
|
|
1920
|
+
},
|
|
1921
|
+
entities: {
|
|
1922
|
+
type: new GraphQLList(entityType),
|
|
1923
|
+
resolve: (feature, _args, context) => resolveByIds(context, "entities", feature.entity_ids)
|
|
1924
|
+
},
|
|
1925
|
+
business_rules: {
|
|
1926
|
+
type: new GraphQLList(businessRuleType),
|
|
1927
|
+
resolve: (feature, _args, context) => resolveByIds(context, "business_rules", feature.rule_ids)
|
|
1928
|
+
},
|
|
1929
|
+
constraints: {
|
|
1930
|
+
type: new GraphQLList(constraintType),
|
|
1931
|
+
resolve: (feature, _args, context) => resolveByIds(context, "constraints", feature.constraint_ids)
|
|
1932
|
+
},
|
|
1933
|
+
certainty: { type: GraphQLString },
|
|
1934
|
+
confidence: { type: confidenceType }
|
|
1935
|
+
})
|
|
1936
|
+
});
|
|
1937
|
+
function resolveCollection(store, collection) {
|
|
1938
|
+
if (!store) {
|
|
1939
|
+
return [];
|
|
1940
|
+
}
|
|
1941
|
+
return store[collection];
|
|
1942
|
+
}
|
|
1943
|
+
function resolveByIds(store, collection, ids) {
|
|
1944
|
+
if (!store || !ids?.length) {
|
|
1945
|
+
return [];
|
|
1946
|
+
}
|
|
1947
|
+
const typedCollection = store.byCollection[collection];
|
|
1948
|
+
return ids.map((id) => typedCollection.get(id)).filter((item) => item !== void 0);
|
|
1949
|
+
}
|
|
1950
|
+
var queryType = new GraphQLObjectType({
|
|
1951
|
+
name: "Query",
|
|
1952
|
+
fields: () => ({
|
|
1953
|
+
store_available: {
|
|
1954
|
+
type: GraphQLBoolean,
|
|
1955
|
+
resolve: (_source, _args, context) => Boolean(context)
|
|
1956
|
+
},
|
|
1957
|
+
domains: {
|
|
1958
|
+
type: new GraphQLList(domainType),
|
|
1959
|
+
resolve: (_source, _args, context) => resolveCollection(context, "domains")
|
|
1960
|
+
},
|
|
1961
|
+
domain: {
|
|
1962
|
+
type: domainType,
|
|
1963
|
+
args: {
|
|
1964
|
+
id: { type: GraphQLID }
|
|
1965
|
+
},
|
|
1966
|
+
resolve: (_source, args, context) => context?.byCollection.domains.get(args.id) ?? null
|
|
1967
|
+
},
|
|
1968
|
+
entities: {
|
|
1969
|
+
type: new GraphQLList(entityType),
|
|
1970
|
+
resolve: (_source, _args, context) => resolveCollection(context, "entities")
|
|
1971
|
+
},
|
|
1972
|
+
entity: {
|
|
1973
|
+
type: entityType,
|
|
1974
|
+
args: {
|
|
1975
|
+
id: { type: GraphQLID }
|
|
1976
|
+
},
|
|
1977
|
+
resolve: (_source, args, context) => context?.byCollection.entities.get(args.id) ?? null
|
|
1978
|
+
},
|
|
1979
|
+
relationships: {
|
|
1980
|
+
type: new GraphQLList(entityRelationshipType),
|
|
1981
|
+
resolve: (_source, _args, context) => resolveCollection(context, "relationships")
|
|
1982
|
+
},
|
|
1983
|
+
capabilities: {
|
|
1984
|
+
type: new GraphQLList(capabilityType),
|
|
1985
|
+
resolve: (_source, _args, context) => resolveCollection(context, "capabilities")
|
|
1986
|
+
},
|
|
1987
|
+
capability: {
|
|
1988
|
+
type: capabilityType,
|
|
1989
|
+
args: {
|
|
1990
|
+
id: { type: GraphQLID }
|
|
1991
|
+
},
|
|
1992
|
+
resolve: (_source, args, context) => context?.byCollection.capabilities.get(args.id) ?? null
|
|
1993
|
+
},
|
|
1994
|
+
features: {
|
|
1995
|
+
type: new GraphQLList(featureType),
|
|
1996
|
+
resolve: (_source, _args, context) => resolveCollection(context, "features")
|
|
1997
|
+
},
|
|
1998
|
+
feature: {
|
|
1999
|
+
type: featureType,
|
|
2000
|
+
args: {
|
|
2001
|
+
id: { type: GraphQLID }
|
|
2002
|
+
},
|
|
2003
|
+
resolve: (_source, args, context) => context?.byCollection.features.get(args.id) ?? null
|
|
2004
|
+
},
|
|
2005
|
+
business_rules: {
|
|
2006
|
+
type: new GraphQLList(businessRuleType),
|
|
2007
|
+
resolve: (_source, _args, context) => resolveCollection(context, "business_rules")
|
|
2008
|
+
},
|
|
2009
|
+
business_rule: {
|
|
2010
|
+
type: businessRuleType,
|
|
2011
|
+
args: {
|
|
2012
|
+
id: { type: GraphQLID }
|
|
2013
|
+
},
|
|
2014
|
+
resolve: (_source, args, context) => context?.byCollection.business_rules.get(args.id) ?? null
|
|
2015
|
+
},
|
|
2016
|
+
constraints: {
|
|
2017
|
+
type: new GraphQLList(constraintType),
|
|
2018
|
+
resolve: (_source, _args, context) => resolveCollection(context, "constraints")
|
|
2019
|
+
},
|
|
2020
|
+
constraint: {
|
|
2021
|
+
type: constraintType,
|
|
2022
|
+
args: {
|
|
2023
|
+
id: { type: GraphQLID }
|
|
2024
|
+
},
|
|
2025
|
+
resolve: (_source, args, context) => context?.byCollection.constraints.get(args.id) ?? null
|
|
2026
|
+
},
|
|
2027
|
+
rationales: {
|
|
2028
|
+
type: new GraphQLList(rationaleType),
|
|
2029
|
+
resolve: (_source, _args, context) => resolveCollection(context, "rationales")
|
|
2030
|
+
},
|
|
2031
|
+
rationale: {
|
|
2032
|
+
type: rationaleType,
|
|
2033
|
+
args: {
|
|
2034
|
+
id: { type: GraphQLID }
|
|
2035
|
+
},
|
|
2036
|
+
resolve: (_source, args, context) => context?.byCollection.rationales.get(args.id) ?? null
|
|
2037
|
+
},
|
|
2038
|
+
evidence: {
|
|
2039
|
+
type: new GraphQLList(evidenceType),
|
|
2040
|
+
resolve: (_source, _args, context) => resolveCollection(context, "evidence")
|
|
2041
|
+
},
|
|
2042
|
+
question: {
|
|
2043
|
+
type: questionType,
|
|
2044
|
+
args: {
|
|
2045
|
+
id: { type: GraphQLID }
|
|
2046
|
+
},
|
|
2047
|
+
resolve: (_source, args, context) => context?.byCollection.questions.get(args.id) ?? null
|
|
2048
|
+
},
|
|
2049
|
+
questions: {
|
|
2050
|
+
type: new GraphQLList(questionType),
|
|
2051
|
+
resolve: (_source, _args, context) => resolveCollection(context, "questions")
|
|
2052
|
+
},
|
|
2053
|
+
conflict: {
|
|
2054
|
+
type: conflictType,
|
|
2055
|
+
args: {
|
|
2056
|
+
id: { type: GraphQLID }
|
|
2057
|
+
},
|
|
2058
|
+
resolve: (_source, args, context) => context?.byCollection.conflicts.get(args.id) ?? null
|
|
2059
|
+
},
|
|
2060
|
+
conflicts: {
|
|
2061
|
+
type: new GraphQLList(conflictType),
|
|
2062
|
+
resolve: (_source, _args, context) => resolveCollection(context, "conflicts")
|
|
2063
|
+
}
|
|
2064
|
+
})
|
|
2065
|
+
});
|
|
2066
|
+
var knowledgeGraphSchema = new GraphQLSchema({
|
|
2067
|
+
query: queryType
|
|
2068
|
+
});
|
|
2069
|
+
|
|
2070
|
+
// packages/core/src/knowledge/patch/scope-audit.ts
|
|
2071
|
+
import { z as z6 } from "zod/v4";
|
|
2072
|
+
var compareScopeWithEvidenceInputSchema = z6.object({
|
|
2073
|
+
patchId: z6.string().min(1),
|
|
2074
|
+
rootFolder: z6.string().trim().min(1).optional()
|
|
2075
|
+
});
|
|
2076
|
+
var compareScopeWithEvidenceResultSchema = z6.object({
|
|
2077
|
+
patchId: z6.string().min(1),
|
|
2078
|
+
rootFolder: z6.string().min(1),
|
|
2079
|
+
scopeFiles: z6.array(z6.string()),
|
|
2080
|
+
coveredFiles: z6.array(z6.string()),
|
|
2081
|
+
omittedFiles: z6.array(
|
|
2082
|
+
z6.object({
|
|
2083
|
+
path: z6.string(),
|
|
2084
|
+
reason: z6.string()
|
|
2085
|
+
})
|
|
2086
|
+
),
|
|
2087
|
+
uncoveredFiles: z6.array(z6.string()),
|
|
2088
|
+
outOfRootEvidenceFiles: z6.array(z6.string()),
|
|
2089
|
+
message: z6.string()
|
|
2090
|
+
});
|
|
2091
|
+
|
|
2092
|
+
// packages/core/src/knowledge/schema-discovery.ts
|
|
2093
|
+
import {
|
|
2094
|
+
isAbstractType,
|
|
2095
|
+
isCompositeType,
|
|
2096
|
+
isListType,
|
|
2097
|
+
isNonNullType,
|
|
2098
|
+
isObjectType,
|
|
2099
|
+
isScalarType,
|
|
2100
|
+
isUnionType
|
|
2101
|
+
} from "graphql";
|
|
2102
|
+
|
|
2103
|
+
// packages/rest/src/constants.ts
|
|
2104
|
+
var REST_SERVICE_NAME = "ez-know-rest";
|
|
2105
|
+
var REST_V1_PREFIX = "/api/v1";
|
|
2106
|
+
var REST_OPENAPI_PATH = "/api/openapi.json";
|
|
2107
|
+
var REST_DOCS_PATH = "/api/docs";
|
|
2108
|
+
var REST_HEALTH_PATH = "/api/health";
|
|
2109
|
+
var REST_PATCHES_ROUTE = "patches";
|
|
2110
|
+
var REST_PATCH_PREVIEW_ROUTE = "patches/:patchId/preview";
|
|
2111
|
+
var REST_PATCH_PREVIEW_PATH = "patches/{patchId}/preview";
|
|
2112
|
+
var REST_COLLECTIONS = [
|
|
2113
|
+
{ route: "domains", collection: "domains", label: "Domains" },
|
|
2114
|
+
{ route: "entities", collection: "entities", label: "Entities" },
|
|
2115
|
+
{
|
|
2116
|
+
route: "relationships",
|
|
2117
|
+
collection: "relationships",
|
|
2118
|
+
label: "Relationships"
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
route: "capabilities",
|
|
2122
|
+
collection: "capabilities",
|
|
2123
|
+
label: "Capabilities"
|
|
2124
|
+
},
|
|
2125
|
+
{ route: "features", collection: "features", label: "Features" },
|
|
2126
|
+
{
|
|
2127
|
+
route: "business-rules",
|
|
2128
|
+
collection: "business_rules",
|
|
2129
|
+
label: "Business rules"
|
|
2130
|
+
},
|
|
2131
|
+
{ route: "constraints", collection: "constraints", label: "Constraints" },
|
|
2132
|
+
{ route: "rationales", collection: "rationales", label: "Rationales" },
|
|
2133
|
+
{ route: "evidence", collection: "evidence", label: "Evidence" },
|
|
2134
|
+
{ route: "questions", collection: "questions", label: "Questions" },
|
|
2135
|
+
{ route: "conflicts", collection: "conflicts", label: "Conflicts" }
|
|
2136
|
+
];
|
|
2137
|
+
var REST_COLLECTION_ROUTE_LOOKUP = new Map(
|
|
2138
|
+
REST_COLLECTIONS.map((definition) => [definition.route, definition])
|
|
2139
|
+
);
|
|
2140
|
+
|
|
2141
|
+
// packages/rest/src/docs.ts
|
|
2142
|
+
function buildSwaggerUiHtml(openApiPath = REST_OPENAPI_PATH) {
|
|
2143
|
+
return `<!doctype html>
|
|
2144
|
+
<html lang="en">
|
|
2145
|
+
<head>
|
|
2146
|
+
<meta charset="utf-8" />
|
|
2147
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
2148
|
+
<title>ez-know REST API</title>
|
|
2149
|
+
<link
|
|
2150
|
+
rel="stylesheet"
|
|
2151
|
+
href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css"
|
|
2152
|
+
/>
|
|
2153
|
+
<style>
|
|
2154
|
+
html,
|
|
2155
|
+
body {
|
|
2156
|
+
margin: 0;
|
|
2157
|
+
padding: 0;
|
|
2158
|
+
background: #0f172a;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
#swagger-ui {
|
|
2162
|
+
background: #fff;
|
|
2163
|
+
}
|
|
2164
|
+
</style>
|
|
2165
|
+
</head>
|
|
2166
|
+
<body>
|
|
2167
|
+
<div id="swagger-ui"></div>
|
|
2168
|
+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js" crossorigin="anonymous"></script>
|
|
2169
|
+
<script>
|
|
2170
|
+
window.ui = SwaggerUIBundle({
|
|
2171
|
+
url: '${openApiPath}',
|
|
2172
|
+
dom_id: '#swagger-ui',
|
|
2173
|
+
deepLinking: true,
|
|
2174
|
+
presets: [SwaggerUIBundle.presets.apis],
|
|
2175
|
+
});
|
|
2176
|
+
</script>
|
|
2177
|
+
</body>
|
|
2178
|
+
</html>`;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
// packages/rest/src/openapi.ts
|
|
2182
|
+
import {
|
|
2183
|
+
extendZodWithOpenApi as extendZodWithOpenApi2,
|
|
2184
|
+
OpenAPIRegistry,
|
|
2185
|
+
OpenApiGeneratorV3
|
|
2186
|
+
} from "@asteasolutions/zod-to-openapi";
|
|
2187
|
+
import { z as z7 } from "zod/v4";
|
|
2188
|
+
|
|
2189
|
+
// packages/rest/src/schema-registry.ts
|
|
2190
|
+
var REST_COLLECTION_SCHEMA_REGISTRY = {
|
|
2191
|
+
domains: {
|
|
2192
|
+
itemName: "Domain",
|
|
2193
|
+
itemSchema: domainSchema,
|
|
2194
|
+
collectionResponseName: "DomainCollectionResponse",
|
|
2195
|
+
itemResponseName: "DomainItemResponse"
|
|
2196
|
+
},
|
|
2197
|
+
entities: {
|
|
2198
|
+
itemName: "Entity",
|
|
2199
|
+
itemSchema: entitySchema,
|
|
2200
|
+
collectionResponseName: "EntityCollectionResponse",
|
|
2201
|
+
itemResponseName: "EntityItemResponse"
|
|
2202
|
+
},
|
|
2203
|
+
relationships: {
|
|
2204
|
+
itemName: "EntityRelationship",
|
|
2205
|
+
itemSchema: relationshipSchema,
|
|
2206
|
+
collectionResponseName: "EntityRelationshipCollectionResponse",
|
|
2207
|
+
itemResponseName: "EntityRelationshipItemResponse"
|
|
2208
|
+
},
|
|
2209
|
+
capabilities: {
|
|
2210
|
+
itemName: "Capability",
|
|
2211
|
+
itemSchema: capabilitySchema,
|
|
2212
|
+
collectionResponseName: "CapabilityCollectionResponse",
|
|
2213
|
+
itemResponseName: "CapabilityItemResponse"
|
|
2214
|
+
},
|
|
2215
|
+
features: {
|
|
2216
|
+
itemName: "Feature",
|
|
2217
|
+
itemSchema: featureSchema,
|
|
2218
|
+
collectionResponseName: "FeatureCollectionResponse",
|
|
2219
|
+
itemResponseName: "FeatureItemResponse"
|
|
2220
|
+
},
|
|
2221
|
+
business_rules: {
|
|
2222
|
+
itemName: "BusinessRule",
|
|
2223
|
+
itemSchema: businessRuleSchema,
|
|
2224
|
+
collectionResponseName: "BusinessRuleCollectionResponse",
|
|
2225
|
+
itemResponseName: "BusinessRuleItemResponse"
|
|
2226
|
+
},
|
|
2227
|
+
constraints: {
|
|
2228
|
+
itemName: "Constraint",
|
|
2229
|
+
itemSchema: constraintSchema,
|
|
2230
|
+
collectionResponseName: "ConstraintCollectionResponse",
|
|
2231
|
+
itemResponseName: "ConstraintItemResponse"
|
|
2232
|
+
},
|
|
2233
|
+
rationales: {
|
|
2234
|
+
itemName: "Rationale",
|
|
2235
|
+
itemSchema: rationaleSchema,
|
|
2236
|
+
collectionResponseName: "RationaleCollectionResponse",
|
|
2237
|
+
itemResponseName: "RationaleItemResponse"
|
|
2238
|
+
},
|
|
2239
|
+
evidence: {
|
|
2240
|
+
itemName: "Evidence",
|
|
2241
|
+
itemSchema: evidenceSchema,
|
|
2242
|
+
collectionResponseName: "EvidenceCollectionResponse",
|
|
2243
|
+
itemResponseName: "EvidenceItemResponse"
|
|
2244
|
+
},
|
|
2245
|
+
questions: {
|
|
2246
|
+
itemName: "Question",
|
|
2247
|
+
itemSchema: questionSchema,
|
|
2248
|
+
collectionResponseName: "QuestionCollectionResponse",
|
|
2249
|
+
itemResponseName: "QuestionItemResponse"
|
|
2250
|
+
},
|
|
2251
|
+
conflicts: {
|
|
2252
|
+
itemName: "Conflict",
|
|
2253
|
+
itemSchema: conflictSchema,
|
|
2254
|
+
collectionResponseName: "ConflictCollectionResponse",
|
|
2255
|
+
itemResponseName: "ConflictItemResponse"
|
|
2256
|
+
}
|
|
2257
|
+
};
|
|
2258
|
+
function getRestCollectionSchemaDefinition(collection) {
|
|
2259
|
+
return REST_COLLECTION_SCHEMA_REGISTRY[collection];
|
|
2260
|
+
}
|
|
2261
|
+
var REST_CONFIDENCE_SCHEMA = confidenceSchema;
|
|
2262
|
+
var REST_CERTAINTY_SCHEMA = certaintySchema;
|
|
2263
|
+
|
|
2264
|
+
// packages/rest/src/openapi.ts
|
|
2265
|
+
extendZodWithOpenApi2(z7);
|
|
2266
|
+
var openApiRegistry = new OpenAPIRegistry();
|
|
2267
|
+
var restErrorResponseSchema = openApiRegistry.register(
|
|
2268
|
+
"RestErrorResponse",
|
|
2269
|
+
z7.object({
|
|
2270
|
+
error: z7.object({
|
|
2271
|
+
code: z7.string(),
|
|
2272
|
+
message: z7.string()
|
|
2273
|
+
})
|
|
2274
|
+
}).openapi({
|
|
2275
|
+
description: "Standard REST error envelope."
|
|
2276
|
+
})
|
|
2277
|
+
);
|
|
2278
|
+
var restHealthResponseSchema = openApiRegistry.register(
|
|
2279
|
+
"RestHealthResponse",
|
|
2280
|
+
z7.object({
|
|
2281
|
+
service: z7.string(),
|
|
2282
|
+
status: z7.literal("ok"),
|
|
2283
|
+
storeAvailable: z7.boolean()
|
|
2284
|
+
}).openapi({
|
|
2285
|
+
description: "Service health payload."
|
|
2286
|
+
})
|
|
2287
|
+
);
|
|
2288
|
+
var restPatchSummarySchema = openApiRegistry.register(
|
|
2289
|
+
"RestPatchSummary",
|
|
2290
|
+
z7.object({
|
|
2291
|
+
id: z7.string(),
|
|
2292
|
+
status: z7.string(),
|
|
2293
|
+
title: z7.string(),
|
|
2294
|
+
description: z7.string().optional(),
|
|
2295
|
+
rootFolder: z7.string(),
|
|
2296
|
+
omittedFiles: z7.array(
|
|
2297
|
+
z7.object({
|
|
2298
|
+
path: z7.string(),
|
|
2299
|
+
reason: z7.string()
|
|
2300
|
+
})
|
|
2301
|
+
),
|
|
2302
|
+
operationCount: z7.number().int().min(0),
|
|
2303
|
+
createdAt: z7.string(),
|
|
2304
|
+
updatedAt: z7.string()
|
|
2305
|
+
}).openapi({
|
|
2306
|
+
description: "Patch inventory summary."
|
|
2307
|
+
})
|
|
2308
|
+
);
|
|
2309
|
+
var restPatchProvenanceEntrySchema = openApiRegistry.register(
|
|
2310
|
+
"RestPatchProvenanceEntry",
|
|
2311
|
+
z7.object({
|
|
2312
|
+
patchIds: z7.array(z7.string()),
|
|
2313
|
+
patched: z7.boolean()
|
|
2314
|
+
}).openapi({
|
|
2315
|
+
description: "Patch provenance for a previewed record."
|
|
2316
|
+
})
|
|
2317
|
+
);
|
|
2318
|
+
var restPatchFacetItemSchema = openApiRegistry.register(
|
|
2319
|
+
"RestPatchFacetItem",
|
|
2320
|
+
z7.object({
|
|
2321
|
+
id: z7.string(),
|
|
2322
|
+
label: z7.string(),
|
|
2323
|
+
patched: z7.boolean(),
|
|
2324
|
+
provenancePatchIds: z7.array(z7.string())
|
|
2325
|
+
}).openapi({
|
|
2326
|
+
description: "Patch-aware facet option."
|
|
2327
|
+
})
|
|
2328
|
+
);
|
|
2329
|
+
var confidenceRef = openApiRegistry.register(
|
|
2330
|
+
"Confidence",
|
|
2331
|
+
REST_CONFIDENCE_SCHEMA
|
|
2332
|
+
);
|
|
2333
|
+
var certaintyRef = openApiRegistry.register(
|
|
2334
|
+
"Certainty",
|
|
2335
|
+
REST_CERTAINTY_SCHEMA
|
|
2336
|
+
);
|
|
2337
|
+
var restOpenApiDocumentSchema = openApiRegistry.register(
|
|
2338
|
+
"RestOpenApiDocument",
|
|
2339
|
+
z7.record(z7.string(), z7.unknown())
|
|
2340
|
+
);
|
|
2341
|
+
var previewCollectionResponseSchemas = /* @__PURE__ */ new Map();
|
|
2342
|
+
for (const collection of REST_COLLECTIONS) {
|
|
2343
|
+
const schemaDefinition = getRestCollectionSchemaDefinition(
|
|
2344
|
+
collection.collection
|
|
2345
|
+
);
|
|
2346
|
+
const schemaShape = schemaDefinition.itemSchema.shape;
|
|
2347
|
+
const itemSchemaSource = schemaShape && "certainty" in schemaShape && "confidence" in schemaShape ? schemaDefinition.itemSchema.extend({
|
|
2348
|
+
certainty: certaintyRef,
|
|
2349
|
+
confidence: confidenceRef
|
|
2350
|
+
}) : schemaShape && "confidence" in schemaShape ? schemaDefinition.itemSchema.extend({
|
|
2351
|
+
confidence: confidenceRef
|
|
2352
|
+
}) : schemaDefinition.itemSchema;
|
|
2353
|
+
const itemSchema = openApiRegistry.register(
|
|
2354
|
+
schemaDefinition.itemName,
|
|
2355
|
+
itemSchemaSource
|
|
2356
|
+
);
|
|
2357
|
+
const previewItemSchema = openApiRegistry.register(
|
|
2358
|
+
`${schemaDefinition.itemName}PreviewRecord`,
|
|
2359
|
+
itemSchemaSource.extend({
|
|
2360
|
+
patched: z7.boolean(),
|
|
2361
|
+
provenancePatchIds: z7.array(z7.string())
|
|
2362
|
+
})
|
|
2363
|
+
);
|
|
2364
|
+
const collectionResponseSchema = openApiRegistry.register(
|
|
2365
|
+
schemaDefinition.collectionResponseName,
|
|
2366
|
+
z7.object({
|
|
2367
|
+
items: z7.array(itemSchema),
|
|
2368
|
+
count: z7.number().int().min(0)
|
|
2369
|
+
}).openapi({
|
|
2370
|
+
description: `${schemaDefinition.itemName} collection response.`
|
|
2371
|
+
})
|
|
2372
|
+
);
|
|
2373
|
+
const previewCollectionResponseSchema = openApiRegistry.register(
|
|
2374
|
+
`${schemaDefinition.itemName}PreviewCollectionResponse`,
|
|
2375
|
+
z7.object({
|
|
2376
|
+
items: z7.array(previewItemSchema),
|
|
2377
|
+
count: z7.number().int().min(0)
|
|
2378
|
+
}).openapi({
|
|
2379
|
+
description: `${schemaDefinition.itemName} preview collection response.`
|
|
2380
|
+
})
|
|
2381
|
+
);
|
|
2382
|
+
previewCollectionResponseSchemas.set(
|
|
2383
|
+
collection.collection,
|
|
2384
|
+
previewCollectionResponseSchema
|
|
2385
|
+
);
|
|
2386
|
+
const itemResponseSchema = openApiRegistry.register(
|
|
2387
|
+
schemaDefinition.itemResponseName,
|
|
2388
|
+
z7.object({
|
|
2389
|
+
item: itemSchema
|
|
2390
|
+
}).openapi({
|
|
2391
|
+
description: `${schemaDefinition.itemName} item response.`
|
|
2392
|
+
})
|
|
2393
|
+
);
|
|
2394
|
+
openApiRegistry.registerPath({
|
|
2395
|
+
method: "get",
|
|
2396
|
+
path: `${REST_V1_PREFIX}/${collection.route}`,
|
|
2397
|
+
summary: `List ${collection.label.toLowerCase()}`,
|
|
2398
|
+
responses: {
|
|
2399
|
+
200: {
|
|
2400
|
+
description: "Default Response",
|
|
2401
|
+
content: {
|
|
2402
|
+
"application/json": {
|
|
2403
|
+
schema: collectionResponseSchema
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
},
|
|
2407
|
+
404: {
|
|
2408
|
+
description: "Default Response",
|
|
2409
|
+
content: {
|
|
2410
|
+
"application/json": {
|
|
2411
|
+
schema: restErrorResponseSchema
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
},
|
|
2415
|
+
503: {
|
|
2416
|
+
description: "Default Response",
|
|
2417
|
+
content: {
|
|
2418
|
+
"application/json": {
|
|
2419
|
+
schema: restErrorResponseSchema
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
openApiRegistry.registerPath({
|
|
2426
|
+
method: "get",
|
|
2427
|
+
path: `${REST_V1_PREFIX}/${collection.route}/{id}`,
|
|
2428
|
+
summary: `Get ${collection.label.toLowerCase()} by id`,
|
|
2429
|
+
request: {
|
|
2430
|
+
params: z7.object({
|
|
2431
|
+
id: z7.string()
|
|
2432
|
+
})
|
|
2433
|
+
},
|
|
2434
|
+
responses: {
|
|
2435
|
+
200: {
|
|
2436
|
+
description: "Default Response",
|
|
2437
|
+
content: {
|
|
2438
|
+
"application/json": {
|
|
2439
|
+
schema: itemResponseSchema
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
},
|
|
2443
|
+
404: {
|
|
2444
|
+
description: "Default Response",
|
|
2445
|
+
content: {
|
|
2446
|
+
"application/json": {
|
|
2447
|
+
schema: restErrorResponseSchema
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
},
|
|
2451
|
+
503: {
|
|
2452
|
+
description: "Default Response",
|
|
2453
|
+
content: {
|
|
2454
|
+
"application/json": {
|
|
2455
|
+
schema: restErrorResponseSchema
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
var restPatchInventoryResponseSchema = openApiRegistry.register(
|
|
2463
|
+
"RestPatchInventoryResponse",
|
|
2464
|
+
z7.object({
|
|
2465
|
+
items: z7.array(restPatchSummarySchema),
|
|
2466
|
+
count: z7.number().int().min(0)
|
|
2467
|
+
}).openapi({
|
|
2468
|
+
description: "Patch inventory response."
|
|
2469
|
+
})
|
|
2470
|
+
);
|
|
2471
|
+
var restPatchPreviewResponseSchema = openApiRegistry.register(
|
|
2472
|
+
"RestPatchPreviewResponse",
|
|
2473
|
+
z7.object({
|
|
2474
|
+
selectedPatchId: z7.string(),
|
|
2475
|
+
prefixPatchIds: z7.array(z7.string()),
|
|
2476
|
+
patches: z7.array(restPatchSummarySchema),
|
|
2477
|
+
provenance: z7.record(z7.string(), restPatchProvenanceEntrySchema),
|
|
2478
|
+
facets: z7.object({
|
|
2479
|
+
domains: z7.array(restPatchFacetItemSchema),
|
|
2480
|
+
entities: z7.array(restPatchFacetItemSchema)
|
|
2481
|
+
}).strict(),
|
|
2482
|
+
collections: z7.object({
|
|
2483
|
+
domains: previewCollectionResponseSchemas.get("domains"),
|
|
2484
|
+
entities: previewCollectionResponseSchemas.get("entities"),
|
|
2485
|
+
relationships: previewCollectionResponseSchemas.get("relationships"),
|
|
2486
|
+
capabilities: previewCollectionResponseSchemas.get("capabilities"),
|
|
2487
|
+
features: previewCollectionResponseSchemas.get("features"),
|
|
2488
|
+
business_rules: previewCollectionResponseSchemas.get("business_rules"),
|
|
2489
|
+
constraints: previewCollectionResponseSchemas.get("constraints"),
|
|
2490
|
+
rationales: previewCollectionResponseSchemas.get("rationales"),
|
|
2491
|
+
evidence: previewCollectionResponseSchemas.get("evidence"),
|
|
2492
|
+
questions: previewCollectionResponseSchemas.get("questions"),
|
|
2493
|
+
conflicts: previewCollectionResponseSchemas.get("conflicts")
|
|
2494
|
+
}).strict()
|
|
2495
|
+
}).openapi({
|
|
2496
|
+
description: "Patch-aware preview snapshot response."
|
|
2497
|
+
})
|
|
2498
|
+
);
|
|
2499
|
+
openApiRegistry.registerPath({
|
|
2500
|
+
method: "get",
|
|
2501
|
+
path: `${REST_V1_PREFIX}/${REST_PATCHES_ROUTE}`,
|
|
2502
|
+
summary: "List patch inventory",
|
|
2503
|
+
responses: {
|
|
2504
|
+
200: {
|
|
2505
|
+
description: "Default Response",
|
|
2506
|
+
content: {
|
|
2507
|
+
"application/json": {
|
|
2508
|
+
schema: restPatchInventoryResponseSchema
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
},
|
|
2512
|
+
404: {
|
|
2513
|
+
description: "Default Response",
|
|
2514
|
+
content: {
|
|
2515
|
+
"application/json": {
|
|
2516
|
+
schema: restErrorResponseSchema
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
});
|
|
2522
|
+
openApiRegistry.registerPath({
|
|
2523
|
+
method: "get",
|
|
2524
|
+
path: `${REST_V1_PREFIX}/${REST_PATCH_PREVIEW_PATH}`,
|
|
2525
|
+
summary: "Preview a patch prefix",
|
|
2526
|
+
request: {
|
|
2527
|
+
params: z7.object({
|
|
2528
|
+
patchId: z7.string()
|
|
2529
|
+
})
|
|
2530
|
+
},
|
|
2531
|
+
responses: {
|
|
2532
|
+
200: {
|
|
2533
|
+
description: "Default Response",
|
|
2534
|
+
content: {
|
|
2535
|
+
"application/json": {
|
|
2536
|
+
schema: restPatchPreviewResponseSchema
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
},
|
|
2540
|
+
404: {
|
|
2541
|
+
description: "Default Response",
|
|
2542
|
+
content: {
|
|
2543
|
+
"application/json": {
|
|
2544
|
+
schema: restErrorResponseSchema
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
},
|
|
2548
|
+
503: {
|
|
2549
|
+
description: "Default Response",
|
|
2550
|
+
content: {
|
|
2551
|
+
"application/json": {
|
|
2552
|
+
schema: restErrorResponseSchema
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
});
|
|
2558
|
+
openApiRegistry.registerPath({
|
|
2559
|
+
method: "get",
|
|
2560
|
+
path: REST_HEALTH_PATH,
|
|
2561
|
+
summary: "Service health",
|
|
2562
|
+
responses: {
|
|
2563
|
+
200: {
|
|
2564
|
+
description: "Default Response",
|
|
2565
|
+
content: {
|
|
2566
|
+
"application/json": {
|
|
2567
|
+
schema: restHealthResponseSchema
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
});
|
|
2573
|
+
openApiRegistry.registerPath({
|
|
2574
|
+
method: "get",
|
|
2575
|
+
path: REST_OPENAPI_PATH,
|
|
2576
|
+
summary: "OpenAPI document",
|
|
2577
|
+
responses: {
|
|
2578
|
+
200: {
|
|
2579
|
+
description: "Default Response",
|
|
2580
|
+
content: {
|
|
2581
|
+
"application/json": {
|
|
2582
|
+
schema: restOpenApiDocumentSchema
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
});
|
|
2588
|
+
openApiRegistry.registerPath({
|
|
2589
|
+
method: "get",
|
|
2590
|
+
path: REST_DOCS_PATH,
|
|
2591
|
+
summary: "Swagger UI",
|
|
2592
|
+
responses: {
|
|
2593
|
+
200: {
|
|
2594
|
+
description: "Default Response",
|
|
2595
|
+
content: {
|
|
2596
|
+
"text/html": {
|
|
2597
|
+
schema: z7.string()
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
});
|
|
2603
|
+
function buildRestOpenApiDocument() {
|
|
2604
|
+
return new OpenApiGeneratorV3(openApiRegistry.definitions).generateDocument({
|
|
2605
|
+
openapi: "3.1.0",
|
|
2606
|
+
info: {
|
|
2607
|
+
title: "ez-know REST API",
|
|
2608
|
+
version: "0.1.0",
|
|
2609
|
+
description: "Read-only HTTP interface for loaded ez-know knowledge collections."
|
|
2610
|
+
},
|
|
2611
|
+
servers: [{ url: "/" }]
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
var REST_OPENAPI_DOCUMENT = buildRestOpenApiDocument();
|
|
2615
|
+
|
|
2616
|
+
// packages/rest/src/preview.ts
|
|
2617
|
+
function buildPreviewRecord(record, provenance) {
|
|
2618
|
+
return {
|
|
2619
|
+
...structuredClone(record),
|
|
2620
|
+
patched: Boolean(provenance?.patched),
|
|
2621
|
+
provenancePatchIds: provenance?.patchIds ?? []
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
function buildFacetItem(record, provenance) {
|
|
2625
|
+
const label = "name" in record && typeof record.name === "string" ? record.name : "question" in record && typeof record.question === "string" ? record.question : record.description ?? "";
|
|
2626
|
+
return {
|
|
2627
|
+
id: record.id,
|
|
2628
|
+
label,
|
|
2629
|
+
patched: Boolean(provenance?.patched),
|
|
2630
|
+
provenancePatchIds: provenance?.patchIds ?? []
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
function buildCollectionPreview(records, provenance) {
|
|
2634
|
+
const items = records.map(
|
|
2635
|
+
(record) => buildPreviewRecord(record, provenance.get(record.id))
|
|
2636
|
+
);
|
|
2637
|
+
return {
|
|
2638
|
+
items,
|
|
2639
|
+
count: items.length
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
async function buildPatchInventory(envValue, cwd = process.cwd()) {
|
|
2643
|
+
return listKnowledgePatches(envValue, cwd);
|
|
2644
|
+
}
|
|
2645
|
+
function getCollectionNames() {
|
|
2646
|
+
return [
|
|
2647
|
+
"domains",
|
|
2648
|
+
"entities",
|
|
2649
|
+
"relationships",
|
|
2650
|
+
"capabilities",
|
|
2651
|
+
"features",
|
|
2652
|
+
"business_rules",
|
|
2653
|
+
"constraints",
|
|
2654
|
+
"rationales",
|
|
2655
|
+
"evidence",
|
|
2656
|
+
"questions",
|
|
2657
|
+
"conflicts"
|
|
2658
|
+
];
|
|
2659
|
+
}
|
|
2660
|
+
function emptyPreviewCollections() {
|
|
2661
|
+
const emptyCollection = { items: [], count: 0 };
|
|
2662
|
+
return {
|
|
2663
|
+
domains: structuredClone(emptyCollection),
|
|
2664
|
+
entities: structuredClone(emptyCollection),
|
|
2665
|
+
relationships: structuredClone(emptyCollection),
|
|
2666
|
+
capabilities: structuredClone(emptyCollection),
|
|
2667
|
+
features: structuredClone(emptyCollection),
|
|
2668
|
+
business_rules: structuredClone(emptyCollection),
|
|
2669
|
+
constraints: structuredClone(emptyCollection),
|
|
2670
|
+
rationales: structuredClone(emptyCollection),
|
|
2671
|
+
evidence: structuredClone(emptyCollection),
|
|
2672
|
+
questions: structuredClone(emptyCollection),
|
|
2673
|
+
conflicts: structuredClone(emptyCollection)
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
async function buildPatchPreview(selectedPatchId, knowledgeRoot) {
|
|
2677
|
+
const patches = await listKnowledgePatches(knowledgeRoot);
|
|
2678
|
+
const selectedIndex = patches.findIndex(
|
|
2679
|
+
(patch) => patch.id === selectedPatchId
|
|
2680
|
+
);
|
|
2681
|
+
if (selectedIndex === -1) {
|
|
2682
|
+
throw new Error(`Patch "${selectedPatchId}" was not found.`);
|
|
2683
|
+
}
|
|
2684
|
+
const prefixPatches = patches.slice(0, selectedIndex + 1);
|
|
2685
|
+
const effectiveState = await loadEffectiveKnowledgeStoreFromRoot(
|
|
2686
|
+
knowledgeRoot,
|
|
2687
|
+
{ selectedPatchId }
|
|
2688
|
+
);
|
|
2689
|
+
const currentState = effectiveState.store;
|
|
2690
|
+
const provenanceMap = new Map(
|
|
2691
|
+
effectiveState.provenance.entries()
|
|
2692
|
+
);
|
|
2693
|
+
const collections = emptyPreviewCollections();
|
|
2694
|
+
const provenanceObject = Object.fromEntries(provenanceMap.entries());
|
|
2695
|
+
for (const collection of getCollectionNames()) {
|
|
2696
|
+
const records = currentState[collection];
|
|
2697
|
+
const previewCollection = buildCollectionPreview(records, provenanceMap);
|
|
2698
|
+
collections[collection] = previewCollection;
|
|
2699
|
+
}
|
|
2700
|
+
const domainItems = currentState.domains.map(
|
|
2701
|
+
(record) => buildFacetItem(record, provenanceMap.get(record.id))
|
|
2702
|
+
);
|
|
2703
|
+
const entityItems = currentState.entities.map(
|
|
2704
|
+
(record) => buildFacetItem(record, provenanceMap.get(record.id))
|
|
2705
|
+
);
|
|
2706
|
+
return {
|
|
2707
|
+
selectedPatchId,
|
|
2708
|
+
prefixPatchIds: effectiveState.patchIds,
|
|
2709
|
+
patches: prefixPatches,
|
|
2710
|
+
provenance: provenanceObject,
|
|
2711
|
+
facets: {
|
|
2712
|
+
domains: domainItems,
|
|
2713
|
+
entities: entityItems
|
|
2714
|
+
},
|
|
2715
|
+
collections
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// packages/rest/src/payloads.ts
|
|
2720
|
+
function buildHealthPayload(serviceName, store) {
|
|
2721
|
+
return {
|
|
2722
|
+
service: serviceName,
|
|
2723
|
+
status: "ok",
|
|
2724
|
+
storeAvailable: store !== void 0
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
function buildErrorPayload(code, message) {
|
|
2728
|
+
return {
|
|
2729
|
+
error: {
|
|
2730
|
+
code,
|
|
2731
|
+
message
|
|
2732
|
+
}
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2735
|
+
function buildCollectionPayload(items) {
|
|
2736
|
+
return {
|
|
2737
|
+
items,
|
|
2738
|
+
count: items.length
|
|
2739
|
+
};
|
|
2740
|
+
}
|
|
2741
|
+
function buildItemPayload(item) {
|
|
2742
|
+
return {
|
|
2743
|
+
item
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
// packages/rest/src/schemas.ts
|
|
2748
|
+
var restObjectSchema = {
|
|
2749
|
+
type: "object",
|
|
2750
|
+
required: ["id"],
|
|
2751
|
+
properties: {
|
|
2752
|
+
id: {
|
|
2753
|
+
type: "string"
|
|
2754
|
+
}
|
|
2755
|
+
},
|
|
2756
|
+
additionalProperties: true
|
|
2757
|
+
};
|
|
2758
|
+
var restErrorResponseSchema2 = {
|
|
2759
|
+
type: "object",
|
|
2760
|
+
required: ["error"],
|
|
2761
|
+
properties: {
|
|
2762
|
+
error: {
|
|
2763
|
+
type: "object",
|
|
2764
|
+
required: ["code", "message"],
|
|
2765
|
+
properties: {
|
|
2766
|
+
code: { type: "string" },
|
|
2767
|
+
message: { type: "string" }
|
|
2768
|
+
},
|
|
2769
|
+
additionalProperties: false
|
|
2770
|
+
}
|
|
2771
|
+
},
|
|
2772
|
+
additionalProperties: false
|
|
2773
|
+
};
|
|
2774
|
+
var restHealthResponseSchema2 = {
|
|
2775
|
+
type: "object",
|
|
2776
|
+
required: ["service", "status", "storeAvailable"],
|
|
2777
|
+
properties: {
|
|
2778
|
+
service: { type: "string" },
|
|
2779
|
+
status: { type: "string", const: "ok" },
|
|
2780
|
+
storeAvailable: { type: "boolean" }
|
|
2781
|
+
},
|
|
2782
|
+
additionalProperties: false
|
|
2783
|
+
};
|
|
2784
|
+
var restCollectionResponseSchema = {
|
|
2785
|
+
type: "object",
|
|
2786
|
+
required: ["items", "count"],
|
|
2787
|
+
properties: {
|
|
2788
|
+
items: {
|
|
2789
|
+
type: "array",
|
|
2790
|
+
items: restObjectSchema
|
|
2791
|
+
},
|
|
2792
|
+
count: {
|
|
2793
|
+
type: "integer",
|
|
2794
|
+
minimum: 0
|
|
2795
|
+
}
|
|
2796
|
+
},
|
|
2797
|
+
additionalProperties: false
|
|
2798
|
+
};
|
|
2799
|
+
var restPatchSummarySchema2 = {
|
|
2800
|
+
type: "object",
|
|
2801
|
+
required: [
|
|
2802
|
+
"id",
|
|
2803
|
+
"status",
|
|
2804
|
+
"title",
|
|
2805
|
+
"rootFolder",
|
|
2806
|
+
"omittedFiles",
|
|
2807
|
+
"operationCount",
|
|
2808
|
+
"createdAt",
|
|
2809
|
+
"updatedAt"
|
|
2810
|
+
],
|
|
2811
|
+
properties: {
|
|
2812
|
+
id: { type: "string" },
|
|
2813
|
+
status: { type: "string" },
|
|
2814
|
+
title: { type: "string" },
|
|
2815
|
+
description: { type: "string" },
|
|
2816
|
+
rootFolder: { type: "string" },
|
|
2817
|
+
omittedFiles: {
|
|
2818
|
+
type: "array",
|
|
2819
|
+
items: {
|
|
2820
|
+
type: "object",
|
|
2821
|
+
required: ["path", "reason"],
|
|
2822
|
+
properties: {
|
|
2823
|
+
path: { type: "string" },
|
|
2824
|
+
reason: { type: "string" }
|
|
2825
|
+
},
|
|
2826
|
+
additionalProperties: false
|
|
2827
|
+
}
|
|
2828
|
+
},
|
|
2829
|
+
operationCount: { type: "integer", minimum: 0 },
|
|
2830
|
+
createdAt: { type: "string" },
|
|
2831
|
+
updatedAt: { type: "string" }
|
|
2832
|
+
},
|
|
2833
|
+
additionalProperties: false
|
|
2834
|
+
};
|
|
2835
|
+
var restPatchProvenanceEntrySchema2 = {
|
|
2836
|
+
type: "object",
|
|
2837
|
+
required: ["patchIds", "patched"],
|
|
2838
|
+
properties: {
|
|
2839
|
+
patchIds: {
|
|
2840
|
+
type: "array",
|
|
2841
|
+
items: { type: "string" }
|
|
2842
|
+
},
|
|
2843
|
+
patched: { type: "boolean" }
|
|
2844
|
+
},
|
|
2845
|
+
additionalProperties: false
|
|
2846
|
+
};
|
|
2847
|
+
var restPatchFacetItemSchema2 = {
|
|
2848
|
+
type: "object",
|
|
2849
|
+
required: ["id", "label", "patched", "provenancePatchIds"],
|
|
2850
|
+
properties: {
|
|
2851
|
+
id: { type: "string" },
|
|
2852
|
+
label: { type: "string" },
|
|
2853
|
+
patched: { type: "boolean" },
|
|
2854
|
+
provenancePatchIds: {
|
|
2855
|
+
type: "array",
|
|
2856
|
+
items: { type: "string" }
|
|
2857
|
+
}
|
|
2858
|
+
},
|
|
2859
|
+
additionalProperties: false
|
|
2860
|
+
};
|
|
2861
|
+
var restPatchPreviewRecordSchema = {
|
|
2862
|
+
type: "object",
|
|
2863
|
+
required: ["patched", "provenancePatchIds"],
|
|
2864
|
+
properties: {
|
|
2865
|
+
patched: { type: "boolean" },
|
|
2866
|
+
provenancePatchIds: {
|
|
2867
|
+
type: "array",
|
|
2868
|
+
items: { type: "string" }
|
|
2869
|
+
}
|
|
2870
|
+
},
|
|
2871
|
+
additionalProperties: true
|
|
2872
|
+
};
|
|
2873
|
+
var restPatchPreviewCollectionSchema = {
|
|
2874
|
+
type: "object",
|
|
2875
|
+
required: ["items", "count"],
|
|
2876
|
+
properties: {
|
|
2877
|
+
items: {
|
|
2878
|
+
type: "array",
|
|
2879
|
+
items: restPatchPreviewRecordSchema
|
|
2880
|
+
},
|
|
2881
|
+
count: {
|
|
2882
|
+
type: "integer",
|
|
2883
|
+
minimum: 0
|
|
2884
|
+
}
|
|
2885
|
+
},
|
|
2886
|
+
additionalProperties: false
|
|
2887
|
+
};
|
|
2888
|
+
var restPatchInventoryResponseSchema2 = {
|
|
2889
|
+
type: "object",
|
|
2890
|
+
required: ["items", "count"],
|
|
2891
|
+
properties: {
|
|
2892
|
+
items: {
|
|
2893
|
+
type: "array",
|
|
2894
|
+
items: restPatchSummarySchema2
|
|
2895
|
+
},
|
|
2896
|
+
count: {
|
|
2897
|
+
type: "integer",
|
|
2898
|
+
minimum: 0
|
|
2899
|
+
}
|
|
2900
|
+
},
|
|
2901
|
+
additionalProperties: false
|
|
2902
|
+
};
|
|
2903
|
+
var restPatchPreviewResponseSchema2 = {
|
|
2904
|
+
type: "object",
|
|
2905
|
+
required: [
|
|
2906
|
+
"selectedPatchId",
|
|
2907
|
+
"prefixPatchIds",
|
|
2908
|
+
"patches",
|
|
2909
|
+
"provenance",
|
|
2910
|
+
"facets",
|
|
2911
|
+
"collections"
|
|
2912
|
+
],
|
|
2913
|
+
properties: {
|
|
2914
|
+
selectedPatchId: { type: "string" },
|
|
2915
|
+
prefixPatchIds: {
|
|
2916
|
+
type: "array",
|
|
2917
|
+
items: { type: "string" }
|
|
2918
|
+
},
|
|
2919
|
+
patches: {
|
|
2920
|
+
type: "array",
|
|
2921
|
+
items: restPatchSummarySchema2
|
|
2922
|
+
},
|
|
2923
|
+
provenance: {
|
|
2924
|
+
type: "object",
|
|
2925
|
+
additionalProperties: restPatchProvenanceEntrySchema2
|
|
2926
|
+
},
|
|
2927
|
+
facets: {
|
|
2928
|
+
type: "object",
|
|
2929
|
+
required: ["domains", "entities"],
|
|
2930
|
+
properties: {
|
|
2931
|
+
domains: {
|
|
2932
|
+
type: "array",
|
|
2933
|
+
items: restPatchFacetItemSchema2
|
|
2934
|
+
},
|
|
2935
|
+
entities: {
|
|
2936
|
+
type: "array",
|
|
2937
|
+
items: restPatchFacetItemSchema2
|
|
2938
|
+
}
|
|
2939
|
+
},
|
|
2940
|
+
additionalProperties: false
|
|
2941
|
+
},
|
|
2942
|
+
collections: {
|
|
2943
|
+
type: "object",
|
|
2944
|
+
required: [
|
|
2945
|
+
"domains",
|
|
2946
|
+
"entities",
|
|
2947
|
+
"relationships",
|
|
2948
|
+
"capabilities",
|
|
2949
|
+
"features",
|
|
2950
|
+
"business_rules",
|
|
2951
|
+
"constraints",
|
|
2952
|
+
"rationales",
|
|
2953
|
+
"evidence",
|
|
2954
|
+
"questions",
|
|
2955
|
+
"conflicts"
|
|
2956
|
+
],
|
|
2957
|
+
properties: {
|
|
2958
|
+
domains: restPatchPreviewCollectionSchema,
|
|
2959
|
+
entities: restPatchPreviewCollectionSchema,
|
|
2960
|
+
relationships: restPatchPreviewCollectionSchema,
|
|
2961
|
+
capabilities: restPatchPreviewCollectionSchema,
|
|
2962
|
+
features: restPatchPreviewCollectionSchema,
|
|
2963
|
+
business_rules: restPatchPreviewCollectionSchema,
|
|
2964
|
+
constraints: restPatchPreviewCollectionSchema,
|
|
2965
|
+
rationales: restPatchPreviewCollectionSchema,
|
|
2966
|
+
evidence: restPatchPreviewCollectionSchema,
|
|
2967
|
+
questions: restPatchPreviewCollectionSchema,
|
|
2968
|
+
conflicts: restPatchPreviewCollectionSchema
|
|
2969
|
+
},
|
|
2970
|
+
additionalProperties: false
|
|
2971
|
+
}
|
|
2972
|
+
},
|
|
2973
|
+
additionalProperties: false
|
|
2974
|
+
};
|
|
2975
|
+
var restItemResponseSchema = {
|
|
2976
|
+
type: "object",
|
|
2977
|
+
required: ["item"],
|
|
2978
|
+
properties: {
|
|
2979
|
+
item: restObjectSchema
|
|
2980
|
+
},
|
|
2981
|
+
additionalProperties: false
|
|
2982
|
+
};
|
|
2983
|
+
var restOpenApiDocumentSchema2 = {
|
|
2984
|
+
type: "object",
|
|
2985
|
+
additionalProperties: true
|
|
2986
|
+
};
|
|
2987
|
+
|
|
2988
|
+
// packages/rest/src/routes.ts
|
|
2989
|
+
function getCollectionRecords(store, collection) {
|
|
2990
|
+
return store[collection];
|
|
2991
|
+
}
|
|
2992
|
+
function getCollectionRecordById(store, collection, id) {
|
|
2993
|
+
return store.byCollection[collection].get(id);
|
|
2994
|
+
}
|
|
2995
|
+
function buildItemParamsSchema() {
|
|
2996
|
+
return {
|
|
2997
|
+
type: "object",
|
|
2998
|
+
required: ["id"],
|
|
2999
|
+
properties: {
|
|
3000
|
+
id: { type: "string" }
|
|
3001
|
+
},
|
|
3002
|
+
additionalProperties: false
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
function getPatchRoot(store) {
|
|
3006
|
+
return store?.rootDir ?? process.env.EZ_KNOW_ROOT;
|
|
3007
|
+
}
|
|
3008
|
+
async function loadEffectiveRestStore(store) {
|
|
3009
|
+
if (!store) {
|
|
3010
|
+
return void 0;
|
|
3011
|
+
}
|
|
3012
|
+
const effectiveState = await loadEffectiveKnowledgeStoreFromRoot(
|
|
3013
|
+
store.rootDir
|
|
3014
|
+
);
|
|
3015
|
+
return effectiveState.store;
|
|
3016
|
+
}
|
|
3017
|
+
function registerCollectionRoutes(app, store, collection) {
|
|
3018
|
+
app.get(
|
|
3019
|
+
`${REST_V1_PREFIX}/${collection.route}`,
|
|
3020
|
+
{
|
|
3021
|
+
schema: {
|
|
3022
|
+
response: {
|
|
3023
|
+
200: restCollectionResponseSchema,
|
|
3024
|
+
404: restErrorResponseSchema2,
|
|
3025
|
+
503: restErrorResponseSchema2
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
},
|
|
3029
|
+
async (_request, reply) => {
|
|
3030
|
+
const effectiveStore = await loadEffectiveRestStore(store);
|
|
3031
|
+
if (!effectiveStore) {
|
|
3032
|
+
reply.code(503);
|
|
3033
|
+
return buildErrorPayload(
|
|
3034
|
+
"missing_store",
|
|
3035
|
+
"Knowledge store is unavailable."
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
return buildCollectionPayload(
|
|
3039
|
+
getCollectionRecords(effectiveStore, collection.collection)
|
|
3040
|
+
);
|
|
3041
|
+
}
|
|
3042
|
+
);
|
|
3043
|
+
app.get(
|
|
3044
|
+
`${REST_V1_PREFIX}/${collection.route}/:id`,
|
|
3045
|
+
{
|
|
3046
|
+
schema: {
|
|
3047
|
+
params: buildItemParamsSchema(),
|
|
3048
|
+
response: {
|
|
3049
|
+
200: restItemResponseSchema,
|
|
3050
|
+
404: restErrorResponseSchema2,
|
|
3051
|
+
503: restErrorResponseSchema2
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
},
|
|
3055
|
+
async (request, reply) => {
|
|
3056
|
+
const effectiveStore = await loadEffectiveRestStore(store);
|
|
3057
|
+
if (!effectiveStore) {
|
|
3058
|
+
reply.code(503);
|
|
3059
|
+
return buildErrorPayload(
|
|
3060
|
+
"missing_store",
|
|
3061
|
+
"Knowledge store is unavailable."
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
const item = getCollectionRecordById(
|
|
3065
|
+
effectiveStore,
|
|
3066
|
+
collection.collection,
|
|
3067
|
+
request.params.id
|
|
3068
|
+
);
|
|
3069
|
+
if (!item) {
|
|
3070
|
+
reply.code(404);
|
|
3071
|
+
return buildErrorPayload(
|
|
3072
|
+
"unknown_item",
|
|
3073
|
+
`No item with id "${request.params.id}" exists in ${collection.collection}.`
|
|
3074
|
+
);
|
|
3075
|
+
}
|
|
3076
|
+
return buildItemPayload(item);
|
|
3077
|
+
}
|
|
3078
|
+
);
|
|
3079
|
+
}
|
|
3080
|
+
async function registerRestRoutes(app, store) {
|
|
3081
|
+
app.get(
|
|
3082
|
+
REST_HEALTH_PATH,
|
|
3083
|
+
{
|
|
3084
|
+
schema: {
|
|
3085
|
+
response: {
|
|
3086
|
+
200: restHealthResponseSchema2
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
},
|
|
3090
|
+
async () => buildHealthPayload(REST_SERVICE_NAME, store)
|
|
3091
|
+
);
|
|
3092
|
+
app.get(
|
|
3093
|
+
REST_OPENAPI_PATH,
|
|
3094
|
+
{
|
|
3095
|
+
schema: {
|
|
3096
|
+
response: {
|
|
3097
|
+
200: restOpenApiDocumentSchema2
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
},
|
|
3101
|
+
async () => REST_OPENAPI_DOCUMENT
|
|
3102
|
+
);
|
|
3103
|
+
app.get(
|
|
3104
|
+
REST_DOCS_PATH,
|
|
3105
|
+
{
|
|
3106
|
+
schema: {
|
|
3107
|
+
response: {
|
|
3108
|
+
200: {
|
|
3109
|
+
type: "string"
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
},
|
|
3114
|
+
async (_request, reply) => {
|
|
3115
|
+
reply.type("text/html; charset=utf-8");
|
|
3116
|
+
return buildSwaggerUiHtml(REST_OPENAPI_PATH);
|
|
3117
|
+
}
|
|
3118
|
+
);
|
|
3119
|
+
app.get(
|
|
3120
|
+
`${REST_V1_PREFIX}/${REST_PATCHES_ROUTE}`,
|
|
3121
|
+
{
|
|
3122
|
+
schema: {
|
|
3123
|
+
response: {
|
|
3124
|
+
200: restPatchInventoryResponseSchema2,
|
|
3125
|
+
404: restErrorResponseSchema2
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
},
|
|
3129
|
+
async () => buildCollectionPayload(await buildPatchInventory(getPatchRoot(store)))
|
|
3130
|
+
);
|
|
3131
|
+
app.get(
|
|
3132
|
+
`${REST_V1_PREFIX}/${REST_PATCH_PREVIEW_ROUTE}`,
|
|
3133
|
+
{
|
|
3134
|
+
schema: {
|
|
3135
|
+
params: {
|
|
3136
|
+
type: "object",
|
|
3137
|
+
required: ["patchId"],
|
|
3138
|
+
properties: {
|
|
3139
|
+
patchId: { type: "string" }
|
|
3140
|
+
},
|
|
3141
|
+
additionalProperties: false
|
|
3142
|
+
},
|
|
3143
|
+
response: {
|
|
3144
|
+
200: restPatchPreviewResponseSchema2,
|
|
3145
|
+
404: restErrorResponseSchema2,
|
|
3146
|
+
503: restErrorResponseSchema2
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
},
|
|
3150
|
+
async (request, reply) => {
|
|
3151
|
+
if (!store) {
|
|
3152
|
+
reply.code(503);
|
|
3153
|
+
return buildErrorPayload(
|
|
3154
|
+
"missing_store",
|
|
3155
|
+
"Knowledge store is unavailable."
|
|
3156
|
+
);
|
|
3157
|
+
}
|
|
3158
|
+
try {
|
|
3159
|
+
return await buildPatchPreview(request.params.patchId, store.rootDir);
|
|
3160
|
+
} catch (error) {
|
|
3161
|
+
reply.code(404);
|
|
3162
|
+
return buildErrorPayload(
|
|
3163
|
+
"unknown_patch",
|
|
3164
|
+
error instanceof Error ? error.message : "Patch not found."
|
|
3165
|
+
);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
);
|
|
3169
|
+
for (const collection of REST_COLLECTIONS) {
|
|
3170
|
+
registerCollectionRoutes(app, store, collection);
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
// packages/rest/src/app.ts
|
|
3175
|
+
async function buildRestApp(store, options = {}) {
|
|
3176
|
+
const app = Fastify({
|
|
3177
|
+
logger: false
|
|
3178
|
+
});
|
|
3179
|
+
await registerRestRoutes(app, store);
|
|
3180
|
+
if (options.staticRoot) {
|
|
3181
|
+
try {
|
|
3182
|
+
const staticRoot = resolve5(options.staticRoot);
|
|
3183
|
+
await access(staticRoot);
|
|
3184
|
+
await app.register(fastifyStatic, {
|
|
3185
|
+
root: staticRoot,
|
|
3186
|
+
prefix: "/"
|
|
3187
|
+
});
|
|
3188
|
+
} catch {
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
await app.ready();
|
|
3192
|
+
return app;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
// packages/rest/src/run.ts
|
|
3196
|
+
function parsePort(value, fallback) {
|
|
3197
|
+
if (!value) {
|
|
3198
|
+
return fallback;
|
|
3199
|
+
}
|
|
3200
|
+
const parsed = Number(value);
|
|
3201
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
3202
|
+
throw new Error(`Invalid REST port: ${value}`);
|
|
3203
|
+
}
|
|
3204
|
+
return parsed;
|
|
3205
|
+
}
|
|
3206
|
+
async function startRestServer(options = {}) {
|
|
3207
|
+
const host = options.host ?? process.env.EZ_KNOW_REST_HOST ?? "127.0.0.1";
|
|
3208
|
+
const port = options.port ?? parsePort(process.env.EZ_KNOW_REST_PORT ?? process.env.PORT, 3e3);
|
|
3209
|
+
const knowledgeStore = await loadKnowledgeStoreFromEnv(
|
|
3210
|
+
process.env.EZ_KNOW_ROOT
|
|
3211
|
+
);
|
|
3212
|
+
const app = await buildRestApp(knowledgeStore, {
|
|
3213
|
+
staticRoot: options.staticRoot
|
|
3214
|
+
});
|
|
3215
|
+
await app.listen({ port, host });
|
|
3216
|
+
console.log(`ez-know REST service listening on http://${host}:${port}`);
|
|
3217
|
+
return app;
|
|
3218
|
+
}
|
|
3219
|
+
|
|
3220
|
+
// src/rest-server.ts
|
|
3221
|
+
function resolveRestStaticRoot() {
|
|
3222
|
+
const distPublicRoot = fileURLToPath(new URL("../public", import.meta.url));
|
|
3223
|
+
if (existsSync3(distPublicRoot)) {
|
|
3224
|
+
return distPublicRoot;
|
|
3225
|
+
}
|
|
3226
|
+
const webDistRoot = fileURLToPath(
|
|
3227
|
+
new URL("../packages/web/dist", import.meta.url)
|
|
3228
|
+
);
|
|
3229
|
+
if (existsSync3(webDistRoot)) {
|
|
3230
|
+
return webDistRoot;
|
|
3231
|
+
}
|
|
3232
|
+
return void 0;
|
|
3233
|
+
}
|
|
3234
|
+
await startRestServer({
|
|
3235
|
+
staticRoot: resolveRestStaticRoot()
|
|
3236
|
+
});
|
|
3237
|
+
//# sourceMappingURL=server.js.map
|