capdag 0.177.441 → 0.180.452
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 +27 -17
- package/capdag.test.js +151 -1
- package/package.json +1 -1
package/capdag.js
CHANGED
|
@@ -1118,7 +1118,7 @@ const MEDIA_COLLECTION_LIST = 'media:collection;list;record';
|
|
|
1118
1118
|
// Media URN for adapter selection output - JSON record
|
|
1119
1119
|
const MEDIA_ADAPTER_SELECTION = 'media:adapter-selection;json;record';
|
|
1120
1120
|
// Fabric registry lookup wire types (consumed/produced by cap:lookup-cap;fabric
|
|
1121
|
-
// and cap:lookup-media-spec;fabric, both implemented by
|
|
1121
|
+
// and cap:lookup-media-spec;fabric, both implemented by fetchcartridge).
|
|
1122
1122
|
const MEDIA_CAP_URN = 'media:cap-urn;textable';
|
|
1123
1123
|
const MEDIA_MEDIA_URN = 'media:media-urn;textable';
|
|
1124
1124
|
const MEDIA_CAP_DEFINITION = 'media:cap-definition;json;record;textable';
|
|
@@ -1136,7 +1136,7 @@ const CAP_IDENTITY = 'cap:in=media:;out=media:';
|
|
|
1136
1136
|
// Cartridges that inspect file content override this with a handler that returns {"media_urns": [...]}.
|
|
1137
1137
|
const CAP_ADAPTER_SELECTION = 'cap:in="media:";out="media:adapter-selection;json;record"';
|
|
1138
1138
|
|
|
1139
|
-
// Fabric registry lookup caps. Implemented by
|
|
1139
|
+
// Fabric registry lookup caps. Implemented by fetchcartridge.
|
|
1140
1140
|
// CAP_LOOKUP_CAP_FABRIC resolves a canonical cap URN to its full flattened
|
|
1141
1141
|
// cap definition; CAP_LOOKUP_MEDIA_SPEC_FABRIC does the same for media specs.
|
|
1142
1142
|
const CAP_LOOKUP_CAP_FABRIC = 'cap:in="media:cap-urn;textable";fabric;lookup-cap;out="media:cap-definition;json;record;textable"';
|
|
@@ -2118,9 +2118,9 @@ class CapArg {
|
|
|
2118
2118
|
this.required = required;
|
|
2119
2119
|
this.is_sequence = options.is_sequence || false;
|
|
2120
2120
|
this.sources = sources; // Array of ArgSource
|
|
2121
|
-
this.arg_description = options.arg_description
|
|
2121
|
+
this.arg_description = options.arg_description !== undefined ? options.arg_description : null;
|
|
2122
2122
|
this.default_value = options.default_value !== undefined ? options.default_value : null;
|
|
2123
|
-
this.metadata = options.metadata
|
|
2123
|
+
this.metadata = options.metadata !== undefined ? options.metadata : null;
|
|
2124
2124
|
}
|
|
2125
2125
|
|
|
2126
2126
|
/**
|
|
@@ -2154,11 +2154,15 @@ class CapArg {
|
|
|
2154
2154
|
sources: this.sources.map(s => s.toJSON())
|
|
2155
2155
|
};
|
|
2156
2156
|
if (this.is_sequence) result.is_sequence = true;
|
|
2157
|
-
if (this.arg_description
|
|
2157
|
+
if (this.arg_description !== null && this.arg_description !== undefined) {
|
|
2158
|
+
result.arg_description = this.arg_description;
|
|
2159
|
+
}
|
|
2158
2160
|
if (this.default_value !== null && this.default_value !== undefined) {
|
|
2159
2161
|
result.default_value = this.default_value;
|
|
2160
2162
|
}
|
|
2161
|
-
if (this.metadata
|
|
2163
|
+
if (this.metadata !== null && this.metadata !== undefined) {
|
|
2164
|
+
result.metadata = this.metadata;
|
|
2165
|
+
}
|
|
2162
2166
|
return result;
|
|
2163
2167
|
}
|
|
2164
2168
|
|
|
@@ -2618,10 +2622,10 @@ class CapManifest {
|
|
|
2618
2622
|
* @param {CapGroup[]} capGroups - Cap groups (all caps must be in a group)
|
|
2619
2623
|
*/
|
|
2620
2624
|
constructor(name, version, channel, registryUrl, description, capGroups = []) {
|
|
2621
|
-
if (
|
|
2625
|
+
if (typeof name !== 'string') {
|
|
2622
2626
|
throw new Error('CapManifest name is required and must be a string');
|
|
2623
2627
|
}
|
|
2624
|
-
if (
|
|
2628
|
+
if (typeof version !== 'string') {
|
|
2625
2629
|
throw new Error('CapManifest version is required and must be a string');
|
|
2626
2630
|
}
|
|
2627
2631
|
if (channel !== 'release' && channel !== 'nightly') {
|
|
@@ -2630,7 +2634,7 @@ class CapManifest {
|
|
|
2630
2634
|
if (registryUrl !== null && registryUrl !== undefined && typeof registryUrl !== 'string') {
|
|
2631
2635
|
throw new Error("CapManifest registry_url must be null (dev build) or a string");
|
|
2632
2636
|
}
|
|
2633
|
-
if (
|
|
2637
|
+
if (typeof description !== 'string') {
|
|
2634
2638
|
throw new Error('CapManifest description is required and must be a string');
|
|
2635
2639
|
}
|
|
2636
2640
|
|
|
@@ -2664,10 +2668,10 @@ class CapManifest {
|
|
|
2664
2668
|
* @throws {Error} If required fields are missing or invalid
|
|
2665
2669
|
*/
|
|
2666
2670
|
static fromJSON(json) {
|
|
2667
|
-
if (!json
|
|
2668
|
-
if (!json
|
|
2669
|
-
if (!json
|
|
2670
|
-
if (!json
|
|
2671
|
+
if (!Object.prototype.hasOwnProperty.call(json, 'name')) throw new Error('CapManifest missing required field: name');
|
|
2672
|
+
if (!Object.prototype.hasOwnProperty.call(json, 'version')) throw new Error('CapManifest missing required field: version');
|
|
2673
|
+
if (!Object.prototype.hasOwnProperty.call(json, 'channel')) throw new Error('CapManifest missing required field: channel');
|
|
2674
|
+
if (!Object.prototype.hasOwnProperty.call(json, 'description')) throw new Error('CapManifest missing required field: description');
|
|
2671
2675
|
if (!Array.isArray(json.cap_groups)) throw new Error('CapManifest missing required field: cap_groups');
|
|
2672
2676
|
|
|
2673
2677
|
// registry_url must be present as a key (may be null for dev builds)
|
|
@@ -2700,10 +2704,16 @@ class CapManifest {
|
|
|
2700
2704
|
capGroups
|
|
2701
2705
|
);
|
|
2702
2706
|
|
|
2703
|
-
if (
|
|
2707
|
+
if (Object.prototype.hasOwnProperty.call(json, 'author')) {
|
|
2708
|
+
if (typeof json.author !== 'string') {
|
|
2709
|
+
throw new Error('CapManifest author must be a string when present');
|
|
2710
|
+
}
|
|
2704
2711
|
manifest.author = json.author;
|
|
2705
2712
|
}
|
|
2706
|
-
if (
|
|
2713
|
+
if (Object.prototype.hasOwnProperty.call(json, 'page_url')) {
|
|
2714
|
+
if (typeof json.page_url !== 'string') {
|
|
2715
|
+
throw new Error('CapManifest page_url must be a string when present');
|
|
2716
|
+
}
|
|
2707
2717
|
manifest.page_url = json.page_url;
|
|
2708
2718
|
}
|
|
2709
2719
|
|
|
@@ -2723,8 +2733,8 @@ class CapManifest {
|
|
|
2723
2733
|
description: this.description,
|
|
2724
2734
|
cap_groups: this.cap_groups.map(g => g.toJSON())
|
|
2725
2735
|
};
|
|
2726
|
-
if (this.author) result.author = this.author;
|
|
2727
|
-
if (this.page_url) result.page_url = this.page_url;
|
|
2736
|
+
if (this.author !== null && this.author !== undefined) result.author = this.author;
|
|
2737
|
+
if (this.page_url !== null && this.page_url !== undefined) result.page_url = this.page_url;
|
|
2728
2738
|
return result;
|
|
2729
2739
|
}
|
|
2730
2740
|
}
|
package/capdag.test.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const {
|
|
6
6
|
CapUrn, CapKind, CapUrnBuilder, CapMatcher, CapUrnError, ErrorCodes,
|
|
7
7
|
MediaUrn, MediaUrnError, MediaUrnErrorCodes,
|
|
8
|
-
Cap, MediaSpec, MediaSpecError, MediaSpecErrorCodes,
|
|
8
|
+
Cap, CapGroup, CapManifest, MediaSpec, MediaSpecError, MediaSpecErrorCodes,
|
|
9
9
|
resolveMediaUrn, buildExtensionIndex, mediaUrnsForExtension, getExtensionMappings,
|
|
10
10
|
CartridgeInfo, CartridgeCapSummary, CartridgeSuggestion, CartridgeRepoClient, CartridgeRepoServer,
|
|
11
11
|
CapFabEdge, CapFabStats, CapFab,
|
|
@@ -1209,6 +1209,152 @@ function test110_multipleExtensions() {
|
|
|
1209
1209
|
assertEqual(resolved.extensions[1], 'jpeg', 'Second extension should be jpeg');
|
|
1210
1210
|
}
|
|
1211
1211
|
|
|
1212
|
+
// TEST115: Test CapArg serialization and deserialization with multiple sources
|
|
1213
|
+
function test115_capArgSerialization() {
|
|
1214
|
+
const arg = new CapArg(
|
|
1215
|
+
MEDIA_STRING,
|
|
1216
|
+
true,
|
|
1217
|
+
[new ArgSource({ cli_flag: '--name' }), new ArgSource({ position: 0 })],
|
|
1218
|
+
{
|
|
1219
|
+
arg_description: 'The name argument',
|
|
1220
|
+
default_value: 400,
|
|
1221
|
+
metadata: { kind: 'example', flags: [true, false] }
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
const json = arg.toJSON();
|
|
1226
|
+
assertEqual(json.media_urn, MEDIA_STRING, 'media_urn must serialize');
|
|
1227
|
+
assertEqual(json.required, true, 'required must serialize');
|
|
1228
|
+
assertEqual(json.arg_description, 'The name argument', 'arg_description must serialize');
|
|
1229
|
+
assertEqual(json.default_value, 400, 'numeric default_value must serialize as number');
|
|
1230
|
+
assertEqual(JSON.stringify(json.metadata), JSON.stringify({ kind: 'example', flags: [true, false] }),
|
|
1231
|
+
'metadata must serialize as arbitrary JSON');
|
|
1232
|
+
|
|
1233
|
+
const roundTripped = CapArg.fromJSON(JSON.parse(JSON.stringify(json)));
|
|
1234
|
+
assertEqual(roundTripped.media_urn, arg.media_urn, 'media_urn must round-trip');
|
|
1235
|
+
assertEqual(roundTripped.required, arg.required, 'required must round-trip');
|
|
1236
|
+
assertEqual(roundTripped.arg_description, arg.arg_description, 'arg_description must round-trip');
|
|
1237
|
+
assertEqual(roundTripped.default_value, 400, 'numeric default_value must round-trip');
|
|
1238
|
+
assertEqual(JSON.stringify(roundTripped.metadata), JSON.stringify({ kind: 'example', flags: [true, false] }),
|
|
1239
|
+
'metadata must round-trip');
|
|
1240
|
+
assertEqual(roundTripped.sources.length, 2, 'sources length must round-trip');
|
|
1241
|
+
assertEqual(roundTripped.sources[0].cli_flag, '--name', 'cli_flag source must round-trip');
|
|
1242
|
+
assertEqual(roundTripped.sources[1].position, 0, 'position source must round-trip');
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// TEST116: Test CapArg constructor methods basic and with_description create args correctly
|
|
1246
|
+
function test116_capArgConstructors() {
|
|
1247
|
+
const basicArg = new CapArg(
|
|
1248
|
+
MEDIA_STRING,
|
|
1249
|
+
true,
|
|
1250
|
+
[new ArgSource({ cli_flag: '--name' })]
|
|
1251
|
+
);
|
|
1252
|
+
assertEqual(basicArg.media_urn, MEDIA_STRING, 'basic arg media_urn must match');
|
|
1253
|
+
assertEqual(basicArg.required, true, 'basic arg required must match');
|
|
1254
|
+
assertEqual(basicArg.sources.length, 1, 'basic arg must keep one source');
|
|
1255
|
+
assertEqual(basicArg.arg_description, null, 'basic arg arg_description must be absent');
|
|
1256
|
+
assertEqual(basicArg.default_value, null, 'basic arg default_value must be absent');
|
|
1257
|
+
|
|
1258
|
+
const describedArg = new CapArg(
|
|
1259
|
+
MEDIA_INTEGER,
|
|
1260
|
+
false,
|
|
1261
|
+
[new ArgSource({ position: 0 })],
|
|
1262
|
+
{
|
|
1263
|
+
arg_description: 'The count argument',
|
|
1264
|
+
default_value: 10
|
|
1265
|
+
}
|
|
1266
|
+
);
|
|
1267
|
+
assertEqual(describedArg.media_urn, MEDIA_INTEGER, 'described arg media_urn must match');
|
|
1268
|
+
assertEqual(describedArg.required, false, 'described arg required must match');
|
|
1269
|
+
assertEqual(describedArg.arg_description, 'The count argument', 'described arg description must match');
|
|
1270
|
+
assertEqual(describedArg.default_value, 10, 'described arg default_value must match');
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// TEST150: JSON roundtrip
|
|
1274
|
+
function test150_capManifestJsonSerialization() {
|
|
1275
|
+
const capUrn = CapUrn.fromString(testUrn('extract;target=metadata'));
|
|
1276
|
+
const cap = new Cap(capUrn, 'Extract Metadata', 'extract-metadata');
|
|
1277
|
+
cap.addArg(new CapArg('media:pdf', true, [new ArgSource({ stdin: 'media:pdf' })]));
|
|
1278
|
+
cap.addArg(new CapArg(
|
|
1279
|
+
'media:chunk-size;textable;numeric',
|
|
1280
|
+
false,
|
|
1281
|
+
[new ArgSource({ cli_flag: '--chunk-size' })],
|
|
1282
|
+
{
|
|
1283
|
+
arg_description: 'Chunk size',
|
|
1284
|
+
default_value: 400,
|
|
1285
|
+
metadata: { unit: 'words' }
|
|
1286
|
+
}
|
|
1287
|
+
));
|
|
1288
|
+
cap.addArg(new CapArg(
|
|
1289
|
+
'media:timestamps;textable;bool',
|
|
1290
|
+
false,
|
|
1291
|
+
[new ArgSource({ cli_flag: '--timestamps' })],
|
|
1292
|
+
{
|
|
1293
|
+
arg_description: 'Include timestamps',
|
|
1294
|
+
default_value: false
|
|
1295
|
+
}
|
|
1296
|
+
));
|
|
1297
|
+
|
|
1298
|
+
const manifest = new CapManifest(
|
|
1299
|
+
'TestComponent',
|
|
1300
|
+
'0.1.0',
|
|
1301
|
+
'release',
|
|
1302
|
+
null,
|
|
1303
|
+
'A test component',
|
|
1304
|
+
[new CapGroup('default', [cap], [])]
|
|
1305
|
+
);
|
|
1306
|
+
|
|
1307
|
+
manifest.author = 'Test Author';
|
|
1308
|
+
|
|
1309
|
+
const json = manifest.toJSON();
|
|
1310
|
+
assertEqual(json.name, 'TestComponent', 'manifest name must serialize');
|
|
1311
|
+
assertEqual(json.author, 'Test Author', 'author must serialize');
|
|
1312
|
+
assert(Array.isArray(json.cap_groups), 'cap_groups must serialize');
|
|
1313
|
+
assertEqual(json.cap_groups.length, 1, 'cap_groups length must serialize');
|
|
1314
|
+
assertEqual(json.cap_groups[0].caps[0].args[1].default_value, 400, 'numeric default must serialize as number');
|
|
1315
|
+
assertEqual(json.cap_groups[0].caps[0].args[1].metadata.unit, 'words', 'metadata must serialize');
|
|
1316
|
+
assertEqual(json.cap_groups[0].caps[0].args[2].default_value, false, 'boolean default must serialize as boolean');
|
|
1317
|
+
|
|
1318
|
+
const roundTripped = CapManifest.fromJSON(JSON.parse(JSON.stringify(json)));
|
|
1319
|
+
const decodedCap = roundTripped.allCaps()[0];
|
|
1320
|
+
assertEqual(roundTripped.name, manifest.name, 'manifest name must round-trip');
|
|
1321
|
+
assertEqual(roundTripped.author, 'Test Author', 'author must round-trip');
|
|
1322
|
+
assertEqual(roundTripped.cap_groups.length, 1, 'cap_groups length must round-trip');
|
|
1323
|
+
assertEqual(decodedCap.args[1].default_value, 400, 'numeric default must round-trip');
|
|
1324
|
+
assertEqual(JSON.stringify(decodedCap.args[1].metadata), JSON.stringify({ unit: 'words' }),
|
|
1325
|
+
'metadata must round-trip');
|
|
1326
|
+
assertEqual(decodedCap.args[2].default_value, false, 'boolean default must round-trip');
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// TEST597: CapArg::with_full_definition stores all fields including optional ones
|
|
1330
|
+
function test597_capArgWithFullDefinition() {
|
|
1331
|
+
const arg = new CapArg(
|
|
1332
|
+
MEDIA_STRING,
|
|
1333
|
+
true,
|
|
1334
|
+
[new ArgSource({ cli_flag: '--name' })],
|
|
1335
|
+
{
|
|
1336
|
+
arg_description: 'User name',
|
|
1337
|
+
default_value: { chunk_size: 400, timestamps: false },
|
|
1338
|
+
metadata: { hint: 'enter name' }
|
|
1339
|
+
}
|
|
1340
|
+
);
|
|
1341
|
+
|
|
1342
|
+
assertEqual(arg.media_urn, MEDIA_STRING, 'media_urn must match');
|
|
1343
|
+
assertEqual(arg.required, true, 'required must match');
|
|
1344
|
+
assertEqual(arg.arg_description, 'User name', 'arg_description must match');
|
|
1345
|
+
assertEqual(JSON.stringify(arg.default_value), JSON.stringify({ chunk_size: 400, timestamps: false }),
|
|
1346
|
+
'object default_value must be preserved');
|
|
1347
|
+
assertEqual(JSON.stringify(arg.metadata), JSON.stringify({ hint: 'enter name' }),
|
|
1348
|
+
'metadata must be preserved');
|
|
1349
|
+
|
|
1350
|
+
const roundTripped = CapArg.fromJSON(JSON.parse(JSON.stringify(arg.toJSON())));
|
|
1351
|
+
assertEqual(roundTripped.arg_description, 'User name', 'arg_description must round-trip');
|
|
1352
|
+
assertEqual(JSON.stringify(roundTripped.default_value), JSON.stringify({ chunk_size: 400, timestamps: false }),
|
|
1353
|
+
'object default_value must round-trip');
|
|
1354
|
+
assertEqual(JSON.stringify(roundTripped.metadata), JSON.stringify({ hint: 'enter name' }),
|
|
1355
|
+
'metadata must round-trip');
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1212
1358
|
// ============================================================================
|
|
1213
1359
|
// cap_fab: browse-mode API used by cap-fab-renderer.js
|
|
1214
1360
|
//
|
|
@@ -5785,6 +5931,10 @@ async function runTests() {
|
|
|
5785
5931
|
runTest('TEST108: extensions_serialization', test108_extensionsSerialization);
|
|
5786
5932
|
runTest('TEST109: extensions_with_metadata_and_validation', test109_extensionsWithMetadataAndValidation);
|
|
5787
5933
|
runTest('TEST110: multiple_extensions', test110_multipleExtensions);
|
|
5934
|
+
runTest('TEST115: cap_arg_serialization', test115_capArgSerialization);
|
|
5935
|
+
runTest('TEST116: cap_arg_constructors', test116_capArgConstructors);
|
|
5936
|
+
runTest('TEST150: cap_manifest_json_serialization', test150_capManifestJsonSerialization);
|
|
5937
|
+
runTest('TEST597: cap_arg_with_full_definition', test597_capArgWithFullDefinition);
|
|
5788
5938
|
|
|
5789
5939
|
// cap-fab-renderer.js uses CapFab in browse mode (static registry from
|
|
5790
5940
|
// /api/capabilities). These tests guard the minimal API the renderer relies
|
package/package.json
CHANGED