capdag 1.205.540 → 1.218.588
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/cap-fab-renderer.js +1 -1
- package/capdag.js +142 -0
- package/capdag.test.js +804 -793
- package/package.json +1 -1
package/cap-fab-renderer.js
CHANGED
|
@@ -2087,7 +2087,7 @@ function buildRunGraphData(data) {
|
|
|
2087
2087
|
// labels the anchor→entry edge on the first body.
|
|
2088
2088
|
//
|
|
2089
2089
|
// Without such a preceding sequence cap, the source itself is
|
|
2090
|
-
// already a list (e.g. `media:pdf;list` source_media_urn) and the
|
|
2090
|
+
// already a list (e.g. `media:ext=pdf;list` source_media_urn) and the
|
|
2091
2091
|
// ForEach iterates it directly.
|
|
2092
2092
|
let seqProducerStepIdx = -1;
|
|
2093
2093
|
let seqProducerStep = null;
|
package/capdag.js
CHANGED
|
@@ -6069,6 +6069,14 @@ function probeCartridgeCapGroups(entryPath) {
|
|
|
6069
6069
|
child.on('error', (e) => finish(new Error(`cartridge ${entryPath} spawn error: ${e.message}`)));
|
|
6070
6070
|
child.on('close', () => finish(new Error(`cartridge ${entryPath} HELLO failed: connection closed before receiving HELLO`)));
|
|
6071
6071
|
|
|
6072
|
+
// A cartridge that exits or closes its stdin before reading our HELLO
|
|
6073
|
+
// surfaces the broken pipe as an ASYNCHRONOUS 'error' event on the pipe
|
|
6074
|
+
// (EPIPE), not as a synchronous throw from write(). Without a listener,
|
|
6075
|
+
// Node treats that as an unhandled 'error' and aborts the whole host
|
|
6076
|
+
// process. Catch it here and route it through finish() so a single
|
|
6077
|
+
// misbehaving cartridge fails its own probe instead of killing discovery.
|
|
6078
|
+
child.stdin.on('error', (e) => finish(new Error(`cartridge ${entryPath} HELLO failed: ${e.message}`)));
|
|
6079
|
+
|
|
6072
6080
|
timer = setTimeout(() => finish(new Error(`cartridge ${entryPath} HELLO failed: timeout`)), 5000);
|
|
6073
6081
|
|
|
6074
6082
|
// Send our HELLO first (host side).
|
|
@@ -6120,6 +6128,9 @@ function probeCartridgeCapGroups(entryPath) {
|
|
|
6120
6128
|
buf = Buffer.concat([buf, chunk]);
|
|
6121
6129
|
tryParse();
|
|
6122
6130
|
});
|
|
6131
|
+
// Guard the read side too: an error on the child's stdout (e.g. the pipe
|
|
6132
|
+
// breaking mid-read) must not become an unhandled 'error' event.
|
|
6133
|
+
child.stdout.on('error', (e) => finish(new Error(`cartridge ${entryPath} HELLO failed: ${e.message}`)));
|
|
6123
6134
|
});
|
|
6124
6135
|
}
|
|
6125
6136
|
|
|
@@ -7540,8 +7551,139 @@ class FabricRegistryClient {
|
|
|
7540
7551
|
}
|
|
7541
7552
|
}
|
|
7542
7553
|
|
|
7554
|
+
// ===========================================================================
|
|
7555
|
+
// Aliases (the DNS-analogue translation layer over URNs)
|
|
7556
|
+
// ===========================================================================
|
|
7557
|
+
//
|
|
7558
|
+
// An alias is a first-class fabric definition: a short, contiguous,
|
|
7559
|
+
// case-insensitive name that resolves to exactly one cap or media URN. This
|
|
7560
|
+
// lightweight JS mirror provides the pure alias primitives (name rules,
|
|
7561
|
+
// URN-vs-alias detection, target classification), the StoredAlias wire shape,
|
|
7562
|
+
// and Manifest (de)serialization of the aliases map — the pieces the web UIs
|
|
7563
|
+
// and LSP need to recognize and present aliases. Full registry-side alias
|
|
7564
|
+
// resolution lives in the heavier mirrors (Rust/Go/Py/ObjC).
|
|
7565
|
+
|
|
7566
|
+
const ALIAS_TARGET_CAP = 'cap';
|
|
7567
|
+
const ALIAS_TARGET_MEDIA = 'media';
|
|
7568
|
+
const ALIAS_NAME_RE = /^[a-z0-9._-]+$/;
|
|
7569
|
+
|
|
7570
|
+
/**
|
|
7571
|
+
* A contiguous token "looks like a URN" iff it contains ':'. Every tagged URN
|
|
7572
|
+
* has the shape prefix:..., so the presence of ':' is the unambiguous
|
|
7573
|
+
* discriminator between a URN and an alias name.
|
|
7574
|
+
*/
|
|
7575
|
+
function tokenIsUrn(token) {
|
|
7576
|
+
return typeof token === 'string' && token.includes(':');
|
|
7577
|
+
}
|
|
7578
|
+
|
|
7579
|
+
/** Complement of tokenIsUrn: a colon-free token is an alias candidate. */
|
|
7580
|
+
function isAliasToken(token) {
|
|
7581
|
+
return !tokenIsUrn(token);
|
|
7582
|
+
}
|
|
7583
|
+
|
|
7584
|
+
/**
|
|
7585
|
+
* Normalize and validate an alias name. Lowercases the input, then requires it
|
|
7586
|
+
* non-empty, free of ':' (so it can never look like a tagged URN), free of
|
|
7587
|
+
* whitespace, and matching [a-z0-9._-]+. Returns the canonical lowercased
|
|
7588
|
+
* name or throws — there is no lenient path.
|
|
7589
|
+
*/
|
|
7590
|
+
function normalizeAliasName(name) {
|
|
7591
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
7592
|
+
throw new Error('alias name is empty');
|
|
7593
|
+
}
|
|
7594
|
+
if (name.includes(':')) {
|
|
7595
|
+
throw new Error(`alias name '${name}' contains ':' — aliases must never look like a tagged URN`);
|
|
7596
|
+
}
|
|
7597
|
+
if (/\s/.test(name)) {
|
|
7598
|
+
throw new Error(`alias name '${name}' contains whitespace`);
|
|
7599
|
+
}
|
|
7600
|
+
const lowered = name.toLowerCase();
|
|
7601
|
+
if (!ALIAS_NAME_RE.test(lowered)) {
|
|
7602
|
+
throw new Error(`alias name '${name}' contains invalid characters; allowed: lowercase letters, digits, '.', '_', '-'`);
|
|
7603
|
+
}
|
|
7604
|
+
return lowered;
|
|
7605
|
+
}
|
|
7606
|
+
|
|
7607
|
+
/**
|
|
7608
|
+
* Classify an alias target URN by prefix. Returns 'cap', 'media', or null
|
|
7609
|
+
* (not a cap/media URN).
|
|
7610
|
+
*/
|
|
7611
|
+
function classifyAliasTarget(target) {
|
|
7612
|
+
try {
|
|
7613
|
+
CapUrn.fromString(target);
|
|
7614
|
+
return ALIAS_TARGET_CAP;
|
|
7615
|
+
} catch (_) { /* not a cap URN */ }
|
|
7616
|
+
try {
|
|
7617
|
+
MediaUrn.fromString(target);
|
|
7618
|
+
return ALIAS_TARGET_MEDIA;
|
|
7619
|
+
} catch (_) { /* not a media URN */ }
|
|
7620
|
+
return null;
|
|
7621
|
+
}
|
|
7622
|
+
|
|
7623
|
+
/**
|
|
7624
|
+
* The published wire/cache shape of a single fabric alias. Mirrors
|
|
7625
|
+
* fabric/alias.schema.json: { name, target, version }.
|
|
7626
|
+
*/
|
|
7627
|
+
class StoredAlias {
|
|
7628
|
+
constructor(name, target, version) {
|
|
7629
|
+
this.name = name;
|
|
7630
|
+
this.target = target;
|
|
7631
|
+
this.version = version;
|
|
7632
|
+
}
|
|
7633
|
+
toJSON() {
|
|
7634
|
+
return { name: this.name, target: this.target, version: this.version };
|
|
7635
|
+
}
|
|
7636
|
+
static fromJSON(obj) {
|
|
7637
|
+
return new StoredAlias(obj.name, obj.target, obj.version);
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
|
|
7641
|
+
/**
|
|
7642
|
+
* A versioned registry snapshot. Mirrors fabric/manifest.schema.json:
|
|
7643
|
+
* { version, previous, caps, media, aliases } where each map is
|
|
7644
|
+
* name/urn -> defver.
|
|
7645
|
+
*/
|
|
7646
|
+
class Manifest {
|
|
7647
|
+
constructor(version, previous, caps = {}, media = {}, aliases = {}) {
|
|
7648
|
+
this.version = version;
|
|
7649
|
+
this.previous = previous;
|
|
7650
|
+
this.caps = caps;
|
|
7651
|
+
this.media = media;
|
|
7652
|
+
this.aliases = aliases;
|
|
7653
|
+
}
|
|
7654
|
+
static empty(version) {
|
|
7655
|
+
return new Manifest(version, Math.max(0, version - 1), {}, {}, {});
|
|
7656
|
+
}
|
|
7657
|
+
toJSON() {
|
|
7658
|
+
return {
|
|
7659
|
+
version: this.version,
|
|
7660
|
+
previous: this.previous,
|
|
7661
|
+
caps: this.caps,
|
|
7662
|
+
media: this.media,
|
|
7663
|
+
aliases: this.aliases,
|
|
7664
|
+
};
|
|
7665
|
+
}
|
|
7666
|
+
static fromJSON(obj) {
|
|
7667
|
+
return new Manifest(
|
|
7668
|
+
obj.version,
|
|
7669
|
+
obj.previous,
|
|
7670
|
+
obj.caps || {},
|
|
7671
|
+
obj.media || {},
|
|
7672
|
+
obj.aliases || {},
|
|
7673
|
+
);
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
|
|
7543
7677
|
// Export for CommonJS
|
|
7544
7678
|
module.exports = {
|
|
7679
|
+
ALIAS_TARGET_CAP,
|
|
7680
|
+
ALIAS_TARGET_MEDIA,
|
|
7681
|
+
tokenIsUrn,
|
|
7682
|
+
isAliasToken,
|
|
7683
|
+
normalizeAliasName,
|
|
7684
|
+
classifyAliasTarget,
|
|
7685
|
+
StoredAlias,
|
|
7686
|
+
Manifest,
|
|
7545
7687
|
CapUrn,
|
|
7546
7688
|
CapKind,
|
|
7547
7689
|
CapEffect,
|