@primmel/primmel 1.20.0 → 1.21.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/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/src/check-rules.d.ts +1 -1
- package/dist/src/check-rules.d.ts.map +1 -1
- package/dist/src/check-rules.js +36 -15
- package/dist/src/check-rules.js.map +1 -1
- package/dist/src/check.d.ts +4 -0
- package/dist/src/check.d.ts.map +1 -1
- package/dist/src/check.js +428 -137
- package/dist/src/check.js.map +1 -1
- package/dist/src/export/cddal.d.ts +25 -0
- package/dist/src/export/cddal.d.ts.map +1 -0
- package/dist/src/export/cddal.js +213 -0
- package/dist/src/export/cddal.js.map +1 -0
- package/dist/src/export/retrieval.d.ts +147 -6
- package/dist/src/export/retrieval.d.ts.map +1 -1
- package/dist/src/export/retrieval.js +254 -25
- package/dist/src/export/retrieval.js.map +1 -1
- package/dist/src/export/unitsdb.d.ts +19 -0
- package/dist/src/export/unitsdb.d.ts.map +1 -0
- package/dist/src/export/unitsdb.js +82 -0
- package/dist/src/export/unitsdb.js.map +1 -0
- package/dist/src/export/yaml-lite.d.ts +13 -0
- package/dist/src/export/yaml-lite.d.ts.map +1 -0
- package/dist/src/export/yaml-lite.js +219 -0
- package/dist/src/export/yaml-lite.js.map +1 -0
- package/dist/src/instance-resolution.d.ts +19 -0
- package/dist/src/instance-resolution.d.ts.map +1 -1
- package/dist/src/instance-resolution.js +87 -1
- package/dist/src/instance-resolution.js.map +1 -1
- package/dist/src/model-diff.d.ts.map +1 -1
- package/dist/src/model-diff.js +0 -5
- package/dist/src/model-diff.js.map +1 -1
- package/dist/src/runtime.d.ts +143 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +648 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/ser-des/config/artifact.d.ts.map +1 -1
- package/dist/src/ser-des/config/artifact.js +6 -0
- package/dist/src/ser-des/config/artifact.js.map +1 -1
- package/dist/src/ser-des/config/attestation.d.ts +5 -0
- package/dist/src/ser-des/config/attestation.d.ts.map +1 -0
- package/dist/src/ser-des/config/attestation.js +143 -0
- package/dist/src/ser-des/config/attestation.js.map +1 -0
- package/dist/src/ser-des/config/data.d.ts.map +1 -1
- package/dist/src/ser-des/config/data.js +98 -0
- package/dist/src/ser-des/config/data.js.map +1 -1
- package/dist/src/ser-des/config/documentModule.js +5 -5
- package/dist/src/ser-des/config/documentModule.js.map +1 -1
- package/dist/src/ser-des/config/index.d.ts +8 -0
- package/dist/src/ser-des/config/index.d.ts.map +1 -1
- package/dist/src/ser-des/config/index.js +22 -49
- package/dist/src/ser-des/config/index.js.map +1 -1
- package/dist/src/ser-des/config/instance.d.ts.map +1 -1
- package/dist/src/ser-des/config/instance.js +24 -0
- package/dist/src/ser-des/config/instance.js.map +1 -1
- package/dist/src/ser-des/config/process.d.ts.map +1 -1
- package/dist/src/ser-des/config/process.js +8 -0
- package/dist/src/ser-des/config/process.js.map +1 -1
- package/dist/src/ser-des/config/processSequence.d.ts +19 -0
- package/dist/src/ser-des/config/processSequence.d.ts.map +1 -0
- package/dist/src/ser-des/config/processSequence.js +77 -0
- package/dist/src/ser-des/config/processSequence.js.map +1 -0
- package/dist/src/ser-des/config/quantity.d.ts +4 -1
- package/dist/src/ser-des/config/quantity.d.ts.map +1 -1
- package/dist/src/ser-des/config/quantity.js +63 -1
- package/dist/src/ser-des/config/quantity.js.map +1 -1
- package/dist/src/ser-des/config/subject.d.ts.map +1 -1
- package/dist/src/ser-des/config/subject.js +14 -0
- package/dist/src/ser-des/config/subject.js.map +1 -1
- package/dist/src/ser-des/index.d.ts +4 -1
- package/dist/src/ser-des/index.d.ts.map +1 -1
- package/dist/src/ser-des/index.js +3 -1
- package/dist/src/ser-des/index.js.map +1 -1
- package/dist/src/ser-des/package.d.ts.map +1 -1
- package/dist/src/ser-des/package.js +9 -7
- package/dist/src/ser-des/package.js.map +1 -1
- package/dist/src/ser-des/parse.d.ts +24 -0
- package/dist/src/ser-des/parse.d.ts.map +1 -1
- package/dist/src/ser-des/parse.js +69 -33
- package/dist/src/ser-des/parse.js.map +1 -1
- package/dist/src/ser-des/shape-front-end.d.ts +8 -0
- package/dist/src/ser-des/shape-front-end.d.ts.map +1 -0
- package/dist/src/ser-des/shape-front-end.js +215 -0
- package/dist/src/ser-des/shape-front-end.js.map +1 -0
- package/dist/src/ser-des/types.d.ts +4 -10
- package/dist/src/ser-des/types.d.ts.map +1 -1
- package/dist/src/types/Artifact.d.ts +7 -0
- package/dist/src/types/Artifact.d.ts.map +1 -1
- package/dist/src/types/Attestation.d.ts +60 -0
- package/dist/src/types/Attestation.d.ts.map +1 -0
- package/dist/src/types/Attestation.js +20 -0
- package/dist/src/types/Attestation.js.map +1 -0
- package/dist/src/types/DocumentModule.d.ts +1 -1
- package/dist/src/types/DocumentModule.d.ts.map +1 -1
- package/dist/src/types/Instance.d.ts +6 -2
- package/dist/src/types/Instance.d.ts.map +1 -1
- package/dist/src/types/ProcessModel.d.ts +0 -11
- package/dist/src/types/ProcessModel.d.ts.map +1 -1
- package/dist/src/types/ProcessModel.js +2 -28
- package/dist/src/types/ProcessModel.js.map +1 -1
- package/dist/src/types/Quantity.d.ts +22 -0
- package/dist/src/types/Quantity.d.ts.map +1 -1
- package/dist/src/types/Standard.d.ts +2 -13
- package/dist/src/types/Standard.d.ts.map +1 -1
- package/dist/src/types/Subject.d.ts +14 -0
- package/dist/src/types/Subject.d.ts.map +1 -1
- package/dist/src/types/data.d.ts +13 -0
- package/dist/src/types/data.d.ts.map +1 -1
- package/dist/src/types/process.d.ts +8 -0
- package/dist/src/types/process.d.ts.map +1 -1
- package/dist-browser/index.mjs +1746 -1829
- package/dist-cli/primmel.mjs +1630 -845
- package/package.json +15 -3
- package/dist/src/ser-des/config/processModel.d.ts +0 -13
- package/dist/src/ser-des/config/processModel.d.ts.map +0 -1
- package/dist/src/ser-des/config/processModel.js +0 -163
- package/dist/src/ser-des/config/processModel.js.map +0 -1
- package/dist/src/ser-des/config/storyline.d.ts +0 -7
- package/dist/src/ser-des/config/storyline.d.ts.map +0 -1
- package/dist/src/ser-des/config/storyline.js +0 -264
- package/dist/src/ser-des/config/storyline.js.map +0 -1
- package/dist/src/ser-des/config/workflowConfig.d.ts +0 -5
- package/dist/src/ser-des/config/workflowConfig.d.ts.map +0 -1
- package/dist/src/ser-des/config/workflowConfig.js +0 -129
- package/dist/src/ser-des/config/workflowConfig.js.map +0 -1
- package/dist/src/ser-des/config/workflowStage.d.ts +0 -5
- package/dist/src/ser-des/config/workflowStage.d.ts.map +0 -1
- package/dist/src/ser-des/config/workflowStage.js +0 -96
- package/dist/src/ser-des/config/workflowStage.js.map +0 -1
- package/dist/src/types/Storyline.d.ts +0 -62
- package/dist/src/types/Storyline.d.ts.map +0 -1
- package/dist/src/types/Storyline.js +0 -51
- package/dist/src/types/Storyline.js.map +0 -1
package/dist/src/check.js
CHANGED
|
@@ -360,22 +360,6 @@
|
|
|
360
360
|
// part_annex register's declaration shape — letters unique per
|
|
361
361
|
// package, the source provenance required; the obligation
|
|
362
362
|
// vocabulary is parse-enforced upstream
|
|
363
|
-
// C129 storyline-shape (smart TODO.roadmap/40 batch 4): the demo-seed
|
|
364
|
-
// shapes — the id_prefix pattern (register-free), party →
|
|
365
|
-
// demo_world participant seeds (gated), record stores → entity-
|
|
366
|
-
// class stores (gated), in-construct record cross-references
|
|
367
|
-
// (ungated); field-level discipline stays app-side
|
|
368
|
-
// C144–C146 the license facets (smart TODO.external-refs/04; the
|
|
369
|
-
// entitlement facet + catalog): the catalog-key shape
|
|
370
|
-
// (^std:[a-z0-9-]+$), at most one license_key per package (the
|
|
371
|
-
// parser's earlier declarations surface here), and the
|
|
372
|
-
// license_holder required with the key — a package without a key
|
|
373
|
-
// is public content and every leg keys on the key's presence
|
|
374
|
-
// C147–C148 the executable test programs (smart TODO.twin-demo/03;
|
|
375
|
-
// the model-drive bindings): the preparation/stimulus entry-order
|
|
376
|
-
// and step/point shape, and the hold/fresh_within duration
|
|
377
|
-
// discipline (a stimulus point declares fresh_within — §14.12's
|
|
378
|
-
// no-stale-semantics doctrine)
|
|
379
363
|
//
|
|
380
364
|
// Levels (TODO.roadmap/17): the DEFAULT level runs the normal-level
|
|
381
365
|
// rules at their catalog severities. --audit additionally runs the
|
|
@@ -402,6 +386,10 @@
|
|
|
402
386
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
403
387
|
exports.checkPackage = checkPackage;
|
|
404
388
|
exports.checkManifestResolution = checkManifestResolution;
|
|
389
|
+
exports.checkArtifactContracts = checkArtifactContracts;
|
|
390
|
+
exports.checkTypedPayloads = checkTypedPayloads;
|
|
391
|
+
exports.checkAttestations = checkAttestations;
|
|
392
|
+
exports.checkClosedDerivations = checkClosedDerivations;
|
|
405
393
|
exports.checkSpellingCodes = checkSpellingCodes;
|
|
406
394
|
exports.checkDeclaredPredicates = checkDeclaredPredicates;
|
|
407
395
|
exports.checkTermAliases = checkTermAliases;
|
|
@@ -426,6 +414,7 @@ const FormulasUsed_1 = require("./types/FormulasUsed");
|
|
|
426
414
|
const tokenize_1 = require("./ser-des/tokenize");
|
|
427
415
|
const operational_state_1 = require("./operational-state");
|
|
428
416
|
const check_rules_1 = require("./check-rules");
|
|
417
|
+
const instance_resolution_1 = require("./instance-resolution");
|
|
429
418
|
const check_allowlist_1 = require("./check-allowlist");
|
|
430
419
|
const text_coverage_1 = require("./text-coverage");
|
|
431
420
|
const spelling_1 = require("./spelling");
|
|
@@ -490,7 +479,12 @@ function checkPackage(dir, options = {}) {
|
|
|
490
479
|
});
|
|
491
480
|
standard = loaded.standard;
|
|
492
481
|
for (const i of loaded.issues) {
|
|
493
|
-
|
|
482
|
+
// The deprecation report (phase 2): the parse-time issue maps to
|
|
483
|
+
// its rule.
|
|
484
|
+
if (i.code === 'construct-deprecated') {
|
|
485
|
+
warn('C154', i.message);
|
|
486
|
+
}
|
|
487
|
+
else if (i.code === 'provides-unconsumed') {
|
|
494
488
|
warn('C30', i.message);
|
|
495
489
|
}
|
|
496
490
|
else if (i.code === 'extends-deprecated') {
|
|
@@ -545,11 +539,20 @@ function checkPackage(dir, options = {}) {
|
|
|
545
539
|
issues.push(...checkReferences(standard));
|
|
546
540
|
// ── C116/C117: the verdict chain (MN 114 v3.2, clause 11.3) ────────
|
|
547
541
|
issues.push(...checkVerdictChains(standard));
|
|
542
|
+
// ── C149: the closed-derivation rule (the typed kernel, R3) ────────
|
|
543
|
+
issues.push(...checkClosedDerivations(standard));
|
|
544
|
+
// ── C150/C151: the attestation's references and claim lineage ──────
|
|
545
|
+
issues.push(...checkAttestations(standard));
|
|
546
|
+
// ── C152: the typed payload rule (the typed kernel, R2) ────────────
|
|
547
|
+
issues.push(...checkTypedPayloads(standard));
|
|
548
|
+
// ── C153: the contract-derivation facet (the typed kernel, clause 10)
|
|
549
|
+
issues.push(...checkArtifactContracts(standard));
|
|
548
550
|
const reqIds = new Set((standard.requirements ?? []).map((r) => r.id));
|
|
549
551
|
const testIds = new Set((standard.conformanceTests ?? []).map((t) => t.id));
|
|
550
552
|
const attrIds = new Set((standard.attributeDefinitions ?? []).map((a) => a.id));
|
|
553
|
+
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
551
554
|
const behaviorIds = new Set((standard.behaviors ?? []).map((b) => b.id));
|
|
552
|
-
const subjectIds = new Set((standard.subjects ?? []).map(
|
|
555
|
+
const subjectIds = new Set((standard.subjects ?? []).map(s => s.id));
|
|
553
556
|
// Observables live in the symbols registry, not the attribute layer —
|
|
554
557
|
// binds_to / limit.uses may reference them (e.g. sample.test_context
|
|
555
558
|
// quantities that are measured test outputs).
|
|
@@ -1162,7 +1165,9 @@ function checkPackage(dir, options = {}) {
|
|
|
1162
1165
|
// C45 — artifact-def-contract: the content contract is well-formed.
|
|
1163
1166
|
for (const d of standard.artifactDefinitions ?? []) {
|
|
1164
1167
|
const c = d.contentContract;
|
|
1165
|
-
|
|
1168
|
+
// A derived contract (contract_from, C153) is the referenced
|
|
1169
|
+
// promise set's projection — its fields are the set's.
|
|
1170
|
+
if (!d.contractFrom && c.fields.length === 0) {
|
|
1166
1171
|
err('C45', `artifact definition ${d.id}: content contract declares no fields — a definition must contract SOME content (artifact-def-contract)`);
|
|
1167
1172
|
}
|
|
1168
1173
|
const seenFields = new Set();
|
|
@@ -1697,15 +1702,6 @@ function checkPackage(dir, options = {}) {
|
|
|
1697
1702
|
}
|
|
1698
1703
|
}
|
|
1699
1704
|
}
|
|
1700
|
-
for (const m of standard.processModels ?? []) {
|
|
1701
|
-
const where = `process_model ${m.id}`;
|
|
1702
|
-
for (const s of m.sequence ?? []) {
|
|
1703
|
-
resolveProcessRef(where, 'sequence', s);
|
|
1704
|
-
}
|
|
1705
|
-
for (const r of m.registers ?? []) {
|
|
1706
|
-
resolveOrganAp(`${where} register ${r.id}`, 'maintainer', r.maintainer);
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
1705
|
}
|
|
1710
1706
|
// ── C84: constraint-shape (TODO.roadmap/51 — BUG.R60-SSOT gap 7) ─────
|
|
1711
1707
|
// The subject-intrinsic constraint's DECLARATION shape — the kernel
|
|
@@ -2039,7 +2035,6 @@ function checkPackage(dir, options = {}) {
|
|
|
2039
2035
|
// escalation ACTIONS are runtime — the smart app's monitor service owns
|
|
2040
2036
|
// them; these rules guarantee the declarations the runtime needs.
|
|
2041
2037
|
{
|
|
2042
|
-
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
2043
2038
|
const KNOWN_OUTCOMES = new Set(Monitor_1.MONITOR_OUTCOMES);
|
|
2044
2039
|
const KNOWN_ACTIONS = new Set(Monitor_1.MONITOR_ESCALATION_ACTIONS);
|
|
2045
2040
|
const KNOWN_STREAMS = new Set(Monitor_1.MONITOR_STREAMS);
|
|
@@ -2343,6 +2338,7 @@ function checkPackage(dir, options = {}) {
|
|
|
2343
2338
|
};
|
|
2344
2339
|
const instrumentIds = new Set((standard.instruments ?? []).map(i => i.id));
|
|
2345
2340
|
const instanceIds = new Set((standard.instances ?? []).map(i => i.id));
|
|
2341
|
+
const dataclassIds = new Set((standard.dataclasses ?? []).map(c => c.id));
|
|
2346
2342
|
const attrScopes = new Map((standard.attributeDefinitions ?? []).map(a => [a.id, a.scope]));
|
|
2347
2343
|
// Classification-dimension scopes, by every spelling a classification
|
|
2348
2344
|
// map may use: the instrument dimension id, the is_dimension attribute
|
|
@@ -2436,9 +2432,53 @@ function checkPackage(dir, options = {}) {
|
|
|
2436
2432
|
}
|
|
2437
2433
|
}
|
|
2438
2434
|
// C20 — instance-of-resolves: the `of` reference names a declared
|
|
2439
|
-
// subject (v3)
|
|
2440
|
-
|
|
2441
|
-
|
|
2435
|
+
// subject (v3), instrument (v2), or ANOTHER INSTANCE (the power-type
|
|
2436
|
+
// discipline — every instance can serve as the definition of further
|
|
2437
|
+
// instantiation).
|
|
2438
|
+
if (!subjectIds.has(inst.of) &&
|
|
2439
|
+
!instrumentIds.has(inst.of) &&
|
|
2440
|
+
!instanceIds.has(inst.of) &&
|
|
2441
|
+
!dataclassIds.has(inst.of)) {
|
|
2442
|
+
err('C20', `instance ${inst.id}: of "${inst.of}" is not a declared subject, instrument, class, or instance (instance-of-resolves)`);
|
|
2443
|
+
}
|
|
2444
|
+
// C155 — power-type-chain (coherence leg): an instance whose `of`
|
|
2445
|
+
// names another instance carries NO upward subject-chain link — the
|
|
2446
|
+
// two are one pattern's two spellings, and mixing them makes the
|
|
2447
|
+
// delegation walk ambiguous.
|
|
2448
|
+
if (instanceIds.has(inst.of)) {
|
|
2449
|
+
const links = ['model', 'group', 'family'].filter(k => inst[k]);
|
|
2450
|
+
if (links.length > 0) {
|
|
2451
|
+
err('C155', `instance ${inst.id}: of "${inst.of}" names an instance and the instance carries an upward ${links.join('/')} link — the power-type chain and the subject-chain profile do not mix (power-type-chain)`);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
// C155 — power-type-chain (acyclicity leg): the MIXED delegation
|
|
2456
|
+
// graph — of-chains to instances AND the prototype hop (an `of` to a
|
|
2457
|
+
// class whose extends names an instance) — is acyclic: a cycle over
|
|
2458
|
+
// these edges would make the delegation walk non-terminating, and a
|
|
2459
|
+
// class instantiating its own prototype (`instance x of C` while
|
|
2460
|
+
// `C extends { x }`) is the shapes' own mutual recursion.
|
|
2461
|
+
const delegation = (0, instance_resolution_1.delegationSuccessors)(standard);
|
|
2462
|
+
const ofAdj = new Map();
|
|
2463
|
+
for (const [from, to] of delegation) {
|
|
2464
|
+
if (instanceIds.has(to)) {
|
|
2465
|
+
ofAdj.set(from, [to]);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
const ofCycle = findCycle(ofAdj);
|
|
2469
|
+
if (ofCycle) {
|
|
2470
|
+
err('C155', `the power-type of-chain is cyclic: ${ofCycle.join(' → ')} (power-type-chain)`);
|
|
2471
|
+
}
|
|
2472
|
+
// C156 — class-extends-resolves: a class's extends names a declared
|
|
2473
|
+
// class or a declared INSTANCE (the definition side of the power-type
|
|
2474
|
+
// discipline — a class extending an instance specializes that
|
|
2475
|
+
// individual). Successfully merged links clear at resolve time, so a
|
|
2476
|
+
// surviving link is exactly an unresolved one.
|
|
2477
|
+
for (const c of standard.dataclasses ?? []) {
|
|
2478
|
+
if (c.extends &&
|
|
2479
|
+
!dataclassIds.has(c.extends) &&
|
|
2480
|
+
!instanceIds.has(c.extends)) {
|
|
2481
|
+
warn('C156', `class ${c.id}: extends "${c.extends}" is not a declared class or instance (class-extends-resolves)`);
|
|
2442
2482
|
}
|
|
2443
2483
|
}
|
|
2444
2484
|
// C19 — chain-acyclic (integrity part 2): the resolved link graph is
|
|
@@ -3694,68 +3734,6 @@ function checkPackage(dir, options = {}) {
|
|
|
3694
3734
|
}
|
|
3695
3735
|
}
|
|
3696
3736
|
}
|
|
3697
|
-
// ── C129: storyline-shape (smart TODO.roadmap/40 batch 4; the ──────
|
|
3698
|
-
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
3699
|
-
// The demo-seed shapes: the id_prefix pattern (the register-free shape
|
|
3700
|
-
// leg); the party laboratory/authority resolve against the package's
|
|
3701
|
-
// participant seeds WHEN a demo_world with participants is in scope
|
|
3702
|
-
// (per-register gating, the C58 doctrine); the record stores resolve
|
|
3703
|
-
// against the declared entity-class stores WHEN that register is in
|
|
3704
|
-
// scope; and the in-construct cross-references resolve — a record
|
|
3705
|
-
// field keyed by a sibling record's store names a sibling record of
|
|
3706
|
-
// that store (in-construct, ungated). The field-level discipline (the
|
|
3707
|
-
// schema's per-slot required fields) stays app-side.
|
|
3708
|
-
{
|
|
3709
|
-
const participantSeeds = new Set();
|
|
3710
|
-
for (const w of standard.demoWorlds ?? []) {
|
|
3711
|
-
for (const section of w.participants ?? []) {
|
|
3712
|
-
for (const e of section.entries ?? []) {
|
|
3713
|
-
participantSeeds.add(e.id);
|
|
3714
|
-
}
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
const entityStores = new Set((standard.dataclasses ?? [])
|
|
3718
|
-
.map(c => c.store)
|
|
3719
|
-
.filter((s) => !!s));
|
|
3720
|
-
for (const s of standard.storylines ?? []) {
|
|
3721
|
-
const where = `storyline ${s.id}`;
|
|
3722
|
-
if (s.idPrefix && !/^[a-z0-9-]+$/.test(s.idPrefix)) {
|
|
3723
|
-
err('C129', `${where}: id_prefix "${s.idPrefix}" does not match ^[a-z0-9-]+$ — the id-minting prefix is a lowercase slug (storyline-shape)`);
|
|
3724
|
-
}
|
|
3725
|
-
if (participantSeeds.size > 0) {
|
|
3726
|
-
for (const [slot, ref] of [
|
|
3727
|
-
['laboratory', s.party.laboratory],
|
|
3728
|
-
['authority', s.party.authority],
|
|
3729
|
-
]) {
|
|
3730
|
-
if (ref && !participantSeeds.has(ref)) {
|
|
3731
|
-
err('C129', `${where}: party ${slot} "${ref}" is not a declared participant seed (storyline-shape)`);
|
|
3732
|
-
}
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
const siblingStores = new Map();
|
|
3736
|
-
for (const r of s.records ?? []) {
|
|
3737
|
-
if (entityStores.size > 0 && !entityStores.has(r.store)) {
|
|
3738
|
-
err('C129', `${where}: record store "${r.store}" is not a declared entity-class store (storyline-shape)`);
|
|
3739
|
-
}
|
|
3740
|
-
let ids = siblingStores.get(r.store);
|
|
3741
|
-
if (!ids) {
|
|
3742
|
-
ids = new Set();
|
|
3743
|
-
siblingStores.set(r.store, ids);
|
|
3744
|
-
}
|
|
3745
|
-
ids.add(r.id);
|
|
3746
|
-
}
|
|
3747
|
-
for (const r of s.records ?? []) {
|
|
3748
|
-
for (const [key, value] of Object.entries(r.fields)) {
|
|
3749
|
-
const targets = siblingStores.get(key);
|
|
3750
|
-
if (targets !== undefined &&
|
|
3751
|
-
!Array.isArray(value) &&
|
|
3752
|
-
!targets.has(String(value))) {
|
|
3753
|
-
err('C129', `${where}: record ${r.store} ${r.id}: ${key} "${String(value)}" is not a sibling ${key} record of this storyline (storyline-shape)`);
|
|
3754
|
-
}
|
|
3755
|
-
}
|
|
3756
|
-
}
|
|
3757
|
-
}
|
|
3758
|
-
}
|
|
3759
3737
|
// ── C130: identity-and-aspect-references (smart TODO.roadmap/40 ─────
|
|
3760
3738
|
// batch 3; the packages-as-SSOT epic) ────────────────────────────────
|
|
3761
3739
|
// The documentary identity slots + the qualitative aspect register
|
|
@@ -4126,7 +4104,6 @@ function checkPackage(dir, options = {}) {
|
|
|
4126
4104
|
// (the R26 field-resolution precedent). The phase vocabulary is
|
|
4127
4105
|
// parse-enforced upstream (no check leg).
|
|
4128
4106
|
{
|
|
4129
|
-
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
4130
4107
|
// Class ids may carry a namespace suffix (`Application#data`) while
|
|
4131
4108
|
// the workflow's inputs/outputs spell the bare entity name — index
|
|
4132
4109
|
// both spellings (the semantic-status leg's precedent).
|
|
@@ -4137,28 +4114,6 @@ function checkPackage(dir, options = {}) {
|
|
|
4137
4114
|
dataclassIds.add(c.id.slice(0, c.id.indexOf('#')));
|
|
4138
4115
|
}
|
|
4139
4116
|
}
|
|
4140
|
-
const CLEAN_TOKEN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4141
|
-
for (const w of standard.workflowConfigs ?? []) {
|
|
4142
|
-
for (const s of w.steps ?? []) {
|
|
4143
|
-
const where = `workflow_config ${w.id}: step ${s.id}`;
|
|
4144
|
-
if (s.actor && roleIds.size > 0 && !roleIds.has(s.actor)) {
|
|
4145
|
-
err('C137', `${where}: actor "${s.actor}" is not a declared role (workflow-config-references)`);
|
|
4146
|
-
}
|
|
4147
|
-
for (const [facet, list] of [
|
|
4148
|
-
['inputs', s.inputs],
|
|
4149
|
-
['outputs', s.outputs],
|
|
4150
|
-
]) {
|
|
4151
|
-
for (const entry of list) {
|
|
4152
|
-
if (!CLEAN_TOKEN.test(entry)) {
|
|
4153
|
-
continue; // a composite documentary string — never resolved
|
|
4154
|
-
}
|
|
4155
|
-
if (dataclassIds.size > 0 && !dataclassIds.has(entry)) {
|
|
4156
|
-
err('C137', `${where}: ${facet} entry "${entry}" is not a declared data class (workflow-config-references)`);
|
|
4157
|
-
}
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
4117
|
}
|
|
4163
4118
|
// ── C138: verification-pathway-references (smart TODO.roadmap/40 ───
|
|
4164
4119
|
// batch 3; the packages-as-SSOT epic) ────────────────────────────────
|
|
@@ -4405,26 +4360,6 @@ function checkPackage(dir, options = {}) {
|
|
|
4405
4360
|
}
|
|
4406
4361
|
// The workflow_stage members-resolve legs (step 5d) — the events
|
|
4407
4362
|
// stay documentary, never resolved.
|
|
4408
|
-
const stageApprovalIds = new Set((standard.approvals ?? []).map(a => a.id));
|
|
4409
|
-
const stageGatewayIds = new Set((standard.gateways ?? []).map(g => g.id));
|
|
4410
|
-
for (const s of standard.workflowStages ?? []) {
|
|
4411
|
-
const where = `workflow_stage ${s.id}`;
|
|
4412
|
-
for (const el of s.elements ?? []) {
|
|
4413
|
-
if (gwProcessIds.size > 0 && !gwProcessIds.has(el)) {
|
|
4414
|
-
err('C142', `${where}: element "${el}" is not a declared process (gateway-edges-resolve)`);
|
|
4415
|
-
}
|
|
4416
|
-
}
|
|
4417
|
-
for (const a of s.approvals ?? []) {
|
|
4418
|
-
if (stageApprovalIds.size > 0 && !stageApprovalIds.has(a)) {
|
|
4419
|
-
err('C142', `${where}: approval "${a}" is not a declared approval (gateway-edges-resolve)`);
|
|
4420
|
-
}
|
|
4421
|
-
}
|
|
4422
|
-
for (const g of s.gateways ?? []) {
|
|
4423
|
-
if (stageGatewayIds.size > 0 && !stageGatewayIds.has(g)) {
|
|
4424
|
-
err('C142', `${where}: gateway "${g}" is not a declared gateway (gateway-edges-resolve)`);
|
|
4425
|
-
}
|
|
4426
|
-
}
|
|
4427
|
-
}
|
|
4428
4363
|
}
|
|
4429
4364
|
// ── C143: approval-references-resolve (smart TODO.roadmap/40 batch ──
|
|
4430
4365
|
// 5; the packages-as-SSOT epic) ─────────────────────────────────────
|
|
@@ -5678,6 +5613,362 @@ function textAddressKey(item, segment) {
|
|
|
5678
5613
|
* (titular:source:target:identifying); a `zz-` user-assigned code
|
|
5679
5614
|
* validates with a warning (user-assigned codes are not portable).
|
|
5680
5615
|
*/
|
|
5616
|
+
// ── C153: artifact-contract-derivation (the typed kernel, clause 10) ─
|
|
5617
|
+
// An artifact definition whose content is the projection of declared
|
|
5618
|
+
// claims declares contract_from; the referenced set resolves, and a
|
|
5619
|
+
// definition that restates inline fields while deriving its contract
|
|
5620
|
+
// is the restatement the rule exists to prevent.
|
|
5621
|
+
function checkArtifactContracts(standard) {
|
|
5622
|
+
const issues = [];
|
|
5623
|
+
const err = (check, message) => issues.push({ check, severity: 'error', message });
|
|
5624
|
+
const setIds = new Set();
|
|
5625
|
+
for (const ps of standard.promiseSets ?? []) {
|
|
5626
|
+
setIds.add(ps.id);
|
|
5627
|
+
setIds.add(`${ps.id}.promise_set`);
|
|
5628
|
+
}
|
|
5629
|
+
for (const s of standard.subjects ?? []) {
|
|
5630
|
+
if ((s.is?.promises ?? []).length > 0) {
|
|
5631
|
+
setIds.add(s.id);
|
|
5632
|
+
setIds.add(`${s.id}.promise_set`);
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
for (const d of standard.artifactDefinitions ?? []) {
|
|
5636
|
+
if (!d.contractFrom) {
|
|
5637
|
+
continue;
|
|
5638
|
+
}
|
|
5639
|
+
if (!setIds.has(d.contractFrom)) {
|
|
5640
|
+
err('C153', `artifact_definition ${d.id}: contract_from "${d.contractFrom}" does not resolve to a declared promise set (artifact-contract-derivation)`);
|
|
5641
|
+
}
|
|
5642
|
+
const c = d.contentContract;
|
|
5643
|
+
if (c.fields.length > 0 || c.structure || c.media.length > 0) {
|
|
5644
|
+
err('C153', `artifact_definition ${d.id}: declares both contract_from and inline contract fields — the derived contract is the referenced set's projection, never a restatement (artifact-contract-derivation)`);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
return issues;
|
|
5648
|
+
}
|
|
5649
|
+
// ── C152: payload-typed-by-class (the typed kernel R2, clause 10) ────
|
|
5650
|
+
// A dimension that declares payload_class types every value's
|
|
5651
|
+
// definition: the class resolves, every payload field is a field of
|
|
5652
|
+
// that class, and every value carries its definition — a value IS its
|
|
5653
|
+
// definition, not a symbol pointing at a table. The attribute-side
|
|
5654
|
+
// bridge resolves too: an attribute_definition's class reference names
|
|
5655
|
+
// a declared class.
|
|
5656
|
+
function checkTypedPayloads(standard) {
|
|
5657
|
+
const issues = [];
|
|
5658
|
+
const err = (check, message) => issues.push({ check, severity: 'error', message });
|
|
5659
|
+
const classFields = new Map();
|
|
5660
|
+
for (const c of standard.dataclasses ?? []) {
|
|
5661
|
+
classFields.set(c.id, new Set(c.attributes.map(a => a.id)));
|
|
5662
|
+
}
|
|
5663
|
+
// The power-type discipline reaches the typing bridge: an INSTANCE
|
|
5664
|
+
// is a class, and its shape is the fields of the class its `of`
|
|
5665
|
+
// chain terminates at (the prototype hop's mirror — an individual
|
|
5666
|
+
// typed by its definition's fields).
|
|
5667
|
+
const instanceShape = (id) => {
|
|
5668
|
+
const inst = (standard.instances ?? []).find(i => i.id === id);
|
|
5669
|
+
if (!inst) {
|
|
5670
|
+
return undefined;
|
|
5671
|
+
}
|
|
5672
|
+
let cur = inst;
|
|
5673
|
+
const seen = new Set([cur.id]);
|
|
5674
|
+
while (cur.of) {
|
|
5675
|
+
const next = (standard.instances ?? []).find(i => i.id === cur.of);
|
|
5676
|
+
if (!next || seen.has(next.id)) {
|
|
5677
|
+
break;
|
|
5678
|
+
}
|
|
5679
|
+
seen.add(next.id);
|
|
5680
|
+
cur = next;
|
|
5681
|
+
}
|
|
5682
|
+
return cur.of !== '' ? classFields.get(cur.of) : undefined;
|
|
5683
|
+
};
|
|
5684
|
+
const typedFields = (id) => classFields.get(id) ?? instanceShape(id);
|
|
5685
|
+
const checkDim = (owner, d) => {
|
|
5686
|
+
if (!d.payloadClass) {
|
|
5687
|
+
return;
|
|
5688
|
+
}
|
|
5689
|
+
const fields = typedFields(d.payloadClass);
|
|
5690
|
+
if (!fields) {
|
|
5691
|
+
err('C152', `${owner} dimension ${d.id}: payload_class "${d.payloadClass}" is not a declared class (payload-typed-by-class)`);
|
|
5692
|
+
return;
|
|
5693
|
+
}
|
|
5694
|
+
for (const v of d.values) {
|
|
5695
|
+
const keys = Object.keys(v.payload ?? {});
|
|
5696
|
+
if (keys.length === 0) {
|
|
5697
|
+
err('C152', `${owner} dimension ${d.id}: value ${v.id} carries no payload — a value of a payload_class dimension IS its definition (payload-typed-by-class)`);
|
|
5698
|
+
continue;
|
|
5699
|
+
}
|
|
5700
|
+
for (const key of keys) {
|
|
5701
|
+
if (!fields.has(key)) {
|
|
5702
|
+
err('C152', `${owner} dimension ${d.id}: value ${v.id} payload field "${key}" is not a field of class "${d.payloadClass}" (payload-typed-by-class)`);
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
};
|
|
5707
|
+
for (const inst of standard.instruments ?? []) {
|
|
5708
|
+
for (const d of inst.dimensions ?? []) {
|
|
5709
|
+
checkDim(`instrument ${inst.id}`, d);
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5712
|
+
for (const d of standard.dimensions ?? []) {
|
|
5713
|
+
checkDim('top-level', d);
|
|
5714
|
+
}
|
|
5715
|
+
for (const a of standard.attributeDefinitions ?? []) {
|
|
5716
|
+
if (a.classRef && !typedFields(a.classRef)) {
|
|
5717
|
+
err('C152', `attribute_definition ${a.id}: class "${a.classRef}" is not a declared class (payload-typed-by-class)`);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
return issues;
|
|
5721
|
+
}
|
|
5722
|
+
// ── C150/C151: the attestation's references and claim lineage ───────
|
|
5723
|
+
// C150 attestation-references-resolve: the subject is a declared
|
|
5724
|
+
// instance, the promise set resolves in `Subject.promise_set_id` form,
|
|
5725
|
+
// every basis entry carries kind and id, and every claim's declaration
|
|
5726
|
+
// and verdict references are stated. C151
|
|
5727
|
+
// attestation-claim-lineage: every promise the certificate prints as
|
|
5728
|
+
// mandatory must be carried by a claim — a mandatory characteristic is
|
|
5729
|
+
// carried by lineage, never restated (clause 19).
|
|
5730
|
+
function checkAttestations(standard) {
|
|
5731
|
+
const issues = [];
|
|
5732
|
+
const err = (check, message) => issues.push({ check, severity: 'error', message });
|
|
5733
|
+
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
5734
|
+
const instanceIds = new Set((standard.instances ?? []).map(i => i.id));
|
|
5735
|
+
const verdictIds = new Set((standard.verdicts ?? []).map(v => v.id));
|
|
5736
|
+
const setViews = new Map();
|
|
5737
|
+
for (const ps of standard.promiseSets ?? []) {
|
|
5738
|
+
setViews.set(ps.id, { promises: ps.promises });
|
|
5739
|
+
setViews.set(`${ps.id}.promise_set`, { promises: ps.promises });
|
|
5740
|
+
}
|
|
5741
|
+
for (const s of standard.subjects ?? []) {
|
|
5742
|
+
if ((s.is?.promises ?? []).length > 0) {
|
|
5743
|
+
setViews.set(s.id, { promises: s.is.promises });
|
|
5744
|
+
}
|
|
5745
|
+
}
|
|
5746
|
+
const resolveSet = (ref) => setViews.get(ref) ??
|
|
5747
|
+
setViews.get(ref.split('.').slice(1).join('.')) ??
|
|
5748
|
+
null;
|
|
5749
|
+
const mandatoryOf = (view) => {
|
|
5750
|
+
const out = new Set();
|
|
5751
|
+
for (const p of view.promises) {
|
|
5752
|
+
if (p.certificate?.obligation === 'mandatory') {
|
|
5753
|
+
out.add(p.id);
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
return out;
|
|
5757
|
+
};
|
|
5758
|
+
for (const a of standard.attestations ?? []) {
|
|
5759
|
+
if (!a.subject) {
|
|
5760
|
+
err('C150', `attestation ${a.id}: no subject facet (attestation-references-resolve)`);
|
|
5761
|
+
}
|
|
5762
|
+
else if (!instanceIds.has(a.subject)) {
|
|
5763
|
+
err('C150', `attestation ${a.id}: subject "${a.subject}" is not a declared instance (attestation-references-resolve)`);
|
|
5764
|
+
}
|
|
5765
|
+
if (!a.promises) {
|
|
5766
|
+
err('C150', `attestation ${a.id}: no promises facet (attestation-references-resolve)`);
|
|
5767
|
+
}
|
|
5768
|
+
const set = a.promises ? resolveSet(a.promises) : null;
|
|
5769
|
+
if (a.promises && !set) {
|
|
5770
|
+
err('C150', `attestation ${a.id}: promises "${a.promises}" does not resolve to a declared promise set (attestation-references-resolve)`);
|
|
5771
|
+
}
|
|
5772
|
+
for (const b of a.basis) {
|
|
5773
|
+
if (!b.kind || !b.id) {
|
|
5774
|
+
err('C150', `attestation ${a.id}: a basis entry needs an evidence kind and an id (attestation-references-resolve)`);
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
if (a.authority.role &&
|
|
5778
|
+
roleIds.size > 0 &&
|
|
5779
|
+
!roleIds.has(a.authority.role)) {
|
|
5780
|
+
err('C150', `attestation ${a.id}: authority role "${a.authority.role}" is not a declared role (attestation-references-resolve)`);
|
|
5781
|
+
}
|
|
5782
|
+
const carried = new Set(a.claims.map(c => c.promise));
|
|
5783
|
+
for (const c of a.claims) {
|
|
5784
|
+
if (set && !set.promises.some(p => p.id === c.promise)) {
|
|
5785
|
+
err('C150', `attestation ${a.id}: claim "${c.promise}" is not a promise of "${a.promises}" (attestation-references-resolve)`);
|
|
5786
|
+
}
|
|
5787
|
+
if (!c.declared) {
|
|
5788
|
+
err('C151', `attestation ${a.id}: claim "${c.promise}" carries no declaration reference (attestation-claim-lineage)`);
|
|
5789
|
+
}
|
|
5790
|
+
if (!c.validatedBy) {
|
|
5791
|
+
err('C151', `attestation ${a.id}: claim "${c.promise}" carries no verdict reference (attestation-claim-lineage)`);
|
|
5792
|
+
}
|
|
5793
|
+
else if (verdictIds.size > 0 &&
|
|
5794
|
+
!c.validatedBy.startsWith('/') &&
|
|
5795
|
+
!verdictIds.has(c.validatedBy)) {
|
|
5796
|
+
// Bare ids resolve inside the package; slash ids are the
|
|
5797
|
+
// requirement-anatomy path form and resolve in the linker.
|
|
5798
|
+
err('C150', `attestation ${a.id}: claim "${c.promise}" names verdict "${c.validatedBy}", which is not declared (attestation-references-resolve)`);
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
if (set) {
|
|
5802
|
+
for (const pid of mandatoryOf(set)) {
|
|
5803
|
+
if (!carried.has(pid)) {
|
|
5804
|
+
err('C151', `attestation ${a.id}: the mandatory promise "${pid}" of "${a.promises}" is carried by no claim — a mandatory characteristic is carried by lineage, never restated (attestation-claim-lineage)`);
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
return issues;
|
|
5810
|
+
}
|
|
5811
|
+
// ── C149: closed-derivation-references-declared ─────────────────────
|
|
5812
|
+
// The typed kernel's rule R3: a derivation expression names only
|
|
5813
|
+
// declared definitions — declared calculations, tables, symbols,
|
|
5814
|
+
// measurements/variables, data classes, enums, and attributes of the
|
|
5815
|
+
// model. The checker rejects an expression that references anything
|
|
5816
|
+
// else and names the missing declaration, so the undefined-reference
|
|
5817
|
+
// defect class cannot be authored.
|
|
5818
|
+
const CLOSED_DERIVATION_STOP = new Set([
|
|
5819
|
+
'self',
|
|
5820
|
+
'p',
|
|
5821
|
+
'ocl',
|
|
5822
|
+
'true',
|
|
5823
|
+
'false',
|
|
5824
|
+
'null',
|
|
5825
|
+
'undefined',
|
|
5826
|
+
// OCL navigation and collection operators
|
|
5827
|
+
'and',
|
|
5828
|
+
'or',
|
|
5829
|
+
'not',
|
|
5830
|
+
'implies',
|
|
5831
|
+
'if',
|
|
5832
|
+
'then',
|
|
5833
|
+
'else',
|
|
5834
|
+
'endif',
|
|
5835
|
+
'xor',
|
|
5836
|
+
'let',
|
|
5837
|
+
'in',
|
|
5838
|
+
'exists',
|
|
5839
|
+
'forAll',
|
|
5840
|
+
'select',
|
|
5841
|
+
'reject',
|
|
5842
|
+
'collect',
|
|
5843
|
+
'isEmpty',
|
|
5844
|
+
'size',
|
|
5845
|
+
'sum',
|
|
5846
|
+
'min',
|
|
5847
|
+
'max',
|
|
5848
|
+
'abs',
|
|
5849
|
+
'round',
|
|
5850
|
+
'floor',
|
|
5851
|
+
'sqrt',
|
|
5852
|
+
'includes',
|
|
5853
|
+
'excludes',
|
|
5854
|
+
'includesAll',
|
|
5855
|
+
'excludesAll',
|
|
5856
|
+
'count',
|
|
5857
|
+
'first',
|
|
5858
|
+
'last',
|
|
5859
|
+
'asSet',
|
|
5860
|
+
'asOrderedSet',
|
|
5861
|
+
'asSequence',
|
|
5862
|
+
'asBag',
|
|
5863
|
+
'sortedBy',
|
|
5864
|
+
'iterate',
|
|
5865
|
+
'oclIsUndefined',
|
|
5866
|
+
'oclIsKindOf',
|
|
5867
|
+
'oclIsTypeOf',
|
|
5868
|
+
'allInstances',
|
|
5869
|
+
// table-lookup engine vocabulary (the lookup facet names these)
|
|
5870
|
+
'lookup',
|
|
5871
|
+
'profile',
|
|
5872
|
+
// OCL collection statistics and arithmetic
|
|
5873
|
+
'average',
|
|
5874
|
+
'mean',
|
|
5875
|
+
'size',
|
|
5876
|
+
'div',
|
|
5877
|
+
'mod',
|
|
5878
|
+
'collect',
|
|
5879
|
+
]);
|
|
5880
|
+
// Attribute navigation: `head.field…` resolves when the HEAD is a
|
|
5881
|
+
// declared name (an input, a calculation, an attribute, a class-typed
|
|
5882
|
+
// value); the tail is navigation over that head's type, judged by the
|
|
5883
|
+
// class layer (C152), not by the closed-derivation rule.
|
|
5884
|
+
const CLOSED_DERIVATION_PATH_RE = /\b[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+/g;
|
|
5885
|
+
const CLOSED_DERIVATION_IDENT_RE = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
5886
|
+
function checkClosedDerivations(standard) {
|
|
5887
|
+
const issues = [];
|
|
5888
|
+
const err = (check, message) => issues.push({ check, severity: 'error', message });
|
|
5889
|
+
// The declared universe every derivation may reference.
|
|
5890
|
+
const declared = new Set();
|
|
5891
|
+
for (const c of standard.calculations ?? []) {
|
|
5892
|
+
declared.add(c.id);
|
|
5893
|
+
declared.add(c.name);
|
|
5894
|
+
}
|
|
5895
|
+
for (const tb of standard.tables ?? []) {
|
|
5896
|
+
declared.add(tb.id);
|
|
5897
|
+
}
|
|
5898
|
+
for (const s of standard.symbols ?? []) {
|
|
5899
|
+
declared.add(s.id);
|
|
5900
|
+
}
|
|
5901
|
+
for (const v of standard.variables ?? []) {
|
|
5902
|
+
declared.add(v.id);
|
|
5903
|
+
}
|
|
5904
|
+
for (const a of standard.attributeDefinitions ?? []) {
|
|
5905
|
+
declared.add(a.id);
|
|
5906
|
+
}
|
|
5907
|
+
for (const e of standard.enums ?? []) {
|
|
5908
|
+
declared.add(e.id);
|
|
5909
|
+
}
|
|
5910
|
+
for (const d of standard.dataclasses ?? []) {
|
|
5911
|
+
declared.add(d.id);
|
|
5912
|
+
}
|
|
5913
|
+
for (const inst of standard.instruments ?? []) {
|
|
5914
|
+
for (const d of inst.dimensions ?? []) {
|
|
5915
|
+
declared.add(d.id);
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
for (const d of standard.dimensions ?? []) {
|
|
5919
|
+
declared.add(d.id);
|
|
5920
|
+
}
|
|
5921
|
+
// A calculation's own output name is declared too (requirements bind
|
|
5922
|
+
// it), and so is every input of every calculation.
|
|
5923
|
+
for (const c of standard.calculations ?? []) {
|
|
5924
|
+
if (c.output?.name) {
|
|
5925
|
+
declared.add(c.output.name);
|
|
5926
|
+
}
|
|
5927
|
+
for (const i of c.inputs ?? []) {
|
|
5928
|
+
declared.add(i.name);
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
const localsOf = (extra) => new Set([...declared, ...extra]);
|
|
5932
|
+
const checkExpression = (expr, locals, where) => {
|
|
5933
|
+
if (!expr) {
|
|
5934
|
+
return;
|
|
5935
|
+
}
|
|
5936
|
+
// Values and lambdas are not references: single-quoted literals are
|
|
5937
|
+
// values; an identifier before `|` is a lambda parameter.
|
|
5938
|
+
const noLiterals = expr.replace(/'[^']*'/g, ' ');
|
|
5939
|
+
const noLambdas = noLiterals.replace(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\|/g, ' ');
|
|
5940
|
+
const stripped = noLambdas.replace(CLOSED_DERIVATION_PATH_RE, ' ');
|
|
5941
|
+
const seen = new Set();
|
|
5942
|
+
for (const raw of stripped.match(CLOSED_DERIVATION_IDENT_RE) ?? []) {
|
|
5943
|
+
if (CLOSED_DERIVATION_STOP.has(raw) ||
|
|
5944
|
+
locals.has(raw) ||
|
|
5945
|
+
seen.has(raw) ||
|
|
5946
|
+
/^[a-z][0-9]?$/.test(raw)) {
|
|
5947
|
+
continue;
|
|
5948
|
+
}
|
|
5949
|
+
seen.add(raw);
|
|
5950
|
+
err('C149', `${where}: expression references "${raw}", which no declared calculation, table, symbol, measurement, or attribute defines (closed-derivation-references-declared)`);
|
|
5951
|
+
}
|
|
5952
|
+
};
|
|
5953
|
+
for (const a of standard.attributeDefinitions ?? []) {
|
|
5954
|
+
checkExpression(a.derived, localsOf([]), `attribute_definition ${a.id}`);
|
|
5955
|
+
}
|
|
5956
|
+
for (const c of standard.calculations ?? []) {
|
|
5957
|
+
// The normative body is the declared expression; engine variants
|
|
5958
|
+
// are consumer code and outside the language's semantics.
|
|
5959
|
+
const inputs = (c.inputs ?? []).map(i => i.name);
|
|
5960
|
+
checkExpression(c.expression, localsOf(inputs), `calculation ${c.id}`);
|
|
5961
|
+
}
|
|
5962
|
+
for (const s of standard.symbols ?? []) {
|
|
5963
|
+
if (s.formula?.expression) {
|
|
5964
|
+
checkExpression(s.formula.expression, localsOf(s.formula.inputs ?? []), `symbol ${s.id}`);
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
for (const v of standard.verdicts ?? []) {
|
|
5968
|
+
checkExpression(v.derive, localsOf(v.inputs ?? []), `verdict ${v.id}`);
|
|
5969
|
+
}
|
|
5970
|
+
return issues;
|
|
5971
|
+
}
|
|
5681
5972
|
function checkSpellingCodes(standard) {
|
|
5682
5973
|
const issues = [];
|
|
5683
5974
|
const err = (check, message) => issues.push({ check, severity: 'error', message });
|