@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,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KGEN Attestor - Generate cryptographic attestations for deterministic output
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Content integrity verification
|
|
6
|
+
* - Deterministic output attestation
|
|
7
|
+
* - Reproduction proofs
|
|
8
|
+
* - Audit trails
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
|
|
13
|
+
export class KGenAttestor {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.options = {
|
|
16
|
+
enableAttestation: options.enableAttestation !== false,
|
|
17
|
+
algorithm: options.algorithm || 'sha256',
|
|
18
|
+
attestorId: options.attestorId || 'kgen-templates',
|
|
19
|
+
version: options.version || '2.0.0',
|
|
20
|
+
staticBuildTime: options.staticBuildTime || '2024-01-01T00:00:00.000Z',
|
|
21
|
+
deterministicMode: options.deterministicMode !== false,
|
|
22
|
+
includeMetadata: options.includeMetadata !== false,
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate attestation for rendered content
|
|
29
|
+
*/
|
|
30
|
+
async attest(content, context = {}) {
|
|
31
|
+
if (!this.options.enableAttestation) {
|
|
32
|
+
return {
|
|
33
|
+
attested: false,
|
|
34
|
+
reason: 'Attestation disabled'
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const attestation = await this.createAttestation(content, context);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
attested: true,
|
|
43
|
+
attestation,
|
|
44
|
+
metadata: {
|
|
45
|
+
algorithm: this.options.algorithm,
|
|
46
|
+
attestorId: this.options.attestorId,
|
|
47
|
+
version: this.options.version,
|
|
48
|
+
timestamp: this.getTimestamp()
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return {
|
|
53
|
+
attested: false,
|
|
54
|
+
error: error.message,
|
|
55
|
+
metadata: {
|
|
56
|
+
timestamp: this.getTimestamp()
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create complete attestation record
|
|
64
|
+
*/
|
|
65
|
+
async createAttestation(content, context = {}) {
|
|
66
|
+
const contentHash = this.createContentHash(content);
|
|
67
|
+
const contextHash = context.contextHash || this.createContentHash(JSON.stringify(context));
|
|
68
|
+
const templateHash = context.templateHash || '';
|
|
69
|
+
|
|
70
|
+
const attestationData = {
|
|
71
|
+
// Core hashes
|
|
72
|
+
contentHash,
|
|
73
|
+
contextHash,
|
|
74
|
+
templateHash,
|
|
75
|
+
|
|
76
|
+
// Attestation metadata
|
|
77
|
+
algorithm: this.options.algorithm,
|
|
78
|
+
attestorId: this.options.attestorId,
|
|
79
|
+
version: this.options.version,
|
|
80
|
+
timestamp: this.getTimestamp(),
|
|
81
|
+
deterministicMode: this.options.deterministicMode,
|
|
82
|
+
|
|
83
|
+
// Content metadata
|
|
84
|
+
contentLength: content ? content.length : 0,
|
|
85
|
+
contentType: 'text/plain',
|
|
86
|
+
|
|
87
|
+
// Reproducibility data
|
|
88
|
+
reproducible: this.options.deterministicMode,
|
|
89
|
+
staticBuildTime: this.options.staticBuildTime,
|
|
90
|
+
|
|
91
|
+
// Additional metadata if enabled
|
|
92
|
+
...(this.options.includeMetadata ? this.getAdditionalMetadata(content, context) : {})
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Create attestation signature
|
|
96
|
+
const attestationHash = this.createContentHash(JSON.stringify(attestationData));
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
...attestationData,
|
|
100
|
+
attestationHash,
|
|
101
|
+
signature: this.createSignature(attestationData)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Verify attestation integrity
|
|
107
|
+
*/
|
|
108
|
+
async verify(content, attestation) {
|
|
109
|
+
try {
|
|
110
|
+
// Verify content hash
|
|
111
|
+
const currentContentHash = this.createContentHash(content);
|
|
112
|
+
if (currentContentHash !== attestation.contentHash) {
|
|
113
|
+
return {
|
|
114
|
+
valid: false,
|
|
115
|
+
reason: 'Content hash mismatch',
|
|
116
|
+
expected: attestation.contentHash,
|
|
117
|
+
actual: currentContentHash
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Verify attestation hash
|
|
122
|
+
const { attestationHash, signature, ...attestationData } = attestation;
|
|
123
|
+
const currentAttestationHash = this.createContentHash(JSON.stringify(attestationData));
|
|
124
|
+
|
|
125
|
+
if (currentAttestationHash !== attestationHash) {
|
|
126
|
+
return {
|
|
127
|
+
valid: false,
|
|
128
|
+
reason: 'Attestation hash mismatch',
|
|
129
|
+
expected: attestationHash,
|
|
130
|
+
actual: currentAttestationHash
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Verify signature
|
|
135
|
+
const currentSignature = this.createSignature(attestationData);
|
|
136
|
+
if (currentSignature !== signature) {
|
|
137
|
+
return {
|
|
138
|
+
valid: false,
|
|
139
|
+
reason: 'Signature mismatch',
|
|
140
|
+
expected: signature,
|
|
141
|
+
actual: currentSignature
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
valid: true,
|
|
147
|
+
contentHash: currentContentHash,
|
|
148
|
+
attestationHash: currentAttestationHash,
|
|
149
|
+
verifiedAt: this.getTimestamp()
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
return {
|
|
153
|
+
valid: false,
|
|
154
|
+
reason: 'Verification error',
|
|
155
|
+
error: error.message
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Create reproducibility proof
|
|
162
|
+
*/
|
|
163
|
+
async createReproducibilityProof(content, context, iterations = 3) {
|
|
164
|
+
const proofs = [];
|
|
165
|
+
let isReproducible = true;
|
|
166
|
+
const baseHash = this.createContentHash(content);
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i < iterations; i++) {
|
|
169
|
+
const attestation = await this.createAttestation(content, context);
|
|
170
|
+
|
|
171
|
+
proofs.push({
|
|
172
|
+
iteration: i + 1,
|
|
173
|
+
contentHash: attestation.contentHash,
|
|
174
|
+
attestationHash: attestation.attestationHash,
|
|
175
|
+
timestamp: attestation.timestamp
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Check if content hash is consistent
|
|
179
|
+
if (attestation.contentHash !== baseHash) {
|
|
180
|
+
isReproducible = false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
reproducible: isReproducible,
|
|
186
|
+
baseHash,
|
|
187
|
+
iterations,
|
|
188
|
+
proofs,
|
|
189
|
+
algorithm: this.options.algorithm,
|
|
190
|
+
deterministicMode: this.options.deterministicMode,
|
|
191
|
+
generatedAt: this.getTimestamp()
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Create audit trail for template execution
|
|
197
|
+
*/
|
|
198
|
+
createAuditTrail(steps = []) {
|
|
199
|
+
return {
|
|
200
|
+
trailId: this.createContentHash(Date.now().toString()),
|
|
201
|
+
attestorId: this.options.attestorId,
|
|
202
|
+
version: this.options.version,
|
|
203
|
+
createdAt: this.getTimestamp(),
|
|
204
|
+
deterministicMode: this.options.deterministicMode,
|
|
205
|
+
steps: steps.map((step, index) => ({
|
|
206
|
+
stepId: index + 1,
|
|
207
|
+
...step,
|
|
208
|
+
hash: this.createContentHash(JSON.stringify(step)),
|
|
209
|
+
timestamp: step.timestamp || this.getTimestamp()
|
|
210
|
+
})),
|
|
211
|
+
trailHash: this.createContentHash(JSON.stringify(steps))
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Compare two attestations
|
|
217
|
+
*/
|
|
218
|
+
compareAttestations(attestation1, attestation2) {
|
|
219
|
+
const differences = [];
|
|
220
|
+
|
|
221
|
+
// Compare core hashes
|
|
222
|
+
if (attestation1.contentHash !== attestation2.contentHash) {
|
|
223
|
+
differences.push({
|
|
224
|
+
field: 'contentHash',
|
|
225
|
+
value1: attestation1.contentHash,
|
|
226
|
+
value2: attestation2.contentHash
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (attestation1.contextHash !== attestation2.contextHash) {
|
|
231
|
+
differences.push({
|
|
232
|
+
field: 'contextHash',
|
|
233
|
+
value1: attestation1.contextHash,
|
|
234
|
+
value2: attestation2.contextHash
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (attestation1.templateHash !== attestation2.templateHash) {
|
|
239
|
+
differences.push({
|
|
240
|
+
field: 'templateHash',
|
|
241
|
+
value1: attestation1.templateHash,
|
|
242
|
+
value2: attestation2.templateHash
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Compare metadata
|
|
247
|
+
const metadataFields = ['algorithm', 'version', 'deterministicMode', 'contentLength'];
|
|
248
|
+
metadataFields.forEach(field => {
|
|
249
|
+
if (attestation1[field] !== attestation2[field]) {
|
|
250
|
+
differences.push({
|
|
251
|
+
field,
|
|
252
|
+
value1: attestation1[field],
|
|
253
|
+
value2: attestation2[field]
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
identical: differences.length === 0,
|
|
260
|
+
differences,
|
|
261
|
+
comparedAt: this.getTimestamp()
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Create content hash using specified algorithm
|
|
267
|
+
*/
|
|
268
|
+
createContentHash(content) {
|
|
269
|
+
return crypto.createHash(this.options.algorithm)
|
|
270
|
+
.update(String(content || ''), 'utf8')
|
|
271
|
+
.digest('hex');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Create signature for attestation data
|
|
276
|
+
*/
|
|
277
|
+
createSignature(data) {
|
|
278
|
+
// Simple signature using hash of data + attestor ID
|
|
279
|
+
const signatureInput = JSON.stringify(data) + this.options.attestorId + this.options.version;
|
|
280
|
+
return crypto.createHash(this.options.algorithm)
|
|
281
|
+
.update(signatureInput, 'utf8')
|
|
282
|
+
.digest('hex');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get additional metadata if enabled
|
|
287
|
+
*/
|
|
288
|
+
getAdditionalMetadata(content, context) {
|
|
289
|
+
return {
|
|
290
|
+
systemInfo: {
|
|
291
|
+
platform: process.platform,
|
|
292
|
+
nodeVersion: process.version,
|
|
293
|
+
architecture: process.arch
|
|
294
|
+
},
|
|
295
|
+
environment: {
|
|
296
|
+
deterministicMode: this.options.deterministicMode,
|
|
297
|
+
staticBuildTime: this.options.staticBuildTime
|
|
298
|
+
},
|
|
299
|
+
contentAnalysis: {
|
|
300
|
+
lineCount: content ? content.split('\n').length : 0,
|
|
301
|
+
charCount: content ? content.length : 0,
|
|
302
|
+
hasUnicodeChars: content ? /[^\x00-\x7F]/.test(content) : false
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get deterministic or real timestamp
|
|
309
|
+
*/
|
|
310
|
+
getTimestamp() {
|
|
311
|
+
return this.options.deterministicMode ?
|
|
312
|
+
this.options.staticBuildTime :
|
|
313
|
+
new Date().toISOString();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Export attestation in standard format
|
|
318
|
+
*/
|
|
319
|
+
exportAttestation(attestation, format = 'json') {
|
|
320
|
+
switch (format.toLowerCase()) {
|
|
321
|
+
case 'json':
|
|
322
|
+
return JSON.stringify(attestation, null, 2);
|
|
323
|
+
|
|
324
|
+
case 'compact':
|
|
325
|
+
return JSON.stringify(attestation);
|
|
326
|
+
|
|
327
|
+
case 'yaml':
|
|
328
|
+
// Basic YAML export (simplified)
|
|
329
|
+
return Object.entries(attestation)
|
|
330
|
+
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
331
|
+
.join('\n');
|
|
332
|
+
|
|
333
|
+
case 'base64':
|
|
334
|
+
return Buffer.from(JSON.stringify(attestation)).toString('base64');
|
|
335
|
+
|
|
336
|
+
default:
|
|
337
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Import attestation from standard format
|
|
343
|
+
*/
|
|
344
|
+
importAttestation(data, format = 'json') {
|
|
345
|
+
try {
|
|
346
|
+
switch (format.toLowerCase()) {
|
|
347
|
+
case 'json':
|
|
348
|
+
case 'compact':
|
|
349
|
+
return JSON.parse(data);
|
|
350
|
+
|
|
351
|
+
case 'base64':
|
|
352
|
+
const decoded = Buffer.from(data, 'base64').toString('utf8');
|
|
353
|
+
return JSON.parse(decoded);
|
|
354
|
+
|
|
355
|
+
default:
|
|
356
|
+
throw new Error(`Unsupported import format: ${format}`);
|
|
357
|
+
}
|
|
358
|
+
} catch (error) {
|
|
359
|
+
throw new Error(`Failed to import attestation: ${error.message}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get attestor statistics
|
|
365
|
+
*/
|
|
366
|
+
getStats() {
|
|
367
|
+
return {
|
|
368
|
+
...this.options,
|
|
369
|
+
supportedAlgorithms: ['sha256', 'sha512', 'sha1'],
|
|
370
|
+
supportedFormats: ['json', 'compact', 'yaml', 'base64'],
|
|
371
|
+
features: [
|
|
372
|
+
'content-attestation',
|
|
373
|
+
'reproducibility-proof',
|
|
374
|
+
'audit-trail',
|
|
375
|
+
'signature-verification'
|
|
376
|
+
]
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export default KGenAttestor;
|