@pie-players/pie-assessment-toolkit 0.3.35 → 0.3.36
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 +72 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -308,6 +308,78 @@ Use `subscribeSectionEvents(...)` when you need advanced/custom filtering mixes.
|
|
|
308
308
|
|
|
309
309
|
To persist or snapshot an inactive section, use `coordinator.getSectionController({ sectionId, attemptId })` — that lookup is by id and is unaffected by the active-cohort behavior described above.
|
|
310
310
|
|
|
311
|
+
#### Migrating from `<0.3.35` (BREAKING — pre-1.0)
|
|
312
|
+
|
|
313
|
+
`0.3.35` is the first release where `subscribeSectionEvents` (and its two helper wrappers `subscribeItemEvents` / `subscribeSectionLifecycleEvents`) follows the toolkit's *active section cohort* automatically. The on-the-wire shape of the subscription args object changed.
|
|
314
|
+
|
|
315
|
+
If your host code looked like this:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const unsub = coordinator.subscribeItemEvents({
|
|
319
|
+
sectionId: 'section-1',
|
|
320
|
+
attemptId: 'attempt-1',
|
|
321
|
+
listener: handleEvent,
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Update it to drop `sectionId` / `attemptId`:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
const unsub = coordinator.subscribeItemEvents({
|
|
329
|
+
listener: handleEvent,
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
What this means in practice for typed integrations:
|
|
334
|
+
|
|
335
|
+
- **TypeScript breaking change.** `SectionEventSubscriptionArgs`, `SectionItemEventSubscriptionArgs`, and `SectionScopedEventSubscriptionArgs` no longer declare `sectionId?` / `attemptId?` properties. Any host that imports these arg types directly and passes those keys will fail to compile after upgrade. **Action required.**
|
|
336
|
+
- **Runtime is tolerant.** The runtime silently ignores extra unknown properties, so an untyped or lightly-typed call site that still passes `sectionId` / `attemptId` continues to work without source changes. The args have **no effect** at runtime — the subscription always follows the active cohort.
|
|
337
|
+
- **New precondition.** `subscribe*` now throws if no active section cohort exists. Subscribe **after** the first `getOrCreateSectionController(...)` resolves. Subscribing on `toolkit-ready` alone is no longer sufficient — though in practice the section player emits `toolkit-ready` *after* its first `getOrCreateSectionController(...)` resolves, so a `toolkit-ready` anchor is safe in section-player hosts.
|
|
338
|
+
- **Cohort migration is automatic.** If your wrapper previously re-subscribed on every navigation to keep listeners alive across sections, that wiring is no longer needed (and should be removed). A single subscribe call after the first controller-resolve is now enough — the listener migrates automatically and is replayed the new cohort's snapshot on every transition.
|
|
339
|
+
- **Watch for double-replay if you re-subscribe on every `toolkit-ready`.** Hosts that detached and re-subscribed on every `toolkit-ready` event (the correct pre-Phase D pattern, since each subscription was pinned to a `sectionId`) will now observe **two snapshot replays per navigation**: one delivered automatically when Phase D migrates the existing listener to the new active cohort, and a second when the manual re-subscribe attaches a fresh listener that replays again. Listener handlers that are not strictly idempotent will fire twice — analytics `pageAction`s, non-Set counters, side-effecting hydration. The fix is a one-line guard (`if (this.controllerUnsubscribe) return;`) so the subscribe runs only on the first `toolkit-ready`.
|
|
340
|
+
- **For intentionally-pinned subscriptions to inactive sections** (e.g. a host UI that wants to keep watching section A while the user views section B), the helper API does not support that pattern by design. Use `coordinator.getSectionController({ sectionId, attemptId })` and subscribe directly on the controller handle (`controller.subscribe?.(...)`) — that binding is pinned to one controller instance and does not migrate.
|
|
341
|
+
|
|
342
|
+
If your local types were hand-rolled structural copies of the public arg types (e.g. an Angular wrapper duplicating the shape rather than importing the package types), the legacy keys will compile but are dead code at runtime — recommend dropping them as part of the upgrade.
|
|
343
|
+
|
|
344
|
+
#### Pre-Phase D vs Phase D wrapper pattern
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// BEFORE (pre-Phase D): rebind for every section change because the
|
|
348
|
+
// subscription was pinned to a sectionId.
|
|
349
|
+
public handleToolkitReady(event: Event): void {
|
|
350
|
+
const coordinator = (event as CustomEvent).detail?.coordinator;
|
|
351
|
+
if (!coordinator) return;
|
|
352
|
+
this.controllerUnsubscribe?.(); // detach prior pin
|
|
353
|
+
const itemUnsub = coordinator.subscribeItemEvents({
|
|
354
|
+
sectionId: this.sectionId,
|
|
355
|
+
listener: handleItemEvent,
|
|
356
|
+
});
|
|
357
|
+
const sectionUnsub = coordinator.subscribeSectionLifecycleEvents({
|
|
358
|
+
sectionId: this.sectionId,
|
|
359
|
+
listener: handleSectionEvent,
|
|
360
|
+
});
|
|
361
|
+
this.controllerUnsubscribe = () => { itemUnsub?.(); sectionUnsub?.(); };
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// AFTER (Phase D): subscribe once; the listener follows the active
|
|
367
|
+
// cohort across all subsequent navigation.
|
|
368
|
+
public handleToolkitReady(event: Event): void {
|
|
369
|
+
const coordinator = (event as CustomEvent).detail?.coordinator;
|
|
370
|
+
if (!coordinator) return;
|
|
371
|
+
this.toolkitCoordinator = coordinator;
|
|
372
|
+
if (this.controllerUnsubscribe) return; // already subscribed; do nothing on re-fire
|
|
373
|
+
const itemUnsub = coordinator.subscribeItemEvents({
|
|
374
|
+
listener: handleItemEvent,
|
|
375
|
+
});
|
|
376
|
+
const sectionUnsub = coordinator.subscribeSectionLifecycleEvents({
|
|
377
|
+
listener: handleSectionEvent,
|
|
378
|
+
});
|
|
379
|
+
this.controllerUnsubscribe = () => { itemUnsub?.(); sectionUnsub?.(); };
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
311
383
|
### Option 2: Create Services Manually (Advanced)
|
|
312
384
|
|
|
313
385
|
```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-players/pie-assessment-toolkit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.36",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "PIE assessment toolkit: composable services + reference implementation for assessment players and tool coordination",
|
|
6
6
|
"license": "MIT",
|
|
@@ -79,15 +79,15 @@
|
|
|
79
79
|
"test": "bun test"
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
|
-
"@pie-players/pie-calculator": "0.3.
|
|
83
|
-
"@pie-players/pie-context": "0.3.
|
|
84
|
-
"@pie-players/pie-players-shared": "0.3.
|
|
85
|
-
"@pie-players/pie-tts": "0.3.
|
|
82
|
+
"@pie-players/pie-calculator": "0.3.36",
|
|
83
|
+
"@pie-players/pie-context": "0.3.36",
|
|
84
|
+
"@pie-players/pie-players-shared": "0.3.36",
|
|
85
|
+
"@pie-players/pie-tts": "0.3.36",
|
|
86
86
|
"svelte": "^5.54.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
|
-
"@pie-players/pie-calculator-desmos": "0.3.
|
|
90
|
-
"@pie-players/tts-client-server": "0.3.
|
|
89
|
+
"@pie-players/pie-calculator-desmos": "0.3.36",
|
|
90
|
+
"@pie-players/tts-client-server": "0.3.36"
|
|
91
91
|
},
|
|
92
92
|
"peerDependenciesMeta": {
|
|
93
93
|
"@pie-players/pie-calculator-desmos": {
|