@pie-players/pie-players-shared 0.3.73 → 0.3.75

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 (137) hide show
  1. package/README.md +30 -2
  2. package/dist/components/vendor/nds/README.md +7 -5
  3. package/dist/config/profile.d.ts +0 -2
  4. package/dist/config/profile.js +2 -8
  5. package/dist/i18n/index.d.ts +2 -3
  6. package/dist/i18n/index.js +2 -3
  7. package/dist/i18n/messages/en-US.d.ts +11 -24
  8. package/dist/i18n/messages/en-US.js +13 -26
  9. package/dist/i18n/messages/nl-NL.d.ts +7 -24
  10. package/dist/i18n/messages/nl-NL.js +9 -26
  11. package/dist/i18n/provider.d.ts +4 -5
  12. package/dist/i18n/provider.js +4 -5
  13. package/dist/i18n/types.d.ts +4 -5
  14. package/dist/index.d.ts +4 -2
  15. package/dist/index.js +3 -1
  16. package/dist/instrumentation/index.d.ts +5 -4
  17. package/dist/instrumentation/index.js +5 -4
  18. package/dist/instrumentation/new-relic-agent.d.ts +20 -0
  19. package/dist/instrumentation/new-relic-agent.js +24 -0
  20. package/dist/instrumentation/providers/BaseInstrumentationProvider.d.ts +5 -4
  21. package/dist/instrumentation/providers/NewRelicInstrumentationProvider.d.ts +7 -7
  22. package/dist/instrumentation/providers/NewRelicInstrumentationProvider.js +21 -32
  23. package/dist/instrumentation/types.d.ts +7 -3
  24. package/dist/loader-config.d.ts +16 -6
  25. package/dist/loaders/controller-loading.d.ts +14 -0
  26. package/dist/loaders/controller-loading.js +12 -0
  27. package/dist/loaders/defaults.d.ts +10 -0
  28. package/dist/loaders/defaults.js +10 -0
  29. package/dist/loaders/element-loader.d.ts +20 -8
  30. package/dist/loaders/element-loader.js +102 -36
  31. package/dist/loaders/element-package-policy.d.ts +2 -0
  32. package/dist/loaders/element-package-policy.js +2 -1
  33. package/dist/loaders/esm-adapter.d.ts +29 -0
  34. package/dist/loaders/esm-adapter.js +635 -102
  35. package/dist/loaders/iife-adapter.d.ts +0 -5
  36. package/dist/loaders/iife-adapter.js +3 -9
  37. package/dist/loaders/index.d.ts +12 -3
  38. package/dist/loaders/index.js +9 -3
  39. package/dist/loaders/mathjax-events.d.ts +22 -0
  40. package/dist/loaders/mathjax-events.js +65 -0
  41. package/dist/loaders/module-shim.d.ts +12 -0
  42. package/dist/loaders/module-shim.js +12 -0
  43. package/dist/loaders/preloaded-alignment.d.ts +24 -0
  44. package/dist/loaders/preloaded-alignment.js +57 -0
  45. package/dist/loaders/preloaded-authored-tags.d.ts +26 -0
  46. package/dist/loaders/preloaded-authored-tags.js +69 -0
  47. package/dist/loaders/preloaded-registration.d.ts +72 -0
  48. package/dist/loaders/preloaded-registration.js +141 -0
  49. package/dist/media/index.d.ts +69 -0
  50. package/dist/media/index.js +194 -0
  51. package/dist/pie/authoring.d.ts +1 -1
  52. package/dist/pie/authoring.js +25 -18
  53. package/dist/pie/element-announcements.d.ts +39 -0
  54. package/dist/pie/element-announcements.js +67 -0
  55. package/dist/pie/element-observer.d.ts +3 -1
  56. package/dist/pie/element-observer.js +1 -0
  57. package/dist/pie/elements-rendered.d.ts +25 -0
  58. package/dist/pie/elements-rendered.js +96 -0
  59. package/dist/pie/index.d.ts +8 -10
  60. package/dist/pie/index.js +9 -10
  61. package/dist/pie/initialization.js +13 -22
  62. package/dist/pie/initialize-element.d.ts +3 -0
  63. package/dist/pie/initialize-element.js +3 -4
  64. package/dist/pie/instrumentation-event-bridge.js +23 -1
  65. package/dist/pie/instrumentation-event-map.js +11 -25
  66. package/dist/pie/instrumentation-provider-resolution.d.ts +2 -1
  67. package/dist/pie/instrumentation-provider-resolution.js +0 -1
  68. package/dist/pie/item-controller.js +4 -0
  69. package/dist/pie/item-session-contract.d.ts +8 -0
  70. package/dist/pie/item-session-contract.js +30 -9
  71. package/dist/pie/logger.d.ts +6 -0
  72. package/dist/pie/logger.js +10 -0
  73. package/dist/pie/markup-math.d.ts +20 -0
  74. package/dist/pie/markup-math.js +118 -0
  75. package/dist/pie/math-assets.d.ts +48 -0
  76. package/dist/pie/math-assets.js +81 -0
  77. package/dist/pie/math-rendering.d.ts +10 -21
  78. package/dist/pie/math-rendering.js +50 -35
  79. package/dist/pie/registry.d.ts +13 -1
  80. package/dist/pie/registry.js +39 -1
  81. package/dist/pie/resource-monitor.d.ts +6 -33
  82. package/dist/pie/resource-monitor.js +14 -213
  83. package/dist/pie/scoring.d.ts +12 -8
  84. package/dist/pie/scoring.js +21 -24
  85. package/dist/pie/session-commit.d.ts +16 -0
  86. package/dist/pie/session-commit.js +34 -6
  87. package/dist/pie/session-snapshot.d.ts +80 -0
  88. package/dist/pie/session-snapshot.js +166 -0
  89. package/dist/pie/stages.d.ts +10 -47
  90. package/dist/pie/stages.js +10 -47
  91. package/dist/pie/tag-names.d.ts +1 -0
  92. package/dist/pie/tag-names.js +1 -0
  93. package/dist/pie/types.d.ts +4 -5
  94. package/dist/pie/types.js +1 -0
  95. package/dist/pie/updates.d.ts +2 -2
  96. package/dist/pie/updates.js +33 -15
  97. package/dist/pie/versioned-tag.d.ts +4 -0
  98. package/dist/pie/versioned-tag.js +4 -0
  99. package/dist/security/index.d.ts +2 -3
  100. package/dist/security/index.js +2 -3
  101. package/dist/security/sanitize-item-markup.d.ts +4 -2
  102. package/dist/security/sanitize-item-markup.js +48 -6
  103. package/dist/security/sanitize-svg-icon.d.ts +0 -2
  104. package/dist/security/sanitize-svg-icon.js +0 -5
  105. package/dist/security/wrap-overwide-images.js +19 -0
  106. package/dist/security/wrap-overwide.d.ts +13 -0
  107. package/dist/security/wrap-overwide.js +63 -3
  108. package/dist/timed-media/policy.js +5 -16
  109. package/dist/types/index.d.ts +67 -423
  110. package/dist/types/index.js +0 -68
  111. package/dist/ui/attribute-coercion.d.ts +12 -0
  112. package/dist/ui/attribute-coercion.js +33 -0
  113. package/dist/ui/content-styles.d.ts +18 -16
  114. package/dist/ui/content-styles.js +67 -36
  115. package/dist/ui/overlay-containment.d.ts +31 -0
  116. package/dist/ui/overlay-containment.js +63 -0
  117. package/dist/ui/overlay-placement.d.ts +110 -0
  118. package/dist/ui/overlay-placement.js +222 -0
  119. package/dist/ui/pointer-gesture.d.ts +32 -0
  120. package/dist/ui/pointer-gesture.js +56 -0
  121. package/dist/ui/pointer-rotate.d.ts +29 -0
  122. package/dist/ui/pointer-rotate.js +39 -0
  123. package/package.json +23 -18
  124. package/dist/pie/component-context.d.ts +0 -21
  125. package/dist/pie/component-context.js +0 -29
  126. package/dist/pie/player-initializer.d.ts +0 -54
  127. package/dist/pie/player-initializer.js +0 -122
  128. package/dist/pie/stage-tracker.d.ts +0 -50
  129. package/dist/pie/stage-tracker.js +0 -105
  130. package/dist/security/wrap-model-rich-content.d.ts +0 -1
  131. package/dist/security/wrap-model-rich-content.js +0 -40
  132. package/dist/server/npm-auth-env.d.ts +0 -13
  133. package/dist/server/npm-auth-env.js +0 -64
  134. package/dist/server/npm-registry.d.ts +0 -7
  135. package/dist/server/npm-registry.js +0 -59
  136. package/dist/ui/debug-panel-persistence.d.ts +0 -17
  137. package/dist/ui/debug-panel-persistence.js +0 -37
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * Default provider for existing New Relic Browser Agent instrumentation.
5
5
  *
6
- * This provider wraps the global `window.newrelic` object provided by
7
- * the New Relic Browser Agent.
6
+ * Sends through the New Relic Browser Agent's API on `window.newrelic` or
7
+ * `window.NREUM`, looked up on every call, so an agent that loads after
8
+ * `initialize()` receives everything tracked from then on.
8
9
  *
9
10
  * @example
10
11
  * ```typescript
@@ -27,8 +28,8 @@ export declare class NewRelicInstrumentationProvider extends BaseInstrumentation
27
28
  /**
28
29
  * Initialize the New Relic provider
29
30
  *
30
- * Checks if the New Relic Browser Agent is available via `window.newrelic`.
31
- * If not available, initialization succeeds but `isReady()` will return false.
31
+ * Configures the provider. The agent does not have to be on the page yet:
32
+ * `isReady()` turns true when it arrives.
32
33
  *
33
34
  * @param config Optional configuration
34
35
  */
@@ -36,14 +37,13 @@ export declare class NewRelicInstrumentationProvider extends BaseInstrumentation
36
37
  /**
37
38
  * Check if provider is ready
38
39
  *
39
- * @returns true if New Relic is available and initialized
40
+ * @returns true once initialized, while the New Relic agent's API is on the page
40
41
  */
41
42
  isReady(): boolean;
42
43
  /**
43
44
  * Cleanup provider resources
44
45
  *
45
- * Marks provider as uninitialized. The global `window.newrelic` object
46
- * is not modified.
46
+ * Marks provider as uninitialized. The agent's globals are not modified.
47
47
  */
48
48
  destroy(): void;
49
49
  /**
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * Default provider for existing New Relic Browser Agent instrumentation.
5
5
  *
6
- * This provider wraps the global `window.newrelic` object provided by
7
- * the New Relic Browser Agent.
6
+ * Sends through the New Relic Browser Agent's API on `window.newrelic` or
7
+ * `window.NREUM`, looked up on every call, so an agent that loads after
8
+ * `initialize()` receives everything tracked from then on.
8
9
  *
9
10
  * @example
10
11
  * ```typescript
@@ -19,6 +20,7 @@
19
20
  * }
20
21
  * ```
21
22
  */
23
+ import { probeNewRelicAgent } from "../new-relic-agent.js";
22
24
  import { BaseInstrumentationProvider } from "./BaseInstrumentationProvider.js";
23
25
  export class NewRelicInstrumentationProvider extends BaseInstrumentationProvider {
24
26
  providerId = "newrelic";
@@ -26,43 +28,32 @@ export class NewRelicInstrumentationProvider extends BaseInstrumentationProvider
26
28
  /**
27
29
  * Initialize the New Relic provider
28
30
  *
29
- * Checks if the New Relic Browser Agent is available via `window.newrelic`.
30
- * If not available, initialization succeeds but `isReady()` will return false.
31
+ * Configures the provider. The agent does not have to be on the page yet:
32
+ * `isReady()` turns true when it arrives.
31
33
  *
32
34
  * @param config Optional configuration
33
35
  */
34
36
  async initialize(config) {
35
37
  this.config = config;
36
- // Check if New Relic is available
37
- if (typeof window !== "undefined" && window.newrelic) {
38
- this.initialized = true;
39
- if (this.config?.debug) {
40
- console.log("[NewRelicProvider] Initialized successfully");
41
- }
42
- }
43
- else {
44
- if (this.config?.debug) {
45
- console.warn("[NewRelicProvider] New Relic not available (window.newrelic not found)");
46
- }
47
- // Don't throw - allow graceful degradation
48
- this.initialized = false;
38
+ this.initialized = true;
39
+ if (this.config?.debug) {
40
+ console.log(probeNewRelicAgent()
41
+ ? "[NewRelicProvider] Initialized successfully"
42
+ : "[NewRelicProvider] Initialized; sends once the New Relic agent is on the page");
49
43
  }
50
44
  }
51
45
  /**
52
46
  * Check if provider is ready
53
47
  *
54
- * @returns true if New Relic is available and initialized
48
+ * @returns true once initialized, while the New Relic agent's API is on the page
55
49
  */
56
50
  isReady() {
57
- return (this.initialized &&
58
- typeof window !== "undefined" &&
59
- !!window.newrelic);
51
+ return this.initialized && probeNewRelicAgent() !== undefined;
60
52
  }
61
53
  /**
62
54
  * Cleanup provider resources
63
55
  *
64
- * Marks provider as uninitialized. The global `window.newrelic` object
65
- * is not modified.
56
+ * Marks provider as uninitialized. The agent's globals are not modified.
66
57
  */
67
58
  destroy() {
68
59
  this.initialized = false;
@@ -79,8 +70,7 @@ export class NewRelicInstrumentationProvider extends BaseInstrumentationProvider
79
70
  * @param attributes Transformed attributes (already filtered and transformed by base class)
80
71
  */
81
72
  doTrackError(error, attributes) {
82
- const newrelic = window.newrelic;
83
- newrelic.noticeError(error, attributes);
73
+ probeNewRelicAgent()?.noticeError(error, attributes);
84
74
  }
85
75
  /**
86
76
  * Track a custom event/page action with New Relic
@@ -91,8 +81,7 @@ export class NewRelicInstrumentationProvider extends BaseInstrumentationProvider
91
81
  * @param attributes Transformed attributes (already filtered and transformed by base class)
92
82
  */
93
83
  doTrackEvent(eventName, attributes) {
94
- const newrelic = window.newrelic;
95
- newrelic.addPageAction(eventName, attributes);
84
+ probeNewRelicAgent()?.addPageAction(eventName, attributes);
96
85
  }
97
86
  /**
98
87
  * Set user context for session tracking
@@ -103,13 +92,13 @@ export class NewRelicInstrumentationProvider extends BaseInstrumentationProvider
103
92
  * @param attributes Optional user attributes
104
93
  */
105
94
  doSetUserContext(userId, attributes) {
106
- const newrelic = window.newrelic;
95
+ const newrelic = probeNewRelicAgent();
107
96
  // Set user ID if method exists
108
- if (newrelic.setUserId) {
97
+ if (typeof newrelic?.setUserId === "function") {
109
98
  newrelic.setUserId(userId);
110
99
  }
111
100
  // Set custom attributes for user
112
- if (attributes && newrelic.setCustomAttribute) {
101
+ if (attributes && typeof newrelic?.setCustomAttribute === "function") {
113
102
  for (const [key, value] of Object.entries(attributes)) {
114
103
  newrelic.setCustomAttribute(key, value);
115
104
  }
@@ -123,8 +112,8 @@ export class NewRelicInstrumentationProvider extends BaseInstrumentationProvider
123
112
  * @param attributes Attributes to set globally
124
113
  */
125
114
  doSetGlobalAttributes(attributes) {
126
- const newrelic = window.newrelic;
127
- if (newrelic.setCustomAttribute) {
115
+ const newrelic = probeNewRelicAgent();
116
+ if (typeof newrelic?.setCustomAttribute === "function") {
128
117
  for (const [key, value] of Object.entries(attributes)) {
129
118
  newrelic.setCustomAttribute(key, value);
130
119
  }
@@ -49,7 +49,8 @@ export interface InstrumentationProvider {
49
49
  */
50
50
  readonly providerName: string;
51
51
  /**
52
- * Initialize the provider (load SDKs, configure clients, etc.)
52
+ * Configure the provider. Its backend does not have to be available yet:
53
+ * `isReady()` answers that.
53
54
  *
54
55
  * @param config Optional configuration for the provider
55
56
  */
@@ -105,7 +106,10 @@ export interface InstrumentationProvider {
105
106
  /**
106
107
  * Check if provider is ready to track events
107
108
  *
108
- * @returns true if provider is initialized and ready, false otherwise
109
+ * Evaluated on every call, so a backend that becomes available after
110
+ * `initialize()` makes the provider ready without another call.
111
+ *
112
+ * @returns true if provider is initialized and its backend is available now
109
113
  */
110
114
  isReady(): boolean;
111
115
  }
@@ -195,7 +199,7 @@ export interface ErrorAttributes {
195
199
  /**
196
200
  * Component that generated the error
197
201
  *
198
- * Examples: 'pie-item-player', 'pie-resource-monitor'
202
+ * Examples: 'pie-item-player', 'resource-monitor'
199
203
  */
200
204
  component: string;
201
205
  /**
@@ -24,13 +24,16 @@ export type LoaderConfig = {
24
24
  /**
25
25
  * Instrumentation provider for tracking events and errors.
26
26
  *
27
- * Optional. If not provided, defaults to NewRelicInstrumentationProvider.
28
- * The provider handles instrumentation gracefully - if New Relic (or the configured
29
- * backend) is not available, it will simply not track events (no errors thrown).
27
+ * - Unset or `undefined`: nothing is reported unless `trackPageActions` is
28
+ * `true`, in which case players fall back to a
29
+ * `NewRelicInstrumentationProvider`, which reports while the New Relic
30
+ * browser agent is on the page.
31
+ * - A provider: players report to it. A value that fails the
32
+ * `InstrumentationProvider` contract is treated as `null`.
33
+ * - `null`: disables instrumentation, whatever `trackPageActions` says.
30
34
  *
31
- * @example Using New Relic (default - no configuration needed)
35
+ * @example Using New Relic (the fallback, no provider needed)
32
36
  * ```typescript
33
- * // Just enable tracking - will use New Relic if window.newrelic is available
34
37
  * const loaderConfig = {
35
38
  * trackPageActions: true
36
39
  * };
@@ -47,8 +50,15 @@ export type LoaderConfig = {
47
50
  * instrumentationProvider: provider
48
51
  * };
49
52
  * ```
53
+ * @example Disabling instrumentation
54
+ * ```typescript
55
+ * const loaderConfig = {
56
+ * trackPageActions: true,
57
+ * instrumentationProvider: null
58
+ * };
59
+ * ```
50
60
  */
51
- instrumentationProvider?: InstrumentationProvider;
61
+ instrumentationProvider?: InstrumentationProvider | null;
52
62
  /**
53
63
  * Maximum number of retry attempts for failed resources (images/audio/video).
54
64
  */
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Whether a player's ESM backend fetches controller modules
3
+ * (`EsmBackendConfig.loadControllers`).
4
+ *
5
+ * The host's `loaderOptions.loadControllers` decides when set. Otherwise an
6
+ * author view loads them, and a delivery loads them unless it is hosted: a
7
+ * hosted player takes models and scores from the server and resolves no
8
+ * controller (`findPieController`), so fetching one is wasted.
9
+ */
10
+ export declare function resolveLoadControllers(args: {
11
+ loadControllers?: boolean;
12
+ author: boolean;
13
+ hosted: boolean;
14
+ }): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Whether a player's ESM backend fetches controller modules
3
+ * (`EsmBackendConfig.loadControllers`).
4
+ *
5
+ * The host's `loaderOptions.loadControllers` decides when set. Otherwise an
6
+ * author view loads them, and a delivery loads them unless it is hosted: a
7
+ * hosted player takes models and scores from the server and resolves no
8
+ * controller (`findPieController`), so fetching one is wasted.
9
+ */
10
+ export function resolveLoadControllers(args) {
11
+ return args.loadControllers ?? (args.author || !args.hosted);
12
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Default PIE bundle service base URL. Exported so widgets can use it as
3
+ * the fallback when the host does not supply `loaderOptions.bundleHost`.
4
+ */
5
+ export declare const DEFAULT_BUNDLE_HOST = "https://proxy.pie-api.com/bundles/";
6
+ /**
7
+ * Default ESM CDN base URL, the fallback when the host does not supply
8
+ * `loaderOptions.esmCdnUrl`.
9
+ */
10
+ export declare const DEFAULT_ESM_CDN_URL = "https://cdn.jsdelivr.net/npm";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Default PIE bundle service base URL. Exported so widgets can use it as
3
+ * the fallback when the host does not supply `loaderOptions.bundleHost`.
4
+ */
5
+ export const DEFAULT_BUNDLE_HOST = "https://proxy.pie-api.com/bundles/";
6
+ /**
7
+ * Default ESM CDN base URL, the fallback when the host does not supply
8
+ * `loaderOptions.esmCdnUrl`.
9
+ */
10
+ export const DEFAULT_ESM_CDN_URL = "https://cdn.jsdelivr.net/npm";
@@ -10,9 +10,10 @@
10
10
  * unregistered tags and a per-tag `RegistrationFailureReason` map.
11
11
  *
12
12
  * The primitive delegates the actual fetch/register work to a backend
13
- * adapter (IIFE or ESM) but always verifies the outcome via
13
+ * adapter (IIFE or ESM) and verifies the outcome via
14
14
  * `customElements.whenDefined`. An adapter cannot silently under-register
15
- * — the primitive will catch it and surface a timeout reason.
15
+ * — the primitive will catch it and surface a timeout reason. A tag the
16
+ * adapter reports a failed load for is not waited on.
16
17
  *
17
18
  * See packages/players-shared/tests/element-loader-contract.test.ts for
18
19
  * the executable specification of every failure mode this primitive is
@@ -29,7 +30,7 @@ export type { IifeBackendConfig } from "./iife-adapter.js";
29
30
  export type { EsmBackendConfig } from "./esm-adapter.js";
30
31
  /**
31
32
  * Aggregate error thrown by `ensureRegistered` when one or more requested
32
- * tags were not registered by the time verification timed out.
33
+ * tags were not registered: their loads failed, or verification timed out.
33
34
  */
34
35
  export declare class ElementLoaderError extends Error {
35
36
  readonly name = "ElementLoaderError";
@@ -37,17 +38,24 @@ export declare class ElementLoaderError extends Error {
37
38
  readonly reasons: Map<ElementTag, RegistrationFailureReason>;
38
39
  constructor(message: string, unregisteredTags: Set<ElementTag>, reasons: Map<ElementTag, RegistrationFailureReason>);
39
40
  }
41
+ /**
42
+ * Why each tag of an `ElementLoaderError` failed to register, one clause per
43
+ * tag; `undefined` for any other error. Players report it as the `cause` of a
44
+ * load error.
45
+ */
46
+ export declare function describeRegistrationFailures(error: unknown): string | undefined;
40
47
  /**
41
48
  * Thrown by `assertRegistered` when any requested tag is missing from
42
- * `customElements`. Carries enough detail for the host to diagnose what
43
- * pre-registration step was skipped.
49
+ * `customElements`. The message names, for each missing tag, the tags its
50
+ * package (or, without one, its base tag) is registered under.
44
51
  */
45
52
  export declare class ElementAssertionError extends Error {
46
53
  readonly name = "ElementAssertionError";
47
54
  readonly expectedTags: readonly ElementTag[];
48
55
  readonly missingTags: readonly ElementTag[];
56
+ /** The `window.PIE_REGISTRY` tags `customElements` holds. */
49
57
  readonly currentlyRegisteredTags: readonly ElementTag[];
50
- constructor(expected: ElementTag[], missing: ElementTag[], currentlyRegistered: ElementTag[]);
58
+ constructor(expected: ElementTag[], missing: ElementTag[], currentlyRegistered: ElementTag[], packages?: ElementMap);
51
59
  }
52
60
  export type BackendOption = IifeBackendConfig | EsmBackendConfig | ElementLoaderBackend;
53
61
  export type EnsureRegisteredOptions = {
@@ -85,6 +93,8 @@ export type EnsureRegisteredOptions = {
85
93
  * - After the adapter's `load` settles, a post-load verification pass
86
94
  * (bounded `customElements.whenDefined`) checks every tag. Any missing
87
95
  * tag becomes a rejection.
96
+ * - Verification does not wait on a tag the adapter reports a failed load
97
+ * for, so when that covers every missing tag the rejection is immediate.
88
98
  */
89
99
  export declare function ensureRegistered(elements: ElementMap, options: EnsureRegisteredOptions): Promise<void>;
90
100
  /**
@@ -92,6 +102,8 @@ export declare function ensureRegistered(elements: ElementMap, options: EnsureRe
92
102
  * Throws `ElementAssertionError` with diagnostic detail otherwise.
93
103
  *
94
104
  * Used by hosts that opt into the "preloaded" strategy — they pre-register
95
- * elements out-of-band and want a loud failure if anything is missing.
105
+ * elements out-of-band and want a loud failure if anything is missing. An
106
+ * element map, tag to package spec, lets the error name what each missing
107
+ * tag's package is registered as.
96
108
  */
97
- export declare function assertRegistered(tags: ElementTag[]): void;
109
+ export declare function assertRegistered(tags: ElementTag[] | ElementMap): void;
@@ -10,23 +10,28 @@
10
10
  * unregistered tags and a per-tag `RegistrationFailureReason` map.
11
11
  *
12
12
  * The primitive delegates the actual fetch/register work to a backend
13
- * adapter (IIFE or ESM) but always verifies the outcome via
13
+ * adapter (IIFE or ESM) and verifies the outcome via
14
14
  * `customElements.whenDefined`. An adapter cannot silently under-register
15
- * — the primitive will catch it and surface a timeout reason.
15
+ * — the primitive will catch it and surface a timeout reason. A tag the
16
+ * adapter reports a failed load for is not waited on.
16
17
  *
17
18
  * See packages/players-shared/tests/element-loader-contract.test.ts for
18
19
  * the executable specification of every failure mode this primitive is
19
20
  * required to catch.
20
21
  */
21
22
  import { DEFAULT_IIFE_BUNDLE_RETRY_CONFIG } from "../loader-config.js";
23
+ import { pieRegistry } from "../pie/registry.js";
24
+ import { Status } from "../pie/types.js";
25
+ import { parsePackageName } from "../pie/utils.js";
26
+ import { parseVersionedTagName } from "../pie/versioned-tag.js";
22
27
  import { AdapterFailure, } from "./element-loader-types.js";
23
- import { createEsmBackend } from "./esm-adapter.js";
28
+ import { clearPackageMetadataRequests, createEsmBackend, } from "./esm-adapter.js";
24
29
  import { createIifeBackend } from "./iife-adapter.js";
25
30
  import { assertElementPackagesAllowed, } from "./element-package-policy.js";
26
31
  export { AdapterFailure } from "./element-loader-types.js";
27
32
  /**
28
33
  * Aggregate error thrown by `ensureRegistered` when one or more requested
29
- * tags were not registered by the time verification timed out.
34
+ * tags were not registered: their loads failed, or verification timed out.
30
35
  */
31
36
  export class ElementLoaderError extends Error {
32
37
  name = "ElementLoaderError";
@@ -38,31 +43,85 @@ export class ElementLoaderError extends Error {
38
43
  this.reasons = reasons;
39
44
  }
40
45
  }
46
+ const describeRegistrationFailure = (reason) => {
47
+ switch (reason.kind) {
48
+ case "timeout":
49
+ return `not defined within ${reason.timeoutMs} ms`;
50
+ case "module-load-failed":
51
+ return `${reason.specifier} failed to load: ${reason.cause}`;
52
+ case "bundle-load-failed":
53
+ return `${reason.url} failed to load: ${reason.cause}`;
54
+ case "define-failed":
55
+ case "backend-rejected":
56
+ return reason.cause;
57
+ case "not-a-constructor":
58
+ return `${reason.packageName ?? "its package"} exports no element constructor`;
59
+ case "no-element-class":
60
+ return `${reason.packageName} exports no element class`;
61
+ case "package-not-in-bundle":
62
+ return `${reason.packageName} is not in the bundle, which holds ${reason.availablePackages.join(", ") || "no packages"}`;
63
+ }
64
+ };
65
+ /**
66
+ * Why each tag of an `ElementLoaderError` failed to register, one clause per
67
+ * tag; `undefined` for any other error. Players report it as the `cause` of a
68
+ * load error.
69
+ */
70
+ export function describeRegistrationFailures(error) {
71
+ if (!(error instanceof ElementLoaderError))
72
+ return undefined;
73
+ return [...error.reasons]
74
+ .map(([tag, reason]) => `${tag} (${reason.kind}): ${describeRegistrationFailure(reason)}`)
75
+ .join("; ");
76
+ }
41
77
  /**
42
78
  * Thrown by `assertRegistered` when any requested tag is missing from
43
- * `customElements`. Carries enough detail for the host to diagnose what
44
- * pre-registration step was skipped.
79
+ * `customElements`. The message names, for each missing tag, the tags its
80
+ * package (or, without one, its base tag) is registered under.
45
81
  */
46
82
  export class ElementAssertionError extends Error {
47
83
  name = "ElementAssertionError";
48
84
  expectedTags;
49
85
  missingTags;
86
+ /** The `window.PIE_REGISTRY` tags `customElements` holds. */
50
87
  currentlyRegisteredTags;
51
- constructor(expected, missing, currentlyRegistered) {
52
- super(buildAssertionMessage(expected, missing, currentlyRegistered));
88
+ constructor(expected, missing, currentlyRegistered, packages = {}) {
89
+ super(buildAssertionMessage(expected, missing, currentlyRegistered, packages));
53
90
  this.expectedTags = expected;
54
91
  this.missingTags = missing;
55
92
  this.currentlyRegisteredTags = currentlyRegistered;
56
93
  }
57
94
  }
58
- function buildAssertionMessage(expected, missing, registered) {
59
- const expectedStr = expected.join(", ");
60
- const missingStr = missing.join(", ");
61
- const registeredStr = registered.length
62
- ? registered.join(", ")
63
- : "(none enumerable)";
64
- return (`ElementLoader.assertRegistered: expected [${expectedStr}], ` +
65
- `missing [${missingStr}]. customElements contains: [${registeredStr}].`);
95
+ function buildAssertionMessage(expected, missing, registered, packages) {
96
+ const details = missing.map((tag) => {
97
+ const packageName = packageNameOf(packages[tag]);
98
+ const baseName = parseVersionedTagName(tag).baseName;
99
+ const related = packageName
100
+ ? registered.filter((other) => registeredPackageName(other) === packageName)
101
+ : registered.filter((other) => parseVersionedTagName(other).baseName === baseName);
102
+ if (!related.includes(baseName) && isRegistered(baseName)) {
103
+ related.push(baseName);
104
+ }
105
+ const subject = packageName ?? baseName;
106
+ return related.length > 0
107
+ ? `${subject} is registered as [${related.join(", ")}]`
108
+ : `nothing is registered for ${subject}`;
109
+ });
110
+ return (`ElementLoader.assertRegistered: missing [${missing.join(", ")}] ` +
111
+ `of [${expected.join(", ")}]; ${details.join("; ")}.`);
112
+ }
113
+ function packageNameOf(spec) {
114
+ if (!spec)
115
+ return undefined;
116
+ try {
117
+ return parsePackageName(spec).name || undefined;
118
+ }
119
+ catch {
120
+ return undefined;
121
+ }
122
+ }
123
+ function registeredPackageName(tag) {
124
+ return packageNameOf(pieRegistry()[tag]?.package);
66
125
  }
67
126
  const DEFAULT_WHEN_DEFINED_TIMEOUT_MS = 5000;
68
127
  const DEFAULT_LOAD_TIMEOUT_MS = DEFAULT_IIFE_BUNDLE_RETRY_CONFIG.timeoutMs;
@@ -82,6 +141,8 @@ const resolvedBackendOverrides = new Map();
82
141
  * - After the adapter's `load` settles, a post-load verification pass
83
142
  * (bounded `customElements.whenDefined`) checks every tag. Any missing
84
143
  * tag becomes a rejection.
144
+ * - Verification does not wait on a tag the adapter reports a failed load
145
+ * for, so when that covers every missing tag the rejection is immediate.
85
146
  */
86
147
  export async function ensureRegistered(elements, options) {
87
148
  if (!elements || Object.keys(elements).length === 0)
@@ -121,9 +182,15 @@ async function runEnsureRegistered(elements, tags, backend, context, timeoutMs,
121
182
  catch (err) {
122
183
  adapterError = err instanceof Error ? err : new Error(String(err));
123
184
  }
185
+ // A tag whose load failed cannot register any more, so verification waits
186
+ // only on the others. A timed-out load can still register, so its tag waits.
124
187
  const verification = await Promise.all(tags.map(async (tag) => {
125
188
  if (isRegistered(tag))
126
189
  return { tag, ok: true };
190
+ const adapterReason = extractAdapterReason(adapterError, tag);
191
+ if (adapterReason && adapterReason.kind !== "timeout") {
192
+ return { tag, ok: false };
193
+ }
127
194
  try {
128
195
  await whenDefinedWithTimeout(tag, timeoutMs);
129
196
  return { tag, ok: true };
@@ -184,15 +251,19 @@ async function raceWithLoadTimeout(loadPromise, tags, loadTimeoutMs) {
184
251
  * Throws `ElementAssertionError` with diagnostic detail otherwise.
185
252
  *
186
253
  * Used by hosts that opt into the "preloaded" strategy — they pre-register
187
- * elements out-of-band and want a loud failure if anything is missing.
254
+ * elements out-of-band and want a loud failure if anything is missing. An
255
+ * element map, tag to package spec, lets the error name what each missing
256
+ * tag's package is registered as.
188
257
  */
189
258
  export function assertRegistered(tags) {
190
- if (!tags || tags.length === 0)
259
+ const packages = Array.isArray(tags) ? {} : (tags ?? {});
260
+ const expected = Array.isArray(tags) ? tags : Object.keys(packages);
261
+ if (expected.length === 0)
191
262
  return;
192
- const missing = tags.filter((tag) => !isRegistered(tag));
263
+ const missing = expected.filter((tag) => !isRegistered(tag));
193
264
  if (missing.length === 0)
194
265
  return;
195
- throw new ElementAssertionError(tags, missing, snapshotRegisteredTags());
266
+ throw new ElementAssertionError(expected, missing, snapshotRegisteredTags(), packages);
196
267
  }
197
268
  // ─── Internals ───────────────────────────────────────────────────────────────
198
269
  function resolveBackend(backend) {
@@ -363,24 +434,18 @@ async function whenDefinedWithTimeout(tag, timeoutMs) {
363
434
  clearTimeout(timer);
364
435
  }
365
436
  }
437
+ /**
438
+ * `customElements` cannot be enumerated, so the registered tags are the
439
+ * `window.PIE_REGISTRY` entries it holds.
440
+ */
366
441
  function snapshotRegisteredTags() {
367
- if (typeof customElements === "undefined")
442
+ if (typeof window === "undefined")
368
443
  return [];
369
- // Standard `CustomElementRegistry` does not expose iteration. Tests install
370
- // a scripted registry with a `__pieSnapshot` extension to make diagnostic
371
- // messages assertable. Production falls through to an empty list —
372
- // still strictly better than today's "missing tags: X" error which leaks
373
- // no registry state at all.
374
- const reg = customElements;
375
- if (typeof reg.__pieSnapshot === "function") {
376
- try {
377
- return reg.__pieSnapshot();
378
- }
379
- catch {
380
- return [];
381
- }
382
- }
383
- return [];
444
+ return Object.values(pieRegistry())
445
+ .filter((entry) => entry?.status === Status.loaded)
446
+ .map((entry) => entry.tagName)
447
+ .filter((tag) => typeof tag === "string" && isRegistered(tag))
448
+ .sort();
384
449
  }
385
450
  function extractAdapterReason(err, tag) {
386
451
  if (!err)
@@ -401,6 +466,7 @@ export const __testing = {
401
466
  inFlightRequests.clear();
402
467
  resolvedEsmBackends.clear();
403
468
  resolvedBackendOverrides.clear();
469
+ clearPackageMetadataRequests();
404
470
  },
405
471
  inFlightCount() {
406
472
  return inFlightRequests.size;
@@ -12,6 +12,8 @@ export declare class ElementPackagePolicyError extends Error {
12
12
  readonly name = "ElementPackagePolicyError";
13
13
  constructor(tagName: string, packageSpec: string, message: string);
14
14
  }
15
+ /** Exact semver: no range, tag, `v` prefix or build metadata. */
16
+ export declare function isExactSemver(version: string): boolean;
15
17
  /**
16
18
  * Validate an element map only when a host explicitly supplies a policy.
17
19
  * Omitting the policy preserves the existing trusted-application behavior.
@@ -10,7 +10,8 @@ export class ElementPackagePolicyError extends Error {
10
10
  this.packageSpec = packageSpec;
11
11
  }
12
12
  }
13
- function isExactSemver(version) {
13
+ /** Exact semver: no range, tag, `v` prefix or build metadata. */
14
+ export function isExactSemver(version) {
14
15
  return validSemver(version) === version;
15
16
  }
16
17
  function parsePolicyPackageSpec(spec) {
@@ -27,9 +27,18 @@ export type EsmCdnProviderName = BuiltInEsmCdnProviderName | (string & {});
27
27
  export type EsmCdnProvider = {
28
28
  name: EsmCdnProviderName;
29
29
  packageJsonUrl(packageVersion: string): string;
30
+ /**
31
+ * URL of `dist/browser/<view>/index.js`. `view` can be a nested path: an
32
+ * element's editor-runtime variant is `editor-runtime/<view>`.
33
+ */
30
34
  browserViewUrl(packageVersion: string, view: string): string;
31
35
  browserControllerUrl(packageVersion: string): string;
32
36
  sharedDependencyUrl(dependencyName: string, version: string, subpath?: string): string;
37
+ /**
38
+ * URL of the package's `./runtime-support` module. Without it, the file
39
+ * `dist/runtime-support.js` beside `packageJsonUrl`.
40
+ */
41
+ runtimeSupportUrl?(packageVersion: string): string;
33
42
  };
34
43
  export type EsmCdnProviderOption = EsmCdnProviderName | EsmCdnProvider;
35
44
  export type EsmBackendConfig = {
@@ -56,3 +65,23 @@ export type EsmBackendConfig = {
56
65
  export type EsmBackend = ElementLoaderBackend & {};
57
66
  export declare function createEsmBackend(config: EsmBackendConfig): EsmBackend;
58
67
  export declare function mapEsmViewElements(elements: ElementMap, view?: string, viewConfig?: ViewConfig): ElementMap;
68
+ /**
69
+ * URL of a package's `./runtime-support` module on the ESM CDN the players load
70
+ * elements from, or `undefined` when a custom provider names no package root.
71
+ */
72
+ export declare function resolveEsmRuntimeSupportUrl(packageVersion: string, options: {
73
+ cdnBaseUrl: string;
74
+ cdnProvider?: EsmCdnProviderOption;
75
+ }): string | undefined;
76
+ /**
77
+ * The npm root the ESM CDN the players load elements from serves raw package
78
+ * files under, `<root>/<package>@<version>/<path>`: the base URL for jsDelivr,
79
+ * raw.esm.sh for esm.sh, and for a custom provider whatever its
80
+ * `packageJsonUrl` puts before the package. `undefined` when that URL names
81
+ * no such root.
82
+ */
83
+ export declare function resolveEsmAssetRoot(options: {
84
+ cdnBaseUrl: string;
85
+ cdnProvider?: EsmCdnProviderOption;
86
+ }): string | undefined;
87
+ export declare function pickElementClass(module: any, view: string): unknown;