@mcp-native/react-native 0.7.0 → 0.9.0

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/v1.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../src/v1.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../src/v1.ts"],"names":[],"mappings":"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"}
package/dist/v1.js CHANGED
@@ -1,8 +1,9 @@
1
- import { A2UI_V1_MAX_COMPONENTS, A2UI_V1_MAX_SOURCE_LENGTH, A2uiParseError, evaluateA2uiV1FormatString, validateA2uiV1SurfaceState, } from "@mcp-native/a2ui";
1
+ import { A2UI_V1_MAX_COMPONENTS, A2UI_V1_MAX_SOURCE_LENGTH, A2uiParseError, evaluateA2uiV1FormatString, validateA2uiV1HostExtensionComponent, validateA2uiV1SurfaceState, } from "@mcp-native/a2ui";
2
2
  import { JSON_MAX_STRING_LENGTH, JSON_MAX_VALUES, parseJsonObject, parseJsonValue, } from "@mcp-native/core";
3
3
  import { ISO_4217_CURRENCY_CODES } from "./iso-4217.js";
4
4
  import { A2UI_V1_NATIVE_ICON_NAMES } from "./component-adapters.js";
5
5
  export const A2UI_V1_NATIVE_COMPONENT_NAMES = Object.freeze([
6
+ "AudioPlayer",
6
7
  "Button",
7
8
  "Card",
8
9
  "CheckBox",
@@ -19,6 +20,7 @@ export const A2UI_V1_NATIVE_COMPONENT_NAMES = Object.freeze([
19
20
  "Tabs",
20
21
  "Text",
21
22
  "TextField",
23
+ "Video",
22
24
  ]);
23
25
  /** Maximum expanded native-plan nodes, including repeated component references. */
24
26
  export const A2UI_V1_NATIVE_MAX_RENDER_NODES = A2UI_V1_MAX_COMPONENTS;
@@ -39,6 +41,13 @@ export const A2UI_V1_NATIVE_MAX_IMAGES = 64;
39
41
  export const A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_BYTES = A2UI_V1_NATIVE_MAX_IMAGE_BYTES;
40
42
  /** Maximum sum of host-granted decoded pixels across one expanded render plan. */
41
43
  export const A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_PIXELS = A2UI_V1_NATIVE_MAX_IMAGE_PIXELS;
44
+ export const A2UI_V1_NATIVE_MAX_MEDIA = 16;
45
+ export const A2UI_V1_NATIVE_MAX_MEDIA_URL_LENGTH = 8_192;
46
+ export const A2UI_V1_NATIVE_MAX_MEDIA_MIME_TYPES = 32;
47
+ export const A2UI_V1_NATIVE_MAX_MEDIA_REDIRECT_ORIGINS = 64;
48
+ export const A2UI_V1_NATIVE_MAX_MEDIA_REDIRECTS = 10;
49
+ export const A2UI_V1_NATIVE_MAX_MEDIA_BYTES = 2_147_483_648;
50
+ export const A2UI_V1_NATIVE_MAX_TOTAL_MEDIA_BYTES = 2_147_483_648;
42
51
  /**
43
52
  * Converts a policy-validated A2UI v1 surface into the existing host-owned
44
53
  * native render plan. Unsupported renderer semantics fail closed.
@@ -52,7 +61,7 @@ export function createA2uiV1NativeRenderPlanForLocalEdits(surface, policy, optio
52
61
  }
53
62
  function createNativeRenderPlan(surface, policy, options, tolerateInvalidLocalOpenUrls) {
54
63
  const parsedOptions = parseRenderPlanOptions(options);
55
- const context = createAdapterContext(surface, policy, parsedOptions.dataModel, parsedOptions.locale, tolerateInvalidLocalOpenUrls, parsedOptions.imagePolicy);
64
+ const context = createAdapterContext(surface, policy, parsedOptions.dataModel, parsedOptions.locale, tolerateInvalidLocalOpenUrls, parsedOptions.imagePolicy, parsedOptions.mediaPolicy, parsedOptions.hostExtensionPolicy);
56
65
  return adaptComponent("root", "root", context, undefined);
57
66
  }
58
67
  /** Resolves one validated event against the latest renderer-local data model. */
@@ -61,7 +70,7 @@ export function resolveA2uiV1NativeEvent(surface, policy, sourceComponentId, dat
61
70
  throw new A2uiParseError("Expected a non-empty A2UI source component id");
62
71
  }
63
72
  const parsedOptions = parseEventResolutionOptions(options);
64
- const context = createAdapterContext(surface, policy, dataModel, parsedOptions.locale, false, undefined, false);
73
+ const context = createAdapterContext(surface, policy, dataModel, parsedOptions.locale, false, undefined, undefined, undefined, false);
65
74
  const plan = adaptComponent("root", "root", context, undefined);
66
75
  const events = findNativeEvents(plan, sourceComponentId, parsedOptions.instanceKey);
67
76
  if (events.length === 0) {
@@ -82,7 +91,7 @@ export function resolveA2uiV1NativeOpenUrl(surface, policy, sourceComponentId, d
82
91
  throw new A2uiParseError("Expected a non-empty A2UI openUrl source component id");
83
92
  }
84
93
  const parsedOptions = parseOpenUrlResolutionOptions(options);
85
- const context = createAdapterContext(surface, policy, dataModel, parsedOptions.locale, false, undefined, false);
94
+ const context = createAdapterContext(surface, policy, dataModel, parsedOptions.locale, false, undefined, undefined, undefined, false);
86
95
  const plan = adaptComponent("root", "root", context, undefined);
87
96
  const openUrls = findNativeOpenUrls(plan, sourceComponentId, parsedOptions.instanceKey);
88
97
  if (openUrls.length === 0) {
@@ -123,7 +132,7 @@ function findNativeOpenUrls(element, sourceComponentId, instanceKey, includeDisa
123
132
  }
124
133
  return openUrls;
125
134
  }
126
- function createAdapterContext(surface, policy, dataModel, locale, tolerateInvalidLocalOpenUrls = false, imagePolicy, authorizeImageResources = true) {
135
+ function createAdapterContext(surface, policy, dataModel, locale, tolerateInvalidLocalOpenUrls = false, imagePolicy, mediaPolicy, hostExtensionPolicy, authorizeResources = true) {
127
136
  const localDataModel = dataModel === undefined
128
137
  ? parseJsonObject(surface.dataModel, "surface.dataModel")
129
138
  : parseJsonObject(dataModel, "options.dataModel");
@@ -138,7 +147,10 @@ function createAdapterContext(surface, policy, dataModel, locale, tolerateInvali
138
147
  visiting: new Set(),
139
148
  tolerateInvalidLocalOpenUrls,
140
149
  imagePolicy,
141
- authorizeImageResources,
150
+ mediaPolicy,
151
+ hostExtensionPolicy,
152
+ hostExtensions: policy.hostExtensions,
153
+ authorizeResources,
142
154
  choiceOptionCount: 0,
143
155
  choiceOptionOutputLength: 0,
144
156
  formatStringExpressionCount: 0,
@@ -150,6 +162,13 @@ function createAdapterContext(surface, policy, dataModel, locale, tolerateInvali
150
162
  imageMaximumDecodedPixels: 0,
151
163
  imageRedirectOriginCount: 0,
152
164
  imageResourcePolicyOutputLength: 0,
165
+ mediaCount: 0,
166
+ mediaUrlLength: 0,
167
+ mediaMaximumBytes: 0,
168
+ mediaMimeTypeCount: 0,
169
+ mediaRedirectOriginCount: 0,
170
+ mediaPolicyOutputLength: 0,
171
+ hostExtensionCounts: new Map(),
153
172
  renderNodeCount: 0,
154
173
  validationCheckCount: 0,
155
174
  validationOutputLength: 0,
@@ -158,10 +177,14 @@ function createAdapterContext(surface, policy, dataModel, locale, tolerateInvali
158
177
  function parseRenderPlanOptions(options) {
159
178
  const parsed = parseOptionsObject(options, "A2UI native render plan options", [
160
179
  "dataModel",
180
+ "hostExtensionPolicy",
161
181
  "imagePolicy",
162
182
  "locale",
183
+ "mediaPolicy",
163
184
  ]);
164
185
  const imagePolicy = parseOptionalImagePolicy(parsed.imagePolicy, "options.imagePolicy");
186
+ const mediaPolicy = parseOptionalPolicy(parsed.mediaPolicy, "options.mediaPolicy");
187
+ const hostExtensionPolicy = parseOptionalPolicy(parsed.hostExtensionPolicy, "options.hostExtensionPolicy");
165
188
  return {
166
189
  ...(parsed.dataModel === undefined
167
190
  ? {}
@@ -170,6 +193,8 @@ function parseRenderPlanOptions(options) {
170
193
  ? {}
171
194
  : { locale: parseLocale(parsed.locale, "options.locale") }),
172
195
  ...(imagePolicy === undefined ? {} : { imagePolicy }),
196
+ ...(mediaPolicy === undefined ? {} : { mediaPolicy }),
197
+ ...(hostExtensionPolicy === undefined ? {} : { hostExtensionPolicy }),
173
198
  };
174
199
  }
175
200
  function parseEventResolutionOptions(options) {
@@ -210,6 +235,12 @@ function parseOptionalImagePolicy(value, path) {
210
235
  }
211
236
  return value;
212
237
  }
238
+ function parseOptionalPolicy(value, path) {
239
+ if (value !== undefined && typeof value !== "function") {
240
+ throw new A2uiParseError(`Expected a function at ${path}`);
241
+ }
242
+ return value;
243
+ }
213
244
  function parseOptionsObject(value, label, allowedKeys) {
214
245
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
215
246
  throw new A2uiParseError(`Expected ${label} to be an object`);
@@ -273,6 +304,10 @@ function adaptComponent(id, key, context, scope) {
273
304
  return adaptText(component, key, context, scope);
274
305
  case "Image":
275
306
  return adaptImage(component, key, context, scope);
307
+ case "Video":
308
+ return adaptMedia(component, key, "video", context, scope);
309
+ case "AudioPlayer":
310
+ return adaptMedia(component, key, "audio", context, scope);
276
311
  case "Icon":
277
312
  return adaptIcon(component, key, context, scope);
278
313
  case "Divider":
@@ -294,7 +329,7 @@ function adaptComponent(id, key, context, scope) {
294
329
  case "Modal":
295
330
  return adaptModal(component, key, context, scope);
296
331
  default:
297
- throw new A2uiParseError(`A2UI component ${JSON.stringify(id)} uses ${JSON.stringify(component.component)}, which the native adapter does not support`);
332
+ return adaptHostExtension(component, key, context, scope);
298
333
  }
299
334
  }
300
335
  finally {
@@ -395,47 +430,183 @@ function adaptImage(component, key, context, scope) {
395
430
  fit: component.fit ?? "fill",
396
431
  variant: component.variant ?? "mediumFeature",
397
432
  };
398
- if (context.authorizeImageResources) {
399
- const request = {
400
- url,
433
+ if (context.authorizeResources) {
434
+ props.resourcePolicy = authorizeImageResource(component.id, key, url, `components.${component.id}.imagePolicy`, context);
435
+ }
436
+ if (component.description !== undefined) {
437
+ props.description = resolveDynamicString(component.description, `components.${component.id}.description`, context, scope);
438
+ }
439
+ addCommonProps(component, props, context, scope);
440
+ if (props.accessibilityLabel === undefined && props.description !== undefined) {
441
+ props.accessibilityLabel = props.description;
442
+ }
443
+ return { key, component: "Image", props };
444
+ }
445
+ function authorizeImageResource(componentId, key, url, path, context) {
446
+ const request = Object.freeze({
447
+ url,
448
+ surfaceId: context.surface.surfaceId,
449
+ sourceComponentId: componentId,
450
+ instanceKey: key,
451
+ });
452
+ if (context.imagePolicy === undefined) {
453
+ throw new A2uiParseError(`A2UI image resource ${JSON.stringify(componentId)} requires an explicit host image policy`);
454
+ }
455
+ let decision;
456
+ try {
457
+ decision = context.imagePolicy(request);
458
+ }
459
+ catch (cause) {
460
+ throw new A2uiParseError(`A2UI image resource ${JSON.stringify(componentId)} host policy failed`, { cause });
461
+ }
462
+ if (decision === false) {
463
+ throw new A2uiParseError(`A2UI image resource ${JSON.stringify(componentId)} URL is denied by host policy`);
464
+ }
465
+ const resourcePolicy = parseImageResourcePolicy(decision, path, context);
466
+ context.imageMaximumBytes += resourcePolicy.maximumBytes;
467
+ if (context.imageMaximumBytes > A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_BYTES) {
468
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum total image transfer budget of ${A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_BYTES} bytes`);
469
+ }
470
+ context.imageMaximumDecodedPixels += resourcePolicy.maximumDecodedPixels;
471
+ if (context.imageMaximumDecodedPixels > A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_PIXELS) {
472
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum total decoded image budget of ${A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_PIXELS} pixels`);
473
+ }
474
+ return resourcePolicy;
475
+ }
476
+ function adaptMedia(component, key, kind, context, scope) {
477
+ context.mediaCount += 1;
478
+ if (context.mediaCount > A2UI_V1_NATIVE_MAX_MEDIA) {
479
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum of ${A2UI_V1_NATIVE_MAX_MEDIA} media components`);
480
+ }
481
+ const path = `components.${component.id}.url`;
482
+ const url = normalizeHttpUrl(resolveDynamicString(component.url, path, context, scope), path, kind, A2UI_V1_NATIVE_MAX_MEDIA_URL_LENGTH);
483
+ context.mediaUrlLength += url.length;
484
+ if (context.mediaUrlLength > A2UI_V1_MAX_SOURCE_LENGTH) {
485
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds cumulative media URL length of ${A2UI_V1_MAX_SOURCE_LENGTH}`);
486
+ }
487
+ const props = { uri: url };
488
+ if (context.authorizeResources) {
489
+ props.resourcePolicy = authorizeMediaResource(component.id, key, kind, url, context);
490
+ }
491
+ if (kind === "video" && component.posterUrl !== undefined) {
492
+ const posterPath = `components.${component.id}.posterUrl`;
493
+ const posterUrl = normalizeHttpUrl(resolveDynamicString(component.posterUrl, posterPath, context, scope), posterPath, "video poster", A2UI_V1_NATIVE_MAX_IMAGE_URL_LENGTH);
494
+ context.imageCount += 1;
495
+ if (context.imageCount > A2UI_V1_NATIVE_MAX_IMAGES) {
496
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum of ${A2UI_V1_NATIVE_MAX_IMAGES} image resources`);
497
+ }
498
+ context.imageUrlLength += posterUrl.length;
499
+ if (context.imageUrlLength > A2UI_V1_MAX_SOURCE_LENGTH) {
500
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds cumulative image URL length of ${A2UI_V1_MAX_SOURCE_LENGTH}`);
501
+ }
502
+ props.posterUri = posterUrl;
503
+ if (context.authorizeResources) {
504
+ props.posterResourcePolicy = authorizeImageResource(component.id, `${key}:poster`, posterUrl, `components.${component.id}.posterPolicy`, context);
505
+ }
506
+ }
507
+ if (kind === "audio" && component.description !== undefined) {
508
+ props.description = resolveDynamicString(component.description, `components.${component.id}.description`, context, scope);
509
+ }
510
+ addCommonProps(component, props, context, scope);
511
+ if (props.accessibilityLabel === undefined) {
512
+ props.accessibilityLabel =
513
+ kind === "audio" && typeof props.description === "string"
514
+ ? props.description
515
+ : kind === "audio"
516
+ ? "Audio"
517
+ : "Video";
518
+ }
519
+ return { key, component: kind === "video" ? "Video" : "AudioPlayer", props };
520
+ }
521
+ function authorizeMediaResource(componentId, key, kind, url, context) {
522
+ if (context.mediaPolicy === undefined) {
523
+ throw new A2uiParseError(`A2UI ${kind} ${JSON.stringify(componentId)} requires an explicit host media policy`);
524
+ }
525
+ const sourceOrigin = parseHttpUrlOrigin(url, `components.${componentId}.url`);
526
+ const request = Object.freeze({
527
+ kind,
528
+ url,
529
+ sourceOrigin,
530
+ surfaceId: context.surface.surfaceId,
531
+ sourceComponentId: componentId,
532
+ instanceKey: key,
533
+ });
534
+ let decision;
535
+ try {
536
+ decision = context.mediaPolicy(request);
537
+ }
538
+ catch (cause) {
539
+ throw new A2uiParseError(`A2UI ${kind} ${JSON.stringify(componentId)} host policy failed`, {
540
+ cause,
541
+ });
542
+ }
543
+ if (decision === false) {
544
+ throw new A2uiParseError(`A2UI ${kind} ${JSON.stringify(componentId)} URL is denied by host policy`);
545
+ }
546
+ const resourcePolicy = parseMediaResourcePolicy(decision, request, `components.${componentId}.mediaPolicy`, context);
547
+ context.mediaMaximumBytes += resourcePolicy.maximumBytes;
548
+ if (context.mediaMaximumBytes > A2UI_V1_NATIVE_MAX_TOTAL_MEDIA_BYTES) {
549
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum total media transfer budget of ${A2UI_V1_NATIVE_MAX_TOTAL_MEDIA_BYTES} bytes`);
550
+ }
551
+ return resourcePolicy;
552
+ }
553
+ function adaptHostExtension(component, key, context, scope) {
554
+ const hostExtensions = context.hostExtensions;
555
+ if (hostExtensions === undefined) {
556
+ throw new A2uiParseError(`A2UI component ${JSON.stringify(component.id)} uses an unavailable host extension`);
557
+ }
558
+ const validated = validateA2uiV1HostExtensionComponent(hostExtensions, component, `components.${component.id}`);
559
+ const manifest = validated.manifest;
560
+ const countKey = `${manifest.catalogId}\u0000${manifest.componentName}`;
561
+ const count = (context.hostExtensionCounts.get(countKey) ?? 0) + 1;
562
+ if (count > manifest.limits.maximumInstances) {
563
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum of ${manifest.limits.maximumInstances} instances for ${JSON.stringify(manifest.componentName)}`);
564
+ }
565
+ context.hostExtensionCounts.set(countKey, count);
566
+ const props = {
567
+ extensionId: manifest.extensionId,
568
+ catalogId: manifest.catalogId,
569
+ schemaVersion: manifest.schemaVersion,
570
+ componentName: manifest.componentName,
571
+ manifestFingerprint: validated.manifestFingerprint,
572
+ semanticProps: validated.props,
573
+ sourceComponentId: component.id,
574
+ surfaceId: context.surface.surfaceId,
575
+ };
576
+ if (context.authorizeResources) {
577
+ if (context.hostExtensionPolicy === undefined) {
578
+ throw new A2uiParseError(`Host extension ${JSON.stringify(manifest.componentName)} requires an explicit capability policy`);
579
+ }
580
+ const request = Object.freeze({
581
+ extensionId: manifest.extensionId,
582
+ catalogId: manifest.catalogId,
583
+ schemaVersion: manifest.schemaVersion,
584
+ componentName: manifest.componentName,
401
585
  surfaceId: context.surface.surfaceId,
402
586
  sourceComponentId: component.id,
403
587
  instanceKey: key,
404
- };
405
- if (context.imagePolicy === undefined) {
406
- throw new A2uiParseError(`A2UI Image ${JSON.stringify(component.id)} requires an explicit host image policy`);
407
- }
588
+ platform: hostExtensions.platform,
589
+ semanticProps: validated.props,
590
+ permissionNeeds: manifest.permissionNeeds,
591
+ resourceNeeds: manifest.resourceNeeds,
592
+ });
408
593
  let decision;
409
594
  try {
410
- decision = context.imagePolicy(request);
595
+ decision = context.hostExtensionPolicy(request);
411
596
  }
412
597
  catch (cause) {
413
- throw new A2uiParseError(`A2UI Image ${JSON.stringify(component.id)} host policy failed`, {
414
- cause,
415
- });
598
+ throw new A2uiParseError(`Host extension ${JSON.stringify(manifest.componentName)} capability policy failed`, { cause });
416
599
  }
417
600
  if (decision === false) {
418
- throw new A2uiParseError(`A2UI Image ${JSON.stringify(component.id)} URL is denied by host policy`);
419
- }
420
- const resourcePolicy = parseImageResourcePolicy(decision, `components.${component.id}.imagePolicy`, context);
421
- context.imageMaximumBytes += resourcePolicy.maximumBytes;
422
- if (context.imageMaximumBytes > A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_BYTES) {
423
- throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum total image transfer budget of ${A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_BYTES} bytes`);
601
+ throw new A2uiParseError(`Host extension ${JSON.stringify(manifest.componentName)} is denied by capability policy`);
424
602
  }
425
- context.imageMaximumDecodedPixels += resourcePolicy.maximumDecodedPixels;
426
- if (context.imageMaximumDecodedPixels > A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_PIXELS) {
427
- throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum total decoded image budget of ${A2UI_V1_NATIVE_MAX_TOTAL_IMAGE_PIXELS} pixels`);
428
- }
429
- props.resourcePolicy = resourcePolicy;
430
- }
431
- if (component.description !== undefined) {
432
- props.description = resolveDynamicString(component.description, `components.${component.id}.description`, context, scope);
603
+ props.capabilityGrant = parseHostExtensionCapabilityGrant(decision, manifest.permissionNeeds, manifest.resourceNeeds, `components.${component.id}.hostExtensionPolicy`);
433
604
  }
434
605
  addCommonProps(component, props, context, scope);
435
- if (props.accessibilityLabel === undefined && props.description !== undefined) {
436
- props.accessibilityLabel = props.description;
606
+ if (manifest.accessibility.requiresLabel && props.accessibilityLabel === undefined) {
607
+ throw new A2uiParseError(`Host extension ${JSON.stringify(manifest.componentName)} requires an accessibility label`);
437
608
  }
438
- return { key, component: "Image", props };
609
+ return { key, component: "HostExtension", props };
439
610
  }
440
611
  function adaptIcon(component, key, context, scope) {
441
612
  const path = `components.${component.id}.name`;
@@ -1072,6 +1243,132 @@ function parseImageResourcePolicy(value, path, context) {
1072
1243
  maximumRedirects,
1073
1244
  });
1074
1245
  }
1246
+ function parseMediaResourcePolicy(value, request, path, context) {
1247
+ const policy = parseJsonObject(value, path);
1248
+ const allowedKeys = new Set([
1249
+ "allowedMimeTypes",
1250
+ "allowedRedirectOrigins",
1251
+ "allowsAutoplay",
1252
+ "allowsBackgroundPlayback",
1253
+ "allowsExternalRoutes",
1254
+ "maximumBytes",
1255
+ "maximumRedirects",
1256
+ "requiresUserActivation",
1257
+ "sourceOrigin",
1258
+ ]);
1259
+ for (const key of Object.keys(policy)) {
1260
+ if (!allowedKeys.has(key)) {
1261
+ throw new A2uiParseError(`Unexpected field ${JSON.stringify(key)} at ${path}`);
1262
+ }
1263
+ }
1264
+ const sourceOrigin = parseExactHttpOrigin(policy.sourceOrigin, `${path}.sourceOrigin`);
1265
+ if (sourceOrigin !== request.sourceOrigin) {
1266
+ throw new A2uiParseError(`Expected ${path}.sourceOrigin to match the requested media origin`);
1267
+ }
1268
+ if (!Array.isArray(policy.allowedRedirectOrigins) ||
1269
+ policy.allowedRedirectOrigins.length > A2UI_V1_NATIVE_MAX_MEDIA_REDIRECT_ORIGINS) {
1270
+ throw new A2uiParseError(`Expected at most ${A2UI_V1_NATIVE_MAX_MEDIA_REDIRECT_ORIGINS} redirect origins at ${path}.allowedRedirectOrigins`);
1271
+ }
1272
+ context.mediaRedirectOriginCount += policy.allowedRedirectOrigins.length;
1273
+ if (context.mediaRedirectOriginCount > JSON_MAX_VALUES) {
1274
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum of ${JSON_MAX_VALUES} media redirect origins`);
1275
+ }
1276
+ const allowedRedirectOrigins = policy.allowedRedirectOrigins.map((origin, index) => parseExactHttpOrigin(origin, `${path}.allowedRedirectOrigins[${index}]`));
1277
+ if (new Set(allowedRedirectOrigins).size !== allowedRedirectOrigins.length) {
1278
+ throw new A2uiParseError(`Expected unique redirect origins at ${path}.allowedRedirectOrigins`);
1279
+ }
1280
+ if (!Array.isArray(policy.allowedMimeTypes) ||
1281
+ policy.allowedMimeTypes.length === 0 ||
1282
+ policy.allowedMimeTypes.length > A2UI_V1_NATIVE_MAX_MEDIA_MIME_TYPES) {
1283
+ throw new A2uiParseError(`Expected one through ${A2UI_V1_NATIVE_MAX_MEDIA_MIME_TYPES} MIME types at ${path}.allowedMimeTypes`);
1284
+ }
1285
+ context.mediaMimeTypeCount += policy.allowedMimeTypes.length;
1286
+ if (context.mediaMimeTypeCount > JSON_MAX_VALUES) {
1287
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds maximum of ${JSON_MAX_VALUES} media MIME types`);
1288
+ }
1289
+ const mimePrefix = request.kind === "video" ? "video/" : "audio/";
1290
+ const allowedMimeTypes = policy.allowedMimeTypes.map((mimeType, index) => {
1291
+ const parsed = expectString(mimeType, `${path}.allowedMimeTypes[${index}]`);
1292
+ if (parsed !== parsed.toLowerCase() ||
1293
+ !parsed.startsWith(mimePrefix) ||
1294
+ !/^(?:audio|video)\/[a-z0-9][a-z0-9!#$&^_.+-]*$/.test(parsed)) {
1295
+ throw new A2uiParseError(`Expected an exact lower-case ${request.kind} MIME type at ${path}.allowedMimeTypes[${index}]`);
1296
+ }
1297
+ return parsed;
1298
+ });
1299
+ if (new Set(allowedMimeTypes).size !== allowedMimeTypes.length) {
1300
+ throw new A2uiParseError(`Expected unique MIME types at ${path}.allowedMimeTypes`);
1301
+ }
1302
+ context.mediaPolicyOutputLength +=
1303
+ sourceOrigin.length +
1304
+ allowedRedirectOrigins.reduce((total, origin) => total + origin.length, 0) +
1305
+ allowedMimeTypes.reduce((total, mimeType) => total + mimeType.length, 0);
1306
+ if (context.mediaPolicyOutputLength > A2UI_V1_MAX_SOURCE_LENGTH) {
1307
+ throw new A2uiParseError(`Expanded A2UI native plan exceeds media-policy output length of ${A2UI_V1_MAX_SOURCE_LENGTH}`);
1308
+ }
1309
+ const maximumBytes = parseBoundedPositiveInteger(policy.maximumBytes, A2UI_V1_NATIVE_MAX_MEDIA_BYTES, `${path}.maximumBytes`);
1310
+ const maximumRedirects = parseBoundedNonNegativeInteger(policy.maximumRedirects, A2UI_V1_NATIVE_MAX_MEDIA_REDIRECTS, `${path}.maximumRedirects`);
1311
+ const allowsAutoplay = parsePolicyBoolean(policy.allowsAutoplay, `${path}.allowsAutoplay`);
1312
+ const allowsBackgroundPlayback = parsePolicyBoolean(policy.allowsBackgroundPlayback, `${path}.allowsBackgroundPlayback`);
1313
+ const allowsExternalRoutes = parsePolicyBoolean(policy.allowsExternalRoutes, `${path}.allowsExternalRoutes`);
1314
+ const requiresUserActivation = parsePolicyBoolean(policy.requiresUserActivation, `${path}.requiresUserActivation`);
1315
+ if (allowsAutoplay && requiresUserActivation) {
1316
+ throw new A2uiParseError(`A2UI media policy cannot allow autoplay while requiring user activation at ${path}`);
1317
+ }
1318
+ return Object.freeze({
1319
+ sourceOrigin,
1320
+ allowedRedirectOrigins: Object.freeze(allowedRedirectOrigins),
1321
+ allowedMimeTypes: Object.freeze(allowedMimeTypes),
1322
+ maximumBytes,
1323
+ maximumRedirects,
1324
+ allowsAutoplay,
1325
+ allowsBackgroundPlayback,
1326
+ allowsExternalRoutes,
1327
+ requiresUserActivation,
1328
+ });
1329
+ }
1330
+ function parseHostExtensionCapabilityGrant(value, permissionNeeds, resourceNeeds, path) {
1331
+ const grant = parseJsonObject(value, path);
1332
+ const keys = Object.keys(grant);
1333
+ if (keys.some((key) => key !== "permissions" && key !== "resources")) {
1334
+ throw new A2uiParseError(`Unexpected host-extension capability grant field at ${path}`);
1335
+ }
1336
+ const permissions = parseExactGrantNames(grant.permissions, permissionNeeds, `${path}.permissions`);
1337
+ const resources = parseExactGrantNames(grant.resources, resourceNeeds, `${path}.resources`);
1338
+ return Object.freeze({ permissions, resources });
1339
+ }
1340
+ function parseExactGrantNames(value, expected, path) {
1341
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
1342
+ throw new A2uiParseError(`Expected an array of capability identifiers at ${path}`);
1343
+ }
1344
+ const names = value;
1345
+ if (new Set(names).size !== names.length) {
1346
+ throw new A2uiParseError(`Expected unique capability identifiers at ${path}`);
1347
+ }
1348
+ const actualSet = new Set(names);
1349
+ if (actualSet.size !== expected.length || expected.some((name) => !actualSet.has(name))) {
1350
+ throw new A2uiParseError(`Expected ${path} to exactly grant the manifest-declared needs`);
1351
+ }
1352
+ return Object.freeze([...expected]);
1353
+ }
1354
+ function parsePolicyBoolean(value, path) {
1355
+ if (typeof value !== "boolean") {
1356
+ throw new A2uiParseError(`Expected a boolean at ${path}`);
1357
+ }
1358
+ return value;
1359
+ }
1360
+ function parseHttpUrlOrigin(value, path) {
1361
+ const UrlConstructor = globalThis.URL;
1362
+ if (UrlConstructor === undefined) {
1363
+ throw new A2uiParseError(`The host runtime cannot validate a media origin at ${path}`);
1364
+ }
1365
+ try {
1366
+ return new UrlConstructor(value).origin;
1367
+ }
1368
+ catch (cause) {
1369
+ throw new A2uiParseError(`Expected an HTTP(S) media URL at ${path}`, { cause });
1370
+ }
1371
+ }
1075
1372
  function parseExactHttpOrigin(value, path) {
1076
1373
  const origin = expectString(value, path);
1077
1374
  if (/\s|\p{Cf}/u.test(origin) || hasAsciiControlCharacter(origin)) {
@@ -1079,7 +1376,7 @@ function parseExactHttpOrigin(value, path) {
1079
1376
  }
1080
1377
  const UrlConstructor = globalThis.URL;
1081
1378
  if (UrlConstructor === undefined) {
1082
- throw new A2uiParseError(`The host runtime cannot validate an image origin at ${path}`);
1379
+ throw new A2uiParseError(`The host runtime cannot validate a resource origin at ${path}`);
1083
1380
  }
1084
1381
  let parsed;
1085
1382
  try {