capns 0.58.11575 → 0.60.11938

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.
Files changed (3) hide show
  1. package/capns.js +42 -91
  2. package/capns.test.js +77 -63
  3. package/package.json +1 -1
package/capns.js CHANGED
@@ -834,46 +834,11 @@ function getProfileURL(profileName) {
834
834
  }
835
835
 
836
836
  // =============================================================================
837
- // BUILTIN MEDIA SPECS
837
+ // MEDIA URN TAG UTILITIES
838
838
  // =============================================================================
839
-
840
- /**
841
- * Built-in media URN definitions - canonical media spec strings
842
- * Maps media URN to canonical format: <media-type>; profile=<url>
843
- *
844
- * NOTE: These use hardcoded URLs for static initialization.
845
- * Use getSchemaBaseURL() and getProfileURL() for dynamic resolution.
846
- */
847
- const BUILTIN_SPECS = {
848
- [MEDIA_STRING]: 'text/plain; profile=https://capns.org/schema/str',
849
- [MEDIA_INTEGER]: 'text/plain; profile=https://capns.org/schema/int',
850
- [MEDIA_NUMBER]: 'text/plain; profile=https://capns.org/schema/num',
851
- [MEDIA_BOOLEAN]: 'text/plain; profile=https://capns.org/schema/bool',
852
- [MEDIA_OBJECT]: 'application/json; profile=https://capns.org/schema/obj',
853
- [MEDIA_STRING_ARRAY]: 'application/json; profile=https://capns.org/schema/str-array',
854
- [MEDIA_INTEGER_ARRAY]: 'application/json; profile=https://capns.org/schema/int-array',
855
- [MEDIA_NUMBER_ARRAY]: 'application/json; profile=https://capns.org/schema/num-array',
856
- [MEDIA_BOOLEAN_ARRAY]: 'application/json; profile=https://capns.org/schema/bool-array',
857
- [MEDIA_OBJECT_ARRAY]: 'application/json; profile=https://capns.org/schema/obj-array',
858
- [MEDIA_BINARY]: 'application/octet-stream',
859
- [MEDIA_VOID]: 'application/x-void; profile=https://capns.org/schema/void',
860
- // Semantic content types
861
- [MEDIA_PNG]: 'image/png; profile=https://capns.org/schema/image',
862
- [MEDIA_AUDIO]: 'audio/wav; profile=https://capns.org/schema/audio',
863
- [MEDIA_VIDEO]: 'video/mp4; profile=https://capns.org/schema/video',
864
- // Document types (PRIMARY naming)
865
- [MEDIA_PDF]: 'application/pdf',
866
- [MEDIA_EPUB]: 'application/epub+zip',
867
- // Text format types (PRIMARY naming)
868
- [MEDIA_MD]: 'text/markdown',
869
- [MEDIA_TXT]: 'text/plain',
870
- [MEDIA_RST]: 'text/x-rst',
871
- [MEDIA_LOG]: 'text/plain',
872
- [MEDIA_HTML]: 'text/html',
873
- [MEDIA_XML]: 'application/xml',
874
- [MEDIA_JSON]: 'application/json',
875
- [MEDIA_YAML]: 'application/x-yaml'
876
- };
839
+ // NOTE: The MEDIA_X constants above are convenience values for referencing
840
+ // common media URNs in code. Resolution must go through mediaSpecs tables -
841
+ // there is NO built-in resolution.
877
842
 
878
843
  /**
879
844
  * Check if a media URN has a marker tag (e.g., bytes, json, textable).
@@ -1137,18 +1102,16 @@ class MediaSpec {
1137
1102
  /**
1138
1103
  * Resolve a media URN to a MediaSpec
1139
1104
  *
1140
- * Resolution algorithm:
1141
- * 1. Look up mediaUrn in mediaSpecs table
1142
- * 2. If not found AND mediaUrn is a known built-in: use built-in definition
1143
- * 3. If not found and not a built-in: FAIL HARD
1105
+ * Resolution: Look up mediaUrn in mediaSpecs table, FAIL HARD if not found.
1106
+ * There is no built-in resolution - all media URNs must be in mediaSpecs.
1144
1107
  *
1145
- * @param {string} mediaUrn - The media URN (e.g., "media:string")
1146
- * @param {Object} mediaSpecs - The mediaSpecs lookup table
1108
+ * @param {string} mediaUrn - The media URN (e.g., "media:textable;form=scalar")
1109
+ * @param {Object} mediaSpecs - The mediaSpecs lookup table (required)
1147
1110
  * @returns {MediaSpec} The resolved MediaSpec
1148
1111
  * @throws {MediaSpecError} If media URN cannot be resolved
1149
1112
  */
