@primmel/primmel 1.8.2 → 1.9.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/src/check-rules.d.ts.map +1 -1
- package/dist/src/check-rules.js +55 -0
- package/dist/src/check-rules.js.map +1 -1
- package/dist/src/check.d.ts +74 -2
- package/dist/src/check.d.ts.map +1 -1
- package/dist/src/check.js +514 -15
- package/dist/src/check.js.map +1 -1
- package/dist/src/export/impact.d.ts +45 -0
- package/dist/src/export/impact.d.ts.map +1 -0
- package/dist/src/export/impact.js +234 -0
- package/dist/src/export/impact.js.map +1 -0
- package/dist/src/export/rdf.d.ts.map +1 -1
- package/dist/src/export/rdf.js +4 -1
- package/dist/src/export/rdf.js.map +1 -1
- package/dist/src/export/retrieval.d.ts +408 -0
- package/dist/src/export/retrieval.d.ts.map +1 -0
- package/dist/src/export/retrieval.js +1156 -0
- package/dist/src/export/retrieval.js.map +1 -0
- package/dist/src/model-diff.d.ts +23 -0
- package/dist/src/model-diff.d.ts.map +1 -1
- package/dist/src/model-diff.js +132 -30
- package/dist/src/model-diff.js.map +1 -1
- package/dist/src/ser-des/config/calculation.d.ts.map +1 -1
- package/dist/src/ser-des/config/calculation.js +119 -4
- package/dist/src/ser-des/config/calculation.js.map +1 -1
- package/dist/src/ser-des/config/dimension.d.ts +12 -0
- package/dist/src/ser-des/config/dimension.d.ts.map +1 -0
- package/dist/src/ser-des/config/dimension.js +57 -0
- package/dist/src/ser-des/config/dimension.js.map +1 -0
- package/dist/src/ser-des/config/index.d.ts.map +1 -1
- package/dist/src/ser-des/config/index.js +5 -0
- package/dist/src/ser-des/config/index.js.map +1 -1
- package/dist/src/ser-des/config/packageManifest.d.ts.map +1 -1
- package/dist/src/ser-des/config/packageManifest.js +7 -1
- package/dist/src/ser-des/config/packageManifest.js.map +1 -1
- package/dist/src/ser-des/config/quantityRegister.d.ts.map +1 -1
- package/dist/src/ser-des/config/quantityRegister.js +38 -0
- package/dist/src/ser-des/config/quantityRegister.js.map +1 -1
- package/dist/src/ser-des/config/reference.d.ts.map +1 -1
- package/dist/src/ser-des/config/reference.js +18 -0
- package/dist/src/ser-des/config/reference.js.map +1 -1
- package/dist/src/ser-des/config/requirement.d.ts.map +1 -1
- package/dist/src/ser-des/config/requirement.js +47 -0
- package/dist/src/ser-des/config/requirement.js.map +1 -1
- package/dist/src/ser-des/config/subject.d.ts +10 -1
- package/dist/src/ser-des/config/subject.d.ts.map +1 -1
- package/dist/src/ser-des/config/subject.js +96 -63
- package/dist/src/ser-des/config/subject.js.map +1 -1
- package/dist/src/ser-des/config/term.d.ts.map +1 -1
- package/dist/src/ser-des/config/term.js +82 -20
- package/dist/src/ser-des/config/term.js.map +1 -1
- package/dist/src/ser-des/config/viewProfile.d.ts.map +1 -1
- package/dist/src/ser-des/config/viewProfile.js +22 -5
- package/dist/src/ser-des/config/viewProfile.js.map +1 -1
- package/dist/src/ser-des/parse-block.d.ts +1 -0
- package/dist/src/ser-des/parse-block.d.ts.map +1 -1
- package/dist/src/ser-des/parse-block.js +34 -0
- package/dist/src/ser-des/parse-block.js.map +1 -1
- package/dist/src/ser-des/parse.d.ts.map +1 -1
- package/dist/src/ser-des/parse.js +2 -0
- package/dist/src/ser-des/parse.js.map +1 -1
- package/dist/src/ser-des/types.d.ts +2 -1
- package/dist/src/ser-des/types.d.ts.map +1 -1
- package/dist/src/types/Calculation.d.ts +15 -0
- package/dist/src/types/Calculation.d.ts.map +1 -1
- package/dist/src/types/Package.d.ts +8 -0
- package/dist/src/types/Package.d.ts.map +1 -1
- package/dist/src/types/Quantity.d.ts +7 -0
- package/dist/src/types/Quantity.d.ts.map +1 -1
- package/dist/src/types/Reference.d.ts +11 -0
- package/dist/src/types/Reference.d.ts.map +1 -1
- package/dist/src/types/Requirement.d.ts +17 -0
- package/dist/src/types/Requirement.d.ts.map +1 -1
- package/dist/src/types/Standard.d.ts +2 -1
- package/dist/src/types/Standard.d.ts.map +1 -1
- package/dist/src/types/Subject.d.ts +10 -0
- package/dist/src/types/Subject.d.ts.map +1 -1
- package/dist/src/types/Term.d.ts +33 -1
- package/dist/src/types/Term.d.ts.map +1 -1
- package/dist-browser/index.mjs +1654 -1460
- package/dist-cli/primmel.mjs +2143 -188
- package/package.json +1 -1
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
3
|
+
// Retrieval export (primmel/primmel-ts#65 — the AI-native retrieval
|
|
4
|
+
// projection): the canonical, versioned serialization of a package's
|
|
5
|
+
// typed units for RAG and agent consumers.
|
|
6
|
+
//
|
|
7
|
+
// A Primmel package carries knowledge no document format holds — the
|
|
8
|
+
// machine limits, the applicability filters, the acceptance chains, the
|
|
9
|
+
// subject-intrinsic constraints. Retrieval consumers (the OIML SMART
|
|
10
|
+
// estate's model plane first among them) project every package into
|
|
11
|
+
// typed units and index them next to the prose corpus; today each
|
|
12
|
+
// consumer re-derives that projection and the re-derivation is where
|
|
13
|
+
// the mapping bugs live. This module is the UPSTREAM canonical form of
|
|
14
|
+
// that projection: one export, one contract, versioned as
|
|
15
|
+
// `primmel-retrieval/1`.
|
|
16
|
+
//
|
|
17
|
+
// The contract (the guarantees the issue asks for):
|
|
18
|
+
//
|
|
19
|
+
// 1. CLAUSE URNS FIRST-CLASS, ALWAYS. Every unit's provenance is the
|
|
20
|
+
// DOCUMENT's own clause numbering plus the document identifier —
|
|
21
|
+
// `clause: { doc, clause, urn }` with urn = <doc>#clause-<clause>
|
|
22
|
+
// (e.g. urn:oiml:pub:r:60-1:2021#clause-3.6). A producer-internal
|
|
23
|
+
// anchor (a metanorma UUID, `_eb46a3a3-…` style) is NOT a clause:
|
|
24
|
+
// it rides as the optional `anchor` extra, never as the clause,
|
|
25
|
+
// and units whose only provenance is an anchor are counted in the
|
|
26
|
+
// stats (anchorOnlyProvenance) — visible debt, never silently
|
|
27
|
+
// presented as citable provenance.
|
|
28
|
+
// 2. CANONICAL EDITION SEMANTICS. The document block exposes two
|
|
29
|
+
// distinct, stable fields: `edition` (the PUBLICATION edition the
|
|
30
|
+
// model corresponds to — the manifest's newest `editions` entry,
|
|
31
|
+
// the baseUrn's year segment as fallback) and `model_version`
|
|
32
|
+
// (the package's own `version`). Edition steering reads the first;
|
|
33
|
+
// freshness gating reads the second; neither ever borrows the
|
|
34
|
+
// other's value.
|
|
35
|
+
// 3. THE FLAT RETRIEVAL FACET. Every unit carries `facet` — one flat
|
|
36
|
+
// scalar map (string values only), versioned as
|
|
37
|
+
// `retrieval-facet/1` on the document's `facet_version`: the
|
|
38
|
+
// document fields (identifier, doc number, doctype, edition,
|
|
39
|
+
// language), the clause anchor, the unit kind, the applicability
|
|
40
|
+
// dimensions (`app_<dim>` keys), and the currency keys (unit_id,
|
|
41
|
+
// unit_hash, model_version). Retrieval indexes accept only scalar
|
|
42
|
+
// metadata per vector; the facet is the canonical pre-flattened
|
|
43
|
+
// form so a consumer's ingestion is a MAPPING, never a
|
|
44
|
+
// re-derivation. The facet is a derived projection of the unit's
|
|
45
|
+
// authored content, so it is EXCLUDED from the content_hash input
|
|
46
|
+
// (identity and currency ride the authored fields; the facet
|
|
47
|
+
// re-derives from them deterministically).
|
|
48
|
+
// 4. STABLE UNIT IDS + CONTENT DIGESTS. Unit ids are the package's
|
|
49
|
+
// OWN authored identifiers (requirement `/req/class-a/mpe`, term
|
|
50
|
+
// `/term/durability`, …) — the stability tier is STABLE PUBLIC
|
|
51
|
+
// IDENTIFIER: an id moves only when the package re-authors the
|
|
52
|
+
// identifier, and a rename of display text (name/label/statement)
|
|
53
|
+
// never moves it. Beside the id rides `content_hash` — sha256 over
|
|
54
|
+
// the unit's canonical JSON content (sorted keys, compact
|
|
55
|
+
// separators, UTF-8 — the form every JSON stack reproduces:
|
|
56
|
+
// `json.dumps(c, sort_keys=True, separators=(",", ":"),
|
|
57
|
+
// ensure_ascii=False)`). Identity = id; currency = digest. A
|
|
58
|
+
// rename moves the digest, never the id.
|
|
59
|
+
// 6. THE MACHINE PASSPORT. Every unit carries `passport` — the
|
|
60
|
+
// compact digest an agent (an MCP server) can carry and verify
|
|
61
|
+
// without loading the package: kind, id, the headline text
|
|
62
|
+
// (statement/definition), the machine expression (limit / check /
|
|
63
|
+
// derivation), the declared units, the applicability summary, the
|
|
64
|
+
// acceptance summary, the provenance URNs, and the content hash.
|
|
65
|
+
// `passportCanonical` renders it as the canonical string form.
|
|
66
|
+
// 7. LANGUAGE-TAGGED VARIANTS. Every unit carries `language` — the
|
|
67
|
+
// package's default spelling, the ISO 24229 tag of the INLINE
|
|
68
|
+
// prose values — and, when the package ships `text` blocks,
|
|
69
|
+
// `variants`: the alternate spellings resolved onto the unit by
|
|
70
|
+
// the C89 address rule (the longest registered dot-boundary
|
|
71
|
+
// prefix; the kernel element id, not the namespaced unit id),
|
|
72
|
+
// keyed by the addressed field path, each entry
|
|
73
|
+
// `{ spelling, via?, value }`. Both are authored content and
|
|
74
|
+
// participate in the content_hash; a text block addressed at an
|
|
75
|
+
// element the projection does not ship is counted
|
|
76
|
+
// (droppedTextBlocks), never silently lost.
|
|
77
|
+
//
|
|
78
|
+
// Congruence with the deployed consumer (oimlsmart/smart
|
|
79
|
+
// derive-model-plane.ts → oimlsmart/rag model_plane.py): the unit ids,
|
|
80
|
+
// the clause { doc, clause, urn } shape, the sha256 currency signal,
|
|
81
|
+
// and the bundle-level `source_hash` (the SAME algorithm — sha256 over
|
|
82
|
+
// every byte of the package directory, sorted walk, path + NUL +
|
|
83
|
+
// file-digest + LF) are deliberately identical so the consumer's pins
|
|
84
|
+
// (`standard → { source_hash, node_count }`) key without translation.
|
|
85
|
+
// The deliberate divergences: the document block nests under `package`
|
|
86
|
+
// (the bundle carries label/base_urn top-level), the digest input is
|
|
87
|
+
// the compact-canonical JSON documented above (the consumer's D1
|
|
88
|
+
// content_hash uses Python's default separators — the input shapes
|
|
89
|
+
// differ regardless), and the kind vocabulary is the kernel's honest
|
|
90
|
+
// one (a calculation with a rule type is `formula`, matching the
|
|
91
|
+
// consumer's calculations/formulas split; a verdict is `characteristic`,
|
|
92
|
+
// matching the deployed plane's characteristics.yaml projection).
|
|
93
|
+
//
|
|
94
|
+
// ONE-WAY PROJECTION, NEVER THE KERNEL'S TRUTH — the same doctrine as
|
|
95
|
+
// the ReqIF/RDF surfaces: the package stays the single source of truth;
|
|
96
|
+
// the export is generated, never authored, never re-imported.
|
|
97
|
+
//
|
|
98
|
+
// The diff-as-data API (the issue's ask 5) is the model diff
|
|
99
|
+
// (src/model-diff.ts), whose id keying this projection shares.
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
101
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102
|
+
exports.RETRIEVAL_FACET_VERSION = exports.RETRIEVAL_PROJECTION = void 0;
|
|
103
|
+
exports.canonicalJson = canonicalJson;
|
|
104
|
+
exports.retrievalDigest = retrievalDigest;
|
|
105
|
+
exports.passportCanonical = passportCanonical;
|
|
106
|
+
exports.packageSourceHash = packageSourceHash;
|
|
107
|
+
exports.normalizeClause = normalizeClause;
|
|
108
|
+
exports.collectClauses = collectClauses;
|
|
109
|
+
exports.termClauses = termClauses;
|
|
110
|
+
exports.applicabilitySummary = applicabilitySummary;
|
|
111
|
+
exports.retrievalDocParts = retrievalDocParts;
|
|
112
|
+
exports.retrievalFacet = retrievalFacet;
|
|
113
|
+
exports.resolveTextAddress = resolveTextAddress;
|
|
114
|
+
exports.packageEdition = packageEdition;
|
|
115
|
+
exports.exportStandardRetrieval = exportStandardRetrieval;
|
|
116
|
+
exports.exportPackageRetrieval = exportPackageRetrieval;
|
|
117
|
+
const node_crypto_1 = require("node:crypto");
|
|
118
|
+
const node_fs_1 = require("node:fs");
|
|
119
|
+
const node_path_1 = require("node:path");
|
|
120
|
+
const package_1 = require("../ser-des/package");
|
|
121
|
+
/**
|
|
122
|
+
* The projection shape version (semver'd by the document, not the
|
|
123
|
+
* package): a shape change — a field renamed, removed, or re-typed —
|
|
124
|
+
* bumps the version and is a re-index signal for every consumer;
|
|
125
|
+
* additive fields within a version are legal (consumers ignore what
|
|
126
|
+
* they do not read).
|
|
127
|
+
*/
|
|
128
|
+
exports.RETRIEVAL_PROJECTION = 'primmel-retrieval/1';
|
|
129
|
+
/**
|
|
130
|
+
* The facet shape version (ask 3 — the pre-flattened retrieval facet):
|
|
131
|
+
* semver'd independently of the projection — a facet key renamed,
|
|
132
|
+
* removed, or re-typed bumps it (a re-index signal); additive keys
|
|
133
|
+
* within a version are legal (indexes ignore what they do not read).
|
|
134
|
+
*/
|
|
135
|
+
exports.RETRIEVAL_FACET_VERSION = 'retrieval-facet/1';
|
|
136
|
+
// ── canonical JSON + the content digest ──────────────────────────────
|
|
137
|
+
/** Recursive key sort — the digest and serialization canonicalizer. */
|
|
138
|
+
function sortDeep(v) {
|
|
139
|
+
if (Array.isArray(v)) {
|
|
140
|
+
return v.map(sortDeep);
|
|
141
|
+
}
|
|
142
|
+
if (v && typeof v === 'object') {
|
|
143
|
+
const out = {};
|
|
144
|
+
for (const k of Object.keys(v).sort()) {
|
|
145
|
+
const val = sortDeep(v[k]);
|
|
146
|
+
if (val !== undefined) {
|
|
147
|
+
out[k] = val;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
return v;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The canonical JSON form: sorted keys, compact separators, UTF-8 — the
|
|
156
|
+
* form every JSON stack reproduces (`json.dumps(c, sort_keys=True,
|
|
157
|
+
* separators=(",", ":"), ensure_ascii=False)`). The digest input.
|
|
158
|
+
*/
|
|
159
|
+
function canonicalJson(value) {
|
|
160
|
+
return JSON.stringify(sortDeep(value));
|
|
161
|
+
}
|
|
162
|
+
/** sha256 hex over the canonical JSON of a value — the currency digest. */
|
|
163
|
+
function retrievalDigest(value) {
|
|
164
|
+
return (0, node_crypto_1.createHash)('sha256')
|
|
165
|
+
.update(canonicalJson(value), 'utf8')
|
|
166
|
+
.digest('hex');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The canonical serialized form of a unit's passport (ask 6): the
|
|
170
|
+
* string an agent carries and re-hashes to verify the unit.
|
|
171
|
+
*/
|
|
172
|
+
function passportCanonical(passport) {
|
|
173
|
+
return canonicalJson(passport);
|
|
174
|
+
}
|
|
175
|
+
// ── the package source hash (the bundle freshness signal) ────────────
|
|
176
|
+
function* walkPackage(dir) {
|
|
177
|
+
for (const entry of (0, node_fs_1.readdirSync)(dir).sort()) {
|
|
178
|
+
if (entry === '.DS_Store') {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const p = (0, node_path_1.join)(dir, entry);
|
|
182
|
+
if ((0, node_fs_1.statSync)(p).isDirectory()) {
|
|
183
|
+
yield* walkPackage(p);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
yield p;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* sha256 over every file of the package (path + bytes), deterministic —
|
|
192
|
+
* the SAME algorithm the deployed consumer's derive-model-plane.ts runs
|
|
193
|
+
* (sorted walk, `.DS_Store` skipped, relative path + NUL + the file's
|
|
194
|
+
* own sha256 + LF), so a source_hash here and a source_hash there are
|
|
195
|
+
* the same signal and the consumer's pins key without translation. ANY
|
|
196
|
+
* package change — a constraint re-authored, a clause re-cited — moves
|
|
197
|
+
* the hash; that byte-sensitivity is deliberate (identity rides the
|
|
198
|
+
* unit ids, currency rides the digests).
|
|
199
|
+
*/
|
|
200
|
+
function packageSourceHash(dir) {
|
|
201
|
+
const h = (0, node_crypto_1.createHash)('sha256');
|
|
202
|
+
for (const file of walkPackage(dir)) {
|
|
203
|
+
h.update((0, node_path_1.relative)(dir, file));
|
|
204
|
+
h.update('\0');
|
|
205
|
+
h.update((0, node_crypto_1.createHash)('sha256').update((0, node_fs_1.readFileSync)(file)).digest());
|
|
206
|
+
h.update('\n');
|
|
207
|
+
}
|
|
208
|
+
return h.digest('hex');
|
|
209
|
+
}
|
|
210
|
+
// ── provenance: the clause-URN normalization (ask 1) ─────────────────
|
|
211
|
+
/**
|
|
212
|
+
* A producer-internal anchor is never a clause: the metanorma UUID form
|
|
213
|
+
* (`_eb46a3a3-…` — the shape the consumer strips at ingestion) is
|
|
214
|
+
* detected exactly like the consumer detects it.
|
|
215
|
+
*/
|
|
216
|
+
const UUID_ANCHOR = /^_?[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
217
|
+
const DOC_FRAGMENT = /#([^#]*)$/;
|
|
218
|
+
/**
|
|
219
|
+
* Normalize one raw provenance edge onto the document's own numbering.
|
|
220
|
+
* The `doc` slot of a loaded ref may carry an embedded `#fragment` (the
|
|
221
|
+
* derives-from fold's shape for non-clause anchors — ref.ts); a
|
|
222
|
+
* `#clause-<n>` fragment restores the clause, any other fragment rides
|
|
223
|
+
* as the `anchor` extra. A UUID-shaped clause or fragment is an anchor,
|
|
224
|
+
* never a clause.
|
|
225
|
+
*/
|
|
226
|
+
function normalizeClause(doc, clause, fragment = '') {
|
|
227
|
+
let rest = doc ?? '';
|
|
228
|
+
let clauseOut = clause ?? '';
|
|
229
|
+
let frag = fragment ?? '';
|
|
230
|
+
let anchor;
|
|
231
|
+
const hash = DOC_FRAGMENT.exec(rest);
|
|
232
|
+
if (hash) {
|
|
233
|
+
rest = rest.slice(0, hash.index);
|
|
234
|
+
const embedded = hash[1] ?? '';
|
|
235
|
+
if (!clauseOut && embedded.startsWith('clause-')) {
|
|
236
|
+
const tail = embedded.slice('clause-'.length);
|
|
237
|
+
const slash = tail.indexOf('/');
|
|
238
|
+
if (slash >= 0) {
|
|
239
|
+
clauseOut = tail.slice(0, slash);
|
|
240
|
+
if (!frag) {
|
|
241
|
+
frag = tail.slice(slash + 1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
clauseOut = tail;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else if (embedded && !frag) {
|
|
249
|
+
// A non-clause fragment (a table anchor, a UUID) is an anchor,
|
|
250
|
+
// never a clause number.
|
|
251
|
+
anchor = embedded;
|
|
252
|
+
}
|
|
253
|
+
else if (embedded) {
|
|
254
|
+
anchor = embedded;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (clauseOut && UUID_ANCHOR.test(clauseOut)) {
|
|
258
|
+
// A UUID in the clause slot is a producer-internal anchor that lost
|
|
259
|
+
// its way — demote it, never present it as the document's numbering.
|
|
260
|
+
anchor = anchor ?? clauseOut;
|
|
261
|
+
clauseOut = '';
|
|
262
|
+
}
|
|
263
|
+
const urn = rest +
|
|
264
|
+
(clauseOut
|
|
265
|
+
? `#clause-${clauseOut}${frag ? `/${frag}` : ''}`
|
|
266
|
+
: anchor && !UUID_ANCHOR.test(anchor)
|
|
267
|
+
? `#${anchor}`
|
|
268
|
+
: '');
|
|
269
|
+
const out = { doc: rest, clause: clauseOut, urn };
|
|
270
|
+
if (anchor !== undefined) {
|
|
271
|
+
out.anchor = anchor;
|
|
272
|
+
}
|
|
273
|
+
if (frag) {
|
|
274
|
+
out.fragment = frag;
|
|
275
|
+
}
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
/** A ref target that is a document URN folds into provenance (the §18.4
|
|
279
|
+
* citation fold's defensive mirror — after a package load the fold has
|
|
280
|
+
* already run; a hand-built Standard may carry them unfolded). */
|
|
281
|
+
function refTargetClause(target) {
|
|
282
|
+
const m = /^(urn:[^#]+)(?:#(.+))?$/.exec(target);
|
|
283
|
+
if (!m) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
return { doc: m[1], clause: '', fragment: m[2] ?? '' };
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Collect every provenance edge of an element, normalized and
|
|
290
|
+
* de-duplicated by URN, in authored order: the structured channels
|
|
291
|
+
* (source / sourceRef / sourceRefs), the scalar reference URN (the
|
|
292
|
+
* conformance test's v2 channel), and urn-targeted refs not already
|
|
293
|
+
* folded. The first edge is the unit's primary `clause`.
|
|
294
|
+
*/
|
|
295
|
+
function collectClauses(el) {
|
|
296
|
+
const out = [];
|
|
297
|
+
const seen = new Set();
|
|
298
|
+
const push = (r) => {
|
|
299
|
+
if (!r || (!r.doc && !r.clause)) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const c = normalizeClause(r.doc ?? '', r.clause ?? '', r.fragment);
|
|
303
|
+
if (!seen.has(c.urn)) {
|
|
304
|
+
seen.add(c.urn);
|
|
305
|
+
out.push(c);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
push(el.source);
|
|
309
|
+
push(el.sourceRef);
|
|
310
|
+
for (const r of el.sourceRefs ?? []) {
|
|
311
|
+
push(r);
|
|
312
|
+
}
|
|
313
|
+
// The conformance test's scalar reference (v2) arrives quote-wrapped
|
|
314
|
+
// from the codec's legacy path — strip before the URN test.
|
|
315
|
+
const reference = (el.reference ?? '').replace(/^"|"$/g, '');
|
|
316
|
+
if (reference.startsWith('urn:')) {
|
|
317
|
+
push(refTargetClause(reference));
|
|
318
|
+
}
|
|
319
|
+
for (const r of el.refs ?? []) {
|
|
320
|
+
if (r && typeof r.target === 'string' && r.target.startsWith('urn:')) {
|
|
321
|
+
push(refTargetClause(r.target));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return out;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* A term's provenance (ask 1's sharpest case): the `source` facet is a
|
|
328
|
+
* plain string carrying whitespace-separated URNs (e.g. the VIML URN
|
|
329
|
+
* plus the Recommendation's clause URN); each parses onto a normalized
|
|
330
|
+
* edge. When no parsed edge carries a clause number and the term
|
|
331
|
+
* declares a `section` (the document's own terminology clause), the
|
|
332
|
+
* section becomes an edge on the package's base URN — the document's
|
|
333
|
+
* own numbering, exactly as authored.
|
|
334
|
+
*/
|
|
335
|
+
function termClauses(term, baseUrn) {
|
|
336
|
+
const out = [];
|
|
337
|
+
const seen = new Set();
|
|
338
|
+
const push = (c) => {
|
|
339
|
+
if (c && !seen.has(c.urn)) {
|
|
340
|
+
seen.add(c.urn);
|
|
341
|
+
out.push(c);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
for (const token of (term.source ?? '').split(/\s+/).filter(t => t)) {
|
|
345
|
+
push(token.startsWith('urn:') ? normalizeClause(token, '') : null);
|
|
346
|
+
}
|
|
347
|
+
if (term.section && !out.some(c => c.clause)) {
|
|
348
|
+
push(normalizeClause(baseUrn, term.section));
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
// ── the passport (ask 6) ─────────────────────────────────────────────
|
|
353
|
+
/** The canonical-compact applicability summary: `dim=v1|v2; …`, sorted. */
|
|
354
|
+
function applicabilitySummary(entries) {
|
|
355
|
+
return (entries ?? [])
|
|
356
|
+
.map(e => {
|
|
357
|
+
const mode = e.match && e.match !== 'any' ? `(${e.match})` : '';
|
|
358
|
+
return `${e.dimension}${mode}=${[...e.values].sort().join('|')}`;
|
|
359
|
+
})
|
|
360
|
+
.sort()
|
|
361
|
+
.join(';');
|
|
362
|
+
}
|
|
363
|
+
function passportOf(unit) {
|
|
364
|
+
return {
|
|
365
|
+
v: 1,
|
|
366
|
+
kind: unit.kind,
|
|
367
|
+
id: unit.id,
|
|
368
|
+
text: unit.statement ?? unit.definition ?? unit.name ?? '',
|
|
369
|
+
expression: unit.expression ?? '',
|
|
370
|
+
units: unit.units ?? [],
|
|
371
|
+
applicability: applicabilitySummary(unit.applicability),
|
|
372
|
+
acceptance: unit.acceptance ?? '',
|
|
373
|
+
provenance: (unit.clauses ?? []).map(c => c.urn),
|
|
374
|
+
content_hash: unit.content_hash,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const BASE_URN_PARTS = /^urn:oiml:pub:([rdbge]):(\d[\d-]*)(?::(\d{4}))?$/i;
|
|
378
|
+
function retrievalDocParts(baseUrn) {
|
|
379
|
+
const m = BASE_URN_PARTS.exec(baseUrn);
|
|
380
|
+
if (!m) {
|
|
381
|
+
return { doctype: '', doc_number: '', year: '', label: '' };
|
|
382
|
+
}
|
|
383
|
+
const doctype = (m[1] ?? '').toLowerCase();
|
|
384
|
+
const docNumber = m[2] ?? '';
|
|
385
|
+
const year = m[3] ?? '';
|
|
386
|
+
return {
|
|
387
|
+
doctype,
|
|
388
|
+
doc_number: docNumber,
|
|
389
|
+
year,
|
|
390
|
+
label: `OIML ${doctype.toUpperCase()} ${docNumber}${year ? `:${year}` : ''}`,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Build the unit's flat facet (ask 3): the document fields every vector
|
|
395
|
+
* of this package carries, the unit's kind + currency keys, its primary
|
|
396
|
+
* clause anchor, and its applicability dimensions flattened to
|
|
397
|
+
* `app_<dim>` keys (values sorted, `|`-joined; a non-`any` match mode
|
|
398
|
+
* rides beside as `app_<dim>_match`).
|
|
399
|
+
*
|
|
400
|
+
* Key congruence with the deployed consumer's chunk wire schema
|
|
401
|
+
* (ChunkMetaModel): `doc_id`, `docidentifier`, `doctype`, `doc_number`,
|
|
402
|
+
* `edition`, `language`, `clause_anchor`, `clause_title`, `status`,
|
|
403
|
+
* `unit_id`, `block`, `unit_hash` carry the same names and semantics.
|
|
404
|
+
* The lane constants the schema also carries (`tier`, `corpus`,
|
|
405
|
+
* `producer`, `text_ref`, `superseded_by`) are DEPLOYMENT stamps, not
|
|
406
|
+
* package content — the consumer's adapter sets them per lane; the
|
|
407
|
+
* facet never invents them. Three deliberate value choices:
|
|
408
|
+
*
|
|
409
|
+
* - `language` is the package's authored default spelling (the
|
|
410
|
+
* ISO 24229 code, e.g. `eng-Latn`) — the consumer lanes stamp a
|
|
411
|
+
* constant two-letter code today; the facet carries the authored
|
|
412
|
+
* truth and the adapter maps.
|
|
413
|
+
* - `clause_anchor` is the primary edge's document clause number, ''
|
|
414
|
+
* when the unit names no clause (never a producer UUID — ask 1
|
|
415
|
+
* already demoted those to `anchor`). The consumer's "model"
|
|
416
|
+
* fallback for clause-less model units is its lane marker, applied
|
|
417
|
+
* at its adapter.
|
|
418
|
+
* - `edition` is the package block's canonical edition (ask 2), not
|
|
419
|
+
* a re-parse of the base URN — the two coincide on URN-carrying
|
|
420
|
+
* packages, and the package block is authoritative when they drift.
|
|
421
|
+
*/
|
|
422
|
+
function retrievalFacet(unit, pkg) {
|
|
423
|
+
const parts = retrievalDocParts(pkg.base_urn);
|
|
424
|
+
const facet = {
|
|
425
|
+
unit_id: unit.id,
|
|
426
|
+
unit_hash: unit.content_hash,
|
|
427
|
+
block: unit.kind,
|
|
428
|
+
doc_id: pkg.id,
|
|
429
|
+
docidentifier: parts.label || pkg.title,
|
|
430
|
+
doctype: parts.doctype,
|
|
431
|
+
doc_number: parts.doc_number,
|
|
432
|
+
edition: pkg.edition,
|
|
433
|
+
model_version: pkg.model_version,
|
|
434
|
+
language: pkg.default_spelling,
|
|
435
|
+
clause_anchor: unit.clause?.clause ?? '',
|
|
436
|
+
clause_title: unit.name ?? unit.id,
|
|
437
|
+
...(present(pkg.status) ? { status: pkg.status } : {}),
|
|
438
|
+
};
|
|
439
|
+
const valuesByDim = new Map();
|
|
440
|
+
const matchByDim = new Map();
|
|
441
|
+
for (const entry of unit.applicability ?? []) {
|
|
442
|
+
const values = entry.values.length > 0 ? entry.values : Object.keys(entry.mapping ?? {});
|
|
443
|
+
const set = valuesByDim.get(entry.dimension) ?? new Set();
|
|
444
|
+
for (const v of values) {
|
|
445
|
+
set.add(v);
|
|
446
|
+
}
|
|
447
|
+
valuesByDim.set(entry.dimension, set);
|
|
448
|
+
if (entry.match &&
|
|
449
|
+
entry.match !== 'any' &&
|
|
450
|
+
!matchByDim.has(entry.dimension)) {
|
|
451
|
+
matchByDim.set(entry.dimension, entry.match);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
for (const dim of [...valuesByDim.keys()].sort()) {
|
|
455
|
+
facet[`app_${dim}`] = [...valuesByDim.get(dim)].sort().join('|');
|
|
456
|
+
const match = matchByDim.get(dim);
|
|
457
|
+
if (match) {
|
|
458
|
+
facet[`app_${dim}_match`] = match;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return facet;
|
|
462
|
+
}
|
|
463
|
+
// ── language-tagged variants (ask 7) ─────────────────────────────────
|
|
464
|
+
/**
|
|
465
|
+
* Resolve a `text` block's address onto its element: the LONGEST
|
|
466
|
+
* dot-boundary prefix registered in the package (element ids may
|
|
467
|
+
* themselves carry dots — `r144-3/sec-3.4`); the rest is the path into
|
|
468
|
+
* the element's structure, the terminal segment the prose field. The
|
|
469
|
+
* same rule the C89 linter resolves with (check.ts) — the projection
|
|
470
|
+
* and the linter never disagree about which element a text block
|
|
471
|
+
* addresses.
|
|
472
|
+
*/
|
|
473
|
+
function resolveTextAddress(address, elementIds) {
|
|
474
|
+
const segments = address.split('.');
|
|
475
|
+
if (segments.length < 2 || segments.some(s => s === '')) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
for (let i = segments.length - 1; i >= 1; i--) {
|
|
479
|
+
const candidate = segments.slice(0, i).join('.');
|
|
480
|
+
if (elementIds.has(candidate)) {
|
|
481
|
+
return { elementId: candidate, path: segments.slice(i).join('.') };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* The id-keyed collections a text block may address — the mirror of the
|
|
488
|
+
* C89 linter's element registry (check.ts). Resolution runs against the
|
|
489
|
+
* FULL registry (not only the projected kinds): an address whose
|
|
490
|
+
* longest registered prefix is an unprojected element (a form, an
|
|
491
|
+
* instrument) must NOT fall through to a shorter projected prefix —
|
|
492
|
+
* that misattribution is worse than a drop, and the drop is counted.
|
|
493
|
+
*/
|
|
494
|
+
const TEXT_ADDRESS_COLLECTIONS = [
|
|
495
|
+
'requirements',
|
|
496
|
+
'requirementClasses',
|
|
497
|
+
'conformanceTests',
|
|
498
|
+
'conformanceClasses',
|
|
499
|
+
'terms',
|
|
500
|
+
'forms',
|
|
501
|
+
'subforms',
|
|
502
|
+
'symbols',
|
|
503
|
+
'tables',
|
|
504
|
+
'calculations',
|
|
505
|
+
'notes',
|
|
506
|
+
'provisions',
|
|
507
|
+
'processes',
|
|
508
|
+
'instruments',
|
|
509
|
+
'attributeDefinitions',
|
|
510
|
+
'capabilities',
|
|
511
|
+
'behaviors',
|
|
512
|
+
'conditionSets',
|
|
513
|
+
'subjects',
|
|
514
|
+
'referenceMaterials',
|
|
515
|
+
'artifactDefinitions',
|
|
516
|
+
'monitors',
|
|
517
|
+
'passports',
|
|
518
|
+
'invariants',
|
|
519
|
+
'testSequences',
|
|
520
|
+
'formulasUsed',
|
|
521
|
+
'stateMachines',
|
|
522
|
+
'figures',
|
|
523
|
+
'links',
|
|
524
|
+
];
|
|
525
|
+
/**
|
|
526
|
+
* Attach the language tag and the variants to the built unit contents
|
|
527
|
+
* (pre-digest — both are authored content): `language` is the package's
|
|
528
|
+
* default spelling on every unit; `variants` resolves each `text` block
|
|
529
|
+
* onto its unit (by the KERNEL element id — a term's text block
|
|
530
|
+
* addresses `frobnicator`, not `/term/frobnicator`) and keys the
|
|
531
|
+
* entries by the addressed field path. Returns the count of text blocks
|
|
532
|
+
* whose variants reached no unit (the droppedTextBlocks honesty tally).
|
|
533
|
+
*/
|
|
534
|
+
function attachVariants(built, standard, pkg) {
|
|
535
|
+
if (present(pkg.default_spelling)) {
|
|
536
|
+
for (const b of built) {
|
|
537
|
+
b.content.language = pkg.default_spelling;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const texts = standard.texts ?? [];
|
|
541
|
+
if (texts.length === 0) {
|
|
542
|
+
return 0;
|
|
543
|
+
}
|
|
544
|
+
const registry = new Set();
|
|
545
|
+
for (const field of TEXT_ADDRESS_COLLECTIONS) {
|
|
546
|
+
for (const item of standard[field] ?? []) {
|
|
547
|
+
if (item?.id) {
|
|
548
|
+
registry.add(item.id);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const unitByElement = new Map(built.map(b => [b.elementId, b.content]));
|
|
553
|
+
let dropped = 0;
|
|
554
|
+
for (const t of texts) {
|
|
555
|
+
const resolved = resolveTextAddress(t.id, registry);
|
|
556
|
+
const content = resolved
|
|
557
|
+
? unitByElement.get(resolved.elementId)
|
|
558
|
+
: undefined;
|
|
559
|
+
if (!resolved || !content) {
|
|
560
|
+
dropped++;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const variants = (content.variants ??= {});
|
|
564
|
+
variants[resolved.path] = [
|
|
565
|
+
...(variants[resolved.path] ?? []),
|
|
566
|
+
...t.entries,
|
|
567
|
+
];
|
|
568
|
+
}
|
|
569
|
+
return dropped;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Attach the provenance edges to a built unit content. The digest,
|
|
573
|
+
* passport and facet arrive later (`completeUnit`) — the language tag
|
|
574
|
+
* and variants (ask 7) attach between assembly and digestion, so they
|
|
575
|
+
* participate in the content_hash like any authored field.
|
|
576
|
+
*/
|
|
577
|
+
function assemble(content, clauses) {
|
|
578
|
+
if (clauses.length > 0) {
|
|
579
|
+
content.clause = clauses[0];
|
|
580
|
+
content.clauses = clauses;
|
|
581
|
+
}
|
|
582
|
+
return content;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Complete an assembled unit content into the shipped unit: the content
|
|
586
|
+
* digest (over the authored fields — language and variants included),
|
|
587
|
+
* then the derived projections (passport, facet) that read it.
|
|
588
|
+
*/
|
|
589
|
+
function completeUnit(content, pkg) {
|
|
590
|
+
const hash = retrievalDigest(content);
|
|
591
|
+
const unit = {
|
|
592
|
+
...content,
|
|
593
|
+
content_hash: hash,
|
|
594
|
+
};
|
|
595
|
+
unit.passport = passportOf(unit);
|
|
596
|
+
// The facet attaches last (a derived projection of the digested
|
|
597
|
+
// content — the unit_hash key reads the computed content_hash).
|
|
598
|
+
unit.facet = retrievalFacet(unit, pkg);
|
|
599
|
+
return unit;
|
|
600
|
+
}
|
|
601
|
+
/** The parent class/scope id of a scoped id (`/req/a/b` → `/req/a`). */
|
|
602
|
+
function scopeOf(id) {
|
|
603
|
+
const slash = id.lastIndexOf('/');
|
|
604
|
+
return slash > 0 ? id.slice(0, slash) : undefined;
|
|
605
|
+
}
|
|
606
|
+
const present = (s) => typeof s === 'string' && s.length > 0;
|
|
607
|
+
const presentList = (xs) => xs && xs.length > 0 ? xs : undefined;
|
|
608
|
+
function requirementUnit(r) {
|
|
609
|
+
return assemble({
|
|
610
|
+
id: r.id,
|
|
611
|
+
kind: 'requirement',
|
|
612
|
+
...(scopeOf(r.id) ? { class: scopeOf(r.id) } : {}),
|
|
613
|
+
...(present(r.name) ? { name: r.name } : {}),
|
|
614
|
+
...(present(r.statement) ? { statement: r.statement } : {}),
|
|
615
|
+
...(present(r.guidance) ? { guidance: r.guidance } : {}),
|
|
616
|
+
...(present(r.obligation) ? { obligation: r.obligation } : {}),
|
|
617
|
+
modality: r.limit?.modality || 'shall',
|
|
618
|
+
...(r.limit?.expression
|
|
619
|
+
? {
|
|
620
|
+
expression: r.limit.expression,
|
|
621
|
+
...(presentList(r.limit.uses)
|
|
622
|
+
? { expression_inputs: r.limit.uses }
|
|
623
|
+
: {}),
|
|
624
|
+
}
|
|
625
|
+
: {}),
|
|
626
|
+
...(presentList(r.applicability)
|
|
627
|
+
? { applicability: r.applicability }
|
|
628
|
+
: {}),
|
|
629
|
+
...(presentList(r.bindsTo) ? { binds_to: r.bindsTo } : {}),
|
|
630
|
+
...(r.limit?.accepts
|
|
631
|
+
? {
|
|
632
|
+
acceptance: `${r.limit.accepts.verdict} ${r.limit.accepts.op} ${r.limit.accepts.limit}`,
|
|
633
|
+
}
|
|
634
|
+
: {}),
|
|
635
|
+
...(present(r.acceptanceCriteria)
|
|
636
|
+
? { acceptance_criteria: r.acceptanceCriteria }
|
|
637
|
+
: {}),
|
|
638
|
+
...(present(r.verificationMethod)
|
|
639
|
+
? {
|
|
640
|
+
verification: {
|
|
641
|
+
method: r.verificationMethod,
|
|
642
|
+
description: r.verificationDescription ?? '',
|
|
643
|
+
},
|
|
644
|
+
}
|
|
645
|
+
: {}),
|
|
646
|
+
...(present(r.channel) ? { channel: r.channel } : {}),
|
|
647
|
+
...(presentList(r.dependencies) ? { dependencies: r.dependencies } : {}),
|
|
648
|
+
}, collectClauses(r));
|
|
649
|
+
}
|
|
650
|
+
function conformanceTestUnit(t) {
|
|
651
|
+
const payload = {
|
|
652
|
+
...(present(t.kind) ? { test_kind: t.kind } : {}),
|
|
653
|
+
...(present(t.methodRef) ? { method_ref: t.methodRef } : {}),
|
|
654
|
+
...(presentList(t.preconditions)
|
|
655
|
+
? {
|
|
656
|
+
preconditions: t.preconditions.map(p => ({
|
|
657
|
+
id: p.id,
|
|
658
|
+
check: p.check,
|
|
659
|
+
description: p.description,
|
|
660
|
+
on_violation: p.onViolation,
|
|
661
|
+
})),
|
|
662
|
+
}
|
|
663
|
+
: {}),
|
|
664
|
+
};
|
|
665
|
+
return assemble({
|
|
666
|
+
id: t.id,
|
|
667
|
+
kind: 'conformance_test',
|
|
668
|
+
...(scopeOf(t.id) ? { class: scopeOf(t.id) } : {}),
|
|
669
|
+
...(present(t.name) ? { name: t.name } : {}),
|
|
670
|
+
...(present(t.purpose) ? { statement: t.purpose } : {}),
|
|
671
|
+
...(present(t.method) ? { definition: t.method } : {}),
|
|
672
|
+
...(present(t.guidance) ? { guidance: t.guidance } : {}),
|
|
673
|
+
...(present(t.obligation) ? { obligation: t.obligation } : {}),
|
|
674
|
+
...(presentList(t.applicability)
|
|
675
|
+
? { applicability: t.applicability }
|
|
676
|
+
: {}),
|
|
677
|
+
...(presentList(t.targets) ? { targets: t.targets } : {}),
|
|
678
|
+
...(presentList(t.bindsTo) ? { binds_to: t.bindsTo } : {}),
|
|
679
|
+
...(present(t.acceptancePassIf)
|
|
680
|
+
? { acceptance: t.acceptancePassIf }
|
|
681
|
+
: present(t.acceptanceCriteriaDescription)
|
|
682
|
+
? { acceptance: t.acceptanceCriteriaDescription }
|
|
683
|
+
: {}),
|
|
684
|
+
...(presentList(t.dependencies) ? { dependencies: t.dependencies } : {}),
|
|
685
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
686
|
+
}, collectClauses(t));
|
|
687
|
+
}
|
|
688
|
+
function termUnit(t, baseUrn) {
|
|
689
|
+
const payload = {
|
|
690
|
+
...(present(t.section) ? { section: t.section } : {}),
|
|
691
|
+
...(present(t.language) ? { language: t.language } : {}),
|
|
692
|
+
...(t.vocabRef ? { vocab_ref: t.vocabRef } : {}),
|
|
693
|
+
// The alias family (MN 114 v3.2, clause 13.10.1): the parser folds
|
|
694
|
+
// the v2 `alt` spelling into the canonical aliases channel; the
|
|
695
|
+
// payload keeps the deployed `alt` key (contract stability) and adds
|
|
696
|
+
// the v3.2 facets.
|
|
697
|
+
...(presentList(t.aliases ?? t.alt) ? { alt: (t.aliases ?? t.alt) } : {}),
|
|
698
|
+
...(presentList(t.colloquial) ? { colloquial: t.colloquial } : {}),
|
|
699
|
+
...(t.aliasSpellings && Object.keys(t.aliasSpellings).length > 0
|
|
700
|
+
? { alias_spellings: t.aliasSpellings }
|
|
701
|
+
: {}),
|
|
702
|
+
...(presentList(t.abbreviations) ? { abbreviations: t.abbreviations } : {}),
|
|
703
|
+
...(presentList(t.seeAlso) ? { see_also: t.seeAlso } : {}),
|
|
704
|
+
};
|
|
705
|
+
return assemble({
|
|
706
|
+
id: `/term/${t.id}`,
|
|
707
|
+
kind: 'term',
|
|
708
|
+
...(present(t.label) ? { name: t.label } : {}),
|
|
709
|
+
...(present(t.definition) ? { definition: t.definition } : {}),
|
|
710
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
711
|
+
}, termClauses(t, baseUrn));
|
|
712
|
+
}
|
|
713
|
+
function attributeUnit(a) {
|
|
714
|
+
const payload = {
|
|
715
|
+
...(present(a.symbol) ? { symbol: a.symbol } : {}),
|
|
716
|
+
...(present(a.quantityKind) ? { quantity_kind: a.quantityKind } : {}),
|
|
717
|
+
...(present(a.valueType) ? { value_type: a.valueType } : {}),
|
|
718
|
+
...(present(a.origin) ? { origin: a.origin } : {}),
|
|
719
|
+
...(present(a.scope) ? { scope: a.scope } : {}),
|
|
720
|
+
...(present(a.category) ? { category: a.category } : {}),
|
|
721
|
+
...(present(a.irdi) ? { irdi: a.irdi } : {}),
|
|
722
|
+
...(presentList(a.enumValues) ? { enum_values: a.enumValues } : {}),
|
|
723
|
+
};
|
|
724
|
+
return assemble({
|
|
725
|
+
id: `/attribute/${a.id}`,
|
|
726
|
+
kind: 'attribute',
|
|
727
|
+
...(present(a.name) ? { name: a.name } : {}),
|
|
728
|
+
...(present(a.definition) ? { definition: a.definition } : {}),
|
|
729
|
+
...(present(a.unit) && a.unit !== '1' ? { units: [a.unit] } : {}),
|
|
730
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
731
|
+
}, collectClauses(a));
|
|
732
|
+
}
|
|
733
|
+
function behaviorUnit(b) {
|
|
734
|
+
const payload = {
|
|
735
|
+
...(present(b.kind) ? { behavior_kind: b.kind } : {}),
|
|
736
|
+
...(present(b.stimulus) ? { stimulus: b.stimulus } : {}),
|
|
737
|
+
};
|
|
738
|
+
return assemble({
|
|
739
|
+
id: `/behavior/${b.id}`,
|
|
740
|
+
kind: 'behavior',
|
|
741
|
+
name: b.id,
|
|
742
|
+
...(present(b.response) ? { statement: b.response } : {}),
|
|
743
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
744
|
+
}, collectClauses(b));
|
|
745
|
+
}
|
|
746
|
+
function calculationUnits(c) {
|
|
747
|
+
const out = new Set();
|
|
748
|
+
for (const i of c.inputs ?? []) {
|
|
749
|
+
if (present(i.unit) && i.unit !== '1') {
|
|
750
|
+
out.add(i.unit);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (c.output && present(c.output.unit) && c.output.unit !== '1') {
|
|
754
|
+
out.add(c.output.unit);
|
|
755
|
+
}
|
|
756
|
+
return [...out].sort();
|
|
757
|
+
}
|
|
758
|
+
function calculationUnit(c) {
|
|
759
|
+
// The consumer's calculations/formulas split: a calculation carrying
|
|
760
|
+
// an engine rule type projects as a `formula` unit (the operator
|
|
761
|
+
// signature), the rest as `calculation` (typed IO).
|
|
762
|
+
const kind = present(c.ruleType)
|
|
763
|
+
? 'formula'
|
|
764
|
+
: 'calculation';
|
|
765
|
+
const units = calculationUnits(c);
|
|
766
|
+
const payload = {
|
|
767
|
+
name: c.name,
|
|
768
|
+
...(present(c.ruleType) ? { rule_type: c.ruleType } : {}),
|
|
769
|
+
...(present(c.category) ? { category: c.category } : {}),
|
|
770
|
+
...(presentList(c.inputs)
|
|
771
|
+
? {
|
|
772
|
+
inputs: c.inputs.map(i => ({
|
|
773
|
+
name: i.name,
|
|
774
|
+
type: i.type,
|
|
775
|
+
...(present(i.unit) ? { unit: i.unit } : {}),
|
|
776
|
+
})),
|
|
777
|
+
}
|
|
778
|
+
: {}),
|
|
779
|
+
...(c.output && present(c.output.type) ? { output: c.output } : {}),
|
|
780
|
+
...(c.lookup ? { lookup: c.lookup } : {}),
|
|
781
|
+
...(present(c.profile) ? { profile: c.profile } : {}),
|
|
782
|
+
};
|
|
783
|
+
return assemble({
|
|
784
|
+
id: present(c.identifier) ? c.identifier : `/calculation/${c.id}`,
|
|
785
|
+
kind,
|
|
786
|
+
name: c.label || c.name || c.id,
|
|
787
|
+
...(present(c.description) ? { definition: c.description } : {}),
|
|
788
|
+
...(present(c.expression) ? { expression: c.expression } : {}),
|
|
789
|
+
...(presentList(c.params) ? { expression_inputs: c.params } : {}),
|
|
790
|
+
...(units.length > 0 ? { units } : {}),
|
|
791
|
+
payload,
|
|
792
|
+
}, collectClauses({
|
|
793
|
+
sourceRef: c.sourceRef,
|
|
794
|
+
sourceRefs: c.sourceRefs,
|
|
795
|
+
refs: c.refs,
|
|
796
|
+
}));
|
|
797
|
+
}
|
|
798
|
+
function symbolUnit(s) {
|
|
799
|
+
const units = present(s.unit) && s.unit !== '1' ? [s.unit] : undefined;
|
|
800
|
+
const payload = {
|
|
801
|
+
...(present(s.type) ? { symbol_type: s.type } : {}),
|
|
802
|
+
...(present(s.kind) ? { symbol_kind: s.kind } : {}),
|
|
803
|
+
...(present(s.quantityKind) ? { quantity_kind: s.quantityKind } : {}),
|
|
804
|
+
...(present(s.origin) ? { origin: s.origin } : {}),
|
|
805
|
+
...(present(s.latex) ? { latex: s.latex } : {}),
|
|
806
|
+
...(s.formula && present(s.formula.display)
|
|
807
|
+
? { display: s.formula.display }
|
|
808
|
+
: {}),
|
|
809
|
+
...(present(s.calculation) ? { calculation: s.calculation } : {}),
|
|
810
|
+
};
|
|
811
|
+
return assemble({
|
|
812
|
+
id: `/symbol/${s.id}`,
|
|
813
|
+
kind: 'symbol',
|
|
814
|
+
...(present(s.name) ? { name: s.name } : {}),
|
|
815
|
+
...(present(s.definition) ? { definition: s.definition } : {}),
|
|
816
|
+
...(s.formula && present(s.formula.expression)
|
|
817
|
+
? {
|
|
818
|
+
expression: s.formula.expression,
|
|
819
|
+
...(presentList(s.formula.inputs)
|
|
820
|
+
? { expression_inputs: s.formula.inputs }
|
|
821
|
+
: {}),
|
|
822
|
+
}
|
|
823
|
+
: {}),
|
|
824
|
+
...(units ? { units } : {}),
|
|
825
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
826
|
+
}, collectClauses({
|
|
827
|
+
sourceRef: s.sourceRef,
|
|
828
|
+
sourceRefs: s.sourceRefs,
|
|
829
|
+
refs: s.refs,
|
|
830
|
+
}));
|
|
831
|
+
}
|
|
832
|
+
function constraintUnit(c) {
|
|
833
|
+
return assemble({
|
|
834
|
+
id: `/constraint/${c.id}`,
|
|
835
|
+
kind: 'constraint',
|
|
836
|
+
...(present(c.name) ? { name: c.name } : {}),
|
|
837
|
+
...(present(c.violationMeaning) ? { statement: c.violationMeaning } : {}),
|
|
838
|
+
...(present(c.check) ? { expression: c.check } : {}),
|
|
839
|
+
payload: {
|
|
840
|
+
...(present(c.stereotype) ? { stereotype: c.stereotype } : {}),
|
|
841
|
+
...(present(c.onViolation) ? { on_violation: c.onViolation } : {}),
|
|
842
|
+
},
|
|
843
|
+
}, collectClauses(c));
|
|
844
|
+
}
|
|
845
|
+
function characteristicUnit(v) {
|
|
846
|
+
// The deployed plane's `characteristic` kind: the canonical verdict
|
|
847
|
+
// quantity (derive once, reference everywhere).
|
|
848
|
+
const units = present(v.unit) && v.unit !== '1' ? [v.unit] : undefined;
|
|
849
|
+
const payload = {
|
|
850
|
+
...(present(v.behavior) ? { behavior: v.behavior } : {}),
|
|
851
|
+
...(present(v.quantityKind) ? { quantity_kind: v.quantityKind } : {}),
|
|
852
|
+
...(v.seriesReduction ? { series_reduction: v.seriesReduction } : {}),
|
|
853
|
+
};
|
|
854
|
+
return assemble({
|
|
855
|
+
id: `/characteristic/${v.id}`,
|
|
856
|
+
kind: 'characteristic',
|
|
857
|
+
...(present(v.symbol) ? { name: v.symbol } : {}),
|
|
858
|
+
...(present(v.derive)
|
|
859
|
+
? {
|
|
860
|
+
expression: v.derive,
|
|
861
|
+
...(presentList(v.inputs) ? { expression_inputs: v.inputs } : {}),
|
|
862
|
+
}
|
|
863
|
+
: {}),
|
|
864
|
+
...(units ? { units } : {}),
|
|
865
|
+
...(v.acceptance && present(v.acceptance.rule)
|
|
866
|
+
? { acceptance: v.acceptance.rule }
|
|
867
|
+
: {}),
|
|
868
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
869
|
+
}, collectClauses(v));
|
|
870
|
+
}
|
|
871
|
+
function tableUnit(t) {
|
|
872
|
+
const units = (t.columnDefs ?? [])
|
|
873
|
+
.map(c => c.unit)
|
|
874
|
+
.filter(u => present(u) && u !== '1')
|
|
875
|
+
.sort();
|
|
876
|
+
const payload = {
|
|
877
|
+
...(presentList(t.columnDefs)
|
|
878
|
+
? {
|
|
879
|
+
columns: t.columnDefs.map(c => ({
|
|
880
|
+
name: c.name,
|
|
881
|
+
type: c.type,
|
|
882
|
+
...(present(c.unit) ? { unit: c.unit } : {}),
|
|
883
|
+
})),
|
|
884
|
+
}
|
|
885
|
+
: present(t.columns)
|
|
886
|
+
? { columns: t.columns }
|
|
887
|
+
: {}),
|
|
888
|
+
...(presentList(t.data) ? { rows: t.data } : {}),
|
|
889
|
+
...(t.profiles ? { profiles: t.profiles } : {}),
|
|
890
|
+
};
|
|
891
|
+
return assemble({
|
|
892
|
+
id: `/table/${t.id}`,
|
|
893
|
+
kind: 'table',
|
|
894
|
+
...(present(t.title) ? { name: t.title } : {}),
|
|
895
|
+
...(present(t.description) ? { definition: t.description } : {}),
|
|
896
|
+
...(units.length > 0 ? { units: [...new Set(units)] } : {}),
|
|
897
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
898
|
+
}, collectClauses({
|
|
899
|
+
sourceRef: t.sourceRef,
|
|
900
|
+
sourceRefs: t.sourceRefs,
|
|
901
|
+
refs: t.refs,
|
|
902
|
+
}));
|
|
903
|
+
}
|
|
904
|
+
function sequenceUnit(s) {
|
|
905
|
+
const payload = {
|
|
906
|
+
steps: s.steps.map(st => ({
|
|
907
|
+
order: st.order,
|
|
908
|
+
...(present(st.test) ? { test: st.test } : {}),
|
|
909
|
+
...(present(st.phase) ? { phase: st.phase } : {}),
|
|
910
|
+
...(present(st.role) ? { role: st.role } : {}),
|
|
911
|
+
...(st.dependsOn !== null ? { depends_on: st.dependsOn } : {}),
|
|
912
|
+
})),
|
|
913
|
+
...(present(s.sampleApplicability)
|
|
914
|
+
? { sample_applicability: s.sampleApplicability }
|
|
915
|
+
: {}),
|
|
916
|
+
};
|
|
917
|
+
return assemble({
|
|
918
|
+
id: `/sequence/${s.id}`,
|
|
919
|
+
kind: 'sequence',
|
|
920
|
+
...(present(s.name) ? { name: s.name } : {}),
|
|
921
|
+
...(present(s.description) ? { statement: s.description } : {}),
|
|
922
|
+
payload,
|
|
923
|
+
}, collectClauses({ sourceRefs: s.sourceRefs, refs: s.refs }));
|
|
924
|
+
}
|
|
925
|
+
function noteUnit(n) {
|
|
926
|
+
return assemble({
|
|
927
|
+
id: `/note/${n.id}`,
|
|
928
|
+
kind: 'note',
|
|
929
|
+
name: n.type,
|
|
930
|
+
...(present(n.message) ? { statement: n.message } : {}),
|
|
931
|
+
}, []);
|
|
932
|
+
}
|
|
933
|
+
function stateMachineUnit(m) {
|
|
934
|
+
// The state machine keys by the bound entity's name (the construct
|
|
935
|
+
// declares no id — the model diff's E12 keying).
|
|
936
|
+
const id = m.entityName;
|
|
937
|
+
const payload = {
|
|
938
|
+
...(present(m.kind) ? { machine_kind: m.kind } : {}),
|
|
939
|
+
...(present(m.initialState) ? { initial: m.initialState } : {}),
|
|
940
|
+
...(presentList(m.states) ? { states: m.states.map(s => s.name) } : {}),
|
|
941
|
+
...(presentList(m.transitions)
|
|
942
|
+
? {
|
|
943
|
+
transitions: m.transitions.map(t => ({
|
|
944
|
+
from: t.from,
|
|
945
|
+
to: t.to,
|
|
946
|
+
...(present(t.actionName) ? { action: t.actionName } : {}),
|
|
947
|
+
...(present(t.guard) ? { guard: t.guard } : {}),
|
|
948
|
+
})),
|
|
949
|
+
}
|
|
950
|
+
: {}),
|
|
951
|
+
};
|
|
952
|
+
return assemble({
|
|
953
|
+
id: `/state-machine/${id}`,
|
|
954
|
+
kind: 'state_machine',
|
|
955
|
+
name: id,
|
|
956
|
+
...(Object.keys(payload).length > 0 ? { payload } : {}),
|
|
957
|
+
}, []);
|
|
958
|
+
}
|
|
959
|
+
function dimensionUnit(d) {
|
|
960
|
+
const payload = {
|
|
961
|
+
...(present(d.scope) ? { scope: d.scope } : {}),
|
|
962
|
+
...(present(d.cardinality) ? { cardinality: d.cardinality } : {}),
|
|
963
|
+
values: d.values.map(v => ({
|
|
964
|
+
id: v.id,
|
|
965
|
+
...(present(v.label) ? { label: v.label } : {}),
|
|
966
|
+
...(presentList(v.implies) ? { implies: v.implies } : {}),
|
|
967
|
+
...(present(v.termRef) ? { term_ref: v.termRef } : {}),
|
|
968
|
+
})),
|
|
969
|
+
};
|
|
970
|
+
return assemble({
|
|
971
|
+
id: `/dimension/${d.id}`,
|
|
972
|
+
kind: 'dimension',
|
|
973
|
+
...(present(d.label) ? { name: d.label } : {}),
|
|
974
|
+
...(present(d.description) ? { definition: d.description } : {}),
|
|
975
|
+
payload,
|
|
976
|
+
}, collectClauses(d));
|
|
977
|
+
}
|
|
978
|
+
// ── the document block (ask 2) ───────────────────────────────────────
|
|
979
|
+
/**
|
|
980
|
+
* The publication edition the model corresponds to: the manifest's
|
|
981
|
+
* newest `editions` entry (the register is newest-first), the base
|
|
982
|
+
* URN's trailing year segment as fallback. NEVER the package version —
|
|
983
|
+
* the two fields answer different questions (edition steering vs
|
|
984
|
+
* freshness gating) and never borrow each other's value.
|
|
985
|
+
*/
|
|
986
|
+
function packageEdition(manifest) {
|
|
987
|
+
const first = manifest?.editions?.[0];
|
|
988
|
+
if (present(first)) {
|
|
989
|
+
return first;
|
|
990
|
+
}
|
|
991
|
+
const tail = /:(\d{4})$/.exec(manifest?.baseUrn ?? '');
|
|
992
|
+
return tail?.[1] ?? '';
|
|
993
|
+
}
|
|
994
|
+
function packageBlock(standard) {
|
|
995
|
+
const m = standard.packageManifest;
|
|
996
|
+
if (m) {
|
|
997
|
+
return {
|
|
998
|
+
id: m.id,
|
|
999
|
+
title: m.title,
|
|
1000
|
+
kind: m.kind ?? 'rec',
|
|
1001
|
+
edition: packageEdition(m),
|
|
1002
|
+
model_version: m.version,
|
|
1003
|
+
editions: [...(m.editions ?? [])],
|
|
1004
|
+
base_urn: m.baseUrn,
|
|
1005
|
+
...(present(m.status) ? { status: m.status } : {}),
|
|
1006
|
+
default_spelling: m.defaultSpelling ?? '',
|
|
1007
|
+
spellings: [
|
|
1008
|
+
...(m.spellings ?? (m.defaultSpelling ? [m.defaultSpelling] : [])),
|
|
1009
|
+
],
|
|
1010
|
+
...(presentList(m.supersedes) ? { supersedes: [...m.supersedes] } : {}),
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
// A manifestless Standard (a single-file load): the metadata header
|
|
1014
|
+
// carries what the manifest would.
|
|
1015
|
+
const meta = standard.meta;
|
|
1016
|
+
return {
|
|
1017
|
+
id: meta?.namespace ?? '',
|
|
1018
|
+
title: meta?.title ?? '',
|
|
1019
|
+
kind: '',
|
|
1020
|
+
edition: meta?.edition ?? '',
|
|
1021
|
+
model_version: '',
|
|
1022
|
+
editions: present(meta?.edition) ? [meta.edition] : [],
|
|
1023
|
+
base_urn: '',
|
|
1024
|
+
default_spelling: '',
|
|
1025
|
+
spellings: [],
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Project a loaded Standard into the retrieval document (pure — no I/O;
|
|
1030
|
+
* the id projection, the clause normalization, the digests).
|
|
1031
|
+
*/
|
|
1032
|
+
function exportStandardRetrieval(standard, options = {}) {
|
|
1033
|
+
const pkg = packageBlock(standard);
|
|
1034
|
+
// Build the unit contents paired with the KERNEL element id — the
|
|
1035
|
+
// address a `text` block targets (a term's variants address
|
|
1036
|
+
// `frobnicator`, not the namespaced unit id `/term/frobnicator`).
|
|
1037
|
+
const built = [];
|
|
1038
|
+
const push = (content, elementId) => {
|
|
1039
|
+
built.push({ content, elementId });
|
|
1040
|
+
};
|
|
1041
|
+
for (const r of standard.requirements ?? []) {
|
|
1042
|
+
push(requirementUnit(r), r.id);
|
|
1043
|
+
}
|
|
1044
|
+
for (const t of standard.conformanceTests ?? []) {
|
|
1045
|
+
push(conformanceTestUnit(t), t.id);
|
|
1046
|
+
}
|
|
1047
|
+
for (const t of standard.terms ?? []) {
|
|
1048
|
+
push(termUnit(t, pkg.base_urn), t.id);
|
|
1049
|
+
}
|
|
1050
|
+
for (const a of standard.attributeDefinitions ?? []) {
|
|
1051
|
+
push(attributeUnit(a), a.id);
|
|
1052
|
+
}
|
|
1053
|
+
for (const b of standard.behaviors ?? []) {
|
|
1054
|
+
push(behaviorUnit(b), b.id);
|
|
1055
|
+
}
|
|
1056
|
+
for (const c of standard.calculations ?? []) {
|
|
1057
|
+
// The kernel element id is the construct id (`calculation frobIndex`),
|
|
1058
|
+
// never the declared `identifier` path the unit id prefers.
|
|
1059
|
+
push(calculationUnit(c), c.id);
|
|
1060
|
+
}
|
|
1061
|
+
for (const s of standard.symbols ?? []) {
|
|
1062
|
+
push(symbolUnit(s), s.id);
|
|
1063
|
+
}
|
|
1064
|
+
for (const c of standard.constraints ?? []) {
|
|
1065
|
+
push(constraintUnit(c), c.id);
|
|
1066
|
+
}
|
|
1067
|
+
for (const v of standard.verdicts ?? []) {
|
|
1068
|
+
push(characteristicUnit(v), v.id);
|
|
1069
|
+
}
|
|
1070
|
+
for (const t of standard.tables ?? []) {
|
|
1071
|
+
push(tableUnit(t), t.id);
|
|
1072
|
+
}
|
|
1073
|
+
for (const s of standard.testSequences ?? []) {
|
|
1074
|
+
push(sequenceUnit(s), s.id);
|
|
1075
|
+
}
|
|
1076
|
+
for (const n of standard.notes ?? []) {
|
|
1077
|
+
push(noteUnit(n), n.id);
|
|
1078
|
+
}
|
|
1079
|
+
for (const m of standard.stateMachines ?? []) {
|
|
1080
|
+
// The construct declares no id (the unit keys by the bound entity's
|
|
1081
|
+
// name) — C89 registers nothing, so no text block can address it.
|
|
1082
|
+
push(stateMachineUnit(m), m.entityName);
|
|
1083
|
+
}
|
|
1084
|
+
for (const i of standard.instruments ?? []) {
|
|
1085
|
+
for (const d of i.dimensions ?? []) {
|
|
1086
|
+
// Dimensions nest under the instrument in the C89 registry — a
|
|
1087
|
+
// text block addresses them by path through the instrument, which
|
|
1088
|
+
// the projection does not ship as a unit: those blocks count as
|
|
1089
|
+
// dropped, never misattached.
|
|
1090
|
+
push(dimensionUnit(d), d.id);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
// The language tag + variants attach pre-digest (authored content —
|
|
1094
|
+
// they move the content_hash like any authored field).
|
|
1095
|
+
const droppedTextBlocks = attachVariants(built, standard, pkg);
|
|
1096
|
+
const units = built.map(b => completeUnit(b.content, pkg));
|
|
1097
|
+
const byKind = {};
|
|
1098
|
+
let withClause = 0;
|
|
1099
|
+
let anchorOnlyProvenance = 0;
|
|
1100
|
+
let nonUrnDocRefs = 0;
|
|
1101
|
+
let withoutProvenance = 0;
|
|
1102
|
+
let withVariants = 0;
|
|
1103
|
+
for (const u of units) {
|
|
1104
|
+
byKind[u.kind] = (byKind[u.kind] ?? 0) + 1;
|
|
1105
|
+
if (u.variants && Object.keys(u.variants).length > 0) {
|
|
1106
|
+
withVariants++;
|
|
1107
|
+
}
|
|
1108
|
+
const clauses = u.clauses ?? [];
|
|
1109
|
+
if (clauses.some(c => c.clause)) {
|
|
1110
|
+
withClause++;
|
|
1111
|
+
}
|
|
1112
|
+
else if (clauses.length > 0) {
|
|
1113
|
+
anchorOnlyProvenance++;
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
withoutProvenance++;
|
|
1117
|
+
}
|
|
1118
|
+
if (clauses.some(c => c.doc && !c.doc.startsWith('urn:'))) {
|
|
1119
|
+
nonUrnDocRefs++;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
const document = {
|
|
1123
|
+
projection: exports.RETRIEVAL_PROJECTION,
|
|
1124
|
+
facet_version: exports.RETRIEVAL_FACET_VERSION,
|
|
1125
|
+
package: pkg,
|
|
1126
|
+
...(options.sourceHash !== undefined
|
|
1127
|
+
? { source_hash: options.sourceHash }
|
|
1128
|
+
: {}),
|
|
1129
|
+
units,
|
|
1130
|
+
};
|
|
1131
|
+
return {
|
|
1132
|
+
document,
|
|
1133
|
+
json: JSON.stringify(sortDeep(document), null, 2) + '\n',
|
|
1134
|
+
stats: {
|
|
1135
|
+
units: units.length,
|
|
1136
|
+
byKind,
|
|
1137
|
+
withClause,
|
|
1138
|
+
anchorOnlyProvenance,
|
|
1139
|
+
nonUrnDocRefs,
|
|
1140
|
+
withoutProvenance,
|
|
1141
|
+
withVariants,
|
|
1142
|
+
droppedTextBlocks,
|
|
1143
|
+
},
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Export a package directory: load, hash the package bytes (the bundle
|
|
1148
|
+
* freshness signal — the deployed consumer's exact algorithm), project.
|
|
1149
|
+
*/
|
|
1150
|
+
function exportPackageRetrieval(dir) {
|
|
1151
|
+
const { standard } = (0, package_1.loadPackageWithIssues)(dir);
|
|
1152
|
+
return exportStandardRetrieval(standard, {
|
|
1153
|
+
sourceHash: packageSourceHash(dir),
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
//# sourceMappingURL=retrieval.js.map
|