@adcp/sdk 16.0.0-beta.7 → 16.0.0-beta.8

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 (75) hide show
  1. package/README.md +17 -0
  2. package/dist/lib/conformance/index.d.mts +2 -0
  3. package/dist/lib/conformance/index.d.ts +2 -0
  4. package/dist/lib/conformance/index.js +3 -0
  5. package/dist/lib/conformance/index.mjs +2 -0
  6. package/dist/lib/conformance/property-lists.d.mts +14 -0
  7. package/dist/lib/conformance/property-lists.d.ts +14 -0
  8. package/dist/lib/conformance/property-lists.js +45 -0
  9. package/dist/lib/conformance/property-lists.mjs +21 -0
  10. package/dist/lib/conformance/runConformance.js +20 -1
  11. package/dist/lib/conformance/runConformance.mjs +20 -1
  12. package/dist/lib/conformance/types.d.mts +3 -0
  13. package/dist/lib/conformance/types.d.ts +3 -0
  14. package/dist/lib/core/SingleAgentClient.d.mts +13 -3
  15. package/dist/lib/core/SingleAgentClient.d.ts +13 -3
  16. package/dist/lib/core/SingleAgentClient.js +109 -21
  17. package/dist/lib/core/SingleAgentClient.mjs +112 -22
  18. package/dist/lib/identifiers.d.mts +53 -0
  19. package/dist/lib/identifiers.d.ts +53 -0
  20. package/dist/lib/identifiers.js +212 -0
  21. package/dist/lib/identifiers.mjs +163 -0
  22. package/dist/lib/index.d.mts +3 -1
  23. package/dist/lib/index.d.ts +3 -1
  24. package/dist/lib/index.js +2 -0
  25. package/dist/lib/index.mjs +1 -0
  26. package/dist/lib/media-buy/index.d.mts +1 -1
  27. package/dist/lib/media-buy/index.d.ts +1 -1
  28. package/dist/lib/media-buy/property-policy.d.mts +15 -1
  29. package/dist/lib/media-buy/property-policy.d.ts +15 -1
  30. package/dist/lib/media-buy/property-policy.js +135 -3
  31. package/dist/lib/media-buy/property-policy.mjs +134 -3
  32. package/dist/lib/registry/index.d.mts +12 -0
  33. package/dist/lib/registry/index.d.ts +12 -0
  34. package/dist/lib/registry/index.js +159 -5
  35. package/dist/lib/registry/index.mjs +156 -4
  36. package/dist/lib/registry/types.d.mts +23 -0
  37. package/dist/lib/registry/types.d.ts +23 -0
  38. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  39. package/dist/lib/server/decisioning/specialisms/lists.d.mts +4 -4
  40. package/dist/lib/server/decisioning/specialisms/lists.d.ts +4 -4
  41. package/dist/lib/server/index.d.mts +3 -0
  42. package/dist/lib/server/index.d.ts +3 -0
  43. package/dist/lib/server/index.js +8 -1
  44. package/dist/lib/server/index.mjs +4 -0
  45. package/dist/lib/server/property-lists.d.mts +36 -0
  46. package/dist/lib/server/property-lists.d.ts +36 -0
  47. package/dist/lib/server/property-lists.js +154 -0
  48. package/dist/lib/server/property-lists.mjs +129 -0
  49. package/dist/lib/server/targeting-helpers.d.mts +2 -0
  50. package/dist/lib/server/targeting-helpers.d.ts +2 -0
  51. package/dist/lib/server/targeting-helpers.js +28 -1
  52. package/dist/lib/server/targeting-helpers.mjs +18 -1
  53. package/dist/lib/signing/parser.js +2 -2
  54. package/dist/lib/signing/parser.mjs +2 -2
  55. package/dist/lib/signing/server.d.mts +1 -1
  56. package/dist/lib/signing/server.d.ts +1 -1
  57. package/dist/lib/signing/server.mjs +5 -1
  58. package/dist/lib/signing/webhook-verifier.d.mts +11 -0
  59. package/dist/lib/signing/webhook-verifier.d.ts +11 -0
  60. package/dist/lib/signing/webhook-verifier.js +11 -3
  61. package/dist/lib/signing/webhook-verifier.mjs +11 -3
  62. package/dist/lib/supply-path/verify.js +12 -2
  63. package/dist/lib/supply-path/verify.mjs +2 -2
  64. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  65. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  66. package/dist/lib/version.d.mts +3 -3
  67. package/dist/lib/version.d.ts +3 -3
  68. package/dist/lib/version.js +3 -3
  69. package/dist/lib/version.mjs +3 -3
  70. package/docs/TYPE-SUMMARY.md +2 -2
  71. package/docs/guides/PROPERTY-LISTS.md +264 -0
  72. package/docs/guides/SIGNING-GUIDE.md +25 -0
  73. package/docs/llms.txt +2 -2
  74. package/docs/migration-14.x-rc-worksheet.md +4 -4
  75. package/package.json +1 -1
@@ -163,7 +163,9 @@ import { resolveWebhookUrl, selectWebhookTemplate } from "./webhook-url.mjs";
163
163
  import { getV25Adapter } from "../adapters/legacy/v2-5/index.mjs";
164
164
  import {
165
165
  ProductPropertyPolicyError,
166
- validateProductsAgainstPropertyPolicy
166
+ validateProductsAgainstPropertyPolicy,
167
+ isSupportedPropertyIdentifierType,
168
+ normalizeDomainForPropertyPolicy
167
169
  } from "../media-buy/property-policy.mjs";
168
170
  import { resolvePropertyList } from "../server/targeting-helpers.mjs";
169
171
  import {
@@ -787,7 +789,9 @@ function productIdForPricingDiagnostics(product) {
787
789
  return typeof id === "string" ? id : void 0;
788
790
  }
789
791
  function propertyListReferenceFromRequest(params) {
790
- const value = params.property_list;
792
+ const filters = params.filters;
793
+ const nested = filters && typeof filters === "object" && !Array.isArray(filters) && Object.hasOwn(filters, "property_list") && filters.property_list !== void 0;
794
+ const value = nested ? filters.property_list : params.property_list;
791
795
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
792
796
  const ref = value;
793
797
  if (typeof ref.agent_url !== "string" || typeof ref.list_id !== "string") return void 0;
@@ -800,21 +804,37 @@ function propertyListReferenceFromRequest(params) {
800
804
  function productPolicyRequestSnapshot(requestParams) {
801
805
  const account = canonicalAccountRoutingSnapshot(requestParams.account);
802
806
  const propertyList = propertyListReferenceFromRequest(requestParams);
807
+ const referenceError = propertyListReferenceError(requestParams);
803
808
  return Object.freeze({
804
809
  ...account ? { account } : {},
805
- ...propertyList ? { property_list: Object.freeze(propertyList) } : {}
810
+ ...propertyList ? { property_list: Object.freeze(propertyList) } : {},
811
+ ...referenceError ? { property_list_error: referenceError } : {}
806
812
  });
807
813
  }
814
+ function propertyListReferenceError(params) {
815
+ if (typeof params.property_list_error === "string") return params.property_list_error;
816
+ const filters = params.filters;
817
+ if (params.property_list !== void 0 && !propertyListReferenceFromRequest({ property_list: params.property_list }))
818
+ return "property_list_invalid_reference";
819
+ if (!filters || typeof filters !== "object" || Array.isArray(filters) || !Object.hasOwn(filters, "property_list") || filters.property_list === void 0)
820
+ return void 0;
821
+ const nested = propertyListReferenceFromRequest(params);
822
+ if (!nested) return "property_list_invalid_reference";
823
+ if (params.property_list !== void 0) {
824
+ const legacy = propertyListReferenceFromRequest({ property_list: params.property_list });
825
+ if (!legacy || nested.agent_url !== legacy.agent_url || nested.list_id !== legacy.list_id || nested.auth_token !== legacy.auth_token) {
826
+ return "property_list_conflicting_references";
827
+ }
828
+ }
829
+ return void 0;
830
+ }
808
831
  function hasProductPropertyPolicyRules(policy) {
809
832
  return Boolean(
810
833
  policy.allowedDomains?.length || policy.allowedPropertyIdentifiers?.length || policy.requireAllowedPropertyMatch || policy.excludedDomains?.length || policy.excludedPropertyIds?.length || policy.strict || policy.unknownSelectorBehavior || policy.missingPublisherPropertiesBehavior
811
834
  );
812
835
  }
813
- function comparablePropertyIdentifiers(identifiers) {
814
- return identifiers.filter((identifier) => identifier.type === "domain" || identifier.type === "subdomain").map((identifier) => ({ type: identifier.type, value: identifier.value }));
815
- }
816
836
  function unsupportedPropertyIdentifiers(identifiers) {
817
- return identifiers.filter((identifier) => identifier.type !== "domain" && identifier.type !== "subdomain").map((identifier) => ({ type: identifier.type }));
837
+ return identifiers.filter((identifier) => !isSupportedPropertyIdentifierType(identifier.type)).map((identifier) => ({ type: identifier.type }));
818
838
  }
819
839
  function sanitizeDiagnosticUrl(value) {
820
840
  try {
@@ -1411,8 +1431,17 @@ class SingleAgentClient {
1411
1431
  }
1412
1432
  assertDurablePropertyListCredentialSupported(taskType, requestParams) {
1413
1433
  if (!this.config.deferredStorage || taskType !== "get_products") return;
1414
- const propertyList = propertyListReferenceFromRequest(requestParams);
1415
- if (!propertyList?.auth_token) return;
1434
+ const filters = requestParams.filters;
1435
+ const references = [
1436
+ requestParams.property_list,
1437
+ filters && typeof filters === "object" ? filters.property_list : void 0
1438
+ ];
1439
+ const hasCredential = references.some((reference) => {
1440
+ if (!reference || typeof reference !== "object") return false;
1441
+ const token = reference.auth_token;
1442
+ return token !== void 0 && token !== null && token !== "";
1443
+ });
1444
+ if (!hasCredential) return;
1416
1445
  const policyConfig = this.config.validation?.productPropertyPolicy;
1417
1446
  if (policyConfig === false || policyConfig?.enforceRequestPropertyList === false) return;
1418
1447
  throw new ConfigurationError(
@@ -4223,39 +4252,40 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4223
4252
  message = "Property list not adhered to",
4224
4253
  enforceRequestPropertyList = true,
4225
4254
  propertyListResolveOptions,
4255
+ resolvePublisherProperties,
4256
+ maxPublisherLookups = 100,
4257
+ publisherLookupTimeoutMs = 1e4,
4258
+ publisherLookupBudgetMs = 1e4,
4226
4259
  ...explicitPolicy
4227
4260
  } = policyConfig || {};
4228
4261
  const policy = {
4229
4262
  ...explicitPolicy
4230
4263
  };
4231
4264
  let resolvedRequestPropertyList;
4232
- if (requestPropertyList && enforceRequestPropertyList) {
4265
+ const referenceError = propertyListReferenceError(requestParams);
4266
+ if ((requestPropertyList || referenceError) && enforceRequestPropertyList) {
4233
4267
  try {
4268
+ if (referenceError) throw new Error(referenceError);
4234
4269
  const resolved = await resolvePropertyList(requestPropertyList, propertyListResolveOptions);
4235
- const comparableIdentifiers = comparablePropertyIdentifiers(resolved.identifiers);
4236
4270
  const unsupportedIdentifiers = unsupportedPropertyIdentifiers(resolved.identifiers);
4237
- if (unsupportedIdentifiers.length > 0) {
4238
- throw new Error("property_list_unsupported_identifier_types");
4239
- }
4240
- if (comparableIdentifiers.length > 0 || resolved.identifiers.length === 0) {
4241
- policy.allowedPropertyIdentifiers = [...policy.allowedPropertyIdentifiers ?? [], ...comparableIdentifiers];
4242
- policy.requireAllowedPropertyMatch = true;
4243
- }
4271
+ policy.allowedPropertyIdentifiers = [...policy.allowedPropertyIdentifiers ?? [], ...resolved.identifiers];
4272
+ policy.requireAllowedPropertyMatch = true;
4244
4273
  policy.strict = policy.strict ?? true;
4245
4274
  resolvedRequestPropertyList = {
4246
4275
  listId: resolved.listId,
4247
4276
  agentUrl: resolved.agentUrl,
4248
4277
  identifierCount: resolved.identifiers.length,
4278
+ unsupportedIdentifierTypes: [...new Set(unsupportedIdentifiers.map((identifier) => identifier.type))],
4249
4279
  ...resolved.cacheValidUntil ? { cacheValidUntil: resolved.cacheValidUntil } : {}
4250
4280
  };
4251
4281
  } catch (err) {
4252
4282
  const errorCode = propertyListResolutionErrorCode(err);
4253
- const diagnosticAgentUrl = sanitizeDiagnosticUrl(requestPropertyList.agent_url);
4283
+ const diagnosticAgentUrl = requestPropertyList ? sanitizeDiagnosticUrl(requestPropertyList.agent_url) : "invalid_url";
4254
4284
  return attachMatch({
4255
4285
  success: false,
4256
4286
  status: "failed",
4257
4287
  data: response,
4258
- error: `${message}: could not resolve property_list ${requestPropertyList.list_id}`,
4288
+ error: `${message}: could not resolve property_list`,
4259
4289
  metadata: {
4260
4290
  ...result.metadata,
4261
4291
  status: "failed",
@@ -4268,7 +4298,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4268
4298
  message,
4269
4299
  diagnostics: [],
4270
4300
  request_property_list: {
4271
- list_id: requestPropertyList.list_id,
4301
+ list_id: requestPropertyList?.list_id ?? "invalid_reference",
4272
4302
  agent_url: diagnosticAgentUrl,
4273
4303
  resolution_error: errorCode
4274
4304
  }
@@ -4283,7 +4313,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4283
4313
  mode,
4284
4314
  ok: false,
4285
4315
  request_property_list: {
4286
- list_id: requestPropertyList.list_id,
4316
+ list_id: requestPropertyList?.list_id,
4287
4317
  agent_url: diagnosticAgentUrl,
4288
4318
  resolution_error: errorCode
4289
4319
  }
@@ -4293,6 +4323,65 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4293
4323
  }
4294
4324
  }
4295
4325
  if (!hasProductPropertyPolicyRules(policy)) return result;
4326
+ if (resolvePublisherProperties && policy.allowedPropertyIdentifiers?.some(
4327
+ (identifier) => identifier.type !== "domain" && identifier.type !== "subdomain"
4328
+ )) {
4329
+ if (!Number.isSafeInteger(maxPublisherLookups) || maxPublisherLookups < 1 || !Number.isSafeInteger(publisherLookupTimeoutMs) || publisherLookupTimeoutMs < 1 || publisherLookupTimeoutMs > 2147483647 || !Number.isSafeInteger(publisherLookupBudgetMs) || publisherLookupBudgetMs < 1 || publisherLookupBudgetMs > 2147483647) {
4330
+ throw new ConfigurationError("Publisher lookup limits must be positive bounded integers");
4331
+ }
4332
+ const { validatePublisherDomain } = await import("../identifiers.mjs");
4333
+ const catalogs = Object.assign(
4334
+ /* @__PURE__ */ Object.create(null),
4335
+ policy.publisherProperties
4336
+ );
4337
+ const domains = new Set(
4338
+ response.products.flatMap(
4339
+ (product) => Array.isArray(product.publisher_properties) ? product.publisher_properties.flatMap(
4340
+ (selector) => selector && typeof selector === "object" && "publisher_domain" in selector ? [selector.publisher_domain] : []
4341
+ ) : []
4342
+ )
4343
+ );
4344
+ const hosts = [
4345
+ ...new Set(
4346
+ [...domains].flatMap(
4347
+ (domain) => typeof domain === "string" ? [normalizeDomainForPropertyPolicy(domain)?.host] : []
4348
+ )
4349
+ )
4350
+ ].filter((host) => typeof host === "string" && !Object.hasOwn(catalogs, host));
4351
+ for (const host of hosts) catalogs[host] = void 0;
4352
+ const lookupStartedAt = performance.now();
4353
+ for (const host of hosts.slice(0, maxPublisherLookups)) {
4354
+ throwIfAborted(taskSignal);
4355
+ const remainingBudgetMs = publisherLookupBudgetMs - (performance.now() - lookupStartedAt);
4356
+ if (remainingBudgetMs <= 0) break;
4357
+ if (!validatePublisherDomain(host).ok || isLikelyPrivateUrl(`https://${host}`)) continue;
4358
+ const controller = new AbortController();
4359
+ const signal = taskSignal ? AbortSignal.any([taskSignal, controller.signal]) : controller.signal;
4360
+ let timeout;
4361
+ let budgetExpired = false;
4362
+ try {
4363
+ catalogs[host] = await Promise.race([
4364
+ resolvePublisherProperties(host, { signal }),
4365
+ new Promise((_, reject) => {
4366
+ timeout = setTimeout(
4367
+ () => {
4368
+ if (remainingBudgetMs <= publisherLookupTimeoutMs) budgetExpired = true;
4369
+ controller.abort();
4370
+ reject(new Error("publisher_catalog_timeout"));
4371
+ },
4372
+ Math.min(publisherLookupTimeoutMs, remainingBudgetMs)
4373
+ );
4374
+ })
4375
+ ]);
4376
+ } catch {
4377
+ catalogs[host] = void 0;
4378
+ } finally {
4379
+ if (timeout) clearTimeout(timeout);
4380
+ }
4381
+ if (budgetExpired) break;
4382
+ }
4383
+ policy.publisherProperties = catalogs;
4384
+ }
4296
4385
  let validation;
4297
4386
  try {
4298
4387
  validation = validateProductsAgainstPropertyPolicy({
@@ -4316,6 +4405,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4316
4405
  list_id: resolvedRequestPropertyList.listId,
4317
4406
  agent_url: sanitizeDiagnosticUrl(resolvedRequestPropertyList.agentUrl),
4318
4407
  identifier_count: resolvedRequestPropertyList.identifierCount,
4408
+ unsupported_identifier_types: resolvedRequestPropertyList.unsupportedIdentifierTypes,
4319
4409
  ...resolvedRequestPropertyList.cacheValidUntil ? { cache_valid_until: resolvedRequestPropertyList.cacheValidUntil } : {}
4320
4410
  }
4321
4411
  } : {},
@@ -0,0 +1,53 @@
1
+ import type { PropertyIdentifierTypes } from './types/core.generated.mjs';
2
+ export type IdentifierValidationErrorCode = 'invalid_identifier' | 'unsupported_identifier_type' | 'invalid_publisher_domain';
3
+ export declare class IdentifierValidationError extends TypeError {
4
+ readonly code: IdentifierValidationErrorCode;
5
+ readonly identifierType: string;
6
+ readonly name = "IdentifierValidationError";
7
+ constructor(code: IdentifierValidationErrorCode, identifierType: string, message: string);
8
+ }
9
+ export interface IdentifierRewrite {
10
+ from: string;
11
+ to: string;
12
+ }
13
+ export type IdentifierValidationResult = {
14
+ ok: true;
15
+ value: string;
16
+ rewrite?: IdentifierRewrite;
17
+ } | {
18
+ ok: false;
19
+ error: IdentifierValidationError;
20
+ };
21
+ /** Validate a registration hostname as supplied. This deliberately does not repair URLs or wildcards. */
22
+ export declare function validatePublisherDomain(value: string): IdentifierValidationResult;
23
+ export declare const normalizeDomainIdentifier: (value: string) => IdentifierValidationResult;
24
+ export declare const normalizeSubdomainIdentifier: (value: string) => IdentifierValidationResult;
25
+ /** Bundle identifiers are case-sensitive; normalization preserves their case. */
26
+ export declare const normalizeIosBundleIdentifier: (value: string) => IdentifierValidationResult;
27
+ export declare const normalizeAndroidPackageIdentifier: (value: string) => IdentifierValidationResult;
28
+ export declare const normalizeAppleAppStoreIdentifier: (value: string) => IdentifierValidationResult;
29
+ export declare const normalizeGooglePlayIdentifier: (value: string) => IdentifierValidationResult;
30
+ export declare const normalizeRokuStoreIdentifier: (value: string) => IdentifierValidationResult;
31
+ export declare const normalizeFireTvAsinIdentifier: (value: string) => IdentifierValidationResult;
32
+ export declare const normalizeSamsungAppIdentifier: (value: string) => IdentifierValidationResult;
33
+ export declare const normalizeAppleTvBundleIdentifier: (value: string) => IdentifierValidationResult;
34
+ export declare const normalizeBundleIdentifier: (value: string) => IdentifierValidationResult;
35
+ /**
36
+ * Validate and canonicalize an AdCP identifier. Opaque identifier families keep
37
+ * their spelling and case; only surrounding whitespace is removed. No network
38
+ * lookup or existence/ownership assertion is performed.
39
+ */
40
+ export declare function normalizePropertyIdentifier(type: PropertyIdentifierTypes | string, value: string): IdentifierValidationResult;
41
+ /** Validation includes the same explicit rewrite report as normalization. */
42
+ export declare const validatePropertyIdentifier: typeof normalizePropertyIdentifier;
43
+ export declare const validateDomainIdentifier: (value: string) => IdentifierValidationResult;
44
+ export declare const validateSubdomainIdentifier: (value: string) => IdentifierValidationResult;
45
+ export declare const validateIosBundleIdentifier: (value: string) => IdentifierValidationResult;
46
+ export declare const validateAndroidPackageIdentifier: (value: string) => IdentifierValidationResult;
47
+ export declare const validateAppleAppStoreIdentifier: (value: string) => IdentifierValidationResult;
48
+ export declare const validateGooglePlayIdentifier: (value: string) => IdentifierValidationResult;
49
+ export declare const validateRokuStoreIdentifier: (value: string) => IdentifierValidationResult;
50
+ export declare const validateFireTvAsinIdentifier: (value: string) => IdentifierValidationResult;
51
+ export declare const validateSamsungAppIdentifier: (value: string) => IdentifierValidationResult;
52
+ export declare const validateAppleTvBundleIdentifier: (value: string) => IdentifierValidationResult;
53
+ export declare const validateBundleIdentifier: (value: string) => IdentifierValidationResult;
@@ -0,0 +1,53 @@
1
+ import type { PropertyIdentifierTypes } from './types/core.generated';
2
+ export type IdentifierValidationErrorCode = 'invalid_identifier' | 'unsupported_identifier_type' | 'invalid_publisher_domain';
3
+ export declare class IdentifierValidationError extends TypeError {
4
+ readonly code: IdentifierValidationErrorCode;
5
+ readonly identifierType: string;
6
+ readonly name = "IdentifierValidationError";
7
+ constructor(code: IdentifierValidationErrorCode, identifierType: string, message: string);
8
+ }
9
+ export interface IdentifierRewrite {
10
+ from: string;
11
+ to: string;
12
+ }
13
+ export type IdentifierValidationResult = {
14
+ ok: true;
15
+ value: string;
16
+ rewrite?: IdentifierRewrite;
17
+ } | {
18
+ ok: false;
19
+ error: IdentifierValidationError;
20
+ };
21
+ /** Validate a registration hostname as supplied. This deliberately does not repair URLs or wildcards. */
22
+ export declare function validatePublisherDomain(value: string): IdentifierValidationResult;
23
+ export declare const normalizeDomainIdentifier: (value: string) => IdentifierValidationResult;
24
+ export declare const normalizeSubdomainIdentifier: (value: string) => IdentifierValidationResult;
25
+ /** Bundle identifiers are case-sensitive; normalization preserves their case. */
26
+ export declare const normalizeIosBundleIdentifier: (value: string) => IdentifierValidationResult;
27
+ export declare const normalizeAndroidPackageIdentifier: (value: string) => IdentifierValidationResult;
28
+ export declare const normalizeAppleAppStoreIdentifier: (value: string) => IdentifierValidationResult;
29
+ export declare const normalizeGooglePlayIdentifier: (value: string) => IdentifierValidationResult;
30
+ export declare const normalizeRokuStoreIdentifier: (value: string) => IdentifierValidationResult;
31
+ export declare const normalizeFireTvAsinIdentifier: (value: string) => IdentifierValidationResult;
32
+ export declare const normalizeSamsungAppIdentifier: (value: string) => IdentifierValidationResult;
33
+ export declare const normalizeAppleTvBundleIdentifier: (value: string) => IdentifierValidationResult;
34
+ export declare const normalizeBundleIdentifier: (value: string) => IdentifierValidationResult;
35
+ /**
36
+ * Validate and canonicalize an AdCP identifier. Opaque identifier families keep
37
+ * their spelling and case; only surrounding whitespace is removed. No network
38
+ * lookup or existence/ownership assertion is performed.
39
+ */
40
+ export declare function normalizePropertyIdentifier(type: PropertyIdentifierTypes | string, value: string): IdentifierValidationResult;
41
+ /** Validation includes the same explicit rewrite report as normalization. */
42
+ export declare const validatePropertyIdentifier: typeof normalizePropertyIdentifier;
43
+ export declare const validateDomainIdentifier: (value: string) => IdentifierValidationResult;
44
+ export declare const validateSubdomainIdentifier: (value: string) => IdentifierValidationResult;
45
+ export declare const validateIosBundleIdentifier: (value: string) => IdentifierValidationResult;
46
+ export declare const validateAndroidPackageIdentifier: (value: string) => IdentifierValidationResult;
47
+ export declare const validateAppleAppStoreIdentifier: (value: string) => IdentifierValidationResult;
48
+ export declare const validateGooglePlayIdentifier: (value: string) => IdentifierValidationResult;
49
+ export declare const validateRokuStoreIdentifier: (value: string) => IdentifierValidationResult;
50
+ export declare const validateFireTvAsinIdentifier: (value: string) => IdentifierValidationResult;
51
+ export declare const validateSamsungAppIdentifier: (value: string) => IdentifierValidationResult;
52
+ export declare const validateAppleTvBundleIdentifier: (value: string) => IdentifierValidationResult;
53
+ export declare const validateBundleIdentifier: (value: string) => IdentifierValidationResult;
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var identifiers_exports = {};
20
+ __export(identifiers_exports, {
21
+ IdentifierValidationError: () => IdentifierValidationError,
22
+ normalizeAndroidPackageIdentifier: () => normalizeAndroidPackageIdentifier,
23
+ normalizeAppleAppStoreIdentifier: () => normalizeAppleAppStoreIdentifier,
24
+ normalizeAppleTvBundleIdentifier: () => normalizeAppleTvBundleIdentifier,
25
+ normalizeBundleIdentifier: () => normalizeBundleIdentifier,
26
+ normalizeDomainIdentifier: () => normalizeDomainIdentifier,
27
+ normalizeFireTvAsinIdentifier: () => normalizeFireTvAsinIdentifier,
28
+ normalizeGooglePlayIdentifier: () => normalizeGooglePlayIdentifier,
29
+ normalizeIosBundleIdentifier: () => normalizeIosBundleIdentifier,
30
+ normalizePropertyIdentifier: () => normalizePropertyIdentifier,
31
+ normalizeRokuStoreIdentifier: () => normalizeRokuStoreIdentifier,
32
+ normalizeSamsungAppIdentifier: () => normalizeSamsungAppIdentifier,
33
+ normalizeSubdomainIdentifier: () => normalizeSubdomainIdentifier,
34
+ validateAndroidPackageIdentifier: () => validateAndroidPackageIdentifier,
35
+ validateAppleAppStoreIdentifier: () => validateAppleAppStoreIdentifier,
36
+ validateAppleTvBundleIdentifier: () => validateAppleTvBundleIdentifier,
37
+ validateBundleIdentifier: () => validateBundleIdentifier,
38
+ validateDomainIdentifier: () => validateDomainIdentifier,
39
+ validateFireTvAsinIdentifier: () => validateFireTvAsinIdentifier,
40
+ validateGooglePlayIdentifier: () => validateGooglePlayIdentifier,
41
+ validateIosBundleIdentifier: () => validateIosBundleIdentifier,
42
+ validatePropertyIdentifier: () => validatePropertyIdentifier,
43
+ validatePublisherDomain: () => validatePublisherDomain,
44
+ validateRokuStoreIdentifier: () => validateRokuStoreIdentifier,
45
+ validateSamsungAppIdentifier: () => validateSamsungAppIdentifier,
46
+ validateSubdomainIdentifier: () => validateSubdomainIdentifier
47
+ });
48
+ module.exports = __toCommonJS(identifiers_exports);
49
+ var import_node_net = require("node:net");
50
+ var import_node_url = require("node:url");
51
+ class IdentifierValidationError extends TypeError {
52
+ constructor(code, identifierType, message) {
53
+ super(message);
54
+ this.code = code;
55
+ this.identifierType = identifierType;
56
+ }
57
+ code;
58
+ identifierType;
59
+ name = "IdentifierValidationError";
60
+ }
61
+ const PUBLISHER_DOMAIN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
62
+ function isDomain(value) {
63
+ if (value.length > 253 || !PUBLISHER_DOMAIN.test(value) || value.split(".").some((label) => label.length > 63) || (0, import_node_net.isIP)(value))
64
+ return false;
65
+ try {
66
+ return !(0, import_node_net.isIP)(new URL(`https://${value}`).hostname);
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+ function accepted(input, value) {
72
+ return { ok: true, value, ...input === value ? {} : { rewrite: { from: input, to: value } } };
73
+ }
74
+ function rejected(type, code = "invalid_identifier") {
75
+ return {
76
+ ok: false,
77
+ error: new IdentifierValidationError(
78
+ code,
79
+ type,
80
+ code === "invalid_publisher_domain" ? "publisher_domain must be a canonical lowercase hostname without wildcards, URLs, or IP addresses" : `Invalid ${type} identifier`
81
+ )
82
+ };
83
+ }
84
+ function validatePublisherDomain(value) {
85
+ return typeof value === "string" && isDomain(value) ? accepted(value, value) : rejected("publisher_domain", "invalid_publisher_domain");
86
+ }
87
+ function normalizeHostname(input, type) {
88
+ if (typeof input !== "string") return rejected(type);
89
+ let value = input.trim();
90
+ if (/^https?:\/\//i.test(value)) {
91
+ try {
92
+ const url = new URL(value);
93
+ if (url.username || url.password) return rejected(type);
94
+ value = url.hostname;
95
+ } catch {
96
+ return rejected(type);
97
+ }
98
+ }
99
+ const wildcard = value.startsWith("*.");
100
+ if (wildcard && type !== "domain") return rejected(type);
101
+ if (/[\s/\\:@?#%]/.test(wildcard ? value.slice(2) : value)) return rejected(type);
102
+ const host = (0, import_node_url.domainToASCII)((wildcard ? value.slice(2) : value).toLowerCase().replace(/\.$/, ""));
103
+ if (!isDomain(host)) return rejected(type);
104
+ return accepted(input, `${wildcard ? "*." : ""}${host}`);
105
+ }
106
+ const normalizeDomainIdentifier = (value) => normalizeHostname(value, "domain");
107
+ const normalizeSubdomainIdentifier = (value) => normalizeHostname(value, "subdomain");
108
+ function normalizePattern(type, input, pattern, uppercase = false) {
109
+ if (typeof input !== "string") return rejected(type);
110
+ const value = uppercase ? input.trim().toUpperCase() : input.trim();
111
+ return pattern.test(value) ? accepted(input, value) : rejected(type);
112
+ }
113
+ const normalizeIosBundleIdentifier = (value) => normalizePattern("ios_bundle", value, /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/);
114
+ const normalizeAndroidPackageIdentifier = (value) => normalizePattern("android_package", value, /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+$/);
115
+ const normalizeAppleAppStoreIdentifier = (value) => normalizePattern("apple_app_store_id", value, /^[1-9][0-9]*$/);
116
+ const normalizeGooglePlayIdentifier = (value) => normalizePattern("google_play_id", value, /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+$/);
117
+ const normalizeRokuStoreIdentifier = (value) => normalizePattern("roku_store_id", value, /^[1-9][0-9]*$/);
118
+ const normalizeFireTvAsinIdentifier = (value) => normalizePattern("fire_tv_asin", value, /^[A-Z0-9]{10}$/, true);
119
+ const normalizeSamsungAppIdentifier = (value) => normalizePattern("samsung_app_id", value, /^[A-Za-z0-9._-]+$/);
120
+ const normalizeAppleTvBundleIdentifier = (value) => normalizePattern("apple_tv_bundle", value, /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/);
121
+ const normalizeBundleIdentifier = (value) => normalizePattern("bundle_id", value, /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)+$/);
122
+ function normalizePropertyIdentifier(type, value) {
123
+ switch (type) {
124
+ case "domain":
125
+ return normalizeDomainIdentifier(value);
126
+ case "subdomain":
127
+ return normalizeSubdomainIdentifier(value);
128
+ case "ios_bundle":
129
+ return normalizeIosBundleIdentifier(value);
130
+ case "android_package":
131
+ return normalizeAndroidPackageIdentifier(value);
132
+ case "apple_app_store_id":
133
+ return normalizeAppleAppStoreIdentifier(value);
134
+ case "google_play_id":
135
+ return normalizeGooglePlayIdentifier(value);
136
+ case "roku_store_id":
137
+ return normalizeRokuStoreIdentifier(value);
138
+ case "fire_tv_asin":
139
+ return normalizeFireTvAsinIdentifier(value);
140
+ case "samsung_app_id":
141
+ return normalizeSamsungAppIdentifier(value);
142
+ case "apple_tv_bundle":
143
+ return normalizeAppleTvBundleIdentifier(value);
144
+ case "bundle_id":
145
+ return normalizeBundleIdentifier(value);
146
+ case "apple_podcast_id":
147
+ return normalizePattern(type, value, /^[1-9][0-9]*$/);
148
+ case "rss_url": {
149
+ if (typeof value !== "string") return rejected(type);
150
+ try {
151
+ const url = new URL(value.trim());
152
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || !isDomain(url.hostname))
153
+ return rejected(type);
154
+ return accepted(value, url.toString());
155
+ } catch {
156
+ return rejected(type);
157
+ }
158
+ }
159
+ case "network_id":
160
+ case "venue_id":
161
+ case "screen_id":
162
+ case "openooh_venue_type":
163
+ case "spotify_collection_id":
164
+ case "podcast_guid":
165
+ case "station_id":
166
+ case "facility_id":
167
+ return normalizePattern(type, value, /^[^\x00-\x1f\x7f]+$/);
168
+ default:
169
+ return rejected(type, "unsupported_identifier_type");
170
+ }
171
+ }
172
+ const validatePropertyIdentifier = normalizePropertyIdentifier;
173
+ const validateDomainIdentifier = normalizeDomainIdentifier;
174
+ const validateSubdomainIdentifier = normalizeSubdomainIdentifier;
175
+ const validateIosBundleIdentifier = normalizeIosBundleIdentifier;
176
+ const validateAndroidPackageIdentifier = normalizeAndroidPackageIdentifier;
177
+ const validateAppleAppStoreIdentifier = normalizeAppleAppStoreIdentifier;
178
+ const validateGooglePlayIdentifier = normalizeGooglePlayIdentifier;
179
+ const validateRokuStoreIdentifier = normalizeRokuStoreIdentifier;
180
+ const validateFireTvAsinIdentifier = normalizeFireTvAsinIdentifier;
181
+ const validateSamsungAppIdentifier = normalizeSamsungAppIdentifier;
182
+ const validateAppleTvBundleIdentifier = normalizeAppleTvBundleIdentifier;
183
+ const validateBundleIdentifier = normalizeBundleIdentifier;
184
+ // Annotate the CommonJS export names for ESM import in node:
185
+ 0 && (module.exports = {
186
+ IdentifierValidationError,
187
+ normalizeAndroidPackageIdentifier,
188
+ normalizeAppleAppStoreIdentifier,
189
+ normalizeAppleTvBundleIdentifier,
190
+ normalizeBundleIdentifier,
191
+ normalizeDomainIdentifier,
192
+ normalizeFireTvAsinIdentifier,
193
+ normalizeGooglePlayIdentifier,
194
+ normalizeIosBundleIdentifier,
195
+ normalizePropertyIdentifier,
196
+ normalizeRokuStoreIdentifier,
197
+ normalizeSamsungAppIdentifier,
198
+ normalizeSubdomainIdentifier,
199
+ validateAndroidPackageIdentifier,
200
+ validateAppleAppStoreIdentifier,
201
+ validateAppleTvBundleIdentifier,
202
+ validateBundleIdentifier,
203
+ validateDomainIdentifier,
204
+ validateFireTvAsinIdentifier,
205
+ validateGooglePlayIdentifier,
206
+ validateIosBundleIdentifier,
207
+ validatePropertyIdentifier,
208
+ validatePublisherDomain,
209
+ validateRokuStoreIdentifier,
210
+ validateSamsungAppIdentifier,
211
+ validateSubdomainIdentifier
212
+ });