capdag 1.219.594 → 1.222.603
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/capdag.js +196 -1
- package/capdag.test.js +108 -0
- package/package.json +1 -1
package/capdag.js
CHANGED
|
@@ -5876,10 +5876,11 @@ function unixSecondsNow() {
|
|
|
5876
5876
|
}
|
|
5877
5877
|
|
|
5878
5878
|
// Default protocol limits / version, mirroring capdag::bifaci::frame.
|
|
5879
|
-
const BIFACI_PROTOCOL_VERSION =
|
|
5879
|
+
const BIFACI_PROTOCOL_VERSION = 3;
|
|
5880
5880
|
const BIFACI_DEFAULT_MAX_FRAME = 3670016;
|
|
5881
5881
|
const BIFACI_DEFAULT_MAX_CHUNK = 262144;
|
|
5882
5882
|
const BIFACI_DEFAULT_MAX_REORDER_BUFFER = 64;
|
|
5883
|
+
const BIFACI_DEFAULT_INITIAL_CREDIT = 32;
|
|
5883
5884
|
const BIFACI_FRAME_TYPE_HELLO = 0;
|
|
5884
5885
|
// Frame CBOR integer keys (capdag::bifaci::frame::keys).
|
|
5885
5886
|
const BIFACI_KEY_VERSION = 0;
|
|
@@ -6008,6 +6009,7 @@ function encodeHelloFrame() {
|
|
|
6008
6009
|
['max_frame', BIFACI_DEFAULT_MAX_FRAME],
|
|
6009
6010
|
['max_chunk', BIFACI_DEFAULT_MAX_CHUNK],
|
|
6010
6011
|
['max_reorder_buffer', BIFACI_DEFAULT_MAX_REORDER_BUFFER],
|
|
6012
|
+
['initial_credit', BIFACI_DEFAULT_INITIAL_CREDIT],
|
|
6011
6013
|
['version', BIFACI_PROTOCOL_VERSION],
|
|
6012
6014
|
],
|
|
6013
6015
|
};
|
|
@@ -6825,6 +6827,88 @@ class Machine {
|
|
|
6825
6827
|
return bracketed ? lines.join('') : lines.join('\n');
|
|
6826
6828
|
}
|
|
6827
6829
|
|
|
6830
|
+
/**
|
|
6831
|
+
* Serialize to machine notation rendering each cap by its registered display
|
|
6832
|
+
* alias (shortest name, ties alphabetical) when one exists, falling back to
|
|
6833
|
+
* the canonical-URN `edge_N` header form otherwise. This is the "store
|
|
6834
|
+
* aliased" form: generated and persisted machines use it so the saved
|
|
6835
|
+
* notation reads in terms of aliases.
|
|
6836
|
+
*
|
|
6837
|
+
* Semantics (mirrors Rust Machine::to_machine_notation_aliased):
|
|
6838
|
+
* - A cap WITH a display alias is referenced DIRECTLY in the wiring's
|
|
6839
|
+
* cap-position by its alias name, with NO `[edge_N cap:...]` header (the
|
|
6840
|
+
* grammar forbids an alias in a header's cap position anyway, and a header
|
|
6841
|
+
* would be redundant).
|
|
6842
|
+
* - A cap WITHOUT an alias keeps the synthetic `edge_N` wiring token plus its
|
|
6843
|
+
* `[edge_N cap:...]` header binding it to the canonical cap URN.
|
|
6844
|
+
*
|
|
6845
|
+
* The canonical `edge_N` numbering and node naming are inherited unchanged
|
|
6846
|
+
* from the canonical serializer, so an un-aliased machine produces byte-
|
|
6847
|
+
* identical output to toMachineNotationFormatted.
|
|
6848
|
+
*
|
|
6849
|
+
* @param {FabricRegistryClient} registry - resolves URN → display alias
|
|
6850
|
+
* @param {'bracketed' | 'line-based'} format
|
|
6851
|
+
* @returns {string}
|
|
6852
|
+
*/
|
|
6853
|
+
toMachineNotationAliased(registry, format) {
|
|
6854
|
+
if (this._edges.length === 0) {
|
|
6855
|
+
return '';
|
|
6856
|
+
}
|
|
6857
|
+
|
|
6858
|
+
const { aliases, nodeNames, edgeOrder } = this._buildSerializationMaps();
|
|
6859
|
+
const bracketed = format === 'bracketed';
|
|
6860
|
+
const open = bracketed ? '[' : '';
|
|
6861
|
+
const close = bracketed ? ']' : '';
|
|
6862
|
+
|
|
6863
|
+
// Resolve, per edge index, the cap-position token and whether it needs a
|
|
6864
|
+
// header. An aliased cap uses its display alias directly (no header); an
|
|
6865
|
+
// un-aliased cap keeps its synthetic `edge_N` token (with a header).
|
|
6866
|
+
const edgeToken = new Map(); // edgeIdx → token used in the wiring
|
|
6867
|
+
const edgeNeedsHeader = new Map(); // edgeIdx → bool
|
|
6868
|
+
for (const [edgeAlias, info] of aliases) {
|
|
6869
|
+
const edge = this._edges[info.edgeIdx];
|
|
6870
|
+
const displayAlias = registry.displayAliasForUrn(edge.capUrn.toString());
|
|
6871
|
+
if (displayAlias !== null && displayAlias !== undefined) {
|
|
6872
|
+
edgeToken.set(info.edgeIdx, displayAlias);
|
|
6873
|
+
edgeNeedsHeader.set(info.edgeIdx, false);
|
|
6874
|
+
} else {
|
|
6875
|
+
edgeToken.set(info.edgeIdx, edgeAlias);
|
|
6876
|
+
edgeNeedsHeader.set(info.edgeIdx, true);
|
|
6877
|
+
}
|
|
6878
|
+
}
|
|
6879
|
+
|
|
6880
|
+
const lines = [];
|
|
6881
|
+
|
|
6882
|
+
// Headers only for edges that kept a synthetic `edge_N` token (un-aliased
|
|
6883
|
+
// caps). Emit in alias-sorted order to match the canonical serializer.
|
|
6884
|
+
const sortedAliases = Array.from(aliases.entries()).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
6885
|
+
for (const [, info] of sortedAliases) {
|
|
6886
|
+
if (edgeNeedsHeader.get(info.edgeIdx)) {
|
|
6887
|
+
const edge = this._edges[info.edgeIdx];
|
|
6888
|
+
lines.push(`${open}${edgeToken.get(info.edgeIdx)} ${edge.capUrn}${close}`);
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6892
|
+
// Wirings in canonical edge order, using the resolved cap-position token.
|
|
6893
|
+
for (const edgeIdx of edgeOrder) {
|
|
6894
|
+
const edge = this._edges[edgeIdx];
|
|
6895
|
+
const token = edgeToken.get(edgeIdx);
|
|
6896
|
+
|
|
6897
|
+
const sources = edge.sources.map(s => nodeNames.get(s.toString()));
|
|
6898
|
+
const targetName = nodeNames.get(edge.target.toString());
|
|
6899
|
+
const loopPrefix = edge.isLoop ? 'LOOP ' : '';
|
|
6900
|
+
|
|
6901
|
+
if (sources.length === 1) {
|
|
6902
|
+
lines.push(`${open}${sources[0]} -> ${loopPrefix}${token} -> ${targetName}${close}`);
|
|
6903
|
+
} else {
|
|
6904
|
+
const group = sources.join(', ');
|
|
6905
|
+
lines.push(`${open}(${group}) -> ${loopPrefix}${token} -> ${targetName}${close}`);
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
|
|
6909
|
+
return bracketed ? lines.join('') : lines.join('\n');
|
|
6910
|
+
}
|
|
6911
|
+
|
|
6828
6912
|
/**
|
|
6829
6913
|
* Build the alias map, node name map, and edge ordering for serialization.
|
|
6830
6914
|
*
|
|
@@ -7413,6 +7497,12 @@ class FabricRegistryClient {
|
|
|
7413
7497
|
// canonical key only needs to be unique per equivalence class; we
|
|
7414
7498
|
// store one entry per equivalence class.
|
|
7415
7499
|
this._mediaCache = new Map();
|
|
7500
|
+
// Normalized alias name → StoredAlias. Mirrors Rust
|
|
7501
|
+
// FabricRegistry::cached_aliases. The display/serialization surfaces
|
|
7502
|
+
// read this synchronously; it is warmed by insertCachedAliasForTest (and,
|
|
7503
|
+
// in the heavier mirrors, a background prefetch this lightweight client
|
|
7504
|
+
// does not implement).
|
|
7505
|
+
this._cachedAliases = new Map();
|
|
7416
7506
|
}
|
|
7417
7507
|
|
|
7418
7508
|
/**
|
|
@@ -7542,12 +7632,88 @@ class FabricRegistryClient {
|
|
|
7542
7632
|
return Array.from(ops).sort();
|
|
7543
7633
|
}
|
|
7544
7634
|
|
|
7635
|
+
/**
|
|
7636
|
+
* Insert an alias directly into the in-memory cache, keyed by its
|
|
7637
|
+
* normalized name. Mirrors Rust
|
|
7638
|
+
* FabricRegistry::insert_cached_alias_for_test — it is the warm-cache seam
|
|
7639
|
+
* the display/serialization primitives read from. The lightweight JS client
|
|
7640
|
+
* has no background alias prefetch, so callers seed the cache through here.
|
|
7641
|
+
*
|
|
7642
|
+
* @param {StoredAlias} alias
|
|
7643
|
+
*/
|
|
7644
|
+
insertCachedAliasForTest(alias) {
|
|
7645
|
+
const normalized = normalizeAliasName(alias.name);
|
|
7646
|
+
this._cachedAliases.set(normalized, alias);
|
|
7647
|
+
}
|
|
7648
|
+
|
|
7649
|
+
/**
|
|
7650
|
+
* Reverse lookup: the display alias for a `cap:`/`media:` URN, or null if no
|
|
7651
|
+
* cached alias points at it. This is the canonical primitive every UI surface
|
|
7652
|
+
* and notation generator uses to render an aliased name in place of a raw URN.
|
|
7653
|
+
*
|
|
7654
|
+
* The query URN is canonicalised through its own parser (cap vs media by
|
|
7655
|
+
* prefix) before matching, because alias targets are stored canonically — a
|
|
7656
|
+
* non-canonical query (different tag order, redundant whitespace) would
|
|
7657
|
+
* otherwise miss. A URN that is neither a cap nor a media URN, or that fails
|
|
7658
|
+
* to parse, returns null (it cannot have an alias).
|
|
7659
|
+
*
|
|
7660
|
+
* When multiple aliases target the same URN, the winner is the SHORTEST name,
|
|
7661
|
+
* ties broken alphabetically (see selectDisplayAlias). This is deterministic
|
|
7662
|
+
* and stable across processes for a given alias set.
|
|
7663
|
+
*
|
|
7664
|
+
* Mirrors Rust FabricRegistry::display_alias_for_urn.
|
|
7665
|
+
* @param {string} urn
|
|
7666
|
+
* @returns {string|null}
|
|
7667
|
+
*/
|
|
7668
|
+
displayAliasForUrn(urn) {
|
|
7669
|
+
// Canonicalise by kind. classifyAliasTarget keys off the parser (cap vs
|
|
7670
|
+
// media), the same classifier the alias publisher uses for targets, so a
|
|
7671
|
+
// query and a stored target canonicalise identically.
|
|
7672
|
+
const kind = classifyAliasTarget(urn);
|
|
7673
|
+
let canonical;
|
|
7674
|
+
if (kind === ALIAS_TARGET_CAP) {
|
|
7675
|
+
canonical = CapUrn.fromString(urn).toString();
|
|
7676
|
+
} else if (kind === ALIAS_TARGET_MEDIA) {
|
|
7677
|
+
canonical = MediaUrn.fromString(urn).toString();
|
|
7678
|
+
} else {
|
|
7679
|
+
return null;
|
|
7680
|
+
}
|
|
7681
|
+
|
|
7682
|
+
const names = [];
|
|
7683
|
+
for (const alias of this._cachedAliases.values()) {
|
|
7684
|
+
if (alias.target === canonical) {
|
|
7685
|
+
names.push(alias.name);
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
return selectDisplayAlias(names);
|
|
7689
|
+
}
|
|
7690
|
+
|
|
7691
|
+
/**
|
|
7692
|
+
* All cached aliases whose target is a CAP URN, as [name, capUrn] pairs.
|
|
7693
|
+
* Used by the notation editor to offer registered cap aliases as wiring
|
|
7694
|
+
* completions. Order is unspecified (the caller sorts/filters). Synchronous,
|
|
7695
|
+
* cache-only — relies on the alias cache having been warmed.
|
|
7696
|
+
*
|
|
7697
|
+
* Mirrors Rust FabricRegistry::cached_cap_aliases.
|
|
7698
|
+
* @returns {Array<[string, string]>}
|
|
7699
|
+
*/
|
|
7700
|
+
cachedCapAliases() {
|
|
7701
|
+
const pairs = [];
|
|
7702
|
+
for (const alias of this._cachedAliases.values()) {
|
|
7703
|
+
if (classifyAliasTarget(alias.target) === ALIAS_TARGET_CAP) {
|
|
7704
|
+
pairs.push([alias.name, alias.target]);
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
return pairs;
|
|
7708
|
+
}
|
|
7709
|
+
|
|
7545
7710
|
/**
|
|
7546
7711
|
* Invalidate all caches. Next call to any method fetches fresh data.
|
|
7547
7712
|
*/
|
|
7548
7713
|
invalidate() {
|
|
7549
7714
|
this._capCache = null;
|
|
7550
7715
|
this._mediaCache.clear();
|
|
7716
|
+
this._cachedAliases.clear();
|
|
7551
7717
|
}
|
|
7552
7718
|
}
|
|
7553
7719
|
|
|
@@ -7620,6 +7786,34 @@ function classifyAliasTarget(target) {
|
|
|
7620
7786
|
return null;
|
|
7621
7787
|
}
|
|
7622
7788
|
|
|
7789
|
+
/**
|
|
7790
|
+
* Pick the display alias from a set of alias names that all target the same
|
|
7791
|
+
* URN: the SHORTEST name, ties broken alphabetically. Returns null for an
|
|
7792
|
+
* empty set.
|
|
7793
|
+
*
|
|
7794
|
+
* The ordering is total and deterministic: (length, name) lexicographic. So
|
|
7795
|
+
* `png` beats `png-image` (shorter), and between equal-length `a16` / `a09`
|
|
7796
|
+
* the alphabetical-smaller `a09` wins. Stable across processes for a given
|
|
7797
|
+
* alias set, which is what makes aliased UI/notation reproducible.
|
|
7798
|
+
*
|
|
7799
|
+
* Mirrors Rust select_display_alias.
|
|
7800
|
+
* @param {Iterable<string>} names
|
|
7801
|
+
* @returns {string|null}
|
|
7802
|
+
*/
|
|
7803
|
+
function selectDisplayAlias(names) {
|
|
7804
|
+
let best = null;
|
|
7805
|
+
for (const name of names) {
|
|
7806
|
+
if (
|
|
7807
|
+
best === null ||
|
|
7808
|
+
name.length < best.length ||
|
|
7809
|
+
(name.length === best.length && name < best)
|
|
7810
|
+
) {
|
|
7811
|
+
best = name;
|
|
7812
|
+
}
|
|
7813
|
+
}
|
|
7814
|
+
return best;
|
|
7815
|
+
}
|
|
7816
|
+
|
|
7623
7817
|
/**
|
|
7624
7818
|
* The published wire/cache shape of a single fabric alias. Mirrors
|
|
7625
7819
|
* fabric/alias.schema.json: { name, target, version }.
|
|
@@ -7682,6 +7876,7 @@ module.exports = {
|
|
|
7682
7876
|
isAliasToken,
|
|
7683
7877
|
normalizeAliasName,
|
|
7684
7878
|
classifyAliasTarget,
|
|
7879
|
+
selectDisplayAlias,
|
|
7685
7880
|
StoredAlias,
|
|
7686
7881
|
Manifest,
|
|
7687
7882
|
CapUrn,
|
package/capdag.test.js
CHANGED
|
@@ -36,6 +36,7 @@ const {
|
|
|
36
36
|
CAP_IDENTITY,
|
|
37
37
|
ALIAS_TARGET_CAP, ALIAS_TARGET_MEDIA,
|
|
38
38
|
tokenIsUrn, isAliasToken, normalizeAliasName, classifyAliasTarget,
|
|
39
|
+
selectDisplayAlias,
|
|
39
40
|
StoredAlias, Manifest
|
|
40
41
|
} = require('./capdag.js');
|
|
41
42
|
|
|
@@ -6383,6 +6384,107 @@ function test1887_manifestSerdeRoundTripsAliases() {
|
|
|
6383
6384
|
assertEqual(JSON.stringify(a.toJSON()), '{"name":"pdf2text","target":"cap:effect=none","version":3}', 'alias wire shape');
|
|
6384
6385
|
}
|
|
6385
6386
|
|
|
6387
|
+
// TEST1894: selectDisplayAlias picks the SHORTEST name, ties broken
|
|
6388
|
+
// alphabetically. This is the deterministic ordering every aliased-display
|
|
6389
|
+
// surface relies on; a regression here silently changes which alias the whole
|
|
6390
|
+
// UI renders.
|
|
6391
|
+
function test1894_selectDisplayAliasOrdering() {
|
|
6392
|
+
// Shorter wins over longer regardless of alphabetical order.
|
|
6393
|
+
assertEqual(selectDisplayAlias(['png-image', 'png', 'image-png']), 'png', 'shortest wins');
|
|
6394
|
+
// Equal length → alphabetical (a09 < a16).
|
|
6395
|
+
assertEqual(selectDisplayAlias(['a16', 'a09', 'a12']), 'a09', 'tie → alphabetical');
|
|
6396
|
+
// Single candidate returns itself.
|
|
6397
|
+
assertEqual(selectDisplayAlias(['solo']), 'solo', 'single candidate');
|
|
6398
|
+
// Empty set → null.
|
|
6399
|
+
assertEqual(selectDisplayAlias([]), null, 'empty → null');
|
|
6400
|
+
}
|
|
6401
|
+
|
|
6402
|
+
// TEST1895: displayAliasForUrn reverse-resolves a URN to its display alias.
|
|
6403
|
+
// Proves: (1) the shortest-then-alphabetical winner among multiple aliases on
|
|
6404
|
+
// the same target, (2) a NON-canonical query URN (different tag order) still
|
|
6405
|
+
// resolves because the query is canonicalised before matching, (3) a URN with
|
|
6406
|
+
// no alias returns null, (4) a non-URN string returns null.
|
|
6407
|
+
function test1895_displayAliasForUrn() {
|
|
6408
|
+
const registry = new FabricRegistryClient();
|
|
6409
|
+
const capTarget = CapUrn.fromString('cap:coerce;in="media:integer;numeric";out="media:enc=utf-8"').toString();
|
|
6410
|
+
// Two aliases on the same cap target; "i2s" is shorter than "int2str".
|
|
6411
|
+
registry.insertCachedAliasForTest(new StoredAlias('int2str', capTarget, 1));
|
|
6412
|
+
registry.insertCachedAliasForTest(new StoredAlias('i2s', capTarget, 1));
|
|
6413
|
+
// A media alias too. Stored canonically.
|
|
6414
|
+
const jsonTarget = MediaUrn.fromString('media:fmt=json;record').toString();
|
|
6415
|
+
registry.insertCachedAliasForTest(new StoredAlias('json', jsonTarget, 1));
|
|
6416
|
+
|
|
6417
|
+
// Canonical query → shortest alias wins.
|
|
6418
|
+
assertEqual(
|
|
6419
|
+
registry.displayAliasForUrn('cap:coerce;in="media:integer;numeric";out="media:enc=utf-8"'),
|
|
6420
|
+
'i2s', 'shortest cap alias wins');
|
|
6421
|
+
// NON-canonical query (media tags reordered) must still resolve via
|
|
6422
|
+
// canonicalisation.
|
|
6423
|
+
assertEqual(registry.displayAliasForUrn('media:record;fmt=json'), 'json',
|
|
6424
|
+
'non-canonical media query canonicalises');
|
|
6425
|
+
// A real URN with no alias → null.
|
|
6426
|
+
assertEqual(registry.displayAliasForUrn('media:enc=utf-8;ext=pdf'), null, 'no alias → null');
|
|
6427
|
+
// A non-URN (no cap:/media: prefix) → null, never a throw.
|
|
6428
|
+
assertEqual(registry.displayAliasForUrn('int2str'), null, 'non-URN → null');
|
|
6429
|
+
}
|
|
6430
|
+
|
|
6431
|
+
// TEST1896: cachedCapAliases returns only CAP-targeted aliases as [name,
|
|
6432
|
+
// target] pairs — media aliases are excluded. Drives the notation editor's
|
|
6433
|
+
// registered-alias completions.
|
|
6434
|
+
function test1896_cachedCapAliasesFiltersToCapTargets() {
|
|
6435
|
+
const registry = new FabricRegistryClient();
|
|
6436
|
+
const capTarget = CapUrn.fromString('cap:coerce;in="media:integer;numeric";out="media:enc=utf-8"').toString();
|
|
6437
|
+
registry.insertCachedAliasForTest(new StoredAlias('int2str', capTarget, 1));
|
|
6438
|
+
registry.insertCachedAliasForTest(new StoredAlias('json', MediaUrn.fromString('media:fmt=json;record').toString(), 1));
|
|
6439
|
+
const capAliases = registry.cachedCapAliases();
|
|
6440
|
+
assertEqual(capAliases.length, 1, 'only the cap alias is returned');
|
|
6441
|
+
assertEqual(capAliases[0][0], 'int2str', 'pair name');
|
|
6442
|
+
assertEqual(capAliases[0][1], capTarget, 'pair target');
|
|
6443
|
+
}
|
|
6444
|
+
|
|
6445
|
+
// TEST1196: toMachineNotationAliased references an aliased cap DIRECTLY in the
|
|
6446
|
+
// wiring by its display alias (shortest, then alphabetical) with NO header, and
|
|
6447
|
+
// keeps the synthetic `edge_N` token + header for a cap that has no alias.
|
|
6448
|
+
function test1196_aliasedSerializationUsesAliasAndDropsHeader() {
|
|
6449
|
+
const registry = new FabricRegistryClient();
|
|
6450
|
+
const m = Machine.fromString(
|
|
6451
|
+
'[extract cap:in="media:ext=pdf";extract;out="media:enc=utf-8;ext=txt"]' +
|
|
6452
|
+
'[embed cap:in="media:enc=utf-8;ext=txt";embed;out="media:embedding-vector;enc=utf-8;record"]' +
|
|
6453
|
+
'[doc -> extract -> text]' +
|
|
6454
|
+
'[text -> embed -> vectors]'
|
|
6455
|
+
);
|
|
6456
|
+
// Canonical target strings for the two caps, exactly as the serializer keys.
|
|
6457
|
+
const extractTarget = m.edges().find(e => e.capUrn.toString().includes('extract')).capUrn.toString();
|
|
6458
|
+
// Two aliases on the extract cap; "ex" is shorter than "extract-pdf".
|
|
6459
|
+
registry.insertCachedAliasForTest(new StoredAlias('extract-pdf', extractTarget, 1));
|
|
6460
|
+
registry.insertCachedAliasForTest(new StoredAlias('ex', extractTarget, 1));
|
|
6461
|
+
// No alias for the embed cap → it must stay a raw URN with a header.
|
|
6462
|
+
|
|
6463
|
+
const aliased = m.toMachineNotationAliased(registry, 'bracketed');
|
|
6464
|
+
|
|
6465
|
+
// The extract cap is aliased: referenced directly in the wiring by its
|
|
6466
|
+
// SHORTER alias `ex`, with NO header, and its URN must not appear.
|
|
6467
|
+
assert(aliased.includes('-> ex ->'),
|
|
6468
|
+
`extract cap must be referenced by shortest alias 'ex', got: ${aliased}`);
|
|
6469
|
+
assert(!aliased.includes('extract;out'),
|
|
6470
|
+
`the aliased extract cap URN must not appear, got: ${aliased}`);
|
|
6471
|
+
// The longer alias must not be chosen.
|
|
6472
|
+
assert(!aliased.includes('extract-pdf'),
|
|
6473
|
+
`the longer alias must not be used, got: ${aliased}`);
|
|
6474
|
+
// The un-aliased embed cap keeps its synthetic header binding `edge_N` to the
|
|
6475
|
+
// canonical embed URN.
|
|
6476
|
+
assert(/\[edge_\d+ cap:.*embed/.test(aliased),
|
|
6477
|
+
`the un-aliased embed cap must keep its header URN, got: ${aliased}`);
|
|
6478
|
+
|
|
6479
|
+
// An un-aliased machine (empty registry) is byte-identical to the canonical
|
|
6480
|
+
// formatted form — the aliased path adds nothing when no alias exists.
|
|
6481
|
+
const empty = new FabricRegistryClient();
|
|
6482
|
+
assertEqual(
|
|
6483
|
+
m.toMachineNotationAliased(empty, 'bracketed'),
|
|
6484
|
+
m.toMachineNotationFormatted('bracketed'),
|
|
6485
|
+
'no aliases → identical to canonical bracketed form');
|
|
6486
|
+
}
|
|
6487
|
+
|
|
6386
6488
|
// ============================================================================
|
|
6387
6489
|
// Test runner
|
|
6388
6490
|
// ============================================================================
|
|
@@ -6849,6 +6951,12 @@ async function runTests() {
|
|
|
6849
6951
|
runTest('TEST1882: classify_alias_target_by_prefix', test1882_classifyAliasTargetByPrefix);
|
|
6850
6952
|
runTest('TEST1887: manifest_serde_round_trips_aliases', test1887_manifestSerdeRoundTripsAliases);
|
|
6851
6953
|
|
|
6954
|
+
console.log('\n--- URN→alias display + aliased serialization (test1894–test1896, test1196) ---');
|
|
6955
|
+
runTest('TEST1894: select_display_alias_ordering', test1894_selectDisplayAliasOrdering);
|
|
6956
|
+
runTest('TEST1895: display_alias_for_urn', test1895_displayAliasForUrn);
|
|
6957
|
+
runTest('TEST1896: cached_cap_aliases_filters_to_cap', test1896_cachedCapAliasesFiltersToCapTargets);
|
|
6958
|
+
runTest('TEST1196: aliased_serialization_uses_alias', test1196_aliasedSerializationUsesAliasAndDropsHeader);
|
|
6959
|
+
|
|
6852
6960
|
// Summary
|
|
6853
6961
|
console.log(`\n${passCount + failCount} tests: ${passCount} passed, ${failCount} failed`);
|
|
6854
6962
|
if (failCount > 0) {
|
package/package.json
CHANGED