@primmel/primmel 1.18.0 → 1.20.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/src/check-rules.d.ts.map +1 -1
- package/dist/src/check-rules.js +30 -0
- package/dist/src/check-rules.js.map +1 -1
- package/dist/src/check.d.ts.map +1 -1
- package/dist/src/check.js +126 -0
- package/dist/src/check.js.map +1 -1
- package/dist/src/ser-des/config/conformanceTest.d.ts.map +1 -1
- package/dist/src/ser-des/config/conformanceTest.js +310 -0
- package/dist/src/ser-des/config/conformanceTest.js.map +1 -1
- package/dist/src/ser-des/config/note.d.ts.map +1 -1
- package/dist/src/ser-des/config/note.js.map +1 -1
- package/dist/src/ser-des/config/packageManifest.d.ts.map +1 -1
- package/dist/src/ser-des/config/packageManifest.js +43 -0
- package/dist/src/ser-des/config/packageManifest.js.map +1 -1
- package/dist/src/types/ConformanceTest.d.ts +86 -0
- package/dist/src/types/ConformanceTest.d.ts.map +1 -1
- package/dist/src/types/Package.d.ts +25 -0
- package/dist/src/types/Package.d.ts.map +1 -1
- package/dist-browser/index.mjs +937 -822
- package/dist-cli/primmel.mjs +419 -0
- package/package.json +1 -1
package/dist-cli/primmel.mjs
CHANGED
|
@@ -9169,6 +9169,227 @@ function parseInstances(block) {
|
|
|
9169
9169
|
}
|
|
9170
9170
|
return out;
|
|
9171
9171
|
}
|
|
9172
|
+
function parseProgramArgs(block) {
|
|
9173
|
+
const out = {};
|
|
9174
|
+
const t = tokenize(block);
|
|
9175
|
+
let i = 0;
|
|
9176
|
+
while (i < t.length) {
|
|
9177
|
+
const key = stripColon(t[i++]);
|
|
9178
|
+
if (i >= t.length) {
|
|
9179
|
+
break;
|
|
9180
|
+
}
|
|
9181
|
+
if (t[i] === ":") {
|
|
9182
|
+
i++;
|
|
9183
|
+
}
|
|
9184
|
+
if (i < t.length) {
|
|
9185
|
+
const read = readValueToken(t, i);
|
|
9186
|
+
out[key] = stripWrapping(read.text);
|
|
9187
|
+
i = read.next;
|
|
9188
|
+
}
|
|
9189
|
+
}
|
|
9190
|
+
return out;
|
|
9191
|
+
}
|
|
9192
|
+
function parseVerify(block) {
|
|
9193
|
+
const v = { read: "", tolerance: "" };
|
|
9194
|
+
const t = tokenize(block);
|
|
9195
|
+
let i = 0;
|
|
9196
|
+
while (i < t.length) {
|
|
9197
|
+
const cmd = t[i++];
|
|
9198
|
+
if (i >= t.length) {
|
|
9199
|
+
break;
|
|
9200
|
+
}
|
|
9201
|
+
if (cmd === "read") {
|
|
9202
|
+
v.read = stripWrapping(t[i++]);
|
|
9203
|
+
} else if (cmd === "tolerance") {
|
|
9204
|
+
v.tolerance = stripWrapping(t[i++]);
|
|
9205
|
+
} else {
|
|
9206
|
+
unwrapBlock(t[i++]);
|
|
9207
|
+
}
|
|
9208
|
+
}
|
|
9209
|
+
return v;
|
|
9210
|
+
}
|
|
9211
|
+
function parseProgramBody(block, entryKeyword, makeEntry, parseEntryFacets, errCtx) {
|
|
9212
|
+
const program = {
|
|
9213
|
+
description: "",
|
|
9214
|
+
entries: [],
|
|
9215
|
+
sourceRefs: []
|
|
9216
|
+
};
|
|
9217
|
+
const t = tokenize(block);
|
|
9218
|
+
let i = 0;
|
|
9219
|
+
while (i < t.length) {
|
|
9220
|
+
const command = t[i++];
|
|
9221
|
+
if (i >= t.length) {
|
|
9222
|
+
throw new Error(
|
|
9223
|
+
`Parsing error: ${errCtx}: Expecting value for ${command}`
|
|
9224
|
+
);
|
|
9225
|
+
}
|
|
9226
|
+
if (command === "description") {
|
|
9227
|
+
program.description = stripWrapping(t[i++]);
|
|
9228
|
+
} else if (command === "source") {
|
|
9229
|
+
program.sourceRefs.push(readSource(unwrapBlock(t[i++])));
|
|
9230
|
+
} else if (command === "ref") {
|
|
9231
|
+
const rr = parseRef(t, i, stripWrapping, unwrapBlock);
|
|
9232
|
+
foldRefIntoLegacy(program, rr.ref);
|
|
9233
|
+
i = rr.next;
|
|
9234
|
+
} else if (command === entryKeyword) {
|
|
9235
|
+
const entry = makeEntry();
|
|
9236
|
+
let value = t[i++];
|
|
9237
|
+
if (!value.startsWith("{")) {
|
|
9238
|
+
entry.order = Number(stripWrapping(value));
|
|
9239
|
+
value = i < t.length && t[i].startsWith("{") ? t[i++] : "";
|
|
9240
|
+
}
|
|
9241
|
+
if (value.startsWith("{")) {
|
|
9242
|
+
parseEntryFacets(unwrapBlock(value), entry);
|
|
9243
|
+
}
|
|
9244
|
+
program.entries.push(entry);
|
|
9245
|
+
} else {
|
|
9246
|
+
i = skipUnknownValue(t, i, command);
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
return program;
|
|
9250
|
+
}
|
|
9251
|
+
function parsePreparation(block, id) {
|
|
9252
|
+
return parseProgramBody(
|
|
9253
|
+
block,
|
|
9254
|
+
"step",
|
|
9255
|
+
() => ({
|
|
9256
|
+
order: null,
|
|
9257
|
+
action: "",
|
|
9258
|
+
drive: "",
|
|
9259
|
+
args: {},
|
|
9260
|
+
hold: "",
|
|
9261
|
+
verify: null
|
|
9262
|
+
}),
|
|
9263
|
+
(stepBlock, step) => {
|
|
9264
|
+
forEachEntry(
|
|
9265
|
+
stepBlock,
|
|
9266
|
+
(facet, value) => {
|
|
9267
|
+
if (facet === "action") {
|
|
9268
|
+
step.action = unwrapped(value);
|
|
9269
|
+
} else if (facet === "drive") {
|
|
9270
|
+
step.drive = stripWrapping(value());
|
|
9271
|
+
} else if (facet === "args") {
|
|
9272
|
+
step.args = parseProgramArgs(unwrapBlock(value()));
|
|
9273
|
+
} else if (facet === "hold") {
|
|
9274
|
+
step.hold = stripWrapping(value());
|
|
9275
|
+
} else if (facet === "verify") {
|
|
9276
|
+
step.verify = parseVerify(unwrapBlock(value()));
|
|
9277
|
+
} else {
|
|
9278
|
+
return false;
|
|
9279
|
+
}
|
|
9280
|
+
return true;
|
|
9281
|
+
},
|
|
9282
|
+
{ construct: "conformance_test.preparation", id }
|
|
9283
|
+
);
|
|
9284
|
+
},
|
|
9285
|
+
`conformance_test.preparation. ID ${id}`
|
|
9286
|
+
);
|
|
9287
|
+
}
|
|
9288
|
+
function parseStimulus(block, id) {
|
|
9289
|
+
return parseProgramBody(
|
|
9290
|
+
block,
|
|
9291
|
+
"point",
|
|
9292
|
+
() => ({
|
|
9293
|
+
order: null,
|
|
9294
|
+
drive: "",
|
|
9295
|
+
args: {},
|
|
9296
|
+
hold: "",
|
|
9297
|
+
freshWithin: "",
|
|
9298
|
+
acceptance: ""
|
|
9299
|
+
}),
|
|
9300
|
+
(pointBlock, point) => {
|
|
9301
|
+
forEachEntry(
|
|
9302
|
+
pointBlock,
|
|
9303
|
+
(facet, value) => {
|
|
9304
|
+
if (facet === "drive") {
|
|
9305
|
+
point.drive = stripWrapping(value());
|
|
9306
|
+
} else if (facet === "args") {
|
|
9307
|
+
point.args = parseProgramArgs(unwrapBlock(value()));
|
|
9308
|
+
} else if (facet === "hold") {
|
|
9309
|
+
point.hold = stripWrapping(value());
|
|
9310
|
+
} else if (facet === "fresh_within") {
|
|
9311
|
+
point.freshWithin = stripWrapping(value());
|
|
9312
|
+
} else if (facet === "acceptance") {
|
|
9313
|
+
point.acceptance = stripWrapping(value());
|
|
9314
|
+
} else {
|
|
9315
|
+
return false;
|
|
9316
|
+
}
|
|
9317
|
+
return true;
|
|
9318
|
+
},
|
|
9319
|
+
{ construct: "conformance_test.stimulus", id }
|
|
9320
|
+
);
|
|
9321
|
+
},
|
|
9322
|
+
`conformance_test.stimulus. ID ${id}`
|
|
9323
|
+
);
|
|
9324
|
+
}
|
|
9325
|
+
function dumpProgramArgs(args2) {
|
|
9326
|
+
const keys = Object.keys(args2);
|
|
9327
|
+
if (keys.length === 0) {
|
|
9328
|
+
return "";
|
|
9329
|
+
}
|
|
9330
|
+
return " args { " + keys.map((k) => k + ': "' + escapeString(args2[k]) + '"').join(" ") + " }";
|
|
9331
|
+
}
|
|
9332
|
+
function dumpPreparation(program) {
|
|
9333
|
+
let out = " preparation {\n";
|
|
9334
|
+
if (program.description !== "") {
|
|
9335
|
+
out += ' description "' + escapeString(program.description) + '"\n';
|
|
9336
|
+
}
|
|
9337
|
+
for (const step of program.entries) {
|
|
9338
|
+
out += " step" + (step.order !== null ? " " + step.order : "") + " {";
|
|
9339
|
+
if (step.action !== "") {
|
|
9340
|
+
out += ' action "' + escapeString(step.action) + '"';
|
|
9341
|
+
}
|
|
9342
|
+
if (step.drive !== "") {
|
|
9343
|
+
out += " drive " + dumpBareSafe(step.drive);
|
|
9344
|
+
}
|
|
9345
|
+
out += dumpProgramArgs(step.args);
|
|
9346
|
+
if (step.hold !== "") {
|
|
9347
|
+
out += " hold " + dumpBareSafe(step.hold);
|
|
9348
|
+
}
|
|
9349
|
+
if (step.verify) {
|
|
9350
|
+
out += " verify {";
|
|
9351
|
+
if (step.verify.read !== "") {
|
|
9352
|
+
out += " read " + dumpBareSafe(step.verify.read);
|
|
9353
|
+
}
|
|
9354
|
+
if (step.verify.tolerance !== "") {
|
|
9355
|
+
out += " tolerance " + dumpBareSafe(step.verify.tolerance);
|
|
9356
|
+
}
|
|
9357
|
+
out += " }";
|
|
9358
|
+
}
|
|
9359
|
+
out += " }\n";
|
|
9360
|
+
}
|
|
9361
|
+
for (const src of program.sourceRefs) {
|
|
9362
|
+
out += dumpSourceRefAsRef(src, " ", escapeString);
|
|
9363
|
+
}
|
|
9364
|
+
return out + " }\n";
|
|
9365
|
+
}
|
|
9366
|
+
function dumpStimulus(program) {
|
|
9367
|
+
let out = " stimulus {\n";
|
|
9368
|
+
if (program.description !== "") {
|
|
9369
|
+
out += ' description "' + escapeString(program.description) + '"\n';
|
|
9370
|
+
}
|
|
9371
|
+
for (const point of program.entries) {
|
|
9372
|
+
out += " point" + (point.order !== null ? " " + point.order : "") + " {";
|
|
9373
|
+
if (point.drive !== "") {
|
|
9374
|
+
out += " drive " + dumpBareSafe(point.drive);
|
|
9375
|
+
}
|
|
9376
|
+
out += dumpProgramArgs(point.args);
|
|
9377
|
+
if (point.hold !== "") {
|
|
9378
|
+
out += " hold " + dumpBareSafe(point.hold);
|
|
9379
|
+
}
|
|
9380
|
+
if (point.freshWithin !== "") {
|
|
9381
|
+
out += " fresh_within " + dumpBareSafe(point.freshWithin);
|
|
9382
|
+
}
|
|
9383
|
+
if (point.acceptance !== "") {
|
|
9384
|
+
out += " acceptance " + dumpBareSafe(point.acceptance);
|
|
9385
|
+
}
|
|
9386
|
+
out += " }\n";
|
|
9387
|
+
}
|
|
9388
|
+
for (const src of program.sourceRefs) {
|
|
9389
|
+
out += dumpSourceRefAsRef(src, " ", escapeString);
|
|
9390
|
+
}
|
|
9391
|
+
return out + " }\n";
|
|
9392
|
+
}
|
|
9172
9393
|
var parseConformanceTest = function(id, data) {
|
|
9173
9394
|
const result = {
|
|
9174
9395
|
id,
|
|
@@ -9180,6 +9401,8 @@ var parseConformanceTest = function(id, data) {
|
|
|
9180
9401
|
bindsTo: [],
|
|
9181
9402
|
applicability: [],
|
|
9182
9403
|
procedure: [],
|
|
9404
|
+
preparation: null,
|
|
9405
|
+
stimulus: null,
|
|
9183
9406
|
measurements: [],
|
|
9184
9407
|
kind: "",
|
|
9185
9408
|
obligation: "",
|
|
@@ -9302,6 +9525,10 @@ var parseConformanceTest = function(id, data) {
|
|
|
9302
9525
|
}
|
|
9303
9526
|
} else if (keyword === "procedure_steps") {
|
|
9304
9527
|
result.procedureSteps = readStringList(value());
|
|
9528
|
+
} else if (keyword === "preparation") {
|
|
9529
|
+
result.preparation = parsePreparation(unwrapBlock(value()), id);
|
|
9530
|
+
} else if (keyword === "stimulus") {
|
|
9531
|
+
result.stimulus = parseStimulus(unwrapBlock(value()), id);
|
|
9305
9532
|
} else if (keyword === "kind") {
|
|
9306
9533
|
result.kind = stripWrapping(value());
|
|
9307
9534
|
} else if (keyword === "obligation") {
|
|
@@ -9432,6 +9659,12 @@ var dumpConformanceTest = function(ct) {
|
|
|
9432
9659
|
if (ct.procedureSteps && ct.procedureSteps.length > 0) {
|
|
9433
9660
|
out += " procedure_steps { " + ct.procedureSteps.join(" ") + " }\n";
|
|
9434
9661
|
}
|
|
9662
|
+
if (ct.preparation) {
|
|
9663
|
+
out += dumpPreparation(ct.preparation);
|
|
9664
|
+
}
|
|
9665
|
+
if (ct.stimulus) {
|
|
9666
|
+
out += dumpStimulus(ct.stimulus);
|
|
9667
|
+
}
|
|
9435
9668
|
if (ct.measurements.length > 0) {
|
|
9436
9669
|
out += " validate_measurement {\n";
|
|
9437
9670
|
for (const m of ct.measurements) {
|
|
@@ -11574,6 +11807,13 @@ var parsePackage = function(data) {
|
|
|
11574
11807
|
i = cc.next;
|
|
11575
11808
|
} else if (cmd === "scheme_type" || cmd === "schemeType") {
|
|
11576
11809
|
manifest.schemeType = stripWrapping(t[i++]);
|
|
11810
|
+
} else if (cmd === "license_key" || cmd === "licenseKey") {
|
|
11811
|
+
if (manifest.licenseKey !== void 0) {
|
|
11812
|
+
(manifest.licenseKeyDuplicates ??= []).push(manifest.licenseKey);
|
|
11813
|
+
}
|
|
11814
|
+
manifest.licenseKey = stripWrapping(t[i++]);
|
|
11815
|
+
} else if (cmd === "license_holder" || cmd === "licenseHolder") {
|
|
11816
|
+
manifest.licenseHolder = stripWrapping(t[i++]);
|
|
11577
11817
|
} else if (cmd === "provides") {
|
|
11578
11818
|
manifest.provides = readList(t[i++]);
|
|
11579
11819
|
} else if (cmd === "requires") {
|
|
@@ -22655,6 +22895,40 @@ var CHECK_RULES = [
|
|
|
22655
22895
|
"normal",
|
|
22656
22896
|
"TODO.roadmap/36, doctrine ch. 15 \xA715.3/\xA715.9"
|
|
22657
22897
|
),
|
|
22898
|
+
// ── the license facets (smart TODO.external-refs/04; the entitlement
|
|
22899
|
+
// facet + catalog) ── The manifest declares the package's licensed
|
|
22900
|
+
// nature: one `license_key` (the entitlement catalog key the
|
|
22901
|
+
// platform's license gate reads) and its `license_holder` (the
|
|
22902
|
+
// copyright owner). A package without a key is public content — the
|
|
22903
|
+
// OIML models stay license-free — so every leg keys on the key's
|
|
22904
|
+
// presence. C144: the catalog-key shape. C145: at most one
|
|
22905
|
+
// license_key per package (the parser records the overwritten earlier
|
|
22906
|
+
// declarations). C146: the holder required with the key (a bare
|
|
22907
|
+
// holder stays legal — attribution without an entitlement claim).
|
|
22908
|
+
R(
|
|
22909
|
+
"C144",
|
|
22910
|
+
"license-key-shape",
|
|
22911
|
+
"supply-chain",
|
|
22912
|
+
"error",
|
|
22913
|
+
"normal",
|
|
22914
|
+
"smart TODO.external-refs/04 (the entitlement facet + catalog)"
|
|
22915
|
+
),
|
|
22916
|
+
R(
|
|
22917
|
+
"C145",
|
|
22918
|
+
"license-key-unique",
|
|
22919
|
+
"supply-chain",
|
|
22920
|
+
"error",
|
|
22921
|
+
"normal",
|
|
22922
|
+
"smart TODO.external-refs/04 (the entitlement facet + catalog)"
|
|
22923
|
+
),
|
|
22924
|
+
R(
|
|
22925
|
+
"C146",
|
|
22926
|
+
"license-holder-required",
|
|
22927
|
+
"supply-chain",
|
|
22928
|
+
"error",
|
|
22929
|
+
"normal",
|
|
22930
|
+
"smart TODO.external-refs/04 (the entitlement facet + catalog)"
|
|
22931
|
+
),
|
|
22658
22932
|
// ── subject-intrinsic constraints (TODO.roadmap/51, BUG.R60-SSOT gap 7)
|
|
22659
22933
|
// The constraint construct's declaration shape — the kernel mirror of
|
|
22660
22934
|
// the OIML SMART constraints.yaml schema (stereotype «inv», one ocl{…}
|
|
@@ -22804,6 +23078,37 @@ var CHECK_RULES = [
|
|
|
22804
23078
|
"normal",
|
|
22805
23079
|
"smart architecture-gaps-2026-07.md E10, data/schemas/test-sequences.yaml"
|
|
22806
23080
|
),
|
|
23081
|
+
// ── the executable test programs (smart TODO.twin-demo/03 — the
|
|
23082
|
+
// model-drive bindings) ──
|
|
23083
|
+
// The conformance test's preparation/stimulus facets are the
|
|
23084
|
+
// machine-executable half of the test declaration. C147 is the
|
|
23085
|
+
// program shape: entry orders are positive integers unique in the
|
|
23086
|
+
// program; a preparation step carries action AND/OR drive; a verify
|
|
23087
|
+
// block carries read AND tolerance; a stimulus point carries a drive
|
|
23088
|
+
// and an acceptance reference. C148 is the duration discipline: hold
|
|
23089
|
+
// and fresh_within parse as duration windows (the serve contract's
|
|
23090
|
+
// vocabulary), and a stimulus point declares fresh_within (§14.12's
|
|
23091
|
+
// no-stale-semantics doctrine). Reference RESOLUTION (the drive
|
|
23092
|
+
// operation names against the twin kind's declared vocabulary, the
|
|
23093
|
+
// acceptance/tolerance refs against requirement nodes) is the
|
|
23094
|
+
// consumer-side conformance leg's job — the kernel checks
|
|
23095
|
+
// syntax/shape only (the C92/C93 vs R39 split).
|
|
23096
|
+
R(
|
|
23097
|
+
"C147",
|
|
23098
|
+
"test-program-shape",
|
|
23099
|
+
"base",
|
|
23100
|
+
"error",
|
|
23101
|
+
"normal",
|
|
23102
|
+
"smart TODO.twin-demo/03 (the model-drive bindings)"
|
|
23103
|
+
),
|
|
23104
|
+
R(
|
|
23105
|
+
"C148",
|
|
23106
|
+
"test-program-duration",
|
|
23107
|
+
"base",
|
|
23108
|
+
"error",
|
|
23109
|
+
"normal",
|
|
23110
|
+
"smart TODO.twin-demo/03 (the model-drive bindings)"
|
|
23111
|
+
),
|
|
22807
23112
|
// ── the per-test evaluation-formula traces (smart gap-close E11,
|
|
22808
23113
|
// analysis/architecture-gaps-2026-07.md; the smart contract
|
|
22809
23114
|
// data/schemas/formulas-used.yaml) ──
|
|
@@ -26319,6 +26624,29 @@ function checkPackage(dir, options = {}) {
|
|
|
26319
26624
|
}
|
|
26320
26625
|
}
|
|
26321
26626
|
}
|
|
26627
|
+
{
|
|
26628
|
+
const m = standard.packageManifest;
|
|
26629
|
+
if (m && m.licenseKey !== void 0) {
|
|
26630
|
+
if (!/^std:[a-z0-9-]+$/.test(m.licenseKey)) {
|
|
26631
|
+
err(
|
|
26632
|
+
"C144",
|
|
26633
|
+
`package "${m.id}": license_key "${m.licenseKey}" is not a catalog key \u2014 the key shape is std: followed by a lowercase kebab id, ^std:[a-z0-9-]+$ (license-key-shape)`
|
|
26634
|
+
);
|
|
26635
|
+
}
|
|
26636
|
+
for (const dup of m.licenseKeyDuplicates ?? []) {
|
|
26637
|
+
err(
|
|
26638
|
+
"C145",
|
|
26639
|
+
`package "${m.id}": license_key declared again ("${dup}" before "${m.licenseKey}") \u2014 a package declares at most one license key; the last declaration wins (license-key-unique)`
|
|
26640
|
+
);
|
|
26641
|
+
}
|
|
26642
|
+
if (!m.licenseHolder) {
|
|
26643
|
+
err(
|
|
26644
|
+
"C146",
|
|
26645
|
+
`package "${m.id}": license_key "${m.licenseKey}" without a license_holder \u2014 a licensed package declares its copyright owner (license-holder-required)`
|
|
26646
|
+
);
|
|
26647
|
+
}
|
|
26648
|
+
}
|
|
26649
|
+
}
|
|
26322
26650
|
{
|
|
26323
26651
|
const schemeTypeIds = new Set((standard.schemeTypes ?? []).map((t) => t.id));
|
|
26324
26652
|
const menuIds = new Set(
|
|
@@ -26739,6 +27067,97 @@ function checkPackage(dir, options = {}) {
|
|
|
26739
27067
|
}
|
|
26740
27068
|
}
|
|
26741
27069
|
}
|
|
27070
|
+
{
|
|
27071
|
+
for (const ct of standard.conformanceTests ?? []) {
|
|
27072
|
+
const prep = ct.preparation;
|
|
27073
|
+
if (prep) {
|
|
27074
|
+
const orders = /* @__PURE__ */ new Set();
|
|
27075
|
+
for (const step of prep.entries) {
|
|
27076
|
+
const at = step.order !== null ? `preparation step ${step.order}` : "a preparation step";
|
|
27077
|
+
if (step.order === null || !Number.isInteger(step.order) || step.order < 1) {
|
|
27078
|
+
err(
|
|
27079
|
+
"C147",
|
|
27080
|
+
`conformance_test ${ct.id}: ${at} declares no positive-integer order (got ${step.order === null ? "none" : step.order}) \u2014 the step order is a positive integer (test-program-shape)`
|
|
27081
|
+
);
|
|
27082
|
+
} else if (orders.has(step.order)) {
|
|
27083
|
+
err(
|
|
27084
|
+
"C147",
|
|
27085
|
+
`conformance_test ${ct.id}: preparation step order ${step.order} is declared twice \u2014 the order is unique within the program (test-program-shape)`
|
|
27086
|
+
);
|
|
27087
|
+
} else {
|
|
27088
|
+
orders.add(step.order);
|
|
27089
|
+
}
|
|
27090
|
+
if (step.action === "" && step.drive === "") {
|
|
27091
|
+
err(
|
|
27092
|
+
"C147",
|
|
27093
|
+
`conformance_test ${ct.id}: ${at} declares neither action nor drive \u2014 a preparation step carries a prose action AND/OR a drive reference (test-program-shape)`
|
|
27094
|
+
);
|
|
27095
|
+
}
|
|
27096
|
+
if (step.verify && (step.verify.read === "" || step.verify.tolerance === "")) {
|
|
27097
|
+
err(
|
|
27098
|
+
"C147",
|
|
27099
|
+
`conformance_test ${ct.id}: ${at} declares a verify block without ${step.verify.read === "" ? "read" : "tolerance"} \u2014 the verification names what to read AND the tolerance reference (test-program-shape)`
|
|
27100
|
+
);
|
|
27101
|
+
}
|
|
27102
|
+
if (step.hold !== "" && parseFreshnessWindow(step.hold) === null) {
|
|
27103
|
+
err(
|
|
27104
|
+
"C148",
|
|
27105
|
+
`conformance_test ${ct.id}: ${at} hold "${step.hold}" is not a parseable duration (shorthand 500ms/5s/1min/1h/1d or ISO 8601 with fixed-length components, e.g. PT30M) (test-program-duration)`
|
|
27106
|
+
);
|
|
27107
|
+
}
|
|
27108
|
+
}
|
|
27109
|
+
}
|
|
27110
|
+
const stim = ct.stimulus;
|
|
27111
|
+
if (stim) {
|
|
27112
|
+
const orders = /* @__PURE__ */ new Set();
|
|
27113
|
+
for (const point of stim.entries) {
|
|
27114
|
+
const at = point.order !== null ? `stimulus point ${point.order}` : "a stimulus point";
|
|
27115
|
+
if (point.order === null || !Number.isInteger(point.order) || point.order < 1) {
|
|
27116
|
+
err(
|
|
27117
|
+
"C147",
|
|
27118
|
+
`conformance_test ${ct.id}: ${at} declares no positive-integer order (got ${point.order === null ? "none" : point.order}) \u2014 the point order is a positive integer (test-program-shape)`
|
|
27119
|
+
);
|
|
27120
|
+
} else if (orders.has(point.order)) {
|
|
27121
|
+
err(
|
|
27122
|
+
"C147",
|
|
27123
|
+
`conformance_test ${ct.id}: stimulus point order ${point.order} is declared twice \u2014 the order is unique within the program (test-program-shape)`
|
|
27124
|
+
);
|
|
27125
|
+
} else {
|
|
27126
|
+
orders.add(point.order);
|
|
27127
|
+
}
|
|
27128
|
+
if (point.drive === "") {
|
|
27129
|
+
err(
|
|
27130
|
+
"C147",
|
|
27131
|
+
`conformance_test ${ct.id}: ${at} declares no drive \u2014 a stimulus point names the drive operation (test-program-shape)`
|
|
27132
|
+
);
|
|
27133
|
+
}
|
|
27134
|
+
if (point.acceptance === "") {
|
|
27135
|
+
err(
|
|
27136
|
+
"C147",
|
|
27137
|
+
`conformance_test ${ct.id}: ${at} declares no acceptance \u2014 a stimulus point names the MPE/requirement node it is judged against (test-program-shape)`
|
|
27138
|
+
);
|
|
27139
|
+
}
|
|
27140
|
+
if (point.hold !== "" && parseFreshnessWindow(point.hold) === null) {
|
|
27141
|
+
err(
|
|
27142
|
+
"C148",
|
|
27143
|
+
`conformance_test ${ct.id}: ${at} hold "${point.hold}" is not a parseable duration (shorthand 500ms/5s/1min/1h/1d or ISO 8601 with fixed-length components, e.g. PT30M) (test-program-duration)`
|
|
27144
|
+
);
|
|
27145
|
+
}
|
|
27146
|
+
if (point.freshWithin === "") {
|
|
27147
|
+
err(
|
|
27148
|
+
"C148",
|
|
27149
|
+
`conformance_test ${ct.id}: ${at} declares no fresh_within \u2014 a measurement point's read carries its freshness bound (\xA714.12: no stale semantics) (test-program-duration)`
|
|
27150
|
+
);
|
|
27151
|
+
} else if (parseFreshnessWindow(point.freshWithin) === null) {
|
|
27152
|
+
err(
|
|
27153
|
+
"C148",
|
|
27154
|
+
`conformance_test ${ct.id}: ${at} fresh_within "${point.freshWithin}" is not a parseable freshness window (shorthand 500ms/5s/1min/1h/1d or ISO 8601 with fixed-length components, e.g. PT5S) (test-program-duration)`
|
|
27155
|
+
);
|
|
27156
|
+
}
|
|
27157
|
+
}
|
|
27158
|
+
}
|
|
27159
|
+
}
|
|
27160
|
+
}
|
|
26742
27161
|
{
|
|
26743
27162
|
for (const fu of standard.formulasUsed ?? []) {
|
|
26744
27163
|
if (stripWrapping(fu.id) === "") {
|
package/package.json
CHANGED