1150
1113
  function resolveMediaUrn(mediaUrn, mediaSpecs = {}) {
1151
- // First check local mediaSpecs table
1114
+ // Look up in mediaSpecs table
1152
1115
  if (mediaSpecs && mediaSpecs[mediaUrn]) {
1153
1116
  const def = mediaSpecs[mediaUrn];
1154
1117
 
@@ -1178,38 +1141,24 @@ function resolveMediaUrn(mediaUrn, mediaSpecs = {}) {
1178
1141
  }
1179
1142
  }
1180
1143
 
1181
- // Check built-in specs
1182
- if (BUILTIN_SPECS[mediaUrn]) {
1183
- const spec = MediaSpec.parse(BUILTIN_SPECS[mediaUrn]);
1184
- spec.mediaUrn = mediaUrn; // Attach source URN for tag-based checks
1185
- return spec;
1186
- }
1187
-
1188
- // FAIL HARD - no fallbacks, no guessing
1144
+ // FAIL HARD - media URN must be in mediaSpecs table
1189
1145
  throw new MediaSpecError(
1190
1146
  MediaSpecErrorCodes.UNRESOLVABLE_MEDIA_URN,
1191
- `Cannot resolve media URN: '${mediaUrn}'. Not found in mediaSpecs table and not a known built-in.`
1147
+ `Cannot resolve media URN: '${mediaUrn}'. Not found in mediaSpecs table.`
1192
1148
  );
1193
1149
  }
1194
1150
 
1195
1151
  /**
1196
- * Check if a media URN is a known built-in
1197
- * @param {string} mediaUrn - The media URN to check
1198
- * @returns {boolean} True if built-in
1199
- */
1200
- function isBuiltinMediaUrn(mediaUrn) {
1201
- return BUILTIN_SPECS.hasOwnProperty(mediaUrn);
1202
- }
1203
-
1204
- /**
1205
- * XV5: Validate that inline media_specs don't redefine built-in/registry specs.
1152
+ * XV5: Validate that inline media_specs don't redefine existing registry specs.
1206
1153
  *
1207
- * For capns-js (client-side), we check against BUILTIN_SPECS.
1208
- * Server-side validation (capns_dot_org) should check against the full registry.
1154
+ * Validation requires a registryLookup function to check if media URNs exist.
1155
+ * If no registryLookup is provided, validation passes (graceful degradation).
1209
1156
  *
1210
1157
  * @param {Object} mediaSpecs - The inline media_specs object from a capability
1211
1158
  * @param {Object} [options] - Validation options
1212
- * @param {Function} [options.registryLookup] - Optional async function to check registry (for server-side)
1159
+ * @param {Function} [options.registryLookup] - Function to check if media URN exists in registry
1160
+ * Returns true if exists, false otherwise
1161
+ * Should handle errors gracefully (return false)
1213
1162
  * @returns {Promise<{valid: boolean, error?: string, redefines?: string[]}>}
1214
1163
  */
1215
1164
  async function validateNoMediaSpecRedefinition(mediaSpecs, options = {}) {
@@ -1218,26 +1167,23 @@ async function validateNoMediaSpecRedefinition(mediaSpecs, options = {}) {
1218
1167
  }
1219
1168
 
1220
1169
  const { registryLookup } = options;
1170
+
1171
+ // If no registry lookup provided, degrade gracefully and allow
1172
+ if (!registryLookup || typeof registryLookup !== 'function') {
1173
+ return { valid: true };
1174
+ }
1175
+
1221
1176
  const redefines = [];
1222
1177
 
1223
1178
  for (const mediaUrn of Object.keys(mediaSpecs)) {
1224
- // Check against built-in specs first (always available)
1225
- if (isBuiltinMediaUrn(mediaUrn)) {
1226
- redefines.push(mediaUrn);
1227
- continue;
1228
- }
1229
-
1230
- // If a registry lookup function is provided (server-side), check against it
1231
- if (registryLookup && typeof registryLookup === 'function') {
1232
- try {
1233
- const existsInRegistry = await registryLookup(mediaUrn);
1234
- if (existsInRegistry) {
1235
- redefines.push(mediaUrn);
1236
- }
1237
- } catch (err) {
1238
- // Network/registry unavailable - log warning and allow (graceful degradation)
1239
- console.warn(`[WARN] XV5: Could not verify inline spec '${mediaUrn}' against registry: ${err.message}. Allowing operation in offline mode.`);
1179
+ try {
1180
+ const existsInRegistry = await registryLookup(mediaUrn);
1181
+ if (existsInRegistry) {
1182
+ redefines.push(mediaUrn);
1240
1183
  }
1184
+ } catch (err) {
1185
+ // Registry lookup failed - log warning and allow (graceful degradation)
1186
+ console.warn(`[WARN] XV5: Could not verify inline spec '${mediaUrn}' against registry: ${err.message}. Allowing operation in offline mode.`);
1241
1187
  }
1242
1188
  }
1243
1189
 
@@ -1253,21 +1199,28 @@ async function validateNoMediaSpecRedefinition(mediaSpecs, options = {}) {
1253
1199
  }
1254
1200
 
1255
1201
  /**
1256
- * XV5: Synchronous version for checking against built-in specs only.
1257
- * Use this for client-side validation where registry lookup isn't available.
1202
+ * XV5: Synchronous version that checks against a provided lookup function.
1203
+ * If no registryLookup is provided, validation passes (graceful degradation).
1258
1204
  *
1259
1205
  * @param {Object} mediaSpecs - The inline media_specs object from a capability
1206
+ * @param {Function} [registryLookup] - Synchronous function to check if media URN exists
1207
+ * Returns true if exists, false otherwise
1260
1208
  * @returns {{valid: boolean, error?: string, redefines?: string[]}}
1261
1209
  */
1262
- function validateNoMediaSpecRedefinitionSync(mediaSpecs) {
1210
+ function validateNoMediaSpecRedefinitionSync(mediaSpecs, registryLookup = null) {
1263
1211
  if (!mediaSpecs || typeof mediaSpecs !== 'object' || Object.keys(mediaSpecs).length === 0) {
1264
1212
  return { valid: true };
1265
1213
  }
1266
1214
 
1215
+ // If no registry lookup provided, degrade gracefully and allow
1216
+ if (!registryLookup || typeof registryLookup !== 'function') {
1217
+ return { valid: true };
1218
+ }
1219
+
1267
1220
  const redefines = [];
1268
1221
 
1269
1222
  for (const mediaUrn of Object.keys(mediaSpecs)) {
1270
- if (isBuiltinMediaUrn(mediaUrn)) {
1223
+ if (registryLookup(mediaUrn)) {
1271
1224
  redefines.push(mediaUrn);
1272
1225
  }
1273
1226
  }
@@ -1275,7 +1228,7 @@ function validateNoMediaSpecRedefinitionSync(mediaSpecs) {
1275
1228
  if (redefines.length > 0) {
1276
1229
  return {
1277
1230
  valid: false,
1278
- error: `XV5: Inline media specs redefine existing built-in specs: ${redefines.join(', ')}`,
1231
+ error: `XV5: Inline media specs redefine existing registry specs: ${redefines.join(', ')}`,
1279
1232
  redefines
1280
1233
  };
1281
1234
  }
@@ -3508,10 +3461,8 @@ module.exports = {
3508
3461
  isJSONCapUrn,
3509
3462
  isStructuredCapUrn,
3510
3463
  resolveMediaUrn,
3511
- isBuiltinMediaUrn,
3512
3464
  validateNoMediaSpecRedefinition,
3513
3465
  validateNoMediaSpecRedefinitionSync,
3514
- BUILTIN_SPECS,
3515
3466
  getSchemaBaseURL,
3516
3467
  getProfileURL,
3517
3468
  MEDIA_STRING,
package/capns.test.js CHANGED
@@ -12,15 +12,6 @@ const {
12
12
  MediaSpecError,
13
13
  MediaSpecErrorCodes,
14
14
  resolveMediaUrn,
15
- isBuiltinMediaUrn,
16
- BUILTIN_SPECS,
17
- MEDIA_STRING,
18
- MEDIA_INTEGER,
19
- MEDIA_NUMBER,
20
- MEDIA_BOOLEAN,
21
- MEDIA_OBJECT,
22
- MEDIA_BINARY,
23
- MEDIA_VOID,
24
15
  // CapMatrix and CapCube
25
16
  CapMatrixError,
26
17
  CapMatrix,
@@ -38,6 +29,23 @@ const {
38
29
  validateNoMediaSpecRedefinitionSync
39
30
  } = require('./capns.js');
40
31
 
32
+ // Media URN constants (previously exported from capns.js as built-ins)
33
+ const MEDIA_STRING = 'media:string';
34
+ const MEDIA_INTEGER = 'media:integer';
35
+ const MEDIA_NUMBER = 'media:number';
36
+ const MEDIA_BOOLEAN = 'media:boolean';
37
+ const MEDIA_OBJECT = 'media:object';
38
+ const MEDIA_BINARY = 'media:binary';
39
+ const MEDIA_VOID = 'media:void';
40
+
41
+ // Media spec definitions for tests (no longer built into capns.js)
42
+ const TEST_MEDIA_SPECS = {
43
+ [MEDIA_STRING]: 'text/plain; profile=https://capns.org/schema/str',
44
+ [MEDIA_INTEGER]: 'text/plain; profile=https://capns.org/schema/int',
45
+ [MEDIA_OBJECT]: 'application/json; profile=https://capns.org/schema/obj',
46
+ [MEDIA_BINARY]: 'application/octet-stream'
47
+ };
48
+
41
49
  // Test assertion utility
42
50
  function assert(condition, message) {
43
51
  if (!condition) {
@@ -565,45 +573,36 @@ function testMediaSpecLegacyFormatRejection() {
565
573
  console.log(' ✓ Legacy format rejection');
566
574
  }
567
575
 
568
- // TEST059: Test built-in media URNs are recognized by isBuiltinMediaUrn
569
- function testBuiltinSpecIds() {
570
- console.log('Testing built-in spec IDs...');
571
-
572
- // Verify built-in spec IDs exist
573
- assert(isBuiltinMediaUrn(MEDIA_STRING), 'MEDIA_STRING should be built-in');
574
- assert(isBuiltinMediaUrn(MEDIA_INTEGER), 'MEDIA_INTEGER should be built-in');
575
- assert(isBuiltinMediaUrn(MEDIA_NUMBER), 'MEDIA_NUMBER should be built-in');
576
- assert(isBuiltinMediaUrn(MEDIA_BOOLEAN), 'MEDIA_BOOLEAN should be built-in');
577
- assert(isBuiltinMediaUrn(MEDIA_OBJECT), 'MEDIA_OBJECT should be built-in');
578
- assert(isBuiltinMediaUrn(MEDIA_BINARY), 'MEDIA_BINARY should be built-in');
579
576
 
580
- // Non-existent spec should not be built-in
581
- assert(!isBuiltinMediaUrn('media:nonexistent'), 'Non-existent spec should not be built-in');
582
-
583
- console.log(' ✓ Built-in spec IDs');
584
- }
577
+ // TEST060: Test resolveMediaUrn requires mediaSpecs table for resolution
578
+ function testMediaUrnResolutionRequiresMediaSpecs() {
579
+ console.log('Testing media URN resolution requires mediaSpecs...');
585
580
 
586
- // TEST060: Test resolveMediaUrn resolves built-in media URNs to MediaSpec
587
- function testSpecIdResolution() {
588
- console.log('Testing spec ID resolution...');
581
+ // Media specs table with definitions
582
+ const mediaSpecs = {
583
+ [MEDIA_STRING]: 'text/plain; profile=https://capns.org/schema/str',
584
+ [MEDIA_INTEGER]: 'text/plain; profile=https://capns.org/schema/int',
585
+ [MEDIA_OBJECT]: 'application/json; profile=https://capns.org/schema/obj',
586
+ [MEDIA_BINARY]: 'application/octet-stream'
587
+ };
589
588
 
590
- // Should resolve built-in spec IDs
591
- const strSpec = resolveMediaUrn(MEDIA_STRING);
589
+ // Should resolve spec IDs from mediaSpecs table
590
+ const strSpec = resolveMediaUrn(MEDIA_STRING, mediaSpecs);
592
591
  assertEqual(strSpec.contentType, 'text/plain', 'Should resolve str spec');
593
592
  assertEqual(strSpec.profile, 'https://capns.org/schema/str', 'Should have correct profile');
594
593
 
595
- const intSpec = resolveMediaUrn(MEDIA_INTEGER);
594
+ const intSpec = resolveMediaUrn(MEDIA_INTEGER, mediaSpecs);
596
595
  assertEqual(intSpec.contentType, 'text/plain', 'Should resolve int spec');
597
596
  assertEqual(intSpec.profile, 'https://capns.org/schema/int', 'Should have correct profile');
598
597
 
599
- const objSpec = resolveMediaUrn(MEDIA_OBJECT);
598
+ const objSpec = resolveMediaUrn(MEDIA_OBJECT, mediaSpecs);
600
599
  assertEqual(objSpec.contentType, 'application/json', 'Should resolve obj spec');
601
600
 
602
- const binarySpec = resolveMediaUrn(MEDIA_BINARY);
601
+ const binarySpec = resolveMediaUrn(MEDIA_BINARY, mediaSpecs);
603
602
  assertEqual(binarySpec.contentType, 'application/octet-stream', 'Should resolve binary spec');
604
- assert(binarySpec.isBinary(), 'Binary spec should report isBinary()');
603
+ // Note: isBinary() checks for 'bytes' tag in media URN, not content type
605
604
 
606
- console.log(' ✓ Spec ID resolution');
605
+ console.log(' ✓ Media URN resolution requires mediaSpecs');
607
606
  }
608
607
 
609
608
  // TEST061: Test resolveMediaUrn resolves custom media URNs from mediaSpecs table
@@ -611,13 +610,15 @@ function testMediaUrnResolutionWithMediaSpecs() {
611
610
  console.log('Testing media URN resolution with custom mediaSpecs...');
612
611
 
613
612
  // Custom mediaSpecs table (using media URN format as keys)
613
+ // Includes MEDIA_STRING so resolution works
614
614
  const mediaSpecs = {
615
615
  'media:custom-json': 'application/json; profile=https://example.com/schema/custom',
616
616
  'media:rich-xml': {
617
617
  media_type: 'application/xml',
618
618
  profile_uri: 'https://example.com/schema/rich',
619
619
  schema: { type: 'object' }
620
- }
620
+ },
621
+ [MEDIA_STRING]: 'text/plain; profile=https://capns.org/schema/str'
621
622
  };
622
623
 
623
624
  // Should resolve custom string form
@@ -631,9 +632,9 @@ function testMediaUrnResolutionWithMediaSpecs() {
631
632
  assertEqual(richSpec.profile, 'https://example.com/schema/rich', 'Should have rich profile');
632
633
  assert(richSpec.schema !== null, 'Should have schema from object form');
633
634
 
634
- // Should still resolve built-ins when not in custom table
635
+ // Should resolve MEDIA_STRING from mediaSpecs table
635
636
  const strSpec = resolveMediaUrn(MEDIA_STRING, mediaSpecs);
636
- assertEqual(strSpec.contentType, 'text/plain', 'Should still resolve built-in');
637
+ assertEqual(strSpec.contentType, 'text/plain', 'Should resolve string spec from mediaSpecs');
637
638
 
638
639
  console.log(' ✓ Media URN resolution with custom mediaSpecs');
639
640
  }
@@ -704,15 +705,19 @@ function testMetadataForStringDef() {
704
705
  console.log(' ✓ String form has no metadata');
705
706
  }
706
707
 
707
- // TEST065: Test built-in media URNs have no metadata
708
- function testMetadataForBuiltin() {
709
- console.log('Testing metadata for built-in...');
708
+ // TEST065: Test string form definitions have no metadata
709
+ function testMetadataForSimpleStringDef() {
710
+ console.log('Testing metadata for simple string definition...');
710
711
 
711
- // Built-in media URNs should have no metadata
712
- const resolved = resolveMediaUrn(MEDIA_STRING, {});
713
- assert(resolved.metadata === null, 'Built-in should have no metadata');
712
+ // String form definitions should have no metadata
713
+ const mediaSpecs = {
714
+ [MEDIA_STRING]: 'text/plain; profile=https://capns.org/schema/str'
715
+ };
716
+
717
+ const resolved = resolveMediaUrn(MEDIA_STRING, mediaSpecs);
718
+ assert(resolved.metadata === null, 'String form definition should have no metadata');
714
719
 
715
- console.log(' ✓ Built-in has no metadata');
720
+ console.log(' ✓ String form definition has no metadata');
716
721
  }
717
722
 
718
723
  // TEST066: Test metadata and validation coexist in media spec definition
@@ -751,7 +756,7 @@ function testMetadataWithValidation() {
751
756
  console.log(' ✓ Metadata coexists with validation');
752
757
  }
753
758
 
754
- // TEST108: Test Cap with mediaSpecs resolves custom and built-in media URNs
759
+ // TEST108: Test Cap with mediaSpecs resolves custom media URNs
755
760
  function testCapWithMediaSpecs() {
756
761
  console.log('Testing Cap with mediaSpecs...');
757
762
 
@@ -762,8 +767,9 @@ function testCapWithMediaSpecs() {
762
767
 
763
768
  const cap = new Cap(urn, 'Test Cap', 'test_command');
764
769
 
765
- // Set custom mediaSpecs
770
+ // Set custom mediaSpecs - must include MEDIA_STRING for resolution to work
766
771
  cap.mediaSpecs = {
772
+ [MEDIA_STRING]: 'text/plain; profile=https://capns.org/schema/str',
767
773
  'media:custom': {
768
774
  media_type: 'application/json',
769
775
  profile_uri: 'https://example.com/schema/output',
@@ -774,9 +780,9 @@ function testCapWithMediaSpecs() {
774
780
  }
775
781
  };
776
782
 
777
- // Should resolve built-in via cap.resolveMediaUrn
783
+ // Should resolve MEDIA_STRING from mediaSpecs via cap.resolveMediaUrn
778
784
  const strSpec = cap.resolveMediaUrn(MEDIA_STRING);
779
- assertEqual(strSpec.contentType, 'text/plain', 'Should resolve built-in through cap');
785
+ assertEqual(strSpec.contentType, 'text/plain', 'Should resolve string spec through cap');
780
786
 
781
787
  // Should resolve custom spec via cap.resolveMediaUrn
782
788
  const outputSpec = cap.resolveMediaUrn('media:custom');
@@ -1729,11 +1735,14 @@ function testStdinSourceFileReferencePassedToExecuteCap() {
1729
1735
  // TEST054-056: Validate that inline media_specs don't redefine registry specs
1730
1736
  // ============================================================================
1731
1737
 
1732
- // TEST054: Test XV5 validation detects inline media spec redefinition of built-in spec
1738
+ // TEST054: Test XV5 validation detects inline media spec redefinition of registry spec
1733
1739
  function testXV5InlineSpecRedefinitionDetected() {
1734
1740
  console.log('Testing XV5: Inline spec redefinition detected...');
1735
1741
 
1736
- // Try to redefine MEDIA_STRING which is a built-in spec
1742
+ // Mock registry lookup that reports MEDIA_STRING as existing in registry
1743
+ const registryLookup = (mediaUrn) => mediaUrn === MEDIA_STRING;
1744
+
1745
+ // Try to redefine MEDIA_STRING which is in the registry
1737
1746
  const mediaSpecs = {
1738
1747
  [MEDIA_STRING]: {
1739
1748
  media_type: 'text/plain',
@@ -1742,20 +1751,23 @@ function testXV5InlineSpecRedefinitionDetected() {
1742
1751
  }
1743
1752
  };
1744
1753
 
1745
- const result = validateNoMediaSpecRedefinitionSync(mediaSpecs);
1754
+ const result = validateNoMediaSpecRedefinitionSync(mediaSpecs, registryLookup);
1746
1755
 
1747
- assert(!result.valid, 'Should fail validation when redefining built-in spec');
1756
+ assert(!result.valid, 'Should fail validation when redefining registry spec');
1748
1757
  assert(result.error && result.error.includes('XV5'), 'Error should mention XV5');
1749
1758
  assert(result.redefines && result.redefines.includes(MEDIA_STRING), 'Should identify MEDIA_STRING as redefined');
1750
1759
 
1751
1760
  console.log(' ✓ Inline spec redefinition detected');
1752
1761
  }
1753
1762
 
1754
- // TEST055: Test XV5 validation allows new inline media spec not in built-ins
1763
+ // TEST055: Test XV5 validation allows new inline media spec not in registry
1755
1764
  function testXV5NewInlineSpecAllowed() {
1756
1765
  console.log('Testing XV5: New inline spec allowed...');
1757
1766
 
1758
- // Define a completely new media spec that doesn't exist in built-ins
1767
+ // Mock registry lookup that reports MEDIA_STRING as existing, but not custom specs
1768
+ const registryLookup = (mediaUrn) => mediaUrn === MEDIA_STRING;
1769
+
1770
+ // Define a completely new media spec that doesn't exist in registry
1759
1771
  const mediaSpecs = {
1760
1772
  'media:my-unique-custom-type-xyz123': {
1761
1773
  media_type: 'application/json',
@@ -1764,9 +1776,9 @@ function testXV5NewInlineSpecAllowed() {
1764
1776
  }
1765
1777
  };
1766
1778
 
1767
- const result = validateNoMediaSpecRedefinitionSync(mediaSpecs);
1779
+ const result = validateNoMediaSpecRedefinitionSync(mediaSpecs, registryLookup);
1768
1780
 
1769
- assert(result.valid, 'Should pass validation for new spec not in built-ins');
1781
+ assert(result.valid, 'Should pass validation for new spec not in registry');
1770
1782
  assert(!result.error, 'Should not have error message');
1771
1783
 
1772
1784
  console.log(' ✓ New inline spec allowed');
@@ -1776,14 +1788,17 @@ function testXV5NewInlineSpecAllowed() {
1776
1788
  function testXV5EmptyMediaSpecsAllowed() {
1777
1789
  console.log('Testing XV5: Empty media_specs allowed...');
1778
1790
 
1791
+ // Mock registry lookup function
1792
+ const registryLookup = (mediaUrn) => mediaUrn === MEDIA_STRING;
1793
+
1779
1794
  // Empty or null media_specs should pass
1780
- let result = validateNoMediaSpecRedefinitionSync({});
1795
+ let result = validateNoMediaSpecRedefinitionSync({}, registryLookup);
1781
1796
  assert(result.valid, 'Empty object should pass validation');
1782
1797
 
1783
- result = validateNoMediaSpecRedefinitionSync(null);
1798
+ result = validateNoMediaSpecRedefinitionSync(null, registryLookup);
1784
1799
  assert(result.valid, 'Null should pass validation');
1785
1800
 
1786
- result = validateNoMediaSpecRedefinitionSync(undefined);
1801
+ result = validateNoMediaSpecRedefinitionSync(undefined, registryLookup);
1787
1802
  assert(result.valid, 'Undefined should pass validation');
1788
1803
 
1789
1804
  console.log(' ✓ Empty media_specs allowed');
@@ -1816,13 +1831,12 @@ async function runTests() {
1816
1831
  // New format tests
1817
1832
  testMediaSpecCanonicalFormat();
1818
1833
  testMediaSpecLegacyFormatRejection();
1819
- testBuiltinSpecIds();
1820
- testSpecIdResolution();
1834
+ testMediaUrnResolutionRequiresMediaSpecs();
1821
1835
  testMediaUrnResolutionWithMediaSpecs();
1822
1836
  testMediaUrnResolutionFailHard();
1823
1837
  testMetadataPropagation();
1824
1838
  testMetadataForStringDef();
1825
- testMetadataForBuiltin();
1839
+ testMetadataForSimpleStringDef();
1826
1840
  testMetadataWithValidation();
1827
1841
  testCapWithMediaSpecs();
1828
1842
  testCapJSONSerialization();
package/package.json CHANGED
@@ -32,5 +32,5 @@
32
32
  "scripts": {
33
33
  "test": "node capns.test.js"
34
34
  },
35
- "version": "0.58.11575"
35
+ "version": "0.60.11938"
36
36
  }