elementary-assertions 1.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/CHANGELOG.md +353 -0
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/bin/elementary-assertions.js +8 -0
- package/docs/DEV_TOOLING.md +98 -0
- package/docs/NPM_RELEASE.md +177 -0
- package/docs/OPERATIONAL.md +159 -0
- package/docs/RELEASE_NOTES_TEMPLATE.md +37 -0
- package/docs/REPO_WORKFLOWS.md +48 -0
- package/package.json +46 -0
- package/src/core/accepted-annotations.js +44 -0
- package/src/core/assertions.js +2304 -0
- package/src/core/determinism.js +95 -0
- package/src/core/diagnostics.js +496 -0
- package/src/core/ids.js +9 -0
- package/src/core/mention-builder.js +272 -0
- package/src/core/mention-evidence.js +52 -0
- package/src/core/mention-head-resolution.js +108 -0
- package/src/core/mention-materialization.js +31 -0
- package/src/core/mentions.js +149 -0
- package/src/core/output.js +296 -0
- package/src/core/projection.js +192 -0
- package/src/core/roles.js +164 -0
- package/src/core/strings.js +7 -0
- package/src/core/tokens.js +53 -0
- package/src/core/upstream.js +31 -0
- package/src/index.js +6 -0
- package/src/render/index.js +5 -0
- package/src/render/layouts/compact.js +10 -0
- package/src/render/layouts/meaning.js +7 -0
- package/src/render/layouts/readable.js +7 -0
- package/src/render/layouts/table.js +7 -0
- package/src/render/render.js +931 -0
- package/src/run.js +278 -0
- package/src/schema/seed.elementary-assertions.schema.json +1751 -0
- package/src/tools/cli.js +158 -0
- package/src/tools/index.js +6 -0
- package/src/tools/io.js +55 -0
- package/src/validate/ajv.js +20 -0
- package/src/validate/coverage.js +215 -0
- package/src/validate/determinism.js +115 -0
- package/src/validate/diagnostics-strict.js +392 -0
- package/src/validate/errors.js +19 -0
- package/src/validate/index.js +20 -0
- package/src/validate/integrity.js +41 -0
- package/src/validate/invariants.js +157 -0
- package/src/validate/references.js +110 -0
- package/src/validate/schema.js +50 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const { failValidation } = require("./errors");
|
|
2
|
+
|
|
3
|
+
function ensureUniqueIds(items, label) {
|
|
4
|
+
const ids = (items || []).map((x) => x && x.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
5
|
+
if (new Set(ids).size !== ids.length) {
|
|
6
|
+
failValidation("EA_VALIDATE_DUPLICATE_IDS", `Integrity error: duplicate ${label} ids detected.`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function buildReferenceMaps(doc) {
|
|
11
|
+
const tokenById = new Map((doc.tokens || []).map((t) => [t && t.id, t]).filter(([id]) => typeof id === "string" && id.length > 0));
|
|
12
|
+
const mentionById = new Map((doc.mentions || []).map((m) => [m && m.id, m]).filter(([id]) => typeof id === "string" && id.length > 0));
|
|
13
|
+
const assertionById = new Map((doc.assertions || []).map((a) => [a && a.id, a]).filter(([id]) => typeof id === "string" && id.length > 0));
|
|
14
|
+
return { tokenById, mentionById, assertionById };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function validateMentionReferences(doc, tokenById) {
|
|
18
|
+
for (const mention of doc.mentions || []) {
|
|
19
|
+
const tokenIds = Array.isArray(mention && mention.token_ids) ? mention.token_ids : [];
|
|
20
|
+
if (new Set(tokenIds).size !== tokenIds.length) {
|
|
21
|
+
failValidation("EA_VALIDATE_DUPLICATE_MENTION_TOKEN_IDS", `Integrity error: mention ${mention.id} has duplicate token_ids.`);
|
|
22
|
+
}
|
|
23
|
+
for (const tid of tokenIds) {
|
|
24
|
+
if (!tokenById.has(tid)) {
|
|
25
|
+
failValidation("EA_VALIDATE_UNKNOWN_TOKEN_REFERENCE", `Integrity error: mention ${mention.id} references unknown token ${tid}.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const headTokenId = mention && mention.head_token_id;
|
|
29
|
+
if (typeof headTokenId !== "string" || !tokenById.has(headTokenId)) {
|
|
30
|
+
failValidation("EA_VALIDATE_INVALID_HEAD_TOKEN", `Integrity error: mention ${mention.id} has invalid head_token_id.`);
|
|
31
|
+
}
|
|
32
|
+
if (!tokenIds.includes(headTokenId)) {
|
|
33
|
+
failValidation("EA_VALIDATE_HEAD_TOKEN_NOT_IN_MENTION", `Integrity error: mention ${mention.id} head_token_id must be included in token_ids.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function validateAssertionReferences(assertion, assertionId, mentionById, tokenById) {
|
|
39
|
+
const predMentionId = assertion && assertion.predicate ? assertion.predicate.mention_id : null;
|
|
40
|
+
if (!predMentionId || !mentionById.has(predMentionId)) {
|
|
41
|
+
failValidation("EA_VALIDATE_INVALID_PREDICATE_MENTION", `Integrity error: assertion ${assertionId} has invalid predicate mention.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!Array.isArray(assertion.arguments) || !Array.isArray(assertion.modifiers) || !Array.isArray(assertion.operators)) {
|
|
45
|
+
failValidation("EA_VALIDATE_ROLE_ARRAYS_REQUIRED", `Integrity error: assertion ${assertionId} must contain arguments/modifiers/operators arrays.`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const evidence = assertion && assertion.evidence && typeof assertion.evidence === "object" ? assertion.evidence : {};
|
|
49
|
+
const evidenceTokenIds = Array.isArray(evidence.token_ids) ? evidence.token_ids : [];
|
|
50
|
+
for (const tid of evidenceTokenIds) {
|
|
51
|
+
if (!tokenById.has(tid)) {
|
|
52
|
+
failValidation("EA_VALIDATE_UNKNOWN_ASSERTION_EVIDENCE_TOKEN", `Integrity error: assertion ${assertionId} evidence.token_ids references unknown token ${tid}.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const relationEvidence = Array.isArray(evidence.relation_evidence) ? evidence.relation_evidence : [];
|
|
57
|
+
for (const relEv of relationEvidence) {
|
|
58
|
+
if (relEv && relEv.from_token_id && !tokenById.has(relEv.from_token_id)) {
|
|
59
|
+
failValidation("EA_VALIDATE_UNKNOWN_RELATION_FROM_TOKEN", `Integrity error: assertion ${assertionId} relation_evidence references unknown from_token_id.`);
|
|
60
|
+
}
|
|
61
|
+
if (relEv && relEv.to_token_id && !tokenById.has(relEv.to_token_id)) {
|
|
62
|
+
failValidation("EA_VALIDATE_UNKNOWN_RELATION_TO_TOKEN", `Integrity error: assertion ${assertionId} relation_evidence references unknown to_token_id.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const entry of assertion.arguments || []) {
|
|
67
|
+
const mids = Array.isArray(entry && entry.mention_ids) ? entry.mention_ids : [];
|
|
68
|
+
for (const mid of mids) {
|
|
69
|
+
if (!mentionById.has(mid)) {
|
|
70
|
+
failValidation("EA_VALIDATE_UNKNOWN_ASSERTION_MENTION", `Integrity error: assertion ${assertionId} references unknown mention ${mid}.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const entry of assertion.modifiers || []) {
|
|
76
|
+
const mids = Array.isArray(entry && entry.mention_ids) ? entry.mention_ids : [];
|
|
77
|
+
for (const mid of mids) {
|
|
78
|
+
if (!mentionById.has(mid)) {
|
|
79
|
+
failValidation("EA_VALIDATE_UNKNOWN_ASSERTION_MENTION", `Integrity error: assertion ${assertionId} references unknown mention ${mid}.`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function validateSuppressedReferences(doc, assertionById) {
|
|
86
|
+
const suppressed = Array.isArray((((doc || {}).diagnostics) || {}).suppressed_assertions)
|
|
87
|
+
? doc.diagnostics.suppressed_assertions
|
|
88
|
+
: [];
|
|
89
|
+
ensureUniqueIds(suppressed, "suppressed assertion");
|
|
90
|
+
let prevSuppressedId = null;
|
|
91
|
+
for (const item of suppressed) {
|
|
92
|
+
const cur = String((item && item.id) || "");
|
|
93
|
+
if (prevSuppressedId && prevSuppressedId.localeCompare(cur) > 0) {
|
|
94
|
+
failValidation("EA_VALIDATE_SUPPRESSED_SORT_ORDER", "Integrity error: diagnostics.suppressed_assertions must be sorted by id.");
|
|
95
|
+
}
|
|
96
|
+
prevSuppressedId = cur;
|
|
97
|
+
const targetId = ((((item || {}).diagnostics) || {}).suppressed_by || {}).target_assertion_id;
|
|
98
|
+
if (typeof targetId === "string" && targetId.length > 0 && !assertionById.has(targetId)) {
|
|
99
|
+
failValidation("EA_VALIDATE_UNKNOWN_SUPPRESSED_TARGET", `Integrity error: suppressed assertion ${cur} references unknown target_assertion_id.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
ensureUniqueIds,
|
|
106
|
+
buildReferenceMaps,
|
|
107
|
+
validateMentionReferences,
|
|
108
|
+
validateAssertionReferences,
|
|
109
|
+
validateSuppressedReferences,
|
|
110
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const { failValidation } = require("./errors");
|
|
2
|
+
const { getSchemaValidator } = require("./ajv");
|
|
3
|
+
|
|
4
|
+
function rejectLegacySlots(doc) {
|
|
5
|
+
const assertions = Array.isArray(doc && doc.assertions) ? doc.assertions : [];
|
|
6
|
+
for (const assertion of assertions) {
|
|
7
|
+
if (assertion && Object.prototype.hasOwnProperty.call(assertion, "slots")) {
|
|
8
|
+
failValidation("EA_VALIDATE_LEGACY_SLOTS", "Invalid input: legacy assertions[*].slots is not supported.");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function validateSchemaShape(doc) {
|
|
14
|
+
if (!doc || typeof doc !== "object") failValidation("EA_VALIDATE_DOC_OBJECT", "Document must be an object.");
|
|
15
|
+
if (doc.stage !== "elementary_assertions") failValidation("EA_VALIDATE_STAGE", "Invalid stage: expected elementary_assertions.");
|
|
16
|
+
if (!doc.index_basis || doc.index_basis.text_field !== "canonical_text" || doc.index_basis.span_unit !== "utf16_code_units") {
|
|
17
|
+
failValidation("EA_VALIDATE_INDEX_BASIS", "Invalid index_basis. Expected canonical_text/utf16_code_units.");
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(doc.tokens)) failValidation("EA_VALIDATE_TOKENS_ARRAY", "Invalid document: tokens[] required.");
|
|
20
|
+
if (!Array.isArray(doc.mentions)) failValidation("EA_VALIDATE_MENTIONS_ARRAY", "Invalid document: mentions[] required.");
|
|
21
|
+
if (!Array.isArray(doc.assertions)) failValidation("EA_VALIDATE_ASSERTIONS_ARRAY", "Invalid document: assertions[] required.");
|
|
22
|
+
if (!doc.coverage || typeof doc.coverage !== "object") failValidation("EA_VALIDATE_COVERAGE_OBJECT", "Invalid document: coverage required.");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function toAjvIssue(err) {
|
|
26
|
+
return {
|
|
27
|
+
instancePath: String((err && err.instancePath) || ""),
|
|
28
|
+
schemaPath: String((err && err.schemaPath) || ""),
|
|
29
|
+
keyword: String((err && err.keyword) || ""),
|
|
30
|
+
message: String((err && err.message) || ""),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function validateSchemaContract(doc, options = {}) {
|
|
35
|
+
const validate = getSchemaValidator();
|
|
36
|
+
const ok = validate(doc);
|
|
37
|
+
if (ok) return;
|
|
38
|
+
|
|
39
|
+
const errors = Array.isArray(validate.errors) ? validate.errors.map(toAjvIssue) : [];
|
|
40
|
+
if (options && options.strict) {
|
|
41
|
+
failValidation("EA_VALIDATE_SCHEMA_CONTRACT", "Schema contract validation failed.", errors);
|
|
42
|
+
}
|
|
43
|
+
failValidation("EA_VALIDATE_SCHEMA_CONTRACT", "Schema contract validation failed.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
rejectLegacySlots,
|
|
48
|
+
validateSchemaShape,
|
|
49
|
+
validateSchemaContract,
|
|
50
|
+
};
|