@unrdf/kgn 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/package.json +90 -0
- package/src/MIGRATION_COMPLETE.md +186 -0
- package/src/PORT-MAP.md +302 -0
- package/src/base/filter-templates.js +479 -0
- package/src/base/index.js +92 -0
- package/src/base/injection-targets.js +583 -0
- package/src/base/macro-templates.js +298 -0
- package/src/base/macro-templates.js.bak +461 -0
- package/src/base/shacl-templates.js +617 -0
- package/src/base/template-base.js +388 -0
- package/src/core/attestor.js +381 -0
- package/src/core/filters.js +518 -0
- package/src/core/index.js +21 -0
- package/src/core/kgen-engine.js +372 -0
- package/src/core/parser.js +447 -0
- package/src/core/post-processor.js +313 -0
- package/src/core/renderer.js +469 -0
- package/src/doc-generator/cli.mjs +122 -0
- package/src/doc-generator/index.mjs +28 -0
- package/src/doc-generator/mdx-generator.mjs +71 -0
- package/src/doc-generator/nav-generator.mjs +136 -0
- package/src/doc-generator/parser.mjs +291 -0
- package/src/doc-generator/rdf-builder.mjs +306 -0
- package/src/doc-generator/scanner.mjs +189 -0
- package/src/engine/index.js +42 -0
- package/src/engine/pipeline.js +448 -0
- package/src/engine/renderer.js +604 -0
- package/src/engine/template-engine.js +566 -0
- package/src/filters/array.js +436 -0
- package/src/filters/data.js +479 -0
- package/src/filters/index.js +270 -0
- package/src/filters/rdf.js +264 -0
- package/src/filters/text.js +369 -0
- package/src/index.js +109 -0
- package/src/inheritance/index.js +40 -0
- package/src/injection/api.js +260 -0
- package/src/injection/atomic-writer.js +327 -0
- package/src/injection/constants.js +136 -0
- package/src/injection/idempotency-manager.js +295 -0
- package/src/injection/index.js +28 -0
- package/src/injection/injection-engine.js +378 -0
- package/src/injection/integration.js +339 -0
- package/src/injection/modes/index.js +341 -0
- package/src/injection/rollback-manager.js +373 -0
- package/src/injection/target-resolver.js +323 -0
- package/src/injection/tests/atomic-writer.test.js +382 -0
- package/src/injection/tests/injection-engine.test.js +611 -0
- package/src/injection/tests/integration.test.js +392 -0
- package/src/injection/tests/run-tests.js +283 -0
- package/src/injection/validation-engine.js +547 -0
- package/src/linter/determinism-linter.js +473 -0
- package/src/linter/determinism.js +410 -0
- package/src/linter/index.js +6 -0
- package/src/linter/test-doubles.js +475 -0
- package/src/parser/frontmatter.js +228 -0
- package/src/parser/variables.js +344 -0
- package/src/renderer/deterministic.js +245 -0
- package/src/renderer/index.js +6 -0
- package/src/templates/latex/academic-paper.njk +186 -0
- package/src/templates/latex/index.js +104 -0
- package/src/templates/nextjs/app-page.njk +66 -0
- package/src/templates/nextjs/index.js +80 -0
- package/src/templates/office/docx/document.njk +368 -0
- package/src/templates/office/index.js +79 -0
- package/src/templates/office/word-report.njk +129 -0
- package/src/utils/template-utils.js +426 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DETERMINISTIC Pipeline Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the 4-stage deterministic rendering pipeline:
|
|
5
|
+
* 1. PLAN - Parse and validate
|
|
6
|
+
* 2. RENDER - Execute with fixed order
|
|
7
|
+
* 3. POST - Normalize output
|
|
8
|
+
* 4. ATTEST - Generate cryptographic proof
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Atomic pipeline execution
|
|
12
|
+
* - Rollback on stage failure
|
|
13
|
+
* - Comprehensive error handling
|
|
14
|
+
* - Performance monitoring
|
|
15
|
+
* - Reproducibility verification
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { DeterministicRenderer } from './renderer.js';
|
|
19
|
+
|
|
20
|
+
export class DeterministicPipeline {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.renderer = new DeterministicRenderer(options);
|
|
23
|
+
this.enableValidation = options.enableValidation !== false;
|
|
24
|
+
this.enableProfiling = options.enableProfiling !== false;
|
|
25
|
+
|
|
26
|
+
// Pipeline statistics
|
|
27
|
+
this.stats = {
|
|
28
|
+
pipelineCount: 0,
|
|
29
|
+
successfulRuns: 0,
|
|
30
|
+
failedRuns: 0,
|
|
31
|
+
averageExecutionTime: 0,
|
|
32
|
+
stageFailures: {
|
|
33
|
+
plan: 0,
|
|
34
|
+
render: 0,
|
|
35
|
+
post: 0,
|
|
36
|
+
attest: 0
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Execute complete 4-stage deterministic pipeline
|
|
43
|
+
*
|
|
44
|
+
* @param {string} template - Template content
|
|
45
|
+
* @param {object} data - Template data context
|
|
46
|
+
* @param {object} options - Pipeline options
|
|
47
|
+
* @returns {Promise<object>} Final pipeline result with attestation
|
|
48
|
+
*/
|
|
49
|
+
async execute(template, data, options = {}) {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
const pipelineId = this._generatePipelineId();
|
|
52
|
+
|
|
53
|
+
const pipelineContext = {
|
|
54
|
+
id: pipelineId,
|
|
55
|
+
startTime,
|
|
56
|
+
stages: {},
|
|
57
|
+
options,
|
|
58
|
+
template: {
|
|
59
|
+
hash: this.renderer._hashContent(template),
|
|
60
|
+
length: template.length
|
|
61
|
+
},
|
|
62
|
+
data: {
|
|
63
|
+
hash: this.renderer._hashContent(JSON.stringify(data)),
|
|
64
|
+
keys: Object.keys(data).length
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
this.stats.pipelineCount++;
|
|
70
|
+
|
|
71
|
+
// STAGE 1: PLAN
|
|
72
|
+
const planResult = await this._executeStage('plan',
|
|
73
|
+
() => this.renderer.plan(template, data),
|
|
74
|
+
pipelineContext
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!planResult.success) {
|
|
78
|
+
return this._handlePipelineFailure('plan', planResult, pipelineContext);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// STAGE 2: RENDER
|
|
82
|
+
const renderResult = await this._executeStage('render',
|
|
83
|
+
() => this.renderer.render(planResult.plan),
|
|
84
|
+
pipelineContext
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!renderResult.success) {
|
|
88
|
+
return this._handlePipelineFailure('render', renderResult, pipelineContext);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// STAGE 3: POST
|
|
92
|
+
const postResult = await this._executeStage('post',
|
|
93
|
+
() => this.renderer.post(renderResult),
|
|
94
|
+
pipelineContext
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (!postResult.success) {
|
|
98
|
+
return this._handlePipelineFailure('post', postResult, pipelineContext);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// STAGE 4: ATTEST
|
|
102
|
+
const attestResult = await this._executeStage('attest',
|
|
103
|
+
() => this.renderer.attest(postResult),
|
|
104
|
+
pipelineContext
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (!attestResult.success) {
|
|
108
|
+
return this._handlePipelineFailure('attest', attestResult, pipelineContext);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Pipeline completed successfully
|
|
112
|
+
const totalTime = Date.now() - startTime;
|
|
113
|
+
this.stats.successfulRuns++;
|
|
114
|
+
this._updateAverageTime(totalTime);
|
|
115
|
+
|
|
116
|
+
const finalResult = {
|
|
117
|
+
success: true,
|
|
118
|
+
pipelineId,
|
|
119
|
+
content: attestResult.content,
|
|
120
|
+
contentHash: attestResult.contentHash,
|
|
121
|
+
attestation: attestResult.attestation,
|
|
122
|
+
attestationProof: attestResult.attestationProof,
|
|
123
|
+
pipeline: {
|
|
124
|
+
completed: true,
|
|
125
|
+
stages: 4,
|
|
126
|
+
deterministic: true,
|
|
127
|
+
executionTime: totalTime,
|
|
128
|
+
stageMetrics: pipelineContext.stages
|
|
129
|
+
},
|
|
130
|
+
verification: await this._verifyPipelineResult(attestResult, template, data)
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Optional reproducibility validation
|
|
134
|
+
if (this.enableValidation) {
|
|
135
|
+
const validationResult = await this._validateReproducibility(template, data, finalResult);
|
|
136
|
+
finalResult.validation = validationResult;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return finalResult;
|
|
140
|
+
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return this._handlePipelineError(error, pipelineContext);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Verify deterministic output by running pipeline multiple times
|
|
148
|
+
*
|
|
149
|
+
* @param {string} template - Template content
|
|
150
|
+
* @param {object} data - Template data
|
|
151
|
+
* @param {number} iterations - Number of verification runs
|
|
152
|
+
* @returns {Promise<object>} Verification results
|
|
153
|
+
*/
|
|
154
|
+
async verifyDeterminism(template, data, iterations = 3) {
|
|
155
|
+
const verificationStart = Date.now();
|
|
156
|
+
const results = [];
|
|
157
|
+
const hashes = new Set();
|
|
158
|
+
|
|
159
|
+
for (let i = 0; i < iterations; i++) {
|
|
160
|
+
const result = await this.execute(template, data, {
|
|
161
|
+
verification: true,
|
|
162
|
+
iteration: i + 1
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (result.success) {
|
|
166
|
+
results.push({
|
|
167
|
+
iteration: i + 1,
|
|
168
|
+
contentHash: result.contentHash,
|
|
169
|
+
executionTime: result.pipeline.executionTime,
|
|
170
|
+
attestationProof: result.attestationProof.proof
|
|
171
|
+
});
|
|
172
|
+
hashes.add(result.contentHash);
|
|
173
|
+
} else {
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: `Verification failed on iteration ${i + 1}: ${result.error}`,
|
|
177
|
+
iteration: i + 1
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const isDeterministic = hashes.size === 1;
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
deterministic: isDeterministic,
|
|
187
|
+
iterations,
|
|
188
|
+
uniqueOutputs: hashes.size,
|
|
189
|
+
consistentHash: isDeterministic ? Array.from(hashes)[0] : null,
|
|
190
|
+
results: results.slice(0, 2), // First 2 for comparison
|
|
191
|
+
verification: {
|
|
192
|
+
confidence: isDeterministic ? 'HIGH' : 'LOW',
|
|
193
|
+
verifiedAt: this.renderer.staticBuildTime,
|
|
194
|
+
verificationTime: Date.now() - verificationStart
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Execute batch of templates with deterministic guarantees
|
|
201
|
+
*
|
|
202
|
+
* @param {Array} batch - Array of {template, data, outputPath} objects
|
|
203
|
+
* @param {object} options - Batch execution options
|
|
204
|
+
* @returns {Promise<object>} Batch execution results
|
|
205
|
+
*/
|
|
206
|
+
async executeBatch(batch, options = {}) {
|
|
207
|
+
const batchStart = Date.now();
|
|
208
|
+
const batchId = this._generateBatchId();
|
|
209
|
+
const results = [];
|
|
210
|
+
let successCount = 0;
|
|
211
|
+
let failureCount = 0;
|
|
212
|
+
|
|
213
|
+
// Execute templates in deterministic order (sorted by template hash)
|
|
214
|
+
const sortedBatch = [...batch].sort((a, b) =>
|
|
215
|
+
this.renderer._hashContent(a.template).localeCompare(
|
|
216
|
+
this.renderer._hashContent(b.template)
|
|
217
|
+
)
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
for (const [index, item] of sortedBatch.entries()) {
|
|
221
|
+
try {
|
|
222
|
+
const result = await this.execute(item.template, item.data, {
|
|
223
|
+
...options,
|
|
224
|
+
batchId,
|
|
225
|
+
batchIndex: index,
|
|
226
|
+
batchTotal: sortedBatch.length
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
results.push({
|
|
230
|
+
index,
|
|
231
|
+
outputPath: item.outputPath,
|
|
232
|
+
success: result.success,
|
|
233
|
+
contentHash: result.success ? result.contentHash : null,
|
|
234
|
+
error: result.success ? null : result.error,
|
|
235
|
+
executionTime: result.pipeline?.executionTime || 0
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (result.success) {
|
|
239
|
+
successCount++;
|
|
240
|
+
} else {
|
|
241
|
+
failureCount++;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
} catch (error) {
|
|
245
|
+
results.push({
|
|
246
|
+
index,
|
|
247
|
+
outputPath: item.outputPath,
|
|
248
|
+
success: false,
|
|
249
|
+
error: error.message,
|
|
250
|
+
executionTime: 0
|
|
251
|
+
});
|
|
252
|
+
failureCount++;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
success: failureCount === 0,
|
|
258
|
+
batchId,
|
|
259
|
+
totalItems: sortedBatch.length,
|
|
260
|
+
successCount,
|
|
261
|
+
failureCount,
|
|
262
|
+
results,
|
|
263
|
+
batchExecutionTime: Date.now() - batchStart,
|
|
264
|
+
deterministic: true
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// PRIVATE METHODS
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Execute a single pipeline stage with monitoring
|
|
272
|
+
*/
|
|
273
|
+
async _executeStage(stageName, stageFunction, pipelineContext) {
|
|
274
|
+
const stageStart = Date.now();
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
const result = await stageFunction();
|
|
278
|
+
|
|
279
|
+
pipelineContext.stages[stageName] = {
|
|
280
|
+
success: result.success,
|
|
281
|
+
executionTime: Date.now() - stageStart,
|
|
282
|
+
completedAt: this.renderer.staticBuildTime
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
if (this.enableProfiling) {
|
|
286
|
+
pipelineContext.stages[stageName].profiling = {
|
|
287
|
+
memoryUsage: process.memoryUsage(),
|
|
288
|
+
cpuUsage: process.cpuUsage ? process.cpuUsage() : null
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return result;
|
|
293
|
+
|
|
294
|
+
} catch (error) {
|
|
295
|
+
pipelineContext.stages[stageName] = {
|
|
296
|
+
success: false,
|
|
297
|
+
error: error.message,
|
|
298
|
+
executionTime: Date.now() - stageStart,
|
|
299
|
+
failedAt: this.renderer.staticBuildTime
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
this.stats.stageFailures[stageName]++;
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Handle pipeline stage failure
|
|
309
|
+
*/
|
|
310
|
+
_handlePipelineFailure(failedStage, stageResult, pipelineContext) {
|
|
311
|
+
this.stats.failedRuns++;
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
pipelineId: pipelineContext.id,
|
|
316
|
+
failedStage,
|
|
317
|
+
error: stageResult.error,
|
|
318
|
+
errorType: stageResult.errorType,
|
|
319
|
+
pipeline: {
|
|
320
|
+
completed: false,
|
|
321
|
+
failedAt: failedStage,
|
|
322
|
+
executionTime: Date.now() - pipelineContext.startTime,
|
|
323
|
+
stageMetrics: pipelineContext.stages
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Handle unexpected pipeline errors
|
|
330
|
+
*/
|
|
331
|
+
_handlePipelineError(error, pipelineContext) {
|
|
332
|
+
this.stats.failedRuns++;
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
success: false,
|
|
336
|
+
pipelineId: pipelineContext.id,
|
|
337
|
+
error: error.message,
|
|
338
|
+
errorType: error.constructor.name,
|
|
339
|
+
pipeline: {
|
|
340
|
+
completed: false,
|
|
341
|
+
crashed: true,
|
|
342
|
+
executionTime: Date.now() - pipelineContext.startTime,
|
|
343
|
+
stageMetrics: pipelineContext.stages
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Verify pipeline result integrity
|
|
350
|
+
*/
|
|
351
|
+
async _verifyPipelineResult(attestResult, template, data) {
|
|
352
|
+
const expectedHash = this.renderer._hashContent(attestResult.content);
|
|
353
|
+
const actualHash = attestResult.contentHash;
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
hashVerified: expectedHash === actualHash,
|
|
357
|
+
attestationVerified: attestResult.attestation !== null,
|
|
358
|
+
proofVerified: attestResult.attestationProof !== null,
|
|
359
|
+
deterministic: true
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Validate reproducibility with additional pipeline run
|
|
365
|
+
*/
|
|
366
|
+
async _validateReproducibility(template, data, originalResult) {
|
|
367
|
+
try {
|
|
368
|
+
const validationResult = await this.execute(template, data, {
|
|
369
|
+
validation: true,
|
|
370
|
+
originalHash: originalResult.contentHash
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
reproducible: validationResult.success &&
|
|
375
|
+
validationResult.contentHash === originalResult.contentHash,
|
|
376
|
+
validationHash: validationResult.success ? validationResult.contentHash : null,
|
|
377
|
+
originalHash: originalResult.contentHash,
|
|
378
|
+
hashMatch: validationResult.success ?
|
|
379
|
+
validationResult.contentHash === originalResult.contentHash : false
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return {
|
|
384
|
+
reproducible: false,
|
|
385
|
+
error: error.message,
|
|
386
|
+
originalHash: originalResult.contentHash
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Generate unique pipeline ID
|
|
393
|
+
*/
|
|
394
|
+
_generatePipelineId() {
|
|
395
|
+
const timestamp = Date.now();
|
|
396
|
+
const random = Math.floor(Math.random() * 10000);
|
|
397
|
+
return `pipeline-${timestamp}-${random}`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Generate unique batch ID
|
|
402
|
+
*/
|
|
403
|
+
_generateBatchId() {
|
|
404
|
+
const timestamp = Date.now();
|
|
405
|
+
const random = Math.floor(Math.random() * 10000);
|
|
406
|
+
return `batch-${timestamp}-${random}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Update average execution time
|
|
411
|
+
*/
|
|
412
|
+
_updateAverageTime(newTime) {
|
|
413
|
+
const totalRuns = this.stats.successfulRuns;
|
|
414
|
+
this.stats.averageExecutionTime =
|
|
415
|
+
((this.stats.averageExecutionTime * (totalRuns - 1)) + newTime) / totalRuns;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Get pipeline statistics
|
|
420
|
+
*/
|
|
421
|
+
getStats() {
|
|
422
|
+
return {
|
|
423
|
+
...this.stats,
|
|
424
|
+
rendererStats: this.renderer.getStats()
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Reset pipeline statistics
|
|
430
|
+
*/
|
|
431
|
+
resetStats() {
|
|
432
|
+
this.stats = {
|
|
433
|
+
pipelineCount: 0,
|
|
434
|
+
successfulRuns: 0,
|
|
435
|
+
failedRuns: 0,
|
|
436
|
+
averageExecutionTime: 0,
|
|
437
|
+
stageFailures: {
|
|
438
|
+
plan: 0,
|
|
439
|
+
render: 0,
|
|
440
|
+
post: 0,
|
|
441
|
+
attest: 0
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
this.renderer.resetStats();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export default DeterministicPipeline;
|