@pie-players/pie-item-player
v0.3.50
Published
Unified custom element for rendering PIE items with iife/esm/preloaded strategies
Readme
@pie-players/pie-item-player
Unified custom element for rendering PIE assessment items. A single <pie-item-player> tag handles delivery, evaluation, and authoring through a mode attribute, and supports multiple loading strategies (iife, esm, preloaded).
This package replaces the legacy @pie-framework/pie-player-components project, which required separate <pie-player> and <pie-author> elements. See migration guide for details.
Install
npm install @pie-players/pie-item-player
# or
bun add @pie-players/pie-item-playerOr load from a CDN:
<script src="https://cdn.jsdelivr.net/npm/@pie-players/pie-item-player/dist/pie-item-player.js"></script>The script self-registers the <pie-item-player> custom element.
Runtime boundary and migration
- Browser-only package:
@pie-players/pie-item-playeris a DOM custom-element runtime package and is not intended for plain Node runtime imports. - Node-import-safe packages (for server/runtime utilities) are documented in
docs/setup/library-packaging-strategy.md. - Migration direction:
- Legacy migration from
@pie-framework/pie-player-componentsremains documented. - For current hosts, prefer the stable default entrypoint:
- Legacy migration from
import "@pie-players/pie-item-player";Use explicit component subpath exports only when you need targeted registration control (for example the session debugger element export).
Standalone browser variants for this package are intentionally deferred; current
support targets default bundler entrypoints under dist.
Quick start
<pie-item-player
strategy="iife"
config='{"elements":{"my-el":"[email protected]"},"models":[{"id":"1","element":"my-el"}],"markup":"<my-el id=\"1\"></my-el>"}'
env='{"mode":"gather","role":"student"}'
session='{"id":"s1","data":[]}'
></pie-item-player>Custom elements
pie-item-player- Export:
@pie-players/pie-item-player - Description: main player element
- Export:
pie-item-player-session-debugger- Export:
@pie-players/pie-item-player/components/item-session-debugger-element - Description: floating debug panel showing live session and filtered model data
- Export:
Attributes
config:Object, defaultnull. Item config withelements,models, andmarkupfields.session:Object, default{ id: "", data: [] }. Session container.env:Object, default{ mode: "gather", role: "student" }. Environment mode and role.strategy:String, default"iife". Loading strategy:"iife","esm", or"preloaded".mode:String, default"view". Player mode:"view"or"author".authoring-backend:String, default"demo"."demo"uses built-in stubs;"required"requires host-provided handlers.hosted:Boolean, defaultfalse. Whether running in hosted mode; affects IIFE bundle type.add-correct-response:Boolean, defaultfalse. Populate correct responses on models.show-bottom-border:Boolean, defaultfalse. Add bottom border in evaluate mode.debug:String, default"". Truthy values enable verbose logs;"false","0", and""disable them. Also readswindow.PIE_DEBUG.custom-class-name:String, default"". CSS scope class applied to the player container.container-class:String, default"". Extra class on the inner item container.external-style-urls:String, default"". Comma-separated CSS URLs loaded and scoped to the player. URLs must behttp:orhttps:.allowed-style-origins:String, default"". Optional comma-separated origin allow-list. When set,external-style-urlsanditemConfig.resources.stylesheets[*].urlare rejected if their origin is not on the list.loader-config:Object, default package config. Loader instrumentation config.configuration:Object, default{}. Authoring configuration settings. Useconfiguration.authoringfor authoring-only settings.trust-markup:Boolean, defaultfalse. Skip the built-in markup sanitizer. See Content trust boundary.
Properties (JS only)
These are set via JavaScript, not HTML attributes.
loaderOptions:{ bundleHost?: string, esmCdnUrl?: string, esmCdnProvider?: string | object, moduleResolution?: "url" | "import-map", view?: string, loadControllers?: boolean, runtimeSupportCheck?: "off" | "on" }. Strategy-specific loader options. For ESM, the default provider is jsDelivr (https://cdn.jsdelivr.net/npm); useesmCdnProvider: "esm.sh"withesmCdnUrl: "https://esm.sh"for esm.sh, or pass a provider object when package artifacts and shared dependencies use custom routes.sanitizeMarkup:(markup: string) => string. Replace the built-in DOMPurify sanitizer with a host-supplied function. Ignored whentrust-markupis set.backend: JS-only backend integration namespace. Usebackend.deliveryfor API-backed item/session load, autosave, explicit save, and server scoring. Existing player inputs such asenv,strategy,loaderOptions,config, andsessionremain top-level player properties.
Methods
provideScore(): Promise<false | Array<Record<string, unknown> | undefined>>returns one result slot per scored model for legacy-compatible local browser scoring.updateElementModel(update): Promise<void>applies a legacy-compatible preview update for a single loaded PIE model.validateModels(): Promise<AuthoringValidationResult>runs authoring-mode validation for rendered configure elements and returns{ hasErrors, validatedModels }.loadFromBackend(scope?: "delivery" | "authoring"): Promise<void>loads configured backend data into the existing player config/session pipeline.saveSession(): Promise<void>persists the current session throughbackend.delivery.score(options?): Promise<unknown>performs server-backed scoring throughbackend.delivery. This is intentionally separate from localprovideScore().
Events
load-complete: emitted when PIE elements finish loading.session-changed:{ session, ... }. Emitted when the student interacts and session data changes.player-error:{ code?, message?, stage?, strategy?, mode? }. Error event, for exampleAUTHORING_BACKEND_CONFIG_ERRORorITEM_PLAYER_LOAD_ERROR.model-updated: emitted when a PIE element model is updated.model-loaded:{ models, configuration }. Authoring lifecycle event emitted once per renderer initialization after configure elements receive models and configuration.backend-load-complete: emitted afterbackend.deliveryloads config/session data.backend-session-saved: emitted aftersaveSession()or delivery autosave persists successfully.backend-score-complete: emitted after server-backedscore()completes.backend-error: emitted when backend load/save/score fails.
Backend delivery
Backend support is a JS-only namespace for networking and persistence. It does
not duplicate existing delivery inputs such as env, strategy,
loaderOptions, bundleEndpoints, or styling props.
const el = document.querySelector("pie-item-player");
el.env = { mode: "gather", role: "student" };
el.strategy = "iife";
el.loaderOptions = { bundleHost: "https://proxy.pie-api.com/bundles/" };
el.backend = {
delivery: {
enabled: true,
provider: "pie-api",
itemId: "item-1",
sessionId: "session-1",
autosave: { enabled: true, debounceMs: 250 },
endpoints: {
load: "/api/player/load",
saveSession: "/api/player/save",
model: "/api/player/model",
score: "/api/player/score",
},
},
};For a runnable local backend demo, see
docs/item-player/backend-support.md.
PIE Element Packaging Contract
The canonical producer-side contract for @pie-element/* packages lives in the
pie-elements-ng package contract.
<pie-item-player strategy="esm"> assumes that contract is satisfied:
- Element packages publish static browser ESM files such as
dist/browser/delivery/index.js,dist/browser/author/index.js, anddist/browser/controller/index.js. The player imports those files directly; it does not transform element package entrypoints through CDN+esmroutes. - React-backed browser ESM packages declare exact shared browser singleton
versions in
package.jsonunderpie.browserSharedDependencies; dependency and peer-dependency ranges are not used as fallback runtime contracts. ./runtime-supportmetadata is optional for ESM-capable packages unless they need to disable a runtime strategy or view. SetloaderOptions.runtimeSupportCheck = "on"when you want the player to read those hints before loading.strategy="preloaded"is not a separate package shape. It means the host has already registered the versioned custom element tag before the player renders.
Authoring configuration
When mode="author", <pie-item-player> loads editor/config elements and
passes each configure element a resolved configuration object.
Delivery/shared settings stay at the top level of configuration and may be
keyed by package spec, package name, or element tag. Authoring-only settings
belong under configuration.authoring so they do not affect delivery mode.
Authoring keys resolve by specificity:
- Full versioned PIE tag, for example
multiple-choice--version-1-2-3 - Package spec, for example
@pie-element/[email protected] - Package name, for example
@pie-element/multiple-choice - Package base name, for example
multiple-choice
Top-level delivery/shared settings are merged first, then matching
configuration.authoring settings override them for authoring only.
const el = document.querySelector("pie-item-player");
el.mode = "author";
el.configuration = {
"@pie-element/[email protected]": {
sharedSetting: true,
},
authoring: {
"multiple-choice--version-1-2-3": {
authoringOnlySetting: true,
},
},
};Authoring media hooks
When mode="author", the player supports image and sound upload/delete through four handler properties:
onInsertImage(handler: ImageHandler): voidonDeleteImage(src: string, done: (err?: Error) => void): voidonInsertSound(handler: SoundHandler): voidonDeleteSound(src: string, done: (err?: Error) => void): void
With authoring-backend="demo", built-in demo handlers are used. Set authoring-backend="required" to enforce that the host provides all four handlers; missing handlers will block the authoring UI and emit a player-error.
const el = document.querySelector("pie-item-player");
el.mode = "author";
el.authoringBackend = "required";
el.onInsertImage = (handler) => {
handler.done(undefined, "https://example.com/uploaded-image.png");
};
el.onDeleteImage = (_src, done) => done();
el.onInsertSound = (handler) => {
handler.done(undefined, "https://example.com/uploaded-sound.mp3");
};
el.onDeleteSound = (_src, done) => done();Exported types
import type {
PieItemPlayerElement,
PieItemSessionDebuggerElement,
AuthoringBackendMode,
ImageHandler,
SoundHandler,
DeleteDone,
AuthoringValidationResult,
} from "@pie-players/pie-item-player";Content trust boundary
<pie-item-player> injects the markup field from the supplied config
(and from passageConfig.markup when a passage is attached) into the DOM
via Svelte's {@html} directive. To avoid XSS when hosts embed
attacker-influenced item/passage JSON (preview surfaces, multi-tenant
authoring, etc.) the player now ships with a default-on markup
sanitizer powered by DOMPurify. The
sanitizer:
- Strips
<script>,<iframe>,<object>,<embed>,<base>,<form>,<meta>,<link>, and any event-handler attributes (onerror,onload, ...). - Rejects unknown URL protocols (
javascript:,data:unless explicitly marked safe). - Preserves the PIE custom-element contract: any tag matching
pie-*and the attribute familiesdata-*,aria-*,pie-*,model-*,session-*,config-*,context-*pass through unchanged. Third-party custom elements must be registered via thesanitizeMarkupproperty.
Opt out (trusted content)
Hosts that already validate item markup (for example, content-authoring pipelines that only ever render markup produced by their own trusted servers) can disable sanitization:
<pie-item-player trust-markup config='...' session='...'></pie-item-player>el.trustMarkup = true;Provide a custom sanitizer
To extend the allow-list (or use a stricter sanitizer) set
sanitizeMarkup on the element:
import { sanitizeItemMarkup } from "@pie-players/pie-players-shared/security";
el.sanitizeMarkup = (markup: string) =>
sanitizeItemMarkup(markup, {
allowedCustomElements: ["my-custom-widget"],
});When trust-markup is set, sanitizeMarkup is ignored.
