@pie-players/pie-players-shared 0.3.67 → 0.3.69
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 +59 -0
- package/dist/formative/index.d.ts +16 -0
- package/dist/formative/index.js +5 -0
- package/dist/formative/mastery.d.ts +16 -0
- package/dist/formative/mastery.js +43 -0
- package/dist/formative/outcome.d.ts +19 -0
- package/dist/formative/outcome.js +90 -0
- package/dist/formative/policy.d.ts +37 -0
- package/dist/formative/policy.js +86 -0
- package/dist/formative/session.d.ts +19 -0
- package/dist/formative/session.js +113 -0
- package/dist/formative/state.d.ts +60 -0
- package/dist/formative/state.js +140 -0
- package/dist/formative/types.d.ts +164 -0
- package/dist/formative/types.js +13 -0
- package/dist/i18n/catalogs.d.ts +38 -0
- package/dist/i18n/catalogs.js +47 -0
- package/dist/i18n/index.d.ts +28 -4
- package/dist/i18n/index.js +34 -3
- package/dist/i18n/language-tags.d.ts +51 -0
- package/dist/i18n/language-tags.js +84 -0
- package/dist/i18n/messages/en-US.d.ts +601 -0
- package/dist/i18n/messages/en-US.js +617 -0
- package/dist/i18n/messages/nl-NL.d.ts +516 -0
- package/dist/i18n/messages/nl-NL.js +516 -0
- package/dist/i18n/provider.d.ts +168 -0
- package/dist/i18n/provider.js +472 -0
- package/dist/i18n/types.d.ts +177 -28
- package/dist/i18n/types.js +6 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/pie/custom-element-define.d.ts +24 -0
- package/dist/pie/custom-element-define.js +36 -6
- package/dist/pie/index.d.ts +1 -1
- package/dist/pie/index.js +1 -1
- package/dist/pie/versioned-tag.js +3 -2
- package/dist/security/sanitize-forbidden-lists.d.ts +8 -0
- package/dist/security/sanitize-forbidden-lists.js +41 -0
- package/dist/security/sanitize-item-markup.js +3 -36
- package/dist/security/sanitize-svg-icon.js +3 -25
- package/dist/security/wrap-overwide-images.d.ts +5 -10
- package/dist/security/wrap-overwide-images.js +13 -62
- package/dist/security/wrap-overwide-tables.d.ts +5 -10
- package/dist/security/wrap-overwide-tables.js +13 -62
- package/dist/security/wrap-overwide.d.ts +51 -0
- package/dist/security/wrap-overwide.js +91 -0
- package/dist/server/npm-auth-env.d.ts +13 -0
- package/dist/server/npm-auth-env.js +64 -0
- package/dist/timed-media/index.d.ts +17 -0
- package/dist/timed-media/index.js +16 -0
- package/dist/timed-media/media-element-source.d.ts +36 -0
- package/dist/timed-media/media-element-source.js +121 -0
- package/dist/timed-media/policy.d.ts +36 -0
- package/dist/timed-media/policy.js +297 -0
- package/dist/timed-media/session.d.ts +21 -0
- package/dist/timed-media/session.js +85 -0
- package/dist/timed-media/state.d.ts +66 -0
- package/dist/timed-media/state.js +357 -0
- package/dist/timed-media/types.d.ts +343 -0
- package/dist/timed-media/types.js +12 -0
- package/dist/tools/term-lookup.d.ts +189 -0
- package/dist/tools/term-lookup.js +284 -0
- package/dist/types/index.d.ts +105 -1
- package/dist/ui/attribute-coercion.d.ts +1 -0
- package/dist/ui/attribute-coercion.js +19 -0
- package/dist/ui/first-focusable.d.ts +22 -0
- package/dist/ui/first-focusable.js +47 -0
- package/dist/ui/focus-trap.js +7 -2
- package/dist/ui/pointer-drag.d.ts +30 -0
- package/dist/ui/pointer-drag.js +34 -0
- package/package.json +38 -6
- package/dist/i18n/loader.d.ts +0 -35
- package/dist/i18n/loader.js +0 -132
- package/dist/i18n/simple-i18n.d.ts +0 -68
- package/dist/i18n/simple-i18n.js +0 -198
- package/dist/i18n/translations/ar/common.json +0 -36
- package/dist/i18n/translations/ar/toolkit.json +0 -48
- package/dist/i18n/translations/ar/tools.json +0 -103
- package/dist/i18n/translations/en/common.json +0 -36
- package/dist/i18n/translations/en/toolkit.json +0 -48
- package/dist/i18n/translations/en/tools.json +0 -103
- package/dist/i18n/translations/es/common.json +0 -36
- package/dist/i18n/translations/es/toolkit.json +0 -48
- package/dist/i18n/translations/es/tools.json +0 -103
- package/dist/i18n/translations/zh/common.json +0 -36
- package/dist/i18n/translations/zh/toolkit.json +0 -48
- package/dist/i18n/translations/zh/tools.json +0 -103
package/dist/i18n/types.d.ts
CHANGED
|
@@ -1,57 +1,206 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* I18n Type Definitions
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Defined in players-shared to avoid circular dependencies
|
|
4
|
+
* The provider interface and the key/locale types derived from the English
|
|
5
|
+
* catalog. Defined in `players-shared` to avoid circular dependencies, and kept
|
|
6
|
+
* free of any runtime value so a tool can reach the whole contract through
|
|
7
|
+
* `import type` and compile it away entirely.
|
|
8
|
+
*
|
|
9
|
+
* @module @pie-players/pie-players-shared/i18n/types
|
|
10
|
+
*/
|
|
11
|
+
import type enUS from "./messages/en-US.js";
|
|
12
|
+
/**
|
|
13
|
+
* Locale tags this repository ships a catalog for.
|
|
14
|
+
*
|
|
15
|
+
* Full BCP-47 rather than bare language subtags. Content producers disagree on
|
|
16
|
+
* syntax — the Learnosity transform emits POSIX `nl_NL`, QTI catalog cards carry
|
|
17
|
+
* `xml:lang` — and the resolver normalizes both onto these, so this list is the
|
|
18
|
+
* set of catalogs, not the set of accepted inputs.
|
|
6
19
|
*/
|
|
20
|
+
export type BundledLocaleCode = "en-US" | "nl-NL";
|
|
7
21
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
22
|
+
* Any locale tag. A host may supply a locale we ship no catalog for and provide
|
|
23
|
+
* its own messages through {@link I18nConfig.customMessages}, so this is
|
|
24
|
+
* deliberately open while still autocompleting the bundled set.
|
|
25
|
+
*/
|
|
26
|
+
export type LocaleCode = BundledLocaleCode | (string & {});
|
|
27
|
+
/**
|
|
28
|
+
* Plural translation forms, keyed by CLDR plural category.
|
|
29
|
+
*
|
|
30
|
+
* Only `other` is required. `Intl.PluralRules` selects the category for the
|
|
31
|
+
* active locale, so a locale with six forms can carry six and a locale with two
|
|
32
|
+
* carries two; a selected category the catalog lacks resolves to `other`.
|
|
10
33
|
*/
|
|
11
34
|
export interface PluralTranslation {
|
|
12
35
|
zero?: string;
|
|
13
|
-
one
|
|
36
|
+
one?: string;
|
|
37
|
+
two?: string;
|
|
14
38
|
few?: string;
|
|
15
39
|
many?: string;
|
|
16
40
|
other: string;
|
|
17
41
|
}
|
|
42
|
+
/** A catalog node: a leaf string, a plural group, or a namespace. */
|
|
43
|
+
export type MessageNode = string | PluralTranslation | {
|
|
44
|
+
[key: string]: MessageNode;
|
|
45
|
+
};
|
|
46
|
+
/** A whole catalog, as one locale module default-exports it. */
|
|
47
|
+
export type MessageCatalog = {
|
|
48
|
+
[key: string]: MessageNode;
|
|
49
|
+
};
|
|
50
|
+
type PluralCategoryKey = keyof PluralTranslation;
|
|
18
51
|
/**
|
|
19
|
-
*
|
|
52
|
+
* Dot-notation keys of a catalog shape.
|
|
53
|
+
*
|
|
54
|
+
* A plural group terminates the path at the group itself: `plural()` is called
|
|
55
|
+
* with `tools.textToSpeech.charactersSelected`, never with
|
|
56
|
+
* `…charactersSelected.other`. A group is recognized by every one of its keys
|
|
57
|
+
* being a CLDR plural category — the containment has to run this way round,
|
|
58
|
+
* because a group carrying only `one`/`other` does not contain the whole
|
|
59
|
+
* category union.
|
|
60
|
+
*/
|
|
61
|
+
type NestedKeys<T> = {
|
|
62
|
+
[K in keyof T & string]: T[K] extends string ? K : keyof T[K] extends PluralCategoryKey ? K : T[K] extends object ? K | `${K}.${NestedKeys<T[K]>}` : K;
|
|
63
|
+
}[keyof T & string];
|
|
64
|
+
/**
|
|
65
|
+
* Every key the English catalog defines.
|
|
66
|
+
*
|
|
67
|
+
* Derived from the catalog's shape rather than maintained alongside it, so a
|
|
68
|
+
* mistyped key is a compile error instead of a raw key rendered on screen. This
|
|
69
|
+
* is what makes the coverage script a completeness check rather than the only
|
|
70
|
+
* line of defence.
|
|
71
|
+
*/
|
|
72
|
+
export type MessageKey = NestedKeys<typeof enUS>;
|
|
73
|
+
/**
|
|
74
|
+
* A key assembled at runtime from an id the catalog cannot enumerate — an
|
|
75
|
+
* element category, a host-authored registration's key. Branded, so the only way
|
|
76
|
+
* to produce one is `dynamicMessageKey` and a mistyped literal stays a compile
|
|
77
|
+
* error at every ordinary call site.
|
|
78
|
+
*
|
|
79
|
+
* The brand is a named property rather than a `unique symbol`: a module-local
|
|
80
|
+
* symbol is not identity-compatible across two emitted copies of this
|
|
81
|
+
* declaration, and packages here resolve `players-shared` to `src` or to `dist`
|
|
82
|
+
* depending on build order.
|
|
83
|
+
*/
|
|
84
|
+
export type DynamicMessageKey = string & {
|
|
85
|
+
readonly __pieDynamicMessageKey: true;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* A key argument: a catalog key, or an explicitly-asserted computed one. The
|
|
89
|
+
* assertion lives in `provider.ts` as `dynamicMessageKey`, so this module stays
|
|
90
|
+
* type-only.
|
|
91
|
+
*/
|
|
92
|
+
export type MessageKeyInput = MessageKey | DynamicMessageKey;
|
|
93
|
+
/** Interpolation values substituted into `{placeholder}` slots. */
|
|
94
|
+
export type InterpolationValues = Record<string, string | number | boolean | null | undefined>;
|
|
95
|
+
/** Options for {@link I18nProvider.plural}; `count` also interpolates. */
|
|
96
|
+
export interface PluralOptions extends InterpolationValues {
|
|
97
|
+
count: number;
|
|
98
|
+
}
|
|
99
|
+
/** Writing direction of a locale. */
|
|
100
|
+
export type TextDirection = "ltr" | "rtl";
|
|
101
|
+
/**
|
|
102
|
+
* Translation bundle structure.
|
|
103
|
+
*
|
|
104
|
+
* Retained as the resolved, in-memory form of one locale's catalog.
|
|
20
105
|
*/
|
|
21
106
|
export interface TranslationBundle {
|
|
22
107
|
locale: string;
|
|
23
|
-
translations:
|
|
24
|
-
direction:
|
|
108
|
+
translations: MessageCatalog;
|
|
109
|
+
direction: TextDirection;
|
|
25
110
|
}
|
|
26
111
|
/**
|
|
27
|
-
*
|
|
112
|
+
* Provider configuration.
|
|
28
113
|
*/
|
|
29
114
|
export interface I18nConfig {
|
|
30
|
-
/** Initial locale
|
|
31
|
-
locale?:
|
|
32
|
-
/**
|
|
33
|
-
fallbackLocale?:
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
115
|
+
/** Initial locale. Defaults to `en-US`; never auto-detected. */
|
|
116
|
+
locale?: LocaleCode;
|
|
117
|
+
/** Locale every missing key falls back to. Defaults to `en-US`. */
|
|
118
|
+
fallbackLocale?: LocaleCode;
|
|
119
|
+
/**
|
|
120
|
+
* Loader for a locale this repository ships. Omitted, only the statically
|
|
121
|
+
* bundled English catalog and any `customMessages` are available — which is
|
|
122
|
+
* exactly what a tool bundle wants, so no locale chunk is emitted into it.
|
|
123
|
+
*/
|
|
124
|
+
loadCatalog?: (locale: string) => Promise<MessageCatalog>;
|
|
125
|
+
/** Locale tags {@link loadCatalog} can serve. */
|
|
126
|
+
availableLocales?: readonly string[];
|
|
127
|
+
/**
|
|
128
|
+
* Host-supplied messages, keyed by locale tag. Consulted ahead of the
|
|
129
|
+
* bundled catalog for the same locale, so a deployment can override one
|
|
130
|
+
* label without forking a catalog, and can supply a locale we do not ship.
|
|
131
|
+
*/
|
|
132
|
+
customMessages?: Record<string, MessageCatalog>;
|
|
133
|
+
/** Called once per missing key per lookup. */
|
|
39
134
|
onMissingKey?: (key: string, locale: string) => void;
|
|
40
|
-
/**
|
|
135
|
+
/** Reserved for provider-level diagnostics. */
|
|
41
136
|
debug?: boolean;
|
|
42
137
|
}
|
|
43
138
|
/**
|
|
44
|
-
*
|
|
139
|
+
* The contract components resolve against.
|
|
140
|
+
*
|
|
141
|
+
* Deliberately small and free of Svelte, DOM and framework types: a component
|
|
142
|
+
* declares `i18n?: I18nProvider`, and a host is free to implement it over
|
|
143
|
+
* i18next, ICU MessageFormat or its own catalog rather than take ours.
|
|
45
144
|
*/
|
|
46
|
-
export interface
|
|
47
|
-
|
|
48
|
-
t(key: string, params?: Record<string, any>): string;
|
|
49
|
-
tn(key: string, count: number, params?: Record<string, any>): string;
|
|
145
|
+
export interface I18nProvider {
|
|
146
|
+
/** Active locale tag, canonicalized to the resolved catalog's tag. */
|
|
50
147
|
getLocale(): string;
|
|
51
|
-
|
|
52
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Switch locale, loading its catalog if a loader is configured.
|
|
150
|
+
*
|
|
151
|
+
* Subscribers are notified once the catalog is in place, so a component's
|
|
152
|
+
* reactive read re-runs against the new locale rather than the old one.
|
|
153
|
+
*/
|
|
154
|
+
setLocale(locale: LocaleCode): Promise<void> | void;
|
|
155
|
+
/** Translate a key, interpolating `{placeholder}` slots. */
|
|
156
|
+
t(key: MessageKeyInput, values?: InterpolationValues): string;
|
|
157
|
+
/** Translate a plural group, selecting the category for `options.count`. */
|
|
158
|
+
plural?(key: MessageKeyInput, options: PluralOptions): string;
|
|
159
|
+
/**
|
|
160
|
+
* Writing direction of the active locale.
|
|
161
|
+
*
|
|
162
|
+
* Components stamp this onto their own host's `dir`. `direction` is an
|
|
163
|
+
* inherited CSS property, so one attribute on the host crosses a shadow
|
|
164
|
+
* boundary into the component's content with no per-node wiring.
|
|
165
|
+
*/
|
|
166
|
+
getDirection?(): TextDirection;
|
|
167
|
+
/**
|
|
168
|
+
* A view of this provider fixed to another locale.
|
|
169
|
+
*
|
|
170
|
+
* Two players on one page can render different interface locales without either
|
|
171
|
+
* mutating the other's active locale. A provider that cannot produce a view
|
|
172
|
+
* omits this, and callers fall back to the provider itself.
|
|
173
|
+
*/
|
|
174
|
+
withLocale?(locale: LocaleCode): I18nProvider;
|
|
175
|
+
/** Observe locale and catalog changes. Returns an unsubscribe function. */
|
|
176
|
+
subscribe?(listener: () => void): () => void;
|
|
177
|
+
/** Whether a key resolves in the active locale or its fallback chain. */
|
|
178
|
+
hasKey?(key: MessageKeyInput): boolean;
|
|
179
|
+
/** Locale tags this provider can serve. */
|
|
180
|
+
getAvailableLocales?(): string[];
|
|
181
|
+
/** Whether a locale's catalog is already resident. */
|
|
182
|
+
isLocaleLoaded?(locale: LocaleCode): boolean;
|
|
183
|
+
/** Locale-aware number formatting. */
|
|
184
|
+
formatNumber?(value: number, options?: Intl.NumberFormatOptions): string;
|
|
185
|
+
/** Locale-aware date formatting. */
|
|
186
|
+
formatDate?(date: Date, options?: Intl.DateTimeFormatOptions): string;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The full service surface of the bundled provider.
|
|
190
|
+
*
|
|
191
|
+
* `I18nProvider` is what components depend on; this is what the provider and
|
|
192
|
+
* the toolkit's `I18nService` wrapper implement, so the optional members above
|
|
193
|
+
* are all present here.
|
|
194
|
+
*/
|
|
195
|
+
export interface I18nServiceApi extends I18nProvider {
|
|
196
|
+
initialize(config: I18nConfig): Promise<void>;
|
|
197
|
+
plural(key: MessageKeyInput, options: PluralOptions): string;
|
|
198
|
+
getDirection(): TextDirection;
|
|
53
199
|
getAvailableLocales(): string[];
|
|
54
|
-
isLocaleLoaded(locale:
|
|
200
|
+
isLocaleLoaded(locale: LocaleCode): boolean;
|
|
55
201
|
subscribe(listener: () => void): () => void;
|
|
56
|
-
hasKey(key:
|
|
202
|
+
hasKey(key: MessageKeyInput): boolean;
|
|
203
|
+
addCustomMessages(locale: string, messages: MessageCatalog): void;
|
|
204
|
+
withLocale(locale: LocaleCode): I18nProvider;
|
|
57
205
|
}
|
|
206
|
+
export {};
|
package/dist/i18n/types.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* I18n Type Definitions
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Defined in players-shared to avoid circular dependencies
|
|
4
|
+
* The provider interface and the key/locale types derived from the English
|
|
5
|
+
* catalog. Defined in `players-shared` to avoid circular dependencies, and kept
|
|
6
|
+
* free of any runtime value so a tool can reach the whole contract through
|
|
7
|
+
* `import type` and compile it away entirely.
|
|
8
|
+
*
|
|
9
|
+
* @module @pie-players/pie-players-shared/i18n/types
|
|
6
10
|
*/
|
|
7
11
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,11 @@ export * from "./security/index.js";
|
|
|
6
6
|
export * from "./loaders/index.js";
|
|
7
7
|
export * from "./object/index.js";
|
|
8
8
|
export * from "./pie/index.js";
|
|
9
|
-
export type { AssessmentEntity, AssessmentItemRef, AssessmentSection, AdvancedItemConfig, ConfigEntity, ConfigResource, Env, ItemConfig, ItemEntity, ItemSession, OutcomeResponse, PassageEntity, PieController, PieContent, PieDefaultModel, PieItemElement, PieModel, QuestionEntity, RubricBlock, TestPart, } from "./types/index.js";
|
|
9
|
+
export type { AssessmentEntity, AssessmentItemRef, AssessmentSection, AdvancedItemConfig, ConfigEntity, ConfigResource, Env, FormativeDeliveryPolicy, FormativeItemPolicy, TimedMediaSectionData, ItemConfig, ItemEntity, ItemSession, OutcomeResponse, PassageEntity, PieController, PieContent, PieDefaultModel, PieItemElement, PieModel, QuestionEntity, RubricBlock, TestPart, } from "./types/index.js";
|
|
10
10
|
export { editorPostFix } from "./types/index.js";
|
|
11
|
+
export * from "./ui/attribute-coercion.js";
|
|
11
12
|
export * from "./ui/content-styles.js";
|
|
13
|
+
export * from "./ui/pointer-drag.js";
|
|
12
14
|
export * from "./ui/focus-trap.js";
|
|
13
15
|
export * from "./ui/first-focusable.js";
|
|
14
16
|
export * from "./ui/debug-panel-persistence.js";
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,9 @@ export * from "./object/index.js";
|
|
|
8
8
|
// Barrel export for PIE runtime utilities
|
|
9
9
|
export * from "./pie/index.js";
|
|
10
10
|
export { editorPostFix } from "./types/index.js";
|
|
11
|
+
export * from "./ui/attribute-coercion.js";
|
|
11
12
|
export * from "./ui/content-styles.js";
|
|
13
|
+
export * from "./ui/pointer-drag.js";
|
|
12
14
|
export * from "./ui/focus-trap.js";
|
|
13
15
|
export * from "./ui/first-focusable.js";
|
|
14
16
|
export * from "./ui/debug-panel-persistence.js";
|
|
@@ -3,4 +3,28 @@ export type SafeDefineResult = {
|
|
|
3
3
|
status: SafeDefineStatus;
|
|
4
4
|
tagName: string;
|
|
5
5
|
};
|
|
6
|
+
export type SafeDefineOutcome = "defined" | "already-defined" | "defined-wrapped" | "error" | "wrapped-error";
|
|
7
|
+
export interface SafeDefineAttempt {
|
|
8
|
+
outcome: SafeDefineOutcome;
|
|
9
|
+
tagName: string;
|
|
10
|
+
error?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface AttemptCustomElementDefineOptions {
|
|
13
|
+
/**
|
|
14
|
+
* The browser rejects registering a constructor that's already
|
|
15
|
+
* registered under a different tag name with the same
|
|
16
|
+
* `NotSupportedError` used for a duplicate *tag* registration. When set,
|
|
17
|
+
* a collision that isn't actually a same-tag duplicate (`customElements.get`
|
|
18
|
+
* still doesn't return the tag after the failed define) is retried once
|
|
19
|
+
* with a distinct wrapper subclass so this tag name still ends up defined.
|
|
20
|
+
*/
|
|
21
|
+
allowWrappedFallback?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Attempt to define a custom element, classifying the outcome instead of
|
|
25
|
+
* throwing, so callers with different error-surfacing conventions (throw vs.
|
|
26
|
+
* record-and-continue) can build their own public behavior on one shared
|
|
27
|
+
* duplicate-define/collision core.
|
|
28
|
+
*/
|
|
29
|
+
export declare const attemptCustomElementDefine: (tagName: string, elementConstructor: CustomElementConstructor, context?: string, options?: AttemptCustomElementDefineOptions) => SafeDefineAttempt;
|
|
6
30
|
export declare const defineCustomElementSafely: (tagName: string, elementConstructor: CustomElementConstructor, context?: string) => SafeDefineResult;
|
|
@@ -9,22 +9,52 @@ const isDuplicateDefineError = (error) => {
|
|
|
9
9
|
: "";
|
|
10
10
|
return maybeName === "NotSupportedError";
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Attempt to define a custom element, classifying the outcome instead of
|
|
14
|
+
* throwing, so callers with different error-surfacing conventions (throw vs.
|
|
15
|
+
* record-and-continue) can build their own public behavior on one shared
|
|
16
|
+
* duplicate-define/collision core.
|
|
17
|
+
*/
|
|
18
|
+
export const attemptCustomElementDefine = (tagName, elementConstructor, context = "custom element tag", options = {}) => {
|
|
13
19
|
const validTagName = validateCustomElementTag(tagName, context);
|
|
14
20
|
if (customElements.get(validTagName)) {
|
|
15
|
-
return {
|
|
21
|
+
return { outcome: "already-defined", tagName: validTagName };
|
|
16
22
|
}
|
|
17
23
|
try {
|
|
18
24
|
customElements.define(validTagName, elementConstructor);
|
|
19
|
-
return {
|
|
25
|
+
return { outcome: "defined", tagName: validTagName };
|
|
20
26
|
}
|
|
21
27
|
catch (error) {
|
|
22
28
|
if (!isDuplicateDefineError(error)) {
|
|
23
|
-
|
|
29
|
+
return { outcome: "error", tagName: validTagName, error };
|
|
24
30
|
}
|
|
25
31
|
if (customElements.get(validTagName)) {
|
|
26
|
-
return {
|
|
32
|
+
return { outcome: "already-defined", tagName: validTagName };
|
|
33
|
+
}
|
|
34
|
+
if (!options.allowWrappedFallback) {
|
|
35
|
+
return { outcome: "error", tagName: validTagName, error };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
customElements.define(validTagName, class extends elementConstructor {
|
|
39
|
+
});
|
|
40
|
+
return { outcome: "defined-wrapped", tagName: validTagName };
|
|
41
|
+
}
|
|
42
|
+
catch (wrappedError) {
|
|
43
|
+
return {
|
|
44
|
+
outcome: "wrapped-error",
|
|
45
|
+
tagName: validTagName,
|
|
46
|
+
error: wrappedError,
|
|
47
|
+
};
|
|
27
48
|
}
|
|
28
|
-
throw error;
|
|
29
49
|
}
|
|
30
50
|
};
|
|
51
|
+
export const defineCustomElementSafely = (tagName, elementConstructor, context = "custom element tag") => {
|
|
52
|
+
const attempt = attemptCustomElementDefine(tagName, elementConstructor, context);
|
|
53
|
+
if (attempt.outcome === "error" || attempt.outcome === "wrapped-error") {
|
|
54
|
+
throw attempt.error;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
status: attempt.outcome === "already-defined" ? "already-defined" : "defined",
|
|
58
|
+
tagName: attempt.tagName,
|
|
59
|
+
};
|
|
60
|
+
};
|
package/dist/pie/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export { pieRegistry } from "./registry.js";
|
|
|
36
36
|
export { findPieController, scorePieItem } from "./scoring.js";
|
|
37
37
|
export type { Entry, EventListeners, EventListenersMap, LoadPieElementsOptions, PieElement, PieRegistry, UpdatePieElementOptions, } from "./types.js";
|
|
38
38
|
export { BundleType, defaultPieElementOptions, isCustomElementConstructor, isPieAvailable, isPieRegistryAvailable, Status, } from "./types.js";
|
|
39
|
-
export { defineCustomElementSafely, type SafeDefineResult, type SafeDefineStatus, } from "./custom-element-define.js";
|
|
39
|
+
export { attemptCustomElementDefine, defineCustomElementSafely, type AttemptCustomElementDefineOptions, type SafeDefineAttempt, type SafeDefineOutcome, type SafeDefineResult, type SafeDefineStatus, } from "./custom-element-define.js";
|
|
40
40
|
export { toPrintHashedTag, toViewTag, validateCustomElementTag, VIEW_TAG_SUFFIX, } from "./tag-names.js";
|
|
41
41
|
export type { PieViewMode } from "./tag-names.js";
|
|
42
42
|
export { updatePieElement, updatePieElements, updatePieElementWithRef, } from "./updates.js";
|
package/dist/pie/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export { BundleType, defaultPieElementOptions, isCustomElementConstructor,
|
|
|
37
37
|
// Type guards (functions)
|
|
38
38
|
isPieAvailable, isPieRegistryAvailable, Status, } from "./types.js";
|
|
39
39
|
// Tag naming helpers
|
|
40
|
-
export { defineCustomElementSafely, } from "./custom-element-define.js";
|
|
40
|
+
export { attemptCustomElementDefine, defineCustomElementSafely, } from "./custom-element-define.js";
|
|
41
41
|
export { toPrintHashedTag, toViewTag, validateCustomElementTag, VIEW_TAG_SUFFIX, } from "./tag-names.js";
|
|
42
42
|
// Updates
|
|
43
43
|
export { updatePieElement, updatePieElements, updatePieElementWithRef, } from "./updates.js";
|
|
@@ -27,8 +27,9 @@ export function parseVersionedTagName(tagName) {
|
|
|
27
27
|
export function toPackageVersionedTag(tagName, packageSpec, options = {}) {
|
|
28
28
|
const { version } = parsePackageName(packageSpec);
|
|
29
29
|
if (!version.trim() && options.preserveUnversionedTag) {
|
|
30
|
-
// aggregateElements historically kept
|
|
31
|
-
// authored tag
|
|
30
|
+
// pie-item contract compatibility: aggregateElements historically kept
|
|
31
|
+
// unversioned package specs on their authored tag; already-authored item
|
|
32
|
+
// content with no version pinned must keep rendering under that tag.
|
|
32
33
|
return tagName;
|
|
33
34
|
}
|
|
34
35
|
const targetEncodedVersion = encodeVersionForTag(version);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared DOMPurify forbid-lists for every in-repo sanitizer (item/passage
|
|
3
|
+
* markup, SVG icons). Keeping one canonical list means a newly-identified
|
|
4
|
+
* dangerous tag or attribute only needs to be added once to cover every
|
|
5
|
+
* sanitizer instead of drifting between hand-maintained copies.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SANITIZER_FORBIDDEN_TAGS: string[];
|
|
8
|
+
export declare const SANITIZER_FORBIDDEN_ATTRS: string[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared DOMPurify forbid-lists for every in-repo sanitizer (item/passage
|
|
3
|
+
* markup, SVG icons). Keeping one canonical list means a newly-identified
|
|
4
|
+
* dangerous tag or attribute only needs to be added once to cover every
|
|
5
|
+
* sanitizer instead of drifting between hand-maintained copies.
|
|
6
|
+
*/
|
|
7
|
+
export const SANITIZER_FORBIDDEN_TAGS = [
|
|
8
|
+
"script",
|
|
9
|
+
"iframe",
|
|
10
|
+
"object",
|
|
11
|
+
"embed",
|
|
12
|
+
"base",
|
|
13
|
+
"form",
|
|
14
|
+
"meta",
|
|
15
|
+
"link",
|
|
16
|
+
// <foreignObject> inside an <svg> is a well-known escape hatch back into
|
|
17
|
+
// HTML context.
|
|
18
|
+
"foreignobject",
|
|
19
|
+
];
|
|
20
|
+
// DOMPurify already strips `on*` handlers via its default block-list; these
|
|
21
|
+
// entries guarantee they stay stripped even if a consumer tweaks defaults,
|
|
22
|
+
// and they cover the common SVG / math sinks.
|
|
23
|
+
export const SANITIZER_FORBIDDEN_ATTRS = [
|
|
24
|
+
"onerror",
|
|
25
|
+
"onload",
|
|
26
|
+
"onclick",
|
|
27
|
+
"onmouseover",
|
|
28
|
+
"onmouseout",
|
|
29
|
+
"onmouseenter",
|
|
30
|
+
"onmouseleave",
|
|
31
|
+
"onfocus",
|
|
32
|
+
"onblur",
|
|
33
|
+
"onkeydown",
|
|
34
|
+
"onkeyup",
|
|
35
|
+
"onkeypress",
|
|
36
|
+
"onsubmit",
|
|
37
|
+
"onchange",
|
|
38
|
+
"onbeforeunload",
|
|
39
|
+
"formaction",
|
|
40
|
+
"xlink:href",
|
|
41
|
+
];
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* allow-list.
|
|
9
9
|
*/
|
|
10
10
|
import DOMPurify from "dompurify";
|
|
11
|
+
import { SANITIZER_FORBIDDEN_ATTRS, SANITIZER_FORBIDDEN_TAGS, } from "./sanitize-forbidden-lists.js";
|
|
11
12
|
import { wrapOverwideImages } from "./wrap-overwide-images.js";
|
|
12
13
|
import { wrapOverwideTables } from "./wrap-overwide-tables.js";
|
|
13
14
|
// Attributes every PIE element / wrapper is allowed to carry.
|
|
@@ -28,42 +29,8 @@ const BASE_ALLOWED_ATTRS = [
|
|
|
28
29
|
"dir",
|
|
29
30
|
];
|
|
30
31
|
const BASE_URI_SAFE_ATTRS = ["pie-id"];
|
|
31
|
-
const FORBIDDEN_TAGS =
|
|
32
|
-
|
|
33
|
-
"iframe",
|
|
34
|
-
"object",
|
|
35
|
-
"embed",
|
|
36
|
-
"base",
|
|
37
|
-
"form",
|
|
38
|
-
"meta",
|
|
39
|
-
"link",
|
|
40
|
-
// <foreignObject> inside an <svg> is a well-known escape hatch back
|
|
41
|
-
// into HTML context; match the SVG-icon sanitizer and forbid it here
|
|
42
|
-
// so both sanitizers agree on the surface.
|
|
43
|
-
"foreignobject",
|
|
44
|
-
];
|
|
45
|
-
// DOMPurify already strips `on*` handlers via its default block-list;
|
|
46
|
-
// these entries guarantee they stay stripped even if a consumer tweaks
|
|
47
|
-
// defaults, and they cover the common SVG / math sinks.
|
|
48
|
-
const FORBIDDEN_ATTRS = [
|
|
49
|
-
"onerror",
|
|
50
|
-
"onload",
|
|
51
|
-
"onclick",
|
|
52
|
-
"onmouseover",
|
|
53
|
-
"onmouseout",
|
|
54
|
-
"onmouseenter",
|
|
55
|
-
"onmouseleave",
|
|
56
|
-
"onfocus",
|
|
57
|
-
"onblur",
|
|
58
|
-
"onkeydown",
|
|
59
|
-
"onkeyup",
|
|
60
|
-
"onkeypress",
|
|
61
|
-
"onsubmit",
|
|
62
|
-
"onchange",
|
|
63
|
-
"onbeforeunload",
|
|
64
|
-
"formaction",
|
|
65
|
-
"xlink:href",
|
|
66
|
-
];
|
|
32
|
+
const FORBIDDEN_TAGS = SANITIZER_FORBIDDEN_TAGS;
|
|
33
|
+
const FORBIDDEN_ATTRS = SANITIZER_FORBIDDEN_ATTRS;
|
|
67
34
|
// Any tag that looks like a custom element (contains a hyphen) is permitted
|
|
68
35
|
// provided it starts with `pie-` or is explicitly named in
|
|
69
36
|
// `allowedCustomElements`. This intentionally keeps third-party unknown
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* or the input is not a string.
|
|
11
11
|
*/
|
|
12
12
|
import DOMPurify from "dompurify";
|
|
13
|
+
import { SANITIZER_FORBIDDEN_ATTRS, SANITIZER_FORBIDDEN_TAGS, } from "./sanitize-forbidden-lists.js";
|
|
13
14
|
let svgPurifierInstance = null;
|
|
14
15
|
function resolveSvgPurifier() {
|
|
15
16
|
if (svgPurifierInstance)
|
|
@@ -23,31 +24,8 @@ function resolveSvgPurifier() {
|
|
|
23
24
|
: DOMPurify;
|
|
24
25
|
return svgPurifierInstance;
|
|
25
26
|
}
|
|
26
|
-
const FORBIDDEN_TAGS =
|
|
27
|
-
|
|
28
|
-
"foreignobject",
|
|
29
|
-
"iframe",
|
|
30
|
-
"object",
|
|
31
|
-
"embed",
|
|
32
|
-
"base",
|
|
33
|
-
"form",
|
|
34
|
-
];
|
|
35
|
-
const FORBIDDEN_ATTRS = [
|
|
36
|
-
"onerror",
|
|
37
|
-
"onload",
|
|
38
|
-
"onclick",
|
|
39
|
-
"onmouseover",
|
|
40
|
-
"onmouseout",
|
|
41
|
-
"onmouseenter",
|
|
42
|
-
"onmouseleave",
|
|
43
|
-
"onfocus",
|
|
44
|
-
"onblur",
|
|
45
|
-
"onkeydown",
|
|
46
|
-
"onkeyup",
|
|
47
|
-
"onkeypress",
|
|
48
|
-
"formaction",
|
|
49
|
-
"xlink:href",
|
|
50
|
-
];
|
|
27
|
+
const FORBIDDEN_TAGS = SANITIZER_FORBIDDEN_TAGS;
|
|
28
|
+
const FORBIDDEN_ATTRS = SANITIZER_FORBIDDEN_ATTRS;
|
|
51
29
|
const stringCache = new Map();
|
|
52
30
|
const STRING_CACHE_MAX = 64;
|
|
53
31
|
export function sanitizeSvgIcon(icon) {
|
|
@@ -22,17 +22,12 @@
|
|
|
22
22
|
* PIE element paints into its own light DOM (e.g. a `pie-passage`'s
|
|
23
23
|
* model-driven content) get the same scrollable affordance even though
|
|
24
24
|
* they never appeared in the authored markup string.
|
|
25
|
+
*
|
|
26
|
+
* The wrapping itself lives in `./wrap-overwide.js`, shared with the table
|
|
27
|
+
* wrapper: only the four values below and the accessible name differ.
|
|
25
28
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* When `true`, images whose nearest `pie-*` ancestor is *strictly between*
|
|
29
|
-
* the image and `root` are left alone. Used by the string pipeline so the
|
|
30
|
-
* authored-markup pass doesn't restructure a PIE element's own template.
|
|
31
|
-
* Defaults to `false` so the live-DOM pass *does* wrap element-rendered
|
|
32
|
-
* images.
|
|
33
|
-
*/
|
|
34
|
-
skipPieDescendants?: boolean;
|
|
35
|
-
}
|
|
29
|
+
import { type WrapOverwideOptions } from "./wrap-overwide.js";
|
|
30
|
+
export type WrapOverwideImagesInElementOptions = WrapOverwideOptions;
|
|
36
31
|
/**
|
|
37
32
|
* Wrap every unwrapped `<img>` descendant of `root` with the shared
|
|
38
33
|
* horizontal-scroll span. Returns the number of newly-wrapped images so
|