@pie-players/pie-players-shared 0.3.75 → 1.0.0-beta.1
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/README.md +88 -77
- package/dist/components/vendor/nds/README.md +4 -5
- package/dist/config/profile.d.ts +1 -1
- package/dist/config/profile.js +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/language-tags.d.ts +9 -1
- package/dist/i18n/language-tags.js +11 -1
- package/dist/i18n/messages/en-US.d.ts +33 -5
- package/dist/i18n/messages/en-US.js +33 -5
- package/dist/i18n/messages/nl-NL.d.ts +33 -5
- package/dist/i18n/messages/nl-NL.js +33 -5
- package/dist/i18n/provider.d.ts +14 -7
- package/dist/i18n/provider.js +26 -29
- package/dist/index.js +0 -1
- package/dist/instrumentation/providers/BaseInstrumentationProvider.js +0 -14
- package/dist/loader-config.d.ts +0 -2
- package/dist/loader-config.js +0 -2
- package/dist/loaders/element-loader.js +13 -6
- package/dist/loaders/esm-adapter.js +48 -37
- package/dist/loaders/iife-adapter.d.ts +3 -3
- package/dist/loaders/iife-adapter.js +36 -35
- package/dist/media/index.d.ts +4 -6
- package/dist/media/index.js +6 -8
- package/dist/object/index.d.ts +8 -3
- package/dist/object/index.js +8 -6
- package/dist/pie/asset-handler.d.ts +0 -17
- package/dist/pie/asset-handler.js +1 -1
- package/dist/pie/authoring.js +5 -7
- package/dist/pie/elements-rendered.d.ts +0 -2
- package/dist/pie/elements-rendered.js +1 -1
- package/dist/pie/index.d.ts +1 -0
- package/dist/pie/index.js +1 -0
- package/dist/pie/initialization.d.ts +13 -14
- package/dist/pie/initialization.js +61 -75
- package/dist/pie/item-session-contract.js +7 -9
- package/dist/pie/resource-monitor.d.ts +34 -32
- package/dist/pie/resource-monitor.js +152 -231
- package/dist/pie/stages.d.ts +4 -3
- package/dist/pie/stages.js +4 -3
- package/dist/pie/updates.js +5 -4
- package/dist/pie/utils.d.ts +0 -2
- package/dist/pie/utils.js +0 -2
- package/dist/pie/versioned-tag.d.ts +10 -0
- package/dist/pie/versioned-tag.js +27 -0
- package/dist/security/sanitize-item-markup.d.ts +4 -4
- package/dist/security/sanitize-item-markup.js +4 -4
- package/dist/security/sanitize-svg-icon.js +2 -2
- package/dist/security/wrap-overwide-images.d.ts +1 -1
- package/dist/security/wrap-overwide-images.js +1 -1
- package/dist/timed-media/media-element-source.js +1 -2
- package/dist/types/index.d.ts +88 -52
- package/dist/types/index.js +2 -2
- package/dist/ui/attribute-coercion.d.ts +5 -0
- package/dist/ui/attribute-coercion.js +6 -4
- package/dist/ui/content-styles.d.ts +6 -10
- package/dist/ui/content-styles.js +8 -13
- package/dist/ui/first-focusable.d.ts +0 -1
- package/dist/ui/first-focusable.js +0 -1
- package/dist/ui/focus-trap.js +2 -3
- package/dist/ui/scope-css.d.ts +23 -26
- package/dist/ui/scope-css.js +23 -26
- package/package.json +3 -2
package/dist/i18n/provider.d.ts
CHANGED
|
@@ -43,12 +43,10 @@ export declare function detectBrowserLocale(): string;
|
|
|
43
43
|
/**
|
|
44
44
|
* The bundled provider.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* player has no business writing the host page's root), and plural categories
|
|
51
|
-
* come from `Intl.PluralRules` alone.
|
|
46
|
+
* Catalogs are BCP-47 keyed and resolved through RFC 4647 lookup, and plural
|
|
47
|
+
* categories come from `Intl.PluralRules`. It never writes `dir` or `lang` to
|
|
48
|
+
* `document.documentElement`: an embedded player does not own the host page's
|
|
49
|
+
* root.
|
|
52
50
|
*/
|
|
53
51
|
export declare class SimpleI18n implements I18nServiceApi {
|
|
54
52
|
/** Shared across instances and views: `Intl.PluralRules` is not cheap. */
|
|
@@ -138,11 +136,20 @@ export declare class SimpleI18n implements I18nServiceApi {
|
|
|
138
136
|
private selectPluralCategory;
|
|
139
137
|
private notify;
|
|
140
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* The graceful default: an English-only provider, shared process-wide.
|
|
141
|
+
*
|
|
142
|
+
* `composition-context.md` requires a resolver to work with no publisher
|
|
143
|
+
* present, and this is that state. A tool mounted bare — in `print-player` or
|
|
144
|
+
* an authoring harness — resolves this and renders English rather than
|
|
145
|
+
* leaking raw message keys onto the screen. It carries no locale loader, so it
|
|
146
|
+
* pulls no catalog beyond the 5 KB English one already in the bundle.
|
|
147
|
+
*/
|
|
141
148
|
export declare function getDefaultI18n(): I18nServiceApi;
|
|
142
149
|
/**
|
|
143
150
|
* Resolve a component's interface-locale provider from a published context.
|
|
144
151
|
*
|
|
145
|
-
* Returns a **fresh** object on every call
|
|
152
|
+
* Returns a **fresh** object on every call. A
|
|
146
153
|
* provider is mutable: `setLocale` swaps its catalog in place, so its identity is
|
|
147
154
|
* unchanged before and after a locale load. A reactive `$derived` reading the
|
|
148
155
|
* provider directly therefore never invalidates, and a label rendered while the
|
package/dist/i18n/provider.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module @pie-players/pie-players-shared/i18n/provider
|
|
11
11
|
*/
|
|
12
|
-
import { findBestLanguageMatch, normalizeLanguageTag, } from "./language-tags.js";
|
|
12
|
+
import { findBestLanguageMatch, normalizeLanguageTag, toLanguageTag, } from "./language-tags.js";
|
|
13
13
|
import enUS from "./messages/en-US.js";
|
|
14
14
|
/** The locale every fallback chain terminates at. */
|
|
15
15
|
export const DEFAULT_LOCALE = "en-US";
|
|
@@ -31,7 +31,6 @@ export function dynamicMessageKey(key) {
|
|
|
31
31
|
* Consulted only where `Intl.Locale.prototype.textInfo` is unavailable — it is
|
|
32
32
|
* the authoritative CLDR answer but shipped late in Safari, and an assessment
|
|
33
33
|
* laid out left-to-right for an Arabic reader is not a graceful degradation.
|
|
34
|
-
* Replaces a four-entry list that omitted every subtag below.
|
|
35
34
|
*/
|
|
36
35
|
const RTL_LANGUAGES = new Set([
|
|
37
36
|
"ar", // Arabic
|
|
@@ -101,12 +100,10 @@ export function detectBrowserLocale() {
|
|
|
101
100
|
/**
|
|
102
101
|
* The bundled provider.
|
|
103
102
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* player has no business writing the host page's root), and plural categories
|
|
109
|
-
* come from `Intl.PluralRules` alone.
|
|
103
|
+
* Catalogs are BCP-47 keyed and resolved through RFC 4647 lookup, and plural
|
|
104
|
+
* categories come from `Intl.PluralRules`. It never writes `dir` or `lang` to
|
|
105
|
+
* `document.documentElement`: an embedded player does not own the host page's
|
|
106
|
+
* root.
|
|
110
107
|
*/
|
|
111
108
|
export class SimpleI18n {
|
|
112
109
|
/** Shared across instances and views: `Intl.PluralRules` is not cheap. */
|
|
@@ -121,7 +118,7 @@ export class SimpleI18n {
|
|
|
121
118
|
config;
|
|
122
119
|
constructor(config = {}) {
|
|
123
120
|
this.config = { ...config };
|
|
124
|
-
this.fallbackLocale =
|
|
121
|
+
this.fallbackLocale = toLanguageTag(config.fallbackLocale) || DEFAULT_LOCALE;
|
|
125
122
|
this.catalogs = new Map([[DEFAULT_LOCALE, enUS]]);
|
|
126
123
|
this.customMessages = { ...(config.customMessages ?? {}) };
|
|
127
124
|
this.loading = new Map();
|
|
@@ -160,11 +157,15 @@ export class SimpleI18n {
|
|
|
160
157
|
// A locale we ship no catalog for is legitimate: the host may have
|
|
161
158
|
// supplied its own messages for it. Honour the tag and let the
|
|
162
159
|
// fallback chain cover whatever those messages omit.
|
|
163
|
-
this.locale =
|
|
160
|
+
this.locale = toLanguageTag(locale) || DEFAULT_LOCALE;
|
|
164
161
|
this.notify();
|
|
165
162
|
return;
|
|
166
163
|
}
|
|
167
|
-
|
|
164
|
+
// The loader is asked only for tags it declares. A tag that resolved through
|
|
165
|
+
// host messages alone has nothing to load, and is honoured like the
|
|
166
|
+
// unresolved case above.
|
|
167
|
+
if (!this.catalogs.has(resolved) &&
|
|
168
|
+
this.config.availableLocales?.includes(resolved)) {
|
|
168
169
|
const inFlight = this.loading.get(resolved);
|
|
169
170
|
if (inFlight) {
|
|
170
171
|
await inFlight;
|
|
@@ -243,7 +244,7 @@ export class SimpleI18n {
|
|
|
243
244
|
* a catalog.
|
|
244
245
|
*/
|
|
245
246
|
addCustomMessages(locale, messages) {
|
|
246
|
-
const tag =
|
|
247
|
+
const tag = toLanguageTag(locale);
|
|
247
248
|
this.customMessages[tag] = deepMerge(this.customMessages[tag] ?? {}, messages);
|
|
248
249
|
this.notify();
|
|
249
250
|
}
|
|
@@ -256,7 +257,7 @@ export class SimpleI18n {
|
|
|
256
257
|
* locales: the locale is per-view, the catalogs are per-provider.
|
|
257
258
|
*/
|
|
258
259
|
withLocale(locale) {
|
|
259
|
-
const resolved = this.resolveLocaleTag(locale) ??
|
|
260
|
+
const resolved = this.resolveLocaleTag(locale) ?? toLanguageTag(locale);
|
|
260
261
|
if (resolved === this.locale)
|
|
261
262
|
return this;
|
|
262
263
|
const view = new SimpleI18n({ ...this.config, locale: resolved });
|
|
@@ -320,9 +321,9 @@ export class SimpleI18n {
|
|
|
320
321
|
this.catalogs.set(locale, await load(locale));
|
|
321
322
|
}
|
|
322
323
|
catch (error) {
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
324
|
+
// Rethrown with the locale named: `setLocale` rejects before switching
|
|
325
|
+
// (it assigns `this.locale` only after the load), so the previous locale
|
|
326
|
+
// stays active.
|
|
326
327
|
throw new Error(`Failed to load i18n catalog for locale: ${locale}`, {
|
|
327
328
|
cause: error,
|
|
328
329
|
});
|
|
@@ -350,9 +351,9 @@ export class SimpleI18n {
|
|
|
350
351
|
const node = this.lookupNode(key);
|
|
351
352
|
if (typeof node === "string")
|
|
352
353
|
return node;
|
|
353
|
-
// A
|
|
354
|
-
//
|
|
355
|
-
//
|
|
354
|
+
// A plural group resolves to its `other` form. A namespace (`t("common")`)
|
|
355
|
+
// is a miss: returning the object would put one where the caller expects
|
|
356
|
+
// a string, and interpolating it throws.
|
|
356
357
|
if (node !== undefined && isPluralGroup(node))
|
|
357
358
|
return node.other;
|
|
358
359
|
return undefined;
|
|
@@ -375,9 +376,6 @@ export class SimpleI18n {
|
|
|
375
376
|
listener();
|
|
376
377
|
}
|
|
377
378
|
}
|
|
378
|
-
function normalizeTag(locale) {
|
|
379
|
-
return locale ? locale.trim().replace(/_/g, "-") : "";
|
|
380
|
-
}
|
|
381
379
|
function readPath(source, key) {
|
|
382
380
|
if (!source)
|
|
383
381
|
return undefined;
|
|
@@ -415,17 +413,16 @@ function deepMerge(target, source) {
|
|
|
415
413
|
}
|
|
416
414
|
return result;
|
|
417
415
|
}
|
|
416
|
+
let defaultProvider;
|
|
418
417
|
/**
|
|
419
418
|
* The graceful default: an English-only provider, shared process-wide.
|
|
420
419
|
*
|
|
421
420
|
* `composition-context.md` requires a resolver to work with no publisher
|
|
422
|
-
* present, and this is that state. A tool mounted bare — in `print-player
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* bundle.
|
|
421
|
+
* present, and this is that state. A tool mounted bare — in `print-player` or
|
|
422
|
+
* an authoring harness — resolves this and renders English rather than
|
|
423
|
+
* leaking raw message keys onto the screen. It carries no locale loader, so it
|
|
424
|
+
* pulls no catalog beyond the 5 KB English one already in the bundle.
|
|
427
425
|
*/
|
|
428
|
-
let defaultProvider;
|
|
429
426
|
export function getDefaultI18n() {
|
|
430
427
|
if (!defaultProvider)
|
|
431
428
|
defaultProvider = new SimpleI18n();
|
|
@@ -434,7 +431,7 @@ export function getDefaultI18n() {
|
|
|
434
431
|
/**
|
|
435
432
|
* Resolve a component's interface-locale provider from a published context.
|
|
436
433
|
*
|
|
437
|
-
* Returns a **fresh** object on every call
|
|
434
|
+
* Returns a **fresh** object on every call. A
|
|
438
435
|
* provider is mutable: `setLocale` swaps its catalog in place, so its identity is
|
|
439
436
|
* unchanged before and after a locale load. A reactive `$derived` reading the
|
|
440
437
|
* provider directly therefore never invalidates, and a label rendered while the
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,6 @@ export * from "./player-strategy.js";
|
|
|
5
5
|
export * from "./security/index.js";
|
|
6
6
|
export * from "./loaders/index.js";
|
|
7
7
|
export * from "./object/index.js";
|
|
8
|
-
// Barrel export for PIE runtime utilities
|
|
9
8
|
export * from "./pie/index.js";
|
|
10
9
|
export { editorPostFix } from "./types/index.js";
|
|
11
10
|
export * from "./ui/attribute-coercion.js";
|
|
@@ -37,17 +37,13 @@ export class BaseInstrumentationProvider {
|
|
|
37
37
|
* @param attributes Contextual attributes
|
|
38
38
|
*/
|
|
39
39
|
trackError(error, attributes) {
|
|
40
|
-
// Guard: check if provider is ready
|
|
41
40
|
if (!this.guardedOperation("trackError", error, attributes)) {
|
|
42
41
|
return;
|
|
43
42
|
}
|
|
44
|
-
// Apply error filter
|
|
45
43
|
if (!this.applyErrorFilter(error, attributes)) {
|
|
46
44
|
return;
|
|
47
45
|
}
|
|
48
|
-
// Transform attributes
|
|
49
46
|
const finalAttributes = this.transformAttributes(attributes);
|
|
50
|
-
// Delegate to concrete provider
|
|
51
47
|
try {
|
|
52
48
|
this.doTrackError(error, finalAttributes);
|
|
53
49
|
if (this.config?.debug) {
|
|
@@ -73,28 +69,22 @@ export class BaseInstrumentationProvider {
|
|
|
73
69
|
* @param attributes Event attributes
|
|
74
70
|
*/
|
|
75
71
|
trackEvent(eventName, attributes) {
|
|
76
|
-
// Guard: check if provider is ready
|
|
77
72
|
if (!this.guardedOperation("trackEvent", eventName, attributes)) {
|
|
78
73
|
return;
|
|
79
74
|
}
|
|
80
|
-
// Apply event filter
|
|
81
75
|
if (!this.applyEventFilter(eventName, attributes)) {
|
|
82
76
|
return;
|
|
83
77
|
}
|
|
84
|
-
// Apply sampling
|
|
85
78
|
if (!this.applySampling(eventName)) {
|
|
86
79
|
return;
|
|
87
80
|
}
|
|
88
|
-
// Transform attributes
|
|
89
81
|
let finalAttributes = this.transformAttributes(attributes);
|
|
90
|
-
// Add timestamp if not present
|
|
91
82
|
if (!finalAttributes.timestamp) {
|
|
92
83
|
finalAttributes = {
|
|
93
84
|
...finalAttributes,
|
|
94
85
|
timestamp: new Date().toISOString(),
|
|
95
86
|
};
|
|
96
87
|
}
|
|
97
|
-
// Delegate to concrete provider
|
|
98
88
|
try {
|
|
99
89
|
this.doTrackEvent(eventName, finalAttributes);
|
|
100
90
|
if (this.config?.debug) {
|
|
@@ -134,11 +124,9 @@ export class BaseInstrumentationProvider {
|
|
|
134
124
|
* @param attributes Optional user attributes
|
|
135
125
|
*/
|
|
136
126
|
setUserContext(userId, attributes) {
|
|
137
|
-
// Guard: check if provider is ready
|
|
138
127
|
if (!this.guardedOperation("setUserContext", userId, attributes)) {
|
|
139
128
|
return;
|
|
140
129
|
}
|
|
141
|
-
// Delegate to concrete provider
|
|
142
130
|
try {
|
|
143
131
|
this.doSetUserContext(userId, attributes);
|
|
144
132
|
if (this.config?.debug) {
|
|
@@ -158,11 +146,9 @@ export class BaseInstrumentationProvider {
|
|
|
158
146
|
* @param attributes Attributes to set globally
|
|
159
147
|
*/
|
|
160
148
|
setGlobalAttributes(attributes) {
|
|
161
|
-
// Guard: check if provider is ready
|
|
162
149
|
if (!this.guardedOperation("setGlobalAttributes", attributes)) {
|
|
163
150
|
return;
|
|
164
151
|
}
|
|
165
|
-
// Delegate to concrete provider
|
|
166
152
|
try {
|
|
167
153
|
this.doSetGlobalAttributes(attributes);
|
|
168
154
|
if (this.config?.debug) {
|
package/dist/loader-config.d.ts
CHANGED
package/dist/loader-config.js
CHANGED
|
@@ -128,6 +128,9 @@ const DEFAULT_LOAD_TIMEOUT_MS = DEFAULT_IIFE_BUNDLE_RETRY_CONFIG.timeoutMs;
|
|
|
128
128
|
// Module-scoped in-flight cache. Key: backend signature + sorted elements.
|
|
129
129
|
// Keeps concurrent identical requests collapsed to one backend call.
|
|
130
130
|
const inFlightRequests = new Map();
|
|
131
|
+
// An ESM backend keeps page state that later loads build on (the import maps it
|
|
132
|
+
// injected, the shared dependency versions they fixed), so every load with the
|
|
133
|
+
// same config reuses one backend. IIFE backends are created per call.
|
|
131
134
|
const resolvedEsmBackends = new Map();
|
|
132
135
|
const resolvedBackendOverrides = new Map();
|
|
133
136
|
/**
|
|
@@ -350,22 +353,26 @@ function backendKeyOf(backend) {
|
|
|
350
353
|
}
|
|
351
354
|
return "unknown";
|
|
352
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Plain data compares by value, functions and other objects by identity, so
|
|
358
|
+
* two configs get one key only when they behave the same.
|
|
359
|
+
*/
|
|
353
360
|
function fingerprintValue(value) {
|
|
354
361
|
return JSON.stringify(normalizeForFingerprint(value, new Map()));
|
|
355
362
|
}
|
|
356
363
|
function normalizeForFingerprint(value, seen) {
|
|
357
364
|
if (value === null || value === undefined)
|
|
358
365
|
return value;
|
|
359
|
-
const
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
366
|
+
const valueType = typeof value;
|
|
367
|
+
if (valueType === "string" ||
|
|
368
|
+
valueType === "number" ||
|
|
369
|
+
valueType === "boolean") {
|
|
363
370
|
return value;
|
|
364
371
|
}
|
|
365
|
-
if (
|
|
372
|
+
if (valueType === "function") {
|
|
366
373
|
return { __functionRef: getFunctionRefId(value) };
|
|
367
374
|
}
|
|
368
|
-
if (
|
|
375
|
+
if (valueType !== "object") {
|
|
369
376
|
return String(value);
|
|
370
377
|
}
|
|
371
378
|
const objectValue = value;
|
|
@@ -22,6 +22,7 @@ import { parsePackageName } from "../pie/utils.js";
|
|
|
22
22
|
import { AdapterFailure, } from "./element-loader-types.js";
|
|
23
23
|
import { isExactSemver } from "./element-package-policy.js";
|
|
24
24
|
import { forwardMathjaxEvents } from "./mathjax-events.js";
|
|
25
|
+
import { isPlainRecord } from "../object/index.js";
|
|
25
26
|
export const BUILT_IN_VIEWS = {
|
|
26
27
|
delivery: { subpath: "", tagSuffix: "" },
|
|
27
28
|
author: { subpath: "/author", tagSuffix: "-config", fallback: "delivery" },
|
|
@@ -123,10 +124,15 @@ export function createEsmBackend(config) {
|
|
|
123
124
|
let injectedMap;
|
|
124
125
|
if (Object.keys(imports).length > 0) {
|
|
125
126
|
const json = JSON.stringify({ imports }, null, 2);
|
|
127
|
+
// URL resolution maps the shared dependencies and the editor
|
|
128
|
+
// runtime, so it needs import maps too.
|
|
126
129
|
assertImportMapSupported();
|
|
127
130
|
injectedMap = injectImportMap(json, context.doc, runtimePlan?.mappedRuntime);
|
|
128
131
|
importMapObserver?.(json, context.doc);
|
|
129
132
|
}
|
|
133
|
+
// Recorded before the map settles: a load that starts meanwhile skips
|
|
134
|
+
// these packages, builds on the shared dependency versions this map
|
|
135
|
+
// fixed, and its imports wait for the map in `importModule`.
|
|
130
136
|
for (const [pkg, variant] of runtimePlan?.served ?? []) {
|
|
131
137
|
editorRuntimeVariants.set(pkg, variant);
|
|
132
138
|
}
|
|
@@ -152,7 +158,7 @@ export function createEsmBackend(config) {
|
|
|
152
158
|
reasons.set(tag, {
|
|
153
159
|
kind: "define-failed",
|
|
154
160
|
tag,
|
|
155
|
-
cause:
|
|
161
|
+
cause: errorMessage(err),
|
|
156
162
|
});
|
|
157
163
|
return;
|
|
158
164
|
}
|
|
@@ -189,9 +195,7 @@ export function createEsmBackend(config) {
|
|
|
189
195
|
kind: "module-load-failed",
|
|
190
196
|
tag,
|
|
191
197
|
specifier: fallbackSpecifier,
|
|
192
|
-
cause: fallbackErr
|
|
193
|
-
? fallbackErr.message
|
|
194
|
-
: String(fallbackErr),
|
|
198
|
+
cause: errorMessage(fallbackErr),
|
|
195
199
|
});
|
|
196
200
|
return;
|
|
197
201
|
}
|
|
@@ -201,7 +205,7 @@ export function createEsmBackend(config) {
|
|
|
201
205
|
kind: "module-load-failed",
|
|
202
206
|
tag,
|
|
203
207
|
specifier,
|
|
204
|
-
cause:
|
|
208
|
+
cause: errorMessage(err),
|
|
205
209
|
});
|
|
206
210
|
return;
|
|
207
211
|
}
|
|
@@ -225,6 +229,8 @@ export function createEsmBackend(config) {
|
|
|
225
229
|
return;
|
|
226
230
|
}
|
|
227
231
|
try {
|
|
232
|
+
// `customElements` rejects a constructor already defined under
|
|
233
|
+
// another tag, so each versioned tag gets its own subclass.
|
|
228
234
|
defineCustomElementSafely(actualTag, class extends ElementClass {
|
|
229
235
|
}, `element tag for ${packageName}`);
|
|
230
236
|
}
|
|
@@ -232,35 +238,13 @@ export function createEsmBackend(config) {
|
|
|
232
238
|
reasons.set(tag, {
|
|
233
239
|
kind: "define-failed",
|
|
234
240
|
tag,
|
|
235
|
-
cause:
|
|
241
|
+
cause: errorMessage(err),
|
|
236
242
|
});
|
|
237
243
|
return;
|
|
238
244
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const variantController = servedVariant?.views.controller;
|
|
243
|
-
if (servedVariant && variantController) {
|
|
244
|
-
try {
|
|
245
|
-
const controllerModule = await importModule(cdnProvider.browserViewUrl(packageVersion, variantController), context.doc);
|
|
246
|
-
controller = controllerModule?.default ?? controllerModule;
|
|
247
|
-
}
|
|
248
|
-
catch (err) {
|
|
249
|
-
reportEditorRuntimeFallback(packageVersion, servedVariant.declaration, servedVariant.runtime, `its editor-runtime controller failed to load: ${errorMessage(err)}`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (!controller) {
|
|
253
|
-
const controllerSpecifier = resolveControllerSpecifier(packageName, packageVersion, moduleResolution, cdnProvider);
|
|
254
|
-
try {
|
|
255
|
-
const controllerModule = await importModule(controllerSpecifier, context.doc);
|
|
256
|
-
controller = controllerModule?.default ?? controllerModule;
|
|
257
|
-
}
|
|
258
|
-
catch {
|
|
259
|
-
// Controllers are best-effort; element registration is what the
|
|
260
|
-
// primitive verifies.
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
245
|
+
const controller = loadControllers
|
|
246
|
+
? await importController(packageName, packageVersion, context.doc)
|
|
247
|
+
: null;
|
|
264
248
|
writeRegistryEntry({
|
|
265
249
|
package: packageVersion,
|
|
266
250
|
status: Status.loaded,
|
|
@@ -293,6 +277,35 @@ export function createEsmBackend(config) {
|
|
|
293
277
|
return undefined;
|
|
294
278
|
}
|
|
295
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* The package's controller: its editor-runtime variant's when one is served
|
|
282
|
+
* and loads, else the package's own. Controllers are best-effort; element
|
|
283
|
+
* registration is what the primitive verifies.
|
|
284
|
+
*/
|
|
285
|
+
async function importController(packageName, packageVersion, doc) {
|
|
286
|
+
let controller = null;
|
|
287
|
+
const servedVariant = editorRuntimeVariants.get(packageVersion);
|
|
288
|
+
const variantController = servedVariant?.views.controller;
|
|
289
|
+
if (servedVariant && variantController) {
|
|
290
|
+
try {
|
|
291
|
+
const controllerModule = await importModule(cdnProvider.browserViewUrl(packageVersion, variantController), doc);
|
|
292
|
+
controller = controllerModule?.default ?? controllerModule;
|
|
293
|
+
if (controller)
|
|
294
|
+
return controller;
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
reportEditorRuntimeFallback(packageVersion, servedVariant.declaration, servedVariant.runtime, `its editor-runtime controller failed to load: ${errorMessage(err)}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const controllerSpecifier = resolveControllerSpecifier(packageName, packageVersion, moduleResolution, cdnProvider);
|
|
301
|
+
try {
|
|
302
|
+
const controllerModule = await importModule(controllerSpecifier, doc);
|
|
303
|
+
return controllerModule?.default ?? controllerModule;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return controller;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
296
309
|
/**
|
|
297
310
|
* Imports through the browser, or through es-module-shims once the document
|
|
298
311
|
* has rejected an import map, after every map a backend added has settled.
|
|
@@ -547,7 +560,8 @@ export function mapEsmViewElements(elements, view = "delivery", viewConfig) {
|
|
|
547
560
|
]));
|
|
548
561
|
}
|
|
549
562
|
function defaultImporter(specifier) {
|
|
550
|
-
//
|
|
563
|
+
// Host bundlers must leave this import alone: the specifier is a CDN URL or
|
|
564
|
+
// import-map specifier known only at runtime.
|
|
551
565
|
return import(/* webpackIgnore: true */ /* @vite-ignore */ specifier);
|
|
552
566
|
}
|
|
553
567
|
/**
|
|
@@ -872,9 +886,6 @@ const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
|
872
886
|
/** Slash-separated segments, none starting with a dot. */
|
|
873
887
|
const BROWSER_VIEW_PATH = /^[A-Za-z0-9_-][A-Za-z0-9._-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9._-]*)*$/;
|
|
874
888
|
const BARE_SPECIFIER = /^(?:@[A-Za-z0-9_-][A-Za-z0-9._-]*\/)?[A-Za-z0-9_-][A-Za-z0-9._-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9._-]*)*$/;
|
|
875
|
-
function isRecord(value) {
|
|
876
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
877
|
-
}
|
|
878
889
|
/**
|
|
879
890
|
* A package's `pie.browserEditorRuntime`: `undefined` when it declares none,
|
|
880
891
|
* `null` when the declaration is malformed.
|
|
@@ -883,7 +894,7 @@ function readEditorRuntimeDeclaration(metadata) {
|
|
|
883
894
|
const declared = metadata?.pie?.browserEditorRuntime;
|
|
884
895
|
if (declared === undefined)
|
|
885
896
|
return undefined;
|
|
886
|
-
if (!
|
|
897
|
+
if (!isPlainRecord(declared) || !isPlainRecord(declared.views))
|
|
887
898
|
return null;
|
|
888
899
|
const { name, version } = declared;
|
|
889
900
|
if (typeof name !== "string" || !PACKAGE_NAME.test(name))
|
|
@@ -907,7 +918,7 @@ function readEditorRuntimeDeclaration(metadata) {
|
|
|
907
918
|
*/
|
|
908
919
|
function readBrowserModules(metadata) {
|
|
909
920
|
const declared = metadata.pie?.browserModules;
|
|
910
|
-
if (!
|
|
921
|
+
if (!isPlainRecord(declared))
|
|
911
922
|
return null;
|
|
912
923
|
const modules = Object.entries(declared);
|
|
913
924
|
const valid = modules.length > 0 &&
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* IIFE backend adapter for the ElementLoader primitive.
|
|
3
3
|
*
|
|
4
|
-
* Loads PIE elements from IIFE bundles served by the
|
|
4
|
+
* Loads PIE elements from IIFE bundles served by the bundle host,
|
|
5
5
|
* then registers them with `customElements`. On any per-tag failure
|
|
6
6
|
* (missing package, non-constructor element class, define failure),
|
|
7
7
|
* throws `AdapterFailure` with a structured `reasons` map that the
|
|
@@ -38,7 +38,7 @@ export type IifeBundleRetryStatus = {
|
|
|
38
38
|
};
|
|
39
39
|
export type IifeBackendConfig = {
|
|
40
40
|
kind: "iife";
|
|
41
|
-
/** Base URL
|
|
41
|
+
/** Base URL of the bundle host. */
|
|
42
42
|
bundleHost: string;
|
|
43
43
|
/** Bundle variant to request (player / client-player / editor). */
|
|
44
44
|
bundleType?: BundleType;
|
|
@@ -54,7 +54,7 @@ export type IifeBackendConfig = {
|
|
|
54
54
|
/**
|
|
55
55
|
* Retry policy for transient bundle load failures.
|
|
56
56
|
*
|
|
57
|
-
* When the bundle
|
|
57
|
+
* When the bundle host is building a bundle on demand, the first
|
|
58
58
|
* few `<script src>` requests can fail. This policy polls until the
|
|
59
59
|
* bundle is available or `timeoutMs` elapses. Default matches
|
|
60
60
|
* `DEFAULT_IIFE_BUNDLE_RETRY_CONFIG`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* IIFE backend adapter for the ElementLoader primitive.
|
|
3
3
|
*
|
|
4
|
-
* Loads PIE elements from IIFE bundles served by the
|
|
4
|
+
* Loads PIE elements from IIFE bundles served by the bundle host,
|
|
5
5
|
* then registers them with `customElements`. On any per-tag failure
|
|
6
6
|
* (missing package, non-constructor element class, define failure),
|
|
7
7
|
* throws `AdapterFailure` with a structured `reasons` map that the
|
|
@@ -76,7 +76,7 @@ export function createIifeBackend(config) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Wrap the single-shot script loader with poll-until-ready retry.
|
|
78
78
|
*
|
|
79
|
-
* The
|
|
79
|
+
* The bundle host may return a transient failure while it
|
|
80
80
|
* builds a bundle on demand. Retry with fixed-interval polling until
|
|
81
81
|
* the bundle is ready or the cumulative deadline elapses. Emits
|
|
82
82
|
* retry lifecycle events to the host via `onBundleRetryStatus` and to
|
|
@@ -111,7 +111,7 @@ export function createIifeBackend(config) {
|
|
|
111
111
|
catch (error) {
|
|
112
112
|
const elapsedMs = Date.now() - startedAt;
|
|
113
113
|
const remainingMs = timeoutMs - elapsedMs;
|
|
114
|
-
const reason =
|
|
114
|
+
const reason = errorMessage(error);
|
|
115
115
|
if (remainingMs <= 0) {
|
|
116
116
|
const timeoutError = new Error(`IIFE bundle load timed out after ${timeoutMs}ms: ${url}`);
|
|
117
117
|
config.onBundleRetryStatus?.({
|
|
@@ -201,45 +201,33 @@ export function createIifeBackend(config) {
|
|
|
201
201
|
return;
|
|
202
202
|
const conflict = findIifeElementMapConflict(elements);
|
|
203
203
|
if (conflict) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
cause: conflict,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
throw new AdapterFailure(reasons);
|
|
204
|
+
throw failureForEveryTag(elements, (tag) => ({
|
|
205
|
+
kind: "backend-rejected",
|
|
206
|
+
tag,
|
|
207
|
+
cause: conflict,
|
|
208
|
+
}));
|
|
213
209
|
}
|
|
214
210
|
const bundleUrl = buildBundleUrl(elements, bundleType, config);
|
|
215
211
|
try {
|
|
216
212
|
await ensureBundleLoaded(bundleUrl, context.doc);
|
|
217
213
|
}
|
|
218
214
|
catch (err) {
|
|
219
|
-
const cause =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
cause,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
throw new AdapterFailure(reasons);
|
|
215
|
+
const cause = errorMessage(err);
|
|
216
|
+
throw failureForEveryTag(elements, (tag) => ({
|
|
217
|
+
kind: "bundle-load-failed",
|
|
218
|
+
tag,
|
|
219
|
+
url: bundleUrl,
|
|
220
|
+
cause,
|
|
221
|
+
}));
|
|
230
222
|
}
|
|
231
223
|
const pieModule = readPieModule();
|
|
232
224
|
if (!pieModule) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
cause: "window.pie.default missing after bundle load",
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
throw new AdapterFailure(reasons);
|
|
225
|
+
throw failureForEveryTag(elements, (tag) => ({
|
|
226
|
+
kind: "bundle-load-failed",
|
|
227
|
+
tag,
|
|
228
|
+
url: bundleUrl,
|
|
229
|
+
cause: "window.pie.default missing after bundle load",
|
|
230
|
+
}));
|
|
243
231
|
}
|
|
244
232
|
const reasons = new Map();
|
|
245
233
|
const isEditorBundle = bundleType === BundleType.editor;
|
|
@@ -282,11 +270,13 @@ export function createIifeBackend(config) {
|
|
|
282
270
|
reasons.set(tag, {
|
|
283
271
|
kind: "define-failed",
|
|
284
272
|
tag,
|
|
285
|
-
cause:
|
|
273
|
+
cause: errorMessage(err),
|
|
286
274
|
});
|
|
287
275
|
continue;
|
|
288
276
|
}
|
|
289
277
|
try {
|
|
278
|
+
// `customElements` rejects a constructor already defined under
|
|
279
|
+
// another tag, so each versioned tag gets its own subclass.
|
|
290
280
|
defineCustomElementSafely(actualTag, class extends ElementClass {
|
|
291
281
|
}, `element tag for ${packageName}`);
|
|
292
282
|
}
|
|
@@ -294,7 +284,7 @@ export function createIifeBackend(config) {
|
|
|
294
284
|
reasons.set(tag, {
|
|
295
285
|
kind: "define-failed",
|
|
296
286
|
tag,
|
|
297
|
-
cause:
|
|
287
|
+
cause: errorMessage(err),
|
|
298
288
|
});
|
|
299
289
|
continue;
|
|
300
290
|
}
|
|
@@ -320,6 +310,17 @@ export function createIifeBackend(config) {
|
|
|
320
310
|
};
|
|
321
311
|
}
|
|
322
312
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
313
|
+
/** A failure of the whole request, reported once per tag. */
|
|
314
|
+
function failureForEveryTag(elements, reasonFor) {
|
|
315
|
+
const reasons = new Map();
|
|
316
|
+
for (const tag of Object.keys(elements)) {
|
|
317
|
+
reasons.set(tag, reasonFor(tag));
|
|
318
|
+
}
|
|
319
|
+
return new AdapterFailure(reasons);
|
|
320
|
+
}
|
|
321
|
+
function errorMessage(err) {
|
|
322
|
+
return err instanceof Error ? err.message : String(err);
|
|
323
|
+
}
|
|
323
324
|
function defaultLoadBundleScript(url, doc) {
|
|
324
325
|
return new Promise((resolve, reject) => {
|
|
325
326
|
const script = doc.createElement("script");
|