@aikdna/kdna-core 0.3.0 → 0.5.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 +96 -2
- package/package.json +6 -4
- package/schema/KDNA_Core.schema.json +4 -1
- package/schema/KDNA_Core.strict.schema.json +290 -0
- package/schema/KDNA_Patterns.schema.json +12 -6
- package/schema/KDNA_Patterns.strict.schema.json +342 -0
- package/schema/KDNA_Scenarios.schema.json +2 -1
- package/schema/KDNA_Scenarios.strict.schema.json +101 -0
- package/src/asset-reader.js +585 -0
- package/src/compose.js +2 -2
- package/src/crypto-profile.js +106 -0
- package/src/index.js +4 -0
- package/src/index.mjs +11 -0
- package/src/lint-pure.js +159 -4
- package/src/loader.js +30 -20
- package/src/types.d.ts +175 -3
- package/src/validate-pure.js +1 -7
package/src/index.js
CHANGED
|
@@ -6,6 +6,8 @@ const lint = require('./lint-pure');
|
|
|
6
6
|
const validate = require('./validate-pure');
|
|
7
7
|
const render = require('./render');
|
|
8
8
|
const compose = require('./compose');
|
|
9
|
+
const assetReader = require('./asset-reader');
|
|
10
|
+
const cryptoProfile = require('./crypto-profile');
|
|
9
11
|
|
|
10
12
|
module.exports = {
|
|
11
13
|
...loader,
|
|
@@ -13,4 +15,6 @@ module.exports = {
|
|
|
13
15
|
...validate,
|
|
14
16
|
...render,
|
|
15
17
|
...compose,
|
|
18
|
+
...assetReader,
|
|
19
|
+
...cryptoProfile,
|
|
16
20
|
};
|
package/src/index.mjs
CHANGED
|
@@ -17,3 +17,14 @@ export { validateDomainSchema, validateCrossFile } from './validate-pure.js';
|
|
|
17
17
|
export { renderPreviewHTML, escHtml, renderCard } from './render.js';
|
|
18
18
|
|
|
19
19
|
export { composeContext, composeContextWithAttribution, classifySignals, classifySignalsAcrossDomains, composeChecks, loadAndCompose, loadCluster, detectDomainConflicts, generateClusterTrace } from './compose.js';
|
|
20
|
+
|
|
21
|
+
import assetReader from './asset-reader.js';
|
|
22
|
+
import cryptoProfile from './crypto-profile.js';
|
|
23
|
+
|
|
24
|
+
export const STANDARD_ENTRIES = assetReader.STANDARD_ENTRIES;
|
|
25
|
+
export const createKdnaAssetReader = assetReader.createKdnaAssetReader;
|
|
26
|
+
export const LICENSED_ENTRY_PROFILE = cryptoProfile.LICENSED_ENTRY_PROFILE;
|
|
27
|
+
export const deriveLicensedEntryKey = cryptoProfile.deriveLicensedEntryKey;
|
|
28
|
+
export const encryptLicensedEntry = cryptoProfile.encryptLicensedEntry;
|
|
29
|
+
export const decryptLicensedEntry = cryptoProfile.decryptLicensedEntry;
|
|
30
|
+
export const createLicensedDecryptEntry = cryptoProfile.createLicensedDecryptEntry;
|
package/src/lint-pure.js
CHANGED
|
@@ -29,6 +29,15 @@ const OLD_FIELD_HINTS = {
|
|
|
29
29
|
judgment: 'via',
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
const KDNA_DOMAIN_FILES = new Set([
|
|
33
|
+
'KDNA_Core.json',
|
|
34
|
+
'KDNA_Patterns.json',
|
|
35
|
+
'KDNA_Scenarios.json',
|
|
36
|
+
'KDNA_Cases.json',
|
|
37
|
+
'KDNA_Reasoning.json',
|
|
38
|
+
'KDNA_Evolution.json',
|
|
39
|
+
]);
|
|
40
|
+
|
|
32
41
|
/**
|
|
33
42
|
* Lint a KDNA domain from a map of parsed JSON objects.
|
|
34
43
|
*
|
|
@@ -104,9 +113,7 @@ function lintDomain(dataMap) {
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
// Check file count
|
|
107
|
-
const kdnaFiles = Object.keys(dataMap).filter(
|
|
108
|
-
(f) => f.endsWith('.json') && f !== 'kdna.json',
|
|
109
|
-
);
|
|
116
|
+
const kdnaFiles = Object.keys(dataMap).filter((f) => KDNA_DOMAIN_FILES.has(f));
|
|
110
117
|
if (kdnaFiles.length > 6) errors.push(`Domain has ${kdnaFiles.length} JSON files; KDNA allows at most 6.`);
|
|
111
118
|
|
|
112
119
|
// Validate meta on all files
|
|
@@ -245,4 +252,152 @@ function lintDomain(dataMap) {
|
|
|
245
252
|
return { errors, warnings };
|
|
246
253
|
}
|
|
247
254
|
|
|
248
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Canonical enum tables for manifest validation.
|
|
257
|
+
* Single source of truth — keep in sync with schema/kdna-manifest-v1rc.json and specs/enum-tables.md.
|
|
258
|
+
*/
|
|
259
|
+
const VALID_STATUS = new Set(['draft', 'experimental', 'stable', 'deprecated', 'staging']);
|
|
260
|
+
const VALID_BADGE = new Set(['untested', 'tested', 'validated', 'expert_reviewed', 'production_ready']);
|
|
261
|
+
const VALID_ACCESS = new Set(['open', 'licensed', 'runtime']);
|
|
262
|
+
const VALID_RISK = new Set(['R0', 'R1', 'R2', 'R3']);
|
|
263
|
+
const VALID_I18N = new Set(['L0', 'L1', 'L2', 'L3']);
|
|
264
|
+
|
|
265
|
+
const MANIFEST_REQUIRED = [
|
|
266
|
+
'kdna_spec', 'name', 'version', 'judgment_version',
|
|
267
|
+
'description', 'author', 'license', 'status',
|
|
268
|
+
'quality_badge', 'access', 'language',
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Validate a kdna.json manifest against the canonical v1.0-rc schema.
|
|
273
|
+
*
|
|
274
|
+
* @param {Object} manifest — parsed kdna.json
|
|
275
|
+
* @returns {{ errors: string[], warnings: string[] }}
|
|
276
|
+
*/
|
|
277
|
+
function validateManifest(manifest) {
|
|
278
|
+
const errors = [];
|
|
279
|
+
const warnings = [];
|
|
280
|
+
|
|
281
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
282
|
+
errors.push('kdna.json: missing or empty manifest');
|
|
283
|
+
return { errors, warnings };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 1. Check spec_version is NOT in domain manifest (use kdna_spec only)
|
|
287
|
+
if ('spec_version' in manifest) {
|
|
288
|
+
errors.push(
|
|
289
|
+
'kdna.json: spec_version is deprecated in domain manifests. Use kdna_spec. ' +
|
|
290
|
+
'(spec_version is reserved for .kdna container manifests only.)',
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 2. Check required fields
|
|
295
|
+
for (const field of MANIFEST_REQUIRED) {
|
|
296
|
+
if (!(field in manifest) || manifest[field] === undefined || manifest[field] === '') {
|
|
297
|
+
errors.push(`kdna.json: missing required field "${field}"`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 3. Validate name format
|
|
302
|
+
if (manifest.name && !/^@[a-z][a-z0-9-]*\/[a-z][a-z0-9_]*$/.test(manifest.name)) {
|
|
303
|
+
errors.push(`kdna.json.name: invalid format "${manifest.name}". Expected @scope/name.`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 4. Validate enum fields
|
|
307
|
+
if (manifest.status && !VALID_STATUS.has(manifest.status)) {
|
|
308
|
+
errors.push(
|
|
309
|
+
`kdna.json.status: invalid value "${manifest.status}". ` +
|
|
310
|
+
`Valid: ${[...VALID_STATUS].join(', ')}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (manifest.quality_badge && !VALID_BADGE.has(manifest.quality_badge)) {
|
|
314
|
+
errors.push(
|
|
315
|
+
`kdna.json.quality_badge: invalid value "${manifest.quality_badge}". ` +
|
|
316
|
+
`Valid: ${[...VALID_BADGE].join(', ')}`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
if (manifest.access && !VALID_ACCESS.has(manifest.access)) {
|
|
320
|
+
errors.push(
|
|
321
|
+
`kdna.json.access: invalid value "${manifest.access}". ` +
|
|
322
|
+
`Valid: ${[...VALID_ACCESS].join(', ')}`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
if (manifest.risk_level && !VALID_RISK.has(manifest.risk_level)) {
|
|
326
|
+
errors.push(
|
|
327
|
+
`kdna.json.risk_level: invalid value "${manifest.risk_level}". ` +
|
|
328
|
+
`Valid: ${[...VALID_RISK].join(', ')}`,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
if (manifest.i18n_level && !VALID_I18N.has(manifest.i18n_level)) {
|
|
332
|
+
warnings.push(
|
|
333
|
+
`kdna.json.i18n_level: non-standard value "${manifest.i18n_level}". ` +
|
|
334
|
+
`Valid: ${[...VALID_I18N].join(', ')}`,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 5. Deprecated status must have replaced_by
|
|
339
|
+
if (manifest.status === 'deprecated' && !manifest.replaced_by) {
|
|
340
|
+
errors.push('kdna.json: status is "deprecated" but replaced_by is missing');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 6. Tested+ badge must have signature
|
|
344
|
+
const needsSig = ['tested', 'validated', 'expert_reviewed', 'production_ready'];
|
|
345
|
+
if (needsSig.includes(manifest.quality_badge) && !manifest.signature) {
|
|
346
|
+
warnings.push(
|
|
347
|
+
`kdna.json: quality_badge "${manifest.quality_badge}" should have a signature`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 7. Validate author
|
|
352
|
+
if (manifest.author) {
|
|
353
|
+
if (!manifest.author.name) errors.push('kdna.json.author: missing "name"');
|
|
354
|
+
if (!manifest.author.id) errors.push('kdna.json.author: missing "id"');
|
|
355
|
+
if (manifest.author.pubkey && !/^ed25519:[0-9a-f]{64}$/.test(manifest.author.pubkey)) {
|
|
356
|
+
warnings.push('kdna.json.author.pubkey: non-standard format. Expected ed25519:<64 hex chars>.');
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 8. Validate license
|
|
361
|
+
if (manifest.license && !manifest.license.type) {
|
|
362
|
+
errors.push('kdna.json.license: missing "type"');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 9. Validate kdna_spec value
|
|
366
|
+
if (manifest.kdna_spec && manifest.kdna_spec !== '1.0-rc') {
|
|
367
|
+
warnings.push(
|
|
368
|
+
`kdna.json.kdna_spec: non-standard value "${manifest.kdna_spec}". Expected "1.0-rc".`,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 10. Validate version format
|
|
373
|
+
if (manifest.version && !/^\d+\.\d+\.\d+/.test(manifest.version)) {
|
|
374
|
+
warnings.push(
|
|
375
|
+
`kdna.json.version: non-semver format "${manifest.version}". Expected MAJOR.MINOR.PATCH.`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 11. Check for removed fields
|
|
380
|
+
const removedFields = ['release_status', 'domain_field', 'judgment_patterns', 'files', 'registry'];
|
|
381
|
+
for (const field of removedFields) {
|
|
382
|
+
if (field in manifest) {
|
|
383
|
+
warnings.push(
|
|
384
|
+
`kdna.json: field "${field}" is not in the canonical domain manifest and should be removed`,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 12. Check removed license sub-fields
|
|
390
|
+
if (manifest.license && typeof manifest.license === 'object') {
|
|
391
|
+
for (const field of ['commercial', 'allow_agent_use', 'allow_redistribution', 'allow_training']) {
|
|
392
|
+
if (field in manifest.license) {
|
|
393
|
+
warnings.push(
|
|
394
|
+
`kdna.json.license.${field}: license-type-specific field, not universal. Consider removing.`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return { errors, warnings };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
module.exports = { lintDomain, validateManifest };
|
package/src/loader.js
CHANGED
|
@@ -174,6 +174,16 @@ function detectOldFieldNames(obj, path = '', warnings = []) {
|
|
|
174
174
|
* @param {object} domain — result from loadDomainFromData() or loadDomainFromFiles()
|
|
175
175
|
* @returns {string}
|
|
176
176
|
*/
|
|
177
|
+
function sanitize(str) {
|
|
178
|
+
if (typeof str !== 'string') return str;
|
|
179
|
+
return str
|
|
180
|
+
.replace(/^#{1,6}\s/gm, '\\# ') // Escape leading # to prevent fake headers
|
|
181
|
+
.replace(/```/g, '\\`\\`\\`') // Escape code blocks
|
|
182
|
+
.replace(/<\|/g, '<|') // Escape special tokens
|
|
183
|
+
.replace(/\b(ignore|forget|disregard)\s+(all\s+)?(previous|prior|above)\s+(instructions?|directives?|rules?|constraints?)\b/gi,
|
|
184
|
+
'[filtered: $&]'); // Filter prompt injection patterns
|
|
185
|
+
}
|
|
186
|
+
|
|
177
187
|
function formatContext(domain) {
|
|
178
188
|
if (!domain || !domain.core || !domain.patterns) return '';
|
|
179
189
|
|
|
@@ -193,13 +203,13 @@ function formatContext(domain) {
|
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
parts.push('## Domain Cognition (KDNA)');
|
|
196
|
-
parts.push(`Domain: ${core.meta.domain}`);
|
|
206
|
+
parts.push(`Domain: ${sanitize(core.meta.domain)}`);
|
|
197
207
|
parts.push('');
|
|
198
208
|
|
|
199
209
|
if (core.stances && core.stances.length) {
|
|
200
210
|
parts.push('### Stances');
|
|
201
211
|
for (const s of core.stances) {
|
|
202
|
-
parts.push(`- ${s}`);
|
|
212
|
+
parts.push(`- ${sanitize(s)}`);
|
|
203
213
|
}
|
|
204
214
|
parts.push('');
|
|
205
215
|
}
|
|
@@ -207,8 +217,8 @@ function formatContext(domain) {
|
|
|
207
217
|
if (core.axioms && core.axioms.length) {
|
|
208
218
|
parts.push('### Axioms');
|
|
209
219
|
for (const a of core.axioms) {
|
|
210
|
-
parts.push(`- **${a.one_sentence}** ${a.full_statement}`);
|
|
211
|
-
parts.push(` *Why:* ${a.why}`);
|
|
220
|
+
parts.push(`- **${sanitize(a.one_sentence)}** ${sanitize(a.full_statement)}`);
|
|
221
|
+
parts.push(` *Why:* ${sanitize(a.why)}`);
|
|
212
222
|
}
|
|
213
223
|
parts.push('');
|
|
214
224
|
}
|
|
@@ -216,8 +226,8 @@ function formatContext(domain) {
|
|
|
216
226
|
if (core.ontology && core.ontology.length) {
|
|
217
227
|
parts.push('### Key Concepts');
|
|
218
228
|
for (const c of core.ontology) {
|
|
219
|
-
parts.push(`- **${c.id.replace(/_/g, ' ')}** — ${c.one_sentence}`);
|
|
220
|
-
parts.push(` Boundary: ${c.boundary}`);
|
|
229
|
+
parts.push(`- **${sanitize(c.id.replace(/_/g, ' '))}** — ${sanitize(c.one_sentence)}`);
|
|
230
|
+
parts.push(` Boundary: ${sanitize(c.boundary)}`);
|
|
221
231
|
}
|
|
222
232
|
parts.push('');
|
|
223
233
|
}
|
|
@@ -225,7 +235,7 @@ function formatContext(domain) {
|
|
|
225
235
|
if (core.frameworks && core.frameworks.length) {
|
|
226
236
|
parts.push('### Frameworks');
|
|
227
237
|
for (const fw of core.frameworks) {
|
|
228
|
-
parts.push(`- **${fw.name}**: ${fw.when_to_use}`);
|
|
238
|
+
parts.push(`- **${sanitize(fw.name)}**: ${sanitize(fw.when_to_use)}`);
|
|
229
239
|
}
|
|
230
240
|
parts.push('');
|
|
231
241
|
}
|
|
@@ -233,7 +243,7 @@ function formatContext(domain) {
|
|
|
233
243
|
if (pat.terminology && pat.terminology.banned_terms && pat.terminology.banned_terms.length) {
|
|
234
244
|
parts.push('### Avoid These Terms');
|
|
235
245
|
for (const b of pat.terminology.banned_terms) {
|
|
236
|
-
parts.push(`- Avoid "${b.term}". ${b.why} Use "${b.replace_with}" instead.`);
|
|
246
|
+
parts.push(`- Avoid "${sanitize(b.term)}". ${sanitize(b.why)} Use "${sanitize(b.replace_with)}" instead.`);
|
|
237
247
|
}
|
|
238
248
|
parts.push('');
|
|
239
249
|
}
|
|
@@ -241,8 +251,8 @@ function formatContext(domain) {
|
|
|
241
251
|
if (pat.misunderstandings && pat.misunderstandings.length) {
|
|
242
252
|
parts.push('### Watch For These Misunderstandings');
|
|
243
253
|
for (const m of pat.misunderstandings) {
|
|
244
|
-
parts.push(`- **Wrong:** ${m.wrong}`);
|
|
245
|
-
parts.push(` **Correct:** ${m.correct}`);
|
|
254
|
+
parts.push(`- **Wrong:** ${sanitize(m.wrong)}`);
|
|
255
|
+
parts.push(` **Correct:** ${sanitize(m.correct)}`);
|
|
246
256
|
}
|
|
247
257
|
parts.push('');
|
|
248
258
|
}
|
|
@@ -250,7 +260,7 @@ function formatContext(domain) {
|
|
|
250
260
|
if (pat.self_check && pat.self_check.length) {
|
|
251
261
|
parts.push('### Before Responding, Check');
|
|
252
262
|
for (const s of pat.self_check) {
|
|
253
|
-
parts.push(`- [ ] ${s}`);
|
|
263
|
+
parts.push(`- [ ] ${sanitize(s)}`);
|
|
254
264
|
}
|
|
255
265
|
parts.push('');
|
|
256
266
|
}
|
|
@@ -258,7 +268,7 @@ function formatContext(domain) {
|
|
|
258
268
|
if (domain.scenarios && domain.scenarios.scenes) {
|
|
259
269
|
parts.push('### Relevant Scenarios');
|
|
260
270
|
for (const scene of domain.scenarios.scenes) {
|
|
261
|
-
parts.push(`- **${scene.name}**: ${scene.trigger_signal}`);
|
|
271
|
+
parts.push(`- **${sanitize(scene.name)}**: ${sanitize(scene.trigger_signal)}`);
|
|
262
272
|
}
|
|
263
273
|
parts.push('');
|
|
264
274
|
}
|
|
@@ -266,7 +276,7 @@ function formatContext(domain) {
|
|
|
266
276
|
if (domain.reasoning && domain.reasoning.reasoning_chains) {
|
|
267
277
|
parts.push('### Reasoning Chains');
|
|
268
278
|
for (const r of domain.reasoning.reasoning_chains) {
|
|
269
|
-
parts.push(`- **${r.one_sentence}** → ${r.so_what}`);
|
|
279
|
+
parts.push(`- **${sanitize(r.one_sentence)}** → ${sanitize(r.so_what)}`);
|
|
270
280
|
}
|
|
271
281
|
parts.push('');
|
|
272
282
|
}
|
|
@@ -274,11 +284,11 @@ function formatContext(domain) {
|
|
|
274
284
|
if (domain.cases && domain.cases.cases && domain.cases.cases.length) {
|
|
275
285
|
parts.push('### Cases');
|
|
276
286
|
for (const c of domain.cases.cases) {
|
|
277
|
-
parts.push(`- **${c.title}**`);
|
|
278
|
-
parts.push(` Context: ${c.context}`);
|
|
279
|
-
parts.push(` What happened: ${c.what_happened}`);
|
|
280
|
-
parts.push(` Learned: ${c.what_was_learned}`);
|
|
281
|
-
parts.push(` Pattern: ${c.structural_pattern}`);
|
|
287
|
+
parts.push(`- **${sanitize(c.title)}**`);
|
|
288
|
+
parts.push(` Context: ${sanitize(c.context)}`);
|
|
289
|
+
parts.push(` What happened: ${sanitize(c.what_happened)}`);
|
|
290
|
+
parts.push(` Learned: ${sanitize(c.what_was_learned)}`);
|
|
291
|
+
parts.push(` Pattern: ${sanitize(c.structural_pattern)}`);
|
|
282
292
|
}
|
|
283
293
|
parts.push('');
|
|
284
294
|
}
|
|
@@ -288,7 +298,7 @@ function formatContext(domain) {
|
|
|
288
298
|
if (evo.stages && evo.stages.length) {
|
|
289
299
|
parts.push('### Growth Stages');
|
|
290
300
|
for (const stage of evo.stages) {
|
|
291
|
-
parts.push(`- **${stage.name}**: ${stage.description}`);
|
|
301
|
+
parts.push(`- **${sanitize(stage.name)}**: ${sanitize(stage.description)}`);
|
|
292
302
|
}
|
|
293
303
|
parts.push('');
|
|
294
304
|
}
|
|
@@ -296,7 +306,7 @@ function formatContext(domain) {
|
|
|
296
306
|
parts.push('### Capability Layers');
|
|
297
307
|
for (const layer of evo.evolution_layers) {
|
|
298
308
|
parts.push(
|
|
299
|
-
`- **${layer.name}**: ${layer.capability} (${layer.from_stage} → ${layer.to_stage})`,
|
|
309
|
+
`- **${sanitize(layer.name)}**: ${sanitize(layer.capability)} (${sanitize(layer.from_stage)} → ${sanitize(layer.to_stage)})`,
|
|
300
310
|
);
|
|
301
311
|
}
|
|
302
312
|
parts.push('');
|
package/src/types.d.ts
CHANGED
|
@@ -206,12 +206,23 @@ export interface KDNAManifest {
|
|
|
206
206
|
kdna_spec: string;
|
|
207
207
|
name: string;
|
|
208
208
|
version: string;
|
|
209
|
-
|
|
209
|
+
judgment_version?: string;
|
|
210
|
+
status: 'draft' | 'experimental' | 'stable' | 'deprecated' | 'basic' | 'pro';
|
|
210
211
|
access: 'open' | 'licensed' | 'runtime';
|
|
211
|
-
language
|
|
212
|
-
|
|
212
|
+
language?: string;
|
|
213
|
+
default_language?: string;
|
|
214
|
+
languages?: string[];
|
|
215
|
+
author: { name: string; id?: string; pubkey?: string; public_key_pem?: string };
|
|
213
216
|
license: { type: string; url?: string };
|
|
214
217
|
description: string;
|
|
218
|
+
keywords?: string[];
|
|
219
|
+
encryption?: {
|
|
220
|
+
profile?: string;
|
|
221
|
+
encrypted_entries?: string[];
|
|
222
|
+
[key: string]: any;
|
|
223
|
+
};
|
|
224
|
+
content_digest?: string;
|
|
225
|
+
signature?: string;
|
|
215
226
|
}
|
|
216
227
|
|
|
217
228
|
export interface LintResult {
|
|
@@ -241,6 +252,167 @@ export function formatContext(domain: LoadedDomain): string;
|
|
|
241
252
|
|
|
242
253
|
export const FILE_MAP: Record<string, string>;
|
|
243
254
|
|
|
255
|
+
// Asset reader — direct .kdna API
|
|
256
|
+
export const STANDARD_ENTRIES: string[];
|
|
257
|
+
|
|
258
|
+
export interface KdnaAsset {
|
|
259
|
+
path: string | null;
|
|
260
|
+
size: number;
|
|
261
|
+
asset_digest: string;
|
|
262
|
+
entries: Map<string, unknown>;
|
|
263
|
+
readEntry(name: string): Uint8Array;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface KdnaAssetVerifyResult {
|
|
267
|
+
ok: boolean;
|
|
268
|
+
errors: string[];
|
|
269
|
+
warnings: string[];
|
|
270
|
+
entries: string[];
|
|
271
|
+
manifest: KDNAManifest | null;
|
|
272
|
+
asset_digest: string;
|
|
273
|
+
content_digest: string;
|
|
274
|
+
signature_valid: boolean | null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface KdnaAssetIndexProfile {
|
|
278
|
+
profile: 'index';
|
|
279
|
+
manifest: KDNAManifest;
|
|
280
|
+
asset_digest: string;
|
|
281
|
+
content_digest: string;
|
|
282
|
+
entries: string[];
|
|
283
|
+
name: string | null;
|
|
284
|
+
version: string | null;
|
|
285
|
+
judgment_version: string | null;
|
|
286
|
+
keywords: string[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface KdnaAssetLoadProfile {
|
|
290
|
+
profile: string;
|
|
291
|
+
manifest: KDNAManifest;
|
|
292
|
+
domain: LoadedDomain | null;
|
|
293
|
+
context: string | null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface KdnaAssetReader {
|
|
297
|
+
openSync(input: string | Uint8Array): KdnaAsset;
|
|
298
|
+
open(input: string | Uint8Array): Promise<KdnaAsset>;
|
|
299
|
+
listEntriesSync(asset: KdnaAsset): string[];
|
|
300
|
+
listEntries(asset: KdnaAsset): Promise<string[]>;
|
|
301
|
+
readEntrySync(asset: KdnaAsset, entryName: string): Uint8Array;
|
|
302
|
+
readEntrySync(asset: KdnaAsset, entryName: string, encoding: string): string;
|
|
303
|
+
readEntry(asset: KdnaAsset, entryName: string): Promise<Uint8Array>;
|
|
304
|
+
readEntry(asset: KdnaAsset, entryName: string, encoding: string): Promise<string>;
|
|
305
|
+
readJsonSync(asset: KdnaAsset, entryName: string, options?: KdnaDecryptOptions): any;
|
|
306
|
+
readJson(asset: KdnaAsset, entryName: string, options?: KdnaDecryptOptions): Promise<any>;
|
|
307
|
+
readManifestSync(asset: KdnaAsset): KDNAManifest;
|
|
308
|
+
readManifest(asset: KdnaAsset): Promise<KDNAManifest>;
|
|
309
|
+
readDataMapSync(
|
|
310
|
+
asset: KdnaAsset,
|
|
311
|
+
entries?: string[],
|
|
312
|
+
options?: KdnaDecryptOptions,
|
|
313
|
+
): KDNAFileDataMap;
|
|
314
|
+
readDataMap(
|
|
315
|
+
asset: KdnaAsset,
|
|
316
|
+
entries?: string[],
|
|
317
|
+
options?: KdnaDecryptOptions,
|
|
318
|
+
): Promise<KDNAFileDataMap>;
|
|
319
|
+
contentDigestSync(asset: KdnaAsset): string;
|
|
320
|
+
contentDigest(asset: KdnaAsset): Promise<string>;
|
|
321
|
+
verifySync(
|
|
322
|
+
asset: KdnaAsset,
|
|
323
|
+
options?: {
|
|
324
|
+
asset_digest?: string;
|
|
325
|
+
content_digest?: string;
|
|
326
|
+
requireSignature?: boolean;
|
|
327
|
+
requireDecryption?: boolean;
|
|
328
|
+
} & KdnaDecryptOptions,
|
|
329
|
+
): KdnaAssetVerifyResult;
|
|
330
|
+
verify(
|
|
331
|
+
asset: KdnaAsset,
|
|
332
|
+
options?: {
|
|
333
|
+
asset_digest?: string;
|
|
334
|
+
content_digest?: string;
|
|
335
|
+
requireSignature?: boolean;
|
|
336
|
+
requireDecryption?: boolean;
|
|
337
|
+
} & KdnaDecryptOptions,
|
|
338
|
+
): Promise<KdnaAssetVerifyResult>;
|
|
339
|
+
loadProfileSync(
|
|
340
|
+
asset: KdnaAsset,
|
|
341
|
+
profile: 'index',
|
|
342
|
+
options?: { input?: string; context?: boolean } & KdnaDecryptOptions,
|
|
343
|
+
): KdnaAssetIndexProfile;
|
|
344
|
+
loadProfileSync(
|
|
345
|
+
asset: KdnaAsset,
|
|
346
|
+
profile?: 'compact' | 'scenario' | 'full' | string,
|
|
347
|
+
options?: { input?: string; context?: boolean } & KdnaDecryptOptions,
|
|
348
|
+
): KdnaAssetLoadProfile;
|
|
349
|
+
loadProfile(
|
|
350
|
+
asset: KdnaAsset,
|
|
351
|
+
profile: 'index',
|
|
352
|
+
options?: { input?: string; context?: boolean } & KdnaDecryptOptions,
|
|
353
|
+
): Promise<KdnaAssetIndexProfile>;
|
|
354
|
+
loadProfile(
|
|
355
|
+
asset: KdnaAsset,
|
|
356
|
+
profile?: 'compact' | 'scenario' | 'full' | string,
|
|
357
|
+
options?: { input?: string; context?: boolean } & KdnaDecryptOptions,
|
|
358
|
+
): Promise<KdnaAssetLoadProfile>;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export interface KdnaDecryptOptions {
|
|
362
|
+
decryptEntry?: (args: {
|
|
363
|
+
asset: KdnaAsset;
|
|
364
|
+
manifest: KDNAManifest;
|
|
365
|
+
entryName: string;
|
|
366
|
+
ciphertext: Uint8Array;
|
|
367
|
+
}) => string | Uint8Array | Promise<string | Uint8Array>;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function createKdnaAssetReader(): KdnaAssetReader;
|
|
371
|
+
|
|
372
|
+
export const LICENSED_ENTRY_PROFILE: string;
|
|
373
|
+
|
|
374
|
+
export interface LicensedEntryEnvelope {
|
|
375
|
+
profile: string;
|
|
376
|
+
alg: 'AES-256-GCM';
|
|
377
|
+
kdf: 'scrypt-sha256';
|
|
378
|
+
salt: string;
|
|
379
|
+
iv: string;
|
|
380
|
+
tag: string;
|
|
381
|
+
ciphertext: string;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function deriveLicensedEntryKey(options: {
|
|
385
|
+
licenseKey: string;
|
|
386
|
+
machineFingerprint: string;
|
|
387
|
+
salt: string | Uint8Array;
|
|
388
|
+
keyLength?: number;
|
|
389
|
+
}): Uint8Array;
|
|
390
|
+
|
|
391
|
+
export function encryptLicensedEntry(
|
|
392
|
+
plaintext: string | Uint8Array,
|
|
393
|
+
options: {
|
|
394
|
+
entryName: string;
|
|
395
|
+
manifest?: KDNAManifest;
|
|
396
|
+
licenseKey: string;
|
|
397
|
+
machineFingerprint: string;
|
|
398
|
+
},
|
|
399
|
+
): LicensedEntryEnvelope;
|
|
400
|
+
|
|
401
|
+
export function decryptLicensedEntry(
|
|
402
|
+
envelope: string | Uint8Array | LicensedEntryEnvelope,
|
|
403
|
+
options: {
|
|
404
|
+
entryName: string;
|
|
405
|
+
manifest?: KDNAManifest;
|
|
406
|
+
licenseKey: string;
|
|
407
|
+
machineFingerprint: string;
|
|
408
|
+
},
|
|
409
|
+
): Uint8Array;
|
|
410
|
+
|
|
411
|
+
export function createLicensedDecryptEntry(options: {
|
|
412
|
+
licenseKey: string;
|
|
413
|
+
machineFingerprint: string;
|
|
414
|
+
}): NonNullable<KdnaDecryptOptions['decryptEntry']>;
|
|
415
|
+
|
|
244
416
|
// Lint
|
|
245
417
|
export function lintDomain(dataMap: KDNAFileDataMap): LintResult;
|
|
246
418
|
|
package/src/validate-pure.js
CHANGED
|
@@ -45,9 +45,6 @@ function validateDomainSchema(dataMap, schemaMap) {
|
|
|
45
45
|
return { valid: true, errors: [], warnings };
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
let validCount = 0;
|
|
49
|
-
let failCount = 0;
|
|
50
|
-
|
|
51
48
|
for (const [file, schemaFile] of Object.entries(FILE_TO_SCHEMA)) {
|
|
52
49
|
if (!dataMap[file]) continue;
|
|
53
50
|
if (!schemaMap[schemaFile]) {
|
|
@@ -69,10 +66,7 @@ function validateDomainSchema(dataMap, schemaMap) {
|
|
|
69
66
|
const validate = ajvInstance.compile(schema);
|
|
70
67
|
const valid = validate(dataMap[file]);
|
|
71
68
|
|
|
72
|
-
if (valid) {
|
|
73
|
-
validCount++;
|
|
74
|
-
} else {
|
|
75
|
-
failCount++;
|
|
69
|
+
if (!valid) {
|
|
76
70
|
for (const err of validate.errors || []) {
|
|
77
71
|
const instancePath = err.instancePath || '/';
|
|
78
72
|
errors.push(`${file}${instancePath}: ${err.message} (${err.keyword})`);
|