@pie-players/pie-players-shared 0.3.63 → 0.3.65

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.
@@ -145,7 +145,15 @@ export interface PassageEntity extends VersionEntity, ConfigContainerEntity, Sea
145
145
  }
146
146
  export interface ItemEntity extends VersionEntity, ConfigContainerEntity, SearchMetaDataEntity {
147
147
  name?: string;
148
- passage?: string | PassageEntity;
148
+ /**
149
+ * `null` as well as absent, because importers write it. JSON has no
150
+ * `undefined`, so an item transformed from another format carries an explicit
151
+ * `passage: null` for "no passage" — `isPassageEntity` has always tested for
152
+ * null, so the runtime expected it while the type denied it. Excluding null
153
+ * only meant a host feeding real importer output through a typed path had to
154
+ * cast it away.
155
+ */
156
+ passage?: string | PassageEntity | null;
149
157
  /** QTI/APIP-style accessibility catalogs owned by the item root. */
150
158
  accessibilityCatalogs?: AccessibilityCatalog[];
151
159
  retired?: boolean;
@@ -250,6 +258,14 @@ export interface AssessmentSection extends SearchMetaDataEntity, SettingsMetaDat
250
258
  */
251
259
  assessmentItemRefs?: AssessmentItemRef[];
252
260
  rubricBlocks?: RubricBlock[];
261
+ /**
262
+ * QTI 3.0: Personal Needs Profile (PNP 3.0) for section-scoped delivery.
263
+ *
264
+ * Section players read this (falling back to `settings.personalNeedsProfile`,
265
+ * then to the computed default profile) to drive PNP policy when a section is
266
+ * delivered without an enclosing assessment.
267
+ */
268
+ personalNeedsProfile?: PersonalNeedsProfile;
253
269
  sort?: string;
254
270
  }
255
271
  export interface TestPart {
@@ -265,10 +281,160 @@ export interface ContextDeclaration {
265
281
  cardinality: "single" | "multiple" | "ordered" | "record";
266
282
  defaultValue?: any;
267
283
  }
284
+ export type MediaKind = "image" | "audio" | "video" | "other";
285
+ export interface MediaSource {
286
+ src: string;
287
+ type?: string;
288
+ width?: number;
289
+ height?: number;
290
+ bitrate?: number;
291
+ }
292
+ export interface TextTrackRef {
293
+ src: string;
294
+ kind: "captions" | "subtitles" | "descriptions" | "chapters" | "metadata";
295
+ lang: string;
296
+ label: string;
297
+ default?: boolean;
298
+ }
299
+ export interface TranscriptRef {
300
+ src?: string;
301
+ html?: string;
302
+ plainText?: string;
303
+ lang?: string;
304
+ }
305
+ /**
306
+ * A referenced media asset and its accessible alternates.
307
+ *
308
+ * Deliberately one vocabulary for every media consumer (accessibility catalog
309
+ * cards today, stimulus media later) rather than per-consumer media fields.
310
+ * Which fields are *required* is resolved per consumer instead of by making
311
+ * everything optional at the type level — a type where nothing is required
312
+ * stops catching anything. For a sign-language card, `sources` carries the
313
+ * signing recording, `poster`/`durationSeconds` are not applicable, and
314
+ * `tracks`/`transcript` are meaningless (captions on a signing video would be
315
+ * the English text already on screen).
316
+ */
317
+ export interface MediaAssetRef {
318
+ version: 1;
319
+ id: string;
320
+ kind: MediaKind;
321
+ sources: MediaSource[];
322
+ poster?: string;
323
+ thumbnail?: string;
324
+ durationSeconds?: number;
325
+ tracks?: TextTrackRef[];
326
+ transcript?: TranscriptRef;
327
+ label?: string;
328
+ description?: string;
329
+ lang?: string;
330
+ }
331
+ /**
332
+ * Time slice of a longer recording, so one file can serve several content
333
+ * nodes. Mirrors QTI 3's Media Fragments URI usage, which replaced APIP's
334
+ * separate start/end cue elements.
335
+ */
336
+ export interface MediaFragmentRange {
337
+ startSeconds: number;
338
+ endSeconds?: number;
339
+ }
340
+ /**
341
+ * Payload for a `sign-language` catalog card.
342
+ *
343
+ * A signing video cannot be expressed as a string, so it does not use the
344
+ * card's `content` field at all. This payload carries what QTI 3 expresses
345
+ * inside `qti-card-entry` — multiple sources, MIME types, poster, and an
346
+ * optional time range.
347
+ *
348
+ * Note there is no `kind` discriminant: the card's `catalog` field is QTI's
349
+ * `qti-card@support` and is the only discriminator. Restating it here would
350
+ * create a second source of truth that can disagree with the first.
351
+ */
352
+ export interface SignLanguageCardPayload {
353
+ /**
354
+ * ISO 639-3 sign language code of the *adaptation*, not of the item's base
355
+ * content (AfA/PNP's `languageOfAdaptation` distinction). A Spanish item's
356
+ * signed alternate is LSM, not ASL, so this must never be inferred from the
357
+ * item or assessment content language.
358
+ *
359
+ * Optional, and redundant when it equals the card's `language`. The card's
360
+ * `language` is QTI's `xml:lang` on the card entry and is the only field
361
+ * catalog resolution selects on — it decides *which* card is returned, before
362
+ * anything knows the card is a signing card. This one is read after
363
+ * resolution, to name the language in the region's accessible label and to
364
+ * refuse a card in a sign language the learner did not ask for. Author it only
365
+ * where the two genuinely differ, which is a card tagged with the item's
366
+ * content language (`language: "en-US"`, `signLang: "ase"`) so that resolution
367
+ * reaches it by the default-language rung.
368
+ */
369
+ signLang?: string;
370
+ media: MediaAssetRef;
371
+ fragment?: MediaFragmentRange;
372
+ }
373
+ /**
374
+ * Payload for a `spoken` catalog card that is a recording rather than a script.
375
+ *
376
+ * QTI 3 treats recorded audio and synthesized speech as the *same* support:
377
+ * both are `spoken`, and a card holds recorded audio through `qti-file-href`
378
+ * plus a MIME type. So this is not a new accommodation, it is the other form the
379
+ * existing one can take.
380
+ *
381
+ * A node commonly carries both this and a `content` card in the same language —
382
+ * APIP's pattern, which QTI's migration guidance keeps, because the script is
383
+ * both what the audio was generated from and the fallback for when the audio
384
+ * cannot play. Resolution picks between them with `CatalogLookupOptions.form`.
385
+ *
386
+ * No `kind` discriminant, for the same reason `SignLanguageCardPayload` has
387
+ * none: the card's `catalog` already says what this is.
388
+ */
389
+ export interface SpokenAudioCardPayload {
390
+ media: MediaAssetRef;
391
+ fragment?: MediaFragmentRange;
392
+ }
393
+ /**
394
+ * Structured payloads a catalog card may carry instead of a `content` string.
395
+ *
396
+ * Which member applies is decided by the card's `catalog`, not by a field
397
+ * inside the payload, so this union carries no discriminant. Consumers select a
398
+ * card by catalog type and then validate the payload structurally — which they
399
+ * must do regardless, since catalog data is authored, wire-facing, and
400
+ * untrusted.
401
+ */
402
+ export type CatalogCardPayload = SignLanguageCardPayload | SpokenAudioCardPayload;
403
+ /**
404
+ * One alternate representation of a content node, keyed to it by
405
+ * `data-catalog-idref`.
406
+ *
407
+ * Maps onto QTI 3's `qti-card`: `catalog` is `@support`, `language` is the card
408
+ * entry's `xml:lang`, and QTI's single content slot is represented by exactly
409
+ * one of `content` or `payload`.
410
+ *
411
+ * One generic `payload` slot, not a field per accommodation. This is the shape
412
+ * `pie-elements-ng` (PIE-879) and the `pie-api-aws` Learnosity importer
413
+ * (PIE-881) restate structurally; all three read the same authored JSON and none
414
+ * of them takes a package dependency on the others, so keeping the declarations
415
+ * identical is the only thing holding interop together. It briefly was not:
416
+ * those two producers wrote the signing payload under `signLanguage`, this
417
+ * repo tolerated that as an input alias, and the alias was folded in on the
418
+ * resolution path but not the enumeration path — so an imported card rendered
419
+ * its signing video and simultaneously reported that the item had no signed
420
+ * alternate. Both producers now emit `payload` and the alias is gone.
421
+ */
268
422
  export interface CatalogCard {
269
423
  catalog: string;
270
424
  language?: string;
271
- content: string;
425
+ /**
426
+ * The string form of the card's content, for catalog types a string can
427
+ * express: SSML for `spoken`, plain text for `simplified-language`, and so
428
+ * on. Absent on cards that carry a structured `payload` instead — nothing
429
+ * is projected or mirrored into it, so there is never a second copy of the
430
+ * payload's data to fall out of sync.
431
+ */
432
+ content?: string;
433
+ /**
434
+ * The structured form, for catalog types a string cannot express. Interpreted
435
+ * according to `catalog`.
436
+ */
437
+ payload?: CatalogCardPayload;
272
438
  }
273
439
  export interface AccessibilityCatalog {
274
440
  identifier: string;
@@ -579,7 +745,7 @@ export declare class InsertSoundEvent extends CustomEvent<SoundHandler> {
579
745
  static TYPE: string;
580
746
  constructor(handler: SoundHandler);
581
747
  }
582
- export declare const isPassageEntity: (passage: string | PassageEntity | undefined) => passage is PassageEntity;
748
+ export declare const isPassageEntity: (passage: string | PassageEntity | null | undefined) => passage is PassageEntity;
583
749
  export declare function isPrerelease(version: any): version is SemVer;
584
750
  export declare function formatVersion(semVer: SemVer): string;
585
751
  export interface PieElement extends HTMLElement {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-players-shared",
3
- "version": "0.3.63",
3
+ "version": "0.3.65",
4
4
  "type": "module",
5
5
  "description": "Shared runtime + UI utilities for PIE players",
6
6
  "license": "MIT",
@@ -74,8 +74,8 @@
74
74
  "dist"
75
75
  ],
76
76
  "dependencies": {
77
- "@pie-lib/math-rendering-module": "5.1.0",
78
- "dompurify": "^3.4.0",
77
+ "@pie-lib/math-rendering-module": "5.1.2",
78
+ "dompurify": "^3.4.13",
79
79
  "semver": "^7.8.5"
80
80
  },
81
81
  "scripts": {
@@ -84,14 +84,16 @@
84
84
  "typecheck": "tsc -p tsconfig.json --noEmit",
85
85
  "lint": "biome check .",
86
86
  "check": "tsc -p tsconfig.json --noEmit",
87
- "test": "bun test",
87
+ "test": "bun test '.test.ts'",
88
88
  "check-i18n": "bun run src/i18n/scripts/check-coverage.ts",
89
89
  "scan-hardcoded": "bun run src/i18n/scripts/scan-hardcoded.ts"
90
90
  },
91
91
  "devDependencies": {
92
- "@biomejs/biome": "^2.5.6",
92
+ "@biomejs/biome": "^2.5.7",
93
93
  "@happy-dom/global-registrator": "^20.11.1",
94
- "@types/semver": "^7.7.1",
94
+ "@playwright/test": "^1.62.1",
95
+ "@types/semver": "^7.8.0",
96
+ "esbuild": "^0.28.1",
95
97
  "glob": "^13.0.0",
96
98
  "svelte": "^5.56.8",
97
99
  "typescript": "^5.9.3"