@gentomiyano/optimized-web-audio-player 0.1.0 → 0.2.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 +189 -159
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/consumer/index.d.ts +1 -1
- package/dist/consumer/index.js.map +1 -1
- package/dist/contracts/index.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/media-delivery/index.d.ts +1 -1
- package/dist/media-delivery/index.js.map +1 -1
- package/dist/{media-element-C9dis4aG.d.ts → media-element-DyMO-PhK.d.ts} +1 -1
- package/dist/{player-DE9Vqztu.d.ts → player-BxNFzRq4.d.ts} +53 -1
- package/dist/ports/index.d.ts +2 -2
- package/dist/react/index.d.ts +22 -3
- package/dist/react/index.js +721 -12
- package/dist/react/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/ui/index.d.ts +18 -2
- package/dist/ui/index.js +148 -55
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/player-tokens.css +572 -572
- package/dist/ui/player-views.css +713 -654
- package/dist/ui/styles.css +2 -2
- package/package.json +14 -3
- package/dist/consumer-smoke/node_modules/.modules.yaml +0 -30
- package/dist/consumer-smoke/node_modules/.package-map.json +0 -1
- package/dist/consumer-smoke/node_modules/.pnpm/lock.yaml +0 -57
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/LICENSE +0 -21
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/README.md +0 -9
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_mock.development.js +0 -414
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_mock.production.js +0 -406
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_post_task.development.js +0 -150
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_post_task.production.js +0 -140
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.development.js +0 -364
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.native.development.js +0 -350
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.native.production.js +0 -330
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.production.js +0 -340
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.js +0 -7
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.native.js +0 -7
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/package.json +0 -27
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/unstable_mock.js +0 -7
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/unstable_post_task.js +0 -7
- package/dist/consumer-smoke/node_modules/.pnpm-workspace-state-v1.json +0 -29
package/README.md
CHANGED
|
@@ -1,159 +1,189 @@
|
|
|
1
|
-
# Optimized Web Audio Player
|
|
2
|
-
|
|
3
|
-
Consumer-neutral Music Player package published as
|
|
4
|
-
`@gentomiyano/optimized-web-audio-player`.
|
|
5
|
-
|
|
6
|
-
## MVP status
|
|
7
|
-
|
|
8
|
-
The standalone Player MVP was accepted on 2026-08-01. The accepted scope
|
|
9
|
-
includes the framework-independent Core, reusable React Runtime and UI,
|
|
10
|
-
Compact / Wide / Detailed / Podcast / Mini views, Queue, Debug customization,
|
|
11
|
-
local-media Showcase tooling, Media Session integration, responsive mobile
|
|
12
|
-
review, and the media-delivery boundary contract.
|
|
13
|
-
|
|
14
|
-
The tested iOS device continued playback in the background, during sleep, and
|
|
15
|
-
on the Lock Screen. Lock Screen Previous / Next and Seek worked, and starting
|
|
16
|
-
audio in another application interrupted the Player as expected. iOS uses the
|
|
17
|
-
continuity-first native HTML media path by default; fade timing is not
|
|
18
|
-
perceptible there and is an accepted MVP platform limitation. Narrow Compact
|
|
19
|
-
and Mini layouts, including Global Mini, omit their in-player volume control
|
|
20
|
-
because iOS owns device output volume.
|
|
21
|
-
|
|
22
|
-
This acceptance does not connect a consumer application or production media
|
|
23
|
-
origin, and it is not a claim of identical behavior across every iOS version,
|
|
24
|
-
browser, Bluetooth route, or device.
|
|
25
|
-
|
|
26
|
-
## WP-07 consumer integration foundation
|
|
27
|
-
|
|
28
|
-
The production React Runtime, UI, CSS, and consumer queue builder now have
|
|
29
|
-
explicit package entry points. A consumer can mount one empty
|
|
30
|
-
`PlayerProvider` in its application shell, map authorized public media through
|
|
31
|
-
its own Host Adapter, and build collection, selection, or page-order queues
|
|
32
|
-
without copying Player source.
|
|
33
|
-
|
|
34
|
-
Official Website and Timeline are not changed in this phase. Protected-URL
|
|
35
|
-
refresh and the first consumer connection remain explicit checkpoints. See
|
|
36
|
-
`docs/WP-07-CONSUMER-INTEGRATION-FOUNDATION.md`.
|
|
37
|
-
|
|
38
|
-
The public npm registry, scoped package name, and release workflow are fixed.
|
|
39
|
-
Initial npm publication still requires the scope owner's authentication.
|
|
40
|
-
Release and consumer pinning rules are documented in `docs/RELEASING.md`.
|
|
41
|
-
|
|
42
|
-
## WP-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`
|
|
68
|
-
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1
|
+
# Optimized Web Audio Player
|
|
2
|
+
|
|
3
|
+
Consumer-neutral Music Player package published as
|
|
4
|
+
`@gentomiyano/optimized-web-audio-player`.
|
|
5
|
+
|
|
6
|
+
## MVP status
|
|
7
|
+
|
|
8
|
+
The standalone Player MVP was accepted on 2026-08-01. The accepted scope
|
|
9
|
+
includes the framework-independent Core, reusable React Runtime and UI,
|
|
10
|
+
Compact / Wide / Detailed / Podcast / Mini views, Queue, Debug customization,
|
|
11
|
+
local-media Showcase tooling, Media Session integration, responsive mobile
|
|
12
|
+
review, and the media-delivery boundary contract.
|
|
13
|
+
|
|
14
|
+
The tested iOS device continued playback in the background, during sleep, and
|
|
15
|
+
on the Lock Screen. Lock Screen Previous / Next and Seek worked, and starting
|
|
16
|
+
audio in another application interrupted the Player as expected. iOS uses the
|
|
17
|
+
continuity-first native HTML media path by default; fade timing is not
|
|
18
|
+
perceptible there and is an accepted MVP platform limitation. Narrow Compact
|
|
19
|
+
and Mini layouts, including Global Mini, omit their in-player volume control
|
|
20
|
+
because iOS owns device output volume.
|
|
21
|
+
|
|
22
|
+
This acceptance does not connect a consumer application or production media
|
|
23
|
+
origin, and it is not a claim of identical behavior across every iOS version,
|
|
24
|
+
browser, Bluetooth route, or device.
|
|
25
|
+
|
|
26
|
+
## WP-07 consumer integration foundation
|
|
27
|
+
|
|
28
|
+
The production React Runtime, UI, CSS, and consumer queue builder now have
|
|
29
|
+
explicit package entry points. A consumer can mount one empty
|
|
30
|
+
`PlayerProvider` in its application shell, map authorized public media through
|
|
31
|
+
its own Host Adapter, and build collection, selection, or page-order queues
|
|
32
|
+
without copying Player source.
|
|
33
|
+
|
|
34
|
+
Official Website and Timeline are not changed in this phase. Protected-URL
|
|
35
|
+
refresh and the first consumer connection remain explicit checkpoints. See
|
|
36
|
+
`docs/WP-07-CONSUMER-INTEGRATION-FOUNDATION.md`.
|
|
37
|
+
|
|
38
|
+
The public npm registry, scoped package name, and release workflow are fixed.
|
|
39
|
+
Initial npm publication still requires the scope owner's authentication.
|
|
40
|
+
Release and consumer pinning rules are documented in `docs/RELEASING.md`.
|
|
41
|
+
|
|
42
|
+
## WP-04.3 React package boundary
|
|
43
|
+
|
|
44
|
+
Version 0.2.1 keeps React Context consumers in the `/react` entrypoint and
|
|
45
|
+
exports props-only views from `/ui`. Import `PlayerProvider`, `usePlayer`,
|
|
46
|
+
`useBoundPlayer`, and `BoundCompactPlayer` from `/react`; import
|
|
47
|
+
`BoundCompactPlayerView`, `MiniPlayer`, and the other pure views from `/ui`.
|
|
48
|
+
|
|
49
|
+
The release check packs the built package, installs that tarball into an empty
|
|
50
|
+
consumer, renders a bound inline Player with a shared Mini Player, and verifies
|
|
51
|
+
one Store, one active item, and one audio element. See
|
|
52
|
+
`docs/WP-04.3-REACT-BOUNDARY.md`.
|
|
53
|
+
|
|
54
|
+
## WP-04.2 playback contexts and bound inline players
|
|
55
|
+
|
|
56
|
+
Version 0.2.0 adds consumer-neutral `manual-history` and `ordered-queue`
|
|
57
|
+
playback contexts. Contexts change only through an explicit
|
|
58
|
+
`activateContext` command; rerendering or navigating while the application
|
|
59
|
+
shell retains `PlayerProvider` does not replace the active context.
|
|
60
|
+
|
|
61
|
+
`manual-history` records only explicitly played occurrences, never advances
|
|
62
|
+
on natural end, and supports Previous / Next only inside the recorded session
|
|
63
|
+
history. `ordered-queue` keeps normal queue navigation, natural advancement,
|
|
64
|
+
shuffle, and repeat behavior. Queue updates preserve playback when the same
|
|
65
|
+
`queueItemId` and source descriptor remain selected.
|
|
66
|
+
|
|
67
|
+
Consumers can use `useBoundPlayer` or `BoundCompactPlayer` to bind an inline
|
|
68
|
+
control to one queue occurrence without creating another audio element or
|
|
69
|
+
store. See `docs/WP-04.2-PLAYBACK-CONTEXT.md` for contracts and integration
|
|
70
|
+
examples.
|
|
71
|
+
|
|
72
|
+
## WP-06 debug local media import
|
|
73
|
+
|
|
74
|
+
The standalone Showcase has a dev-only Local Media panel for dropping WAV,
|
|
75
|
+
MP3, JPG/JPEG, PNG, and WebP files. Multi-file audio drops become a temporary
|
|
76
|
+
Queue in drop order. Metadata overrides, artwork switching, track-number
|
|
77
|
+
sorting, duplicate policy, re-read, reorder, delete, clear, and cancellable
|
|
78
|
+
waveform analysis remain in tab memory only.
|
|
79
|
+
|
|
80
|
+
No local file, metadata, filename, or object URL is sent, logged, persisted, or
|
|
81
|
+
written back. Reloading clears the import. Audio and artwork object URLs are
|
|
82
|
+
reference-counted, reused for duplicate identities, and revoked after the last
|
|
83
|
+
item is removed, on clear, or when the Showcase unmounts.
|
|
84
|
+
|
|
85
|
+
The parser and UI live only under `showcase/local-media/**`. They are not
|
|
86
|
+
package exports or `tsup` entries. `pnpm build` scans every production
|
|
87
|
+
JavaScript output for WP-06 markers and fails if the debug importer leaks into
|
|
88
|
+
the package. See `docs/WP-06-DEBUG-MEDIA-IMPORT.md` for format coverage,
|
|
89
|
+
limitations, and the manual checkpoint.
|
|
90
|
+
|
|
91
|
+
## WP-05 background playback checkpoint
|
|
92
|
+
|
|
93
|
+
The standalone Showcase now integrates Media Session metadata, playback and
|
|
94
|
+
position state, and OS action handlers. Music exposes play, pause, stop,
|
|
95
|
+
Previous, Next, and seek-to. Podcast additionally exposes configurable skip
|
|
96
|
+
backward and forward actions. Every handler delegates to the existing
|
|
97
|
+
`PlayerCommands`; initial queue load remains non-autoplay.
|
|
98
|
+
|
|
99
|
+
`audioMode=auto` keeps Web Audio fades on non-iOS browsers and uses the native
|
|
100
|
+
HTMLAudioElement path on iPhone/iPadOS, prioritizing background continuity over
|
|
101
|
+
fades. Real-device comparison modes are:
|
|
102
|
+
|
|
103
|
+
- `http://127.0.0.1:4274/?audioMode=web-audio`
|
|
104
|
+
- `http://127.0.0.1:4274/?audioMode=html-media`
|
|
105
|
+
|
|
106
|
+
Double-click `start-player-showcase-device.cmd` for same-LAN iPhone testing,
|
|
107
|
+
then replace the host with this computer's private LAN IP. The original
|
|
108
|
+
`start-player-showcase.cmd` remains the loopback-only desktop launcher.
|
|
109
|
+
`start-player-showcase-mobile.cmd` is an equivalent mobile-review launcher.
|
|
110
|
+
The Showcase also has a `Mobile preview` toggle; `?preview=mobile` opens the
|
|
111
|
+
same constrained review shell directly on desktop.
|
|
112
|
+
|
|
113
|
+
Automated verification and the accepted device observations are recorded in
|
|
114
|
+
`docs/WP-05A-BACKGROUND-PLAYBACK-FOUNDATION.md`. Broader device/version,
|
|
115
|
+
Bluetooth, and production-consumer claims still require their own acceptance
|
|
116
|
+
record.
|
|
117
|
+
|
|
118
|
+
## WP-03 runtime showcase
|
|
119
|
+
|
|
120
|
+
Double-click `start-player-showcase.cmd` to open the standalone Showcase at
|
|
121
|
+
`http://127.0.0.1:4274/`. The runtime keeps one Core store, one browser audio
|
|
122
|
+
element, and one lazily-created Web Audio gain graph above the Compact, Wide,
|
|
123
|
+
Detailed, Podcast, and Mini view switcher. Queue Panel and the fixed Global
|
|
124
|
+
Mini also consume that same state. Switching views therefore does not stop
|
|
125
|
+
playback or create another audio element.
|
|
126
|
+
|
|
127
|
+
The Showcase uses generated music and podcast WAV tone fixtures. They are local,
|
|
128
|
+
copyright-free integration media only and are not production assets. The
|
|
129
|
+
runtime connects UI exclusively through `PlayerState` and `PlayerCommands`;
|
|
130
|
+
the waveform remains visual-only and seeking is owned by the Seek Bar.
|
|
131
|
+
|
|
132
|
+
Manual UX check:
|
|
133
|
+
|
|
134
|
+
1. Start playback and confirm that Audio Context changes from `uninitialized`
|
|
135
|
+
to `running` only after the first user action.
|
|
136
|
+
2. Check Play/Pause, Seek, Volume, Previous, and Next in Wide view. On desktop,
|
|
137
|
+
open the compact vertical Volume control from its icon in Compact and Mini;
|
|
138
|
+
narrow mobile layouts intentionally omit it.
|
|
139
|
+
3. While playing, switch between Compact, Wide, Detailed, Podcast, and Mini and
|
|
140
|
+
confirm that the same track, time, and playback state continue.
|
|
141
|
+
4. Open Queue Panel, select another item, and check the Podcast queue with
|
|
142
|
+
skip-back/forward controls.
|
|
143
|
+
5. Toggle Global Mini and confirm that the page bottom inset is reserved only
|
|
144
|
+
while it is visible.
|
|
145
|
+
6. In browser developer tools, confirm that
|
|
146
|
+
`document.querySelectorAll('audio[data-player-audio="true"]').length` is
|
|
147
|
+
always `1`.
|
|
148
|
+
|
|
149
|
+
## WP-01 entry points
|
|
150
|
+
|
|
151
|
+
- `@gentomiyano/optimized-web-audio-player/contracts`: immutable state, commands, lifecycle,
|
|
152
|
+
host adapter, config, preset, and debug-controller contracts
|
|
153
|
+
- `@gentomiyano/optimized-web-audio-player/ports`: small media, gain, and persistence ports
|
|
154
|
+
- `@gentomiyano/optimized-web-audio-player/config`: preview defaults, strict validation,
|
|
155
|
+
versioned Config JSON schema v2, and initial state
|
|
156
|
+
- `@gentomiyano/optimized-web-audio-player/media-delivery`: WP-04 URL-free delivery
|
|
157
|
+
descriptors, ephemeral playback-lease and Host Adapter contracts, upload
|
|
158
|
+
evidence validation, and waveform validation
|
|
159
|
+
- `@gentomiyano/optimized-web-audio-player/consumer`: ordered consumer-record to Core Queue
|
|
160
|
+
conversion for already-authorized directly playable media
|
|
161
|
+
- `@gentomiyano/optimized-web-audio-player/react`: the single-audio React Runtime and hook
|
|
162
|
+
- `@gentomiyano/optimized-web-audio-player/ui`: reusable Player views and primitives
|
|
163
|
+
- `@gentomiyano/optimized-web-audio-player/styles.css`: production UI tokens and styles
|
|
164
|
+
- `@gentomiyano/optimized-web-audio-player/testing`: privacy-safe fixtures and mock scenarios
|
|
165
|
+
|
|
166
|
+
The root entry point exports production contracts, ports, config, and the
|
|
167
|
+
provider-neutral media-delivery contract only. Runtime/UI are opt-in subpaths,
|
|
168
|
+
and testing helpers are isolated behind `/testing`. No debug Runtime, Showcase,
|
|
169
|
+
local importer, consumer-specific adapter, or production media URL is included
|
|
170
|
+
in the package root.
|
|
171
|
+
|
|
172
|
+
## Commands
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
pnpm lint
|
|
176
|
+
pnpm test
|
|
177
|
+
pnpm typecheck
|
|
178
|
+
pnpm build
|
|
179
|
+
pnpm showcase:build
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Parallel ownership after the WP-01 checkpoint
|
|
183
|
+
|
|
184
|
+
- Core: `src/core/**`, `tests/core/**`
|
|
185
|
+
- WP-02B UI ownership explicitly includes `src/ui/**`, `src/icons/**`,
|
|
186
|
+
`src/debug/ui/**`, and `tests/ui/**`.
|
|
187
|
+
- UI may also render its mock views inside the pre-created `showcase/**` shell.
|
|
188
|
+
- Integration-owned files, including contracts, ports, package configuration,
|
|
189
|
+
adapters, and testing fixtures, must not be changed by Core or UI work.
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p as PlayerExperienceConfig, y as PlayerState } from '../player-BxNFzRq4.js';
|
|
2
2
|
import { PlayerExperienceConfigValidationResult } from '../contracts/index.js';
|
|
3
3
|
|
|
4
4
|
declare const PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION: 2;
|
package/dist/config/index.js
CHANGED
|
@@ -264,6 +264,7 @@ function createInitialPlayerState(experienceConfig) {
|
|
|
264
264
|
shuffleEnabled: resolved.value.initialShuffleEnabled,
|
|
265
265
|
repeatMode: resolved.value.initialRepeatMode,
|
|
266
266
|
queue: Object.freeze([]),
|
|
267
|
+
activeContext: null,
|
|
267
268
|
currentQueueIndex: null,
|
|
268
269
|
canSeek: false,
|
|
269
270
|
canGoPrevious: false,
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/config/defaults.ts","../../src/config/validation.ts","../../src/config/initial-state.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { PlayerExperienceConfig } from \"../contracts/player\";\n\nexport const PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION = 2 as const;\n\nexport const PLAYER_EXPERIENCE_CONFIG_LIMITS = Object.freeze({\n fadeMs: Object.freeze({ min: 0, max: 10_000 }),\n previousRestartThresholdSec: Object.freeze({ min: 0, max: 60 }),\n waveformOpacity: Object.freeze({ min: 0, max: 1 }),\n initialVolume: Object.freeze({ min: 0, max: 1 }),\n podcastSkipSec: Object.freeze({ minExclusive: 0, max: 3_600 }),\n});\n\nexport const PREVIEW_PLAYER_EXPERIENCE_CONFIG: Readonly<PlayerExperienceConfig> =\n Object.freeze({\n playFadeInMs: 300,\n pauseFadeOutMs: 600,\n trackChangeFadeOutMs: 300,\n trackChangeFadeInMs: 300,\n seekFadeMs: 120,\n fadeCurve: \"ease-out\",\n previousRestartThresholdSec: 3,\n seekCommitMode: \"on-release\",\n waveformPlayedStateEnabled: true,\n waveformOpacity: 0.72,\n initialShuffleEnabled: false,\n initialRepeatMode: \"off\",\n initialVolume: 0.8,\n persistMode: \"none\",\n podcastBackSec: 15,\n podcastForwardSec: 30,\n podcastPreviousMode: \"previous-episode\",\n });\n","import type {\n FadeCurve,\n PersistMode,\n PlayerExperienceConfig,\n PodcastPreviousMode,\n RepeatMode,\n SeekCommitMode,\n} from \"../contracts/player\";\nimport type { PlayerExperienceConfigValidationResult } from \"../contracts/config\";\nimport {\n PLAYER_EXPERIENCE_CONFIG_LIMITS,\n PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION,\n PREVIEW_PLAYER_EXPERIENCE_CONFIG,\n} from \"./defaults\";\n\nconst CONFIG_KEYS = [\n \"playFadeInMs\",\n \"pauseFadeOutMs\",\n \"trackChangeFadeOutMs\",\n \"trackChangeFadeInMs\",\n \"seekFadeMs\",\n \"fadeCurve\",\n \"previousRestartThresholdSec\",\n \"seekCommitMode\",\n \"waveformPlayedStateEnabled\",\n \"waveformOpacity\",\n \"initialShuffleEnabled\",\n \"initialRepeatMode\",\n \"initialVolume\",\n \"persistMode\",\n \"podcastBackSec\",\n \"podcastForwardSec\",\n \"podcastPreviousMode\",\n] as const satisfies readonly (keyof PlayerExperienceConfig)[];\n\nconst FADE_CURVES = new Set<FadeCurve>([\n \"linear\",\n \"ease-in\",\n \"ease-out\",\n \"equal-power\",\n]);\nconst SEEK_COMMIT_MODES = new Set<SeekCommitMode>([\"live\", \"on-release\"]);\nconst REPEAT_MODES = new Set<RepeatMode>([\"off\", \"all\", \"one\"]);\nconst PERSIST_MODES = new Set<PersistMode>([\"none\", \"session\", \"local\"]);\nconst PODCAST_PREVIOUS_MODES = new Set<PodcastPreviousMode>([\n \"previous-episode\",\n \"restart-episode\",\n]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction validateFiniteRange(\n value: unknown,\n path: string,\n min: number,\n max: number,\n errors: string[],\n): value is number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < min ||\n value > max\n ) {\n errors.push(\n `${path} must be a finite number between ${String(min)} and ${String(max)}.`,\n );\n return false;\n }\n return true;\n}\n\nfunction validateBoolean(\n value: unknown,\n path: string,\n errors: string[],\n): value is boolean {\n if (typeof value !== \"boolean\") {\n errors.push(`${path} must be a boolean.`);\n return false;\n }\n return true;\n}\n\nfunction validateConfigRecord(\n value: unknown,\n options: { readonly partial: boolean },\n): PlayerExperienceConfigValidationResult {\n if (!isRecord(value)) {\n return { ok: false, errors: [\"config must be an object.\"] };\n }\n\n const errors: string[] = [];\n const allowedKeys = new Set<string>(CONFIG_KEYS);\n\n for (const key of Object.keys(value)) {\n if (!allowedKeys.has(key)) {\n errors.push(`config.${key} is not supported.`);\n }\n }\n\n if (!options.partial) {\n for (const key of CONFIG_KEYS) {\n if (!Object.hasOwn(value, key)) {\n errors.push(`config.${key} is required.`);\n }\n }\n }\n\n const has = (key: keyof PlayerExperienceConfig): boolean =>\n Object.hasOwn(value, key);\n const fadeLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.fadeMs;\n\n for (const key of [\n \"playFadeInMs\",\n \"pauseFadeOutMs\",\n \"trackChangeFadeOutMs\",\n \"trackChangeFadeInMs\",\n \"seekFadeMs\",\n ] as const) {\n if (has(key)) {\n validateFiniteRange(value[key], `config.${key}`, fadeLimits.min, fadeLimits.max, errors);\n }\n }\n\n const thresholdLimits =\n PLAYER_EXPERIENCE_CONFIG_LIMITS.previousRestartThresholdSec;\n if (has(\"previousRestartThresholdSec\")) {\n validateFiniteRange(\n value.previousRestartThresholdSec,\n \"config.previousRestartThresholdSec\",\n thresholdLimits.min,\n thresholdLimits.max,\n errors,\n );\n }\n\n const volumeLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.initialVolume;\n if (has(\"initialVolume\")) {\n validateFiniteRange(\n value.initialVolume,\n \"config.initialVolume\",\n volumeLimits.min,\n volumeLimits.max,\n errors,\n );\n }\n\n const waveformOpacityLimits =\n PLAYER_EXPERIENCE_CONFIG_LIMITS.waveformOpacity;\n if (has(\"waveformOpacity\")) {\n validateFiniteRange(\n value.waveformOpacity,\n \"config.waveformOpacity\",\n waveformOpacityLimits.min,\n waveformOpacityLimits.max,\n errors,\n );\n }\n\n for (const key of [\n \"waveformPlayedStateEnabled\",\n \"initialShuffleEnabled\",\n ] as const) {\n if (has(key)) {\n validateBoolean(value[key], `config.${key}`, errors);\n }\n }\n\n const skipLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.podcastSkipSec;\n for (const key of [\"podcastBackSec\", \"podcastForwardSec\"] as const) {\n if (has(key)) {\n const candidate = value[key];\n const valid = validateFiniteRange(\n candidate,\n `config.${key}`,\n skipLimits.minExclusive,\n skipLimits.max,\n errors,\n );\n if (valid && candidate <= skipLimits.minExclusive) {\n errors.push(`config.${key} must be greater than 0.`);\n }\n }\n }\n\n if (has(\"fadeCurve\") && !FADE_CURVES.has(value.fadeCurve as FadeCurve)) {\n errors.push(\"config.fadeCurve is not supported.\");\n }\n if (\n has(\"seekCommitMode\") &&\n !SEEK_COMMIT_MODES.has(value.seekCommitMode as SeekCommitMode)\n ) {\n errors.push(\"config.seekCommitMode is not supported.\");\n }\n if (\n has(\"initialRepeatMode\") &&\n !REPEAT_MODES.has(value.initialRepeatMode as RepeatMode)\n ) {\n errors.push(\"config.initialRepeatMode is not supported.\");\n }\n if (has(\"persistMode\") && !PERSIST_MODES.has(value.persistMode as PersistMode)) {\n errors.push(\"config.persistMode is not supported.\");\n }\n if (\n has(\"podcastPreviousMode\") &&\n !PODCAST_PREVIOUS_MODES.has(\n value.podcastPreviousMode as PodcastPreviousMode,\n )\n ) {\n errors.push(\"config.podcastPreviousMode is not supported.\");\n }\n\n if (errors.length > 0) {\n return { ok: false, errors: Object.freeze(errors) };\n }\n\n const merged = options.partial\n ? { ...PREVIEW_PLAYER_EXPERIENCE_CONFIG, ...value }\n : value;\n\n return {\n ok: true,\n errors: [],\n value: Object.freeze({ ...merged }) as Readonly<PlayerExperienceConfig>,\n };\n}\n\nexport function validatePlayerExperienceConfig(\n value: unknown,\n): PlayerExperienceConfigValidationResult {\n return validateConfigRecord(value, { partial: false });\n}\n\nexport function validatePlayerExperienceConfigOverride(\n value: unknown,\n): PlayerExperienceConfigValidationResult {\n return validateConfigRecord(value, { partial: true });\n}\n\nexport function resolvePlayerExperienceConfig(\n override?: Partial<PlayerExperienceConfig>,\n): PlayerExperienceConfigValidationResult {\n return validatePlayerExperienceConfigOverride(override ?? {});\n}\n\nexport function parsePlayerExperienceConfigJson(\n json: string,\n): PlayerExperienceConfigValidationResult {\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(json) as unknown;\n } catch {\n return { ok: false, errors: [\"Config JSON is not valid JSON.\"] };\n }\n\n if (!isRecord(parsed)) {\n return { ok: false, errors: [\"Config JSON envelope must be an object.\"] };\n }\n\n const envelopeKeys = Object.keys(parsed);\n const errors: string[] = [];\n\n for (const key of envelopeKeys) {\n if (key !== \"schemaVersion\" && key !== \"config\") {\n errors.push(`envelope.${key} is not supported.`);\n }\n }\n if (!Object.hasOwn(parsed, \"schemaVersion\")) {\n errors.push(\"envelope.schemaVersion is required.\");\n } else if (\n parsed.schemaVersion !== PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION\n ) {\n errors.push(\"envelope.schemaVersion is not supported.\");\n }\n if (!Object.hasOwn(parsed, \"config\")) {\n errors.push(\"envelope.config is required.\");\n }\n\n if (errors.length > 0) {\n return { ok: false, errors: Object.freeze(errors) };\n }\n\n return validatePlayerExperienceConfig(parsed.config);\n}\n\nexport function serializePlayerExperienceConfig(\n config: PlayerExperienceConfig,\n): string {\n const result = validatePlayerExperienceConfig(config);\n\n if (!result.ok) {\n throw new TypeError(result.errors.join(\" \"));\n }\n\n return JSON.stringify(\n {\n schemaVersion: PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION,\n config: result.value,\n },\n null,\n 2,\n );\n}\n","import type {\n PlayerExperienceConfig,\n PlayerState,\n} from \"../contracts/player\";\nimport { resolvePlayerExperienceConfig } from \"./validation\";\n\nexport function createInitialPlayerState(\n experienceConfig?: Partial<PlayerExperienceConfig>,\n): Readonly<PlayerState> {\n const resolved = resolvePlayerExperienceConfig(experienceConfig);\n\n if (!resolved.ok) {\n throw new TypeError(resolved.errors.join(\" \"));\n }\n\n return Object.freeze({\n currentItem: null,\n currentItemId: null,\n currentQueueItemId: null,\n mediaKind: null,\n status: \"idle\",\n currentTimeSec: 0,\n durationSec: null,\n volume: resolved.value.initialVolume,\n muted: false,\n shuffleEnabled: resolved.value.initialShuffleEnabled,\n repeatMode: resolved.value.initialRepeatMode,\n queue: Object.freeze([]),\n currentQueueIndex: null,\n canSeek: false,\n canGoPrevious: false,\n canGoNext: false,\n needsUserGesture: false,\n lastTransitionCause: null,\n error: null,\n experienceConfig: resolved.value,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/config/defaults.ts","../../src/config/validation.ts","../../src/config/initial-state.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { PlayerExperienceConfig } from \"../contracts/player\";\r\n\r\nexport const PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION = 2 as const;\r\n\r\nexport const PLAYER_EXPERIENCE_CONFIG_LIMITS = Object.freeze({\r\n fadeMs: Object.freeze({ min: 0, max: 10_000 }),\r\n previousRestartThresholdSec: Object.freeze({ min: 0, max: 60 }),\r\n waveformOpacity: Object.freeze({ min: 0, max: 1 }),\r\n initialVolume: Object.freeze({ min: 0, max: 1 }),\r\n podcastSkipSec: Object.freeze({ minExclusive: 0, max: 3_600 }),\r\n});\r\n\r\nexport const PREVIEW_PLAYER_EXPERIENCE_CONFIG: Readonly<PlayerExperienceConfig> =\r\n Object.freeze({\r\n playFadeInMs: 300,\r\n pauseFadeOutMs: 600,\r\n trackChangeFadeOutMs: 300,\r\n trackChangeFadeInMs: 300,\r\n seekFadeMs: 120,\r\n fadeCurve: \"ease-out\",\r\n previousRestartThresholdSec: 3,\r\n seekCommitMode: \"on-release\",\r\n waveformPlayedStateEnabled: true,\r\n waveformOpacity: 0.72,\r\n initialShuffleEnabled: false,\r\n initialRepeatMode: \"off\",\r\n initialVolume: 0.8,\r\n persistMode: \"none\",\r\n podcastBackSec: 15,\r\n podcastForwardSec: 30,\r\n podcastPreviousMode: \"previous-episode\",\r\n });\r\n","import type {\r\n FadeCurve,\r\n PersistMode,\r\n PlayerExperienceConfig,\r\n PodcastPreviousMode,\r\n RepeatMode,\r\n SeekCommitMode,\r\n} from \"../contracts/player\";\r\nimport type { PlayerExperienceConfigValidationResult } from \"../contracts/config\";\r\nimport {\r\n PLAYER_EXPERIENCE_CONFIG_LIMITS,\r\n PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION,\r\n PREVIEW_PLAYER_EXPERIENCE_CONFIG,\r\n} from \"./defaults\";\r\n\r\nconst CONFIG_KEYS = [\r\n \"playFadeInMs\",\r\n \"pauseFadeOutMs\",\r\n \"trackChangeFadeOutMs\",\r\n \"trackChangeFadeInMs\",\r\n \"seekFadeMs\",\r\n \"fadeCurve\",\r\n \"previousRestartThresholdSec\",\r\n \"seekCommitMode\",\r\n \"waveformPlayedStateEnabled\",\r\n \"waveformOpacity\",\r\n \"initialShuffleEnabled\",\r\n \"initialRepeatMode\",\r\n \"initialVolume\",\r\n \"persistMode\",\r\n \"podcastBackSec\",\r\n \"podcastForwardSec\",\r\n \"podcastPreviousMode\",\r\n] as const satisfies readonly (keyof PlayerExperienceConfig)[];\r\n\r\nconst FADE_CURVES = new Set<FadeCurve>([\r\n \"linear\",\r\n \"ease-in\",\r\n \"ease-out\",\r\n \"equal-power\",\r\n]);\r\nconst SEEK_COMMIT_MODES = new Set<SeekCommitMode>([\"live\", \"on-release\"]);\r\nconst REPEAT_MODES = new Set<RepeatMode>([\"off\", \"all\", \"one\"]);\r\nconst PERSIST_MODES = new Set<PersistMode>([\"none\", \"session\", \"local\"]);\r\nconst PODCAST_PREVIOUS_MODES = new Set<PodcastPreviousMode>([\r\n \"previous-episode\",\r\n \"restart-episode\",\r\n]);\r\n\r\nfunction isRecord(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction validateFiniteRange(\r\n value: unknown,\r\n path: string,\r\n min: number,\r\n max: number,\r\n errors: string[],\r\n): value is number {\r\n if (\r\n typeof value !== \"number\" ||\r\n !Number.isFinite(value) ||\r\n value < min ||\r\n value > max\r\n ) {\r\n errors.push(\r\n `${path} must be a finite number between ${String(min)} and ${String(max)}.`,\r\n );\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nfunction validateBoolean(\r\n value: unknown,\r\n path: string,\r\n errors: string[],\r\n): value is boolean {\r\n if (typeof value !== \"boolean\") {\r\n errors.push(`${path} must be a boolean.`);\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nfunction validateConfigRecord(\r\n value: unknown,\r\n options: { readonly partial: boolean },\r\n): PlayerExperienceConfigValidationResult {\r\n if (!isRecord(value)) {\r\n return { ok: false, errors: [\"config must be an object.\"] };\r\n }\r\n\r\n const errors: string[] = [];\r\n const allowedKeys = new Set<string>(CONFIG_KEYS);\r\n\r\n for (const key of Object.keys(value)) {\r\n if (!allowedKeys.has(key)) {\r\n errors.push(`config.${key} is not supported.`);\r\n }\r\n }\r\n\r\n if (!options.partial) {\r\n for (const key of CONFIG_KEYS) {\r\n if (!Object.hasOwn(value, key)) {\r\n errors.push(`config.${key} is required.`);\r\n }\r\n }\r\n }\r\n\r\n const has = (key: keyof PlayerExperienceConfig): boolean =>\r\n Object.hasOwn(value, key);\r\n const fadeLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.fadeMs;\r\n\r\n for (const key of [\r\n \"playFadeInMs\",\r\n \"pauseFadeOutMs\",\r\n \"trackChangeFadeOutMs\",\r\n \"trackChangeFadeInMs\",\r\n \"seekFadeMs\",\r\n ] as const) {\r\n if (has(key)) {\r\n validateFiniteRange(value[key], `config.${key}`, fadeLimits.min, fadeLimits.max, errors);\r\n }\r\n }\r\n\r\n const thresholdLimits =\r\n PLAYER_EXPERIENCE_CONFIG_LIMITS.previousRestartThresholdSec;\r\n if (has(\"previousRestartThresholdSec\")) {\r\n validateFiniteRange(\r\n value.previousRestartThresholdSec,\r\n \"config.previousRestartThresholdSec\",\r\n thresholdLimits.min,\r\n thresholdLimits.max,\r\n errors,\r\n );\r\n }\r\n\r\n const volumeLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.initialVolume;\r\n if (has(\"initialVolume\")) {\r\n validateFiniteRange(\r\n value.initialVolume,\r\n \"config.initialVolume\",\r\n volumeLimits.min,\r\n volumeLimits.max,\r\n errors,\r\n );\r\n }\r\n\r\n const waveformOpacityLimits =\r\n PLAYER_EXPERIENCE_CONFIG_LIMITS.waveformOpacity;\r\n if (has(\"waveformOpacity\")) {\r\n validateFiniteRange(\r\n value.waveformOpacity,\r\n \"config.waveformOpacity\",\r\n waveformOpacityLimits.min,\r\n waveformOpacityLimits.max,\r\n errors,\r\n );\r\n }\r\n\r\n for (const key of [\r\n \"waveformPlayedStateEnabled\",\r\n \"initialShuffleEnabled\",\r\n ] as const) {\r\n if (has(key)) {\r\n validateBoolean(value[key], `config.${key}`, errors);\r\n }\r\n }\r\n\r\n const skipLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.podcastSkipSec;\r\n for (const key of [\"podcastBackSec\", \"podcastForwardSec\"] as const) {\r\n if (has(key)) {\r\n const candidate = value[key];\r\n const valid = validateFiniteRange(\r\n candidate,\r\n `config.${key}`,\r\n skipLimits.minExclusive,\r\n skipLimits.max,\r\n errors,\r\n );\r\n if (valid && candidate <= skipLimits.minExclusive) {\r\n errors.push(`config.${key} must be greater than 0.`);\r\n }\r\n }\r\n }\r\n\r\n if (has(\"fadeCurve\") && !FADE_CURVES.has(value.fadeCurve as FadeCurve)) {\r\n errors.push(\"config.fadeCurve is not supported.\");\r\n }\r\n if (\r\n has(\"seekCommitMode\") &&\r\n !SEEK_COMMIT_MODES.has(value.seekCommitMode as SeekCommitMode)\r\n ) {\r\n errors.push(\"config.seekCommitMode is not supported.\");\r\n }\r\n if (\r\n has(\"initialRepeatMode\") &&\r\n !REPEAT_MODES.has(value.initialRepeatMode as RepeatMode)\r\n ) {\r\n errors.push(\"config.initialRepeatMode is not supported.\");\r\n }\r\n if (has(\"persistMode\") && !PERSIST_MODES.has(value.persistMode as PersistMode)) {\r\n errors.push(\"config.persistMode is not supported.\");\r\n }\r\n if (\r\n has(\"podcastPreviousMode\") &&\r\n !PODCAST_PREVIOUS_MODES.has(\r\n value.podcastPreviousMode as PodcastPreviousMode,\r\n )\r\n ) {\r\n errors.push(\"config.podcastPreviousMode is not supported.\");\r\n }\r\n\r\n if (errors.length > 0) {\r\n return { ok: false, errors: Object.freeze(errors) };\r\n }\r\n\r\n const merged = options.partial\r\n ? { ...PREVIEW_PLAYER_EXPERIENCE_CONFIG, ...value }\r\n : value;\r\n\r\n return {\r\n ok: true,\r\n errors: [],\r\n value: Object.freeze({ ...merged }) as Readonly<PlayerExperienceConfig>,\r\n };\r\n}\r\n\r\nexport function validatePlayerExperienceConfig(\r\n value: unknown,\r\n): PlayerExperienceConfigValidationResult {\r\n return validateConfigRecord(value, { partial: false });\r\n}\r\n\r\nexport function validatePlayerExperienceConfigOverride(\r\n value: unknown,\r\n): PlayerExperienceConfigValidationResult {\r\n return validateConfigRecord(value, { partial: true });\r\n}\r\n\r\nexport function resolvePlayerExperienceConfig(\r\n override?: Partial<PlayerExperienceConfig>,\r\n): PlayerExperienceConfigValidationResult {\r\n return validatePlayerExperienceConfigOverride(override ?? {});\r\n}\r\n\r\nexport function parsePlayerExperienceConfigJson(\r\n json: string,\r\n): PlayerExperienceConfigValidationResult {\r\n let parsed: unknown;\r\n\r\n try {\r\n parsed = JSON.parse(json) as unknown;\r\n } catch {\r\n return { ok: false, errors: [\"Config JSON is not valid JSON.\"] };\r\n }\r\n\r\n if (!isRecord(parsed)) {\r\n return { ok: false, errors: [\"Config JSON envelope must be an object.\"] };\r\n }\r\n\r\n const envelopeKeys = Object.keys(parsed);\r\n const errors: string[] = [];\r\n\r\n for (const key of envelopeKeys) {\r\n if (key !== \"schemaVersion\" && key !== \"config\") {\r\n errors.push(`envelope.${key} is not supported.`);\r\n }\r\n }\r\n if (!Object.hasOwn(parsed, \"schemaVersion\")) {\r\n errors.push(\"envelope.schemaVersion is required.\");\r\n } else if (\r\n parsed.schemaVersion !== PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION\r\n ) {\r\n errors.push(\"envelope.schemaVersion is not supported.\");\r\n }\r\n if (!Object.hasOwn(parsed, \"config\")) {\r\n errors.push(\"envelope.config is required.\");\r\n }\r\n\r\n if (errors.length > 0) {\r\n return { ok: false, errors: Object.freeze(errors) };\r\n }\r\n\r\n return validatePlayerExperienceConfig(parsed.config);\r\n}\r\n\r\nexport function serializePlayerExperienceConfig(\r\n config: PlayerExperienceConfig,\r\n): string {\r\n const result = validatePlayerExperienceConfig(config);\r\n\r\n if (!result.ok) {\r\n throw new TypeError(result.errors.join(\" \"));\r\n }\r\n\r\n return JSON.stringify(\r\n {\r\n schemaVersion: PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION,\r\n config: result.value,\r\n },\r\n null,\r\n 2,\r\n );\r\n}\r\n","import type {\r\n PlayerExperienceConfig,\r\n PlayerState,\r\n} from \"../contracts/player\";\r\nimport { resolvePlayerExperienceConfig } from \"./validation\";\r\n\r\nexport function createInitialPlayerState(\r\n experienceConfig?: Partial<PlayerExperienceConfig>,\r\n): Readonly<PlayerState> {\r\n const resolved = resolvePlayerExperienceConfig(experienceConfig);\r\n\r\n if (!resolved.ok) {\r\n throw new TypeError(resolved.errors.join(\" \"));\r\n }\r\n\r\n return Object.freeze({\r\n currentItem: null,\r\n currentItemId: null,\r\n currentQueueItemId: null,\r\n mediaKind: null,\r\n status: \"idle\",\r\n currentTimeSec: 0,\r\n durationSec: null,\r\n volume: resolved.value.initialVolume,\r\n muted: false,\r\n shuffleEnabled: resolved.value.initialShuffleEnabled,\r\n repeatMode: resolved.value.initialRepeatMode,\r\n queue: Object.freeze([]),\r\n activeContext: null,\r\n currentQueueIndex: null,\r\n canSeek: false,\r\n canGoPrevious: false,\r\n canGoNext: false,\r\n needsUserGesture: false,\r\n lastTransitionCause: null,\r\n error: null,\r\n experienceConfig: resolved.value,\r\n });\r\n}\r\n"]}
|
package/dist/consumer/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Q as QueueItem,
|
|
1
|
+
import { Q as QueueItem, u as PlayerMediaAdapter } from '../player-BxNFzRq4.js';
|
|
2
2
|
|
|
3
3
|
interface PlayerQueueCandidate<TSource> {
|
|
4
4
|
/** Host-owned stable identity for this occurrence in a queue context. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/consumer/queue.ts"],"names":[],"mappings":";AAiCO,SAAS,gBAAA,CACd,YACA,OAAA,EACwB;AACxB,EAAA,MAAM,QAAqB,EAAC;AAC5B,EAAA,MAAM,WAAmC,EAAC;AAC1C,EAAA,MAAM,gBAAA,uBAAuB,GAAA,EAAY;AAEzC,EAAA,UAAA,CAAW,OAAA,CAAQ,CAAC,SAAA,EAAW,cAAA,KAAmB;AAChD,IAAA,IAAI,gBAAA,CAAiB,GAAA,CAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC/C,MAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAA,CAAO;AAAA,QAC1B,cAAA;AAAA,QACA,aAAa,SAAA,CAAU,WAAA;AAAA,QACvB,MAAA,EAAQ;AAAA,OACT,CAAC,CAAA;AACF,MAAA;AAAA,IACF;AACA,IAAA,gBAAA,CAAiB,GAAA,CAAI,UAAU,WAAW,CAAA;AAE1C,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,iBAAA,CAAkB,SAAA,CAAU,MAAM,CAAA;AACvD,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAA,CAAO;AAAA,QAC1B,cAAA;AAAA,QACA,aAAa,SAAA,CAAU,WAAA;AAAA,QACvB,MAAA,EAAQ;AAAA,OACT,CAAC,CAAA;AACF,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,OAAO,MAAA,CAAO;AAAA,MACvB,aAAa,SAAA,CAAU,WAAA;AAAA,MACvB;AAAA,KACD,CAAC,CAAA;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACnB,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AAAA,IAC1B,QAAA,EAAU,MAAA,CAAO,MAAA,CAAO,QAAQ;AAAA,GACjC,CAAA;AACH","file":"index.js","sourcesContent":["import type {\n PlayerMediaAdapter,\n QueueItem,\n} from \"../contracts/player\";\n\nexport interface PlayerQueueCandidate<TSource> {\n /** Host-owned stable identity for this occurrence in a queue context. */\n readonly queueItemId: string;\n readonly source: TSource;\n}\n\nexport type PlayerQueueRejectionReason =\n | \"not-playable\"\n | \"duplicate-queue-item-id\";\n\nexport interface PlayerQueueRejection {\n readonly candidateIndex: number;\n readonly queueItemId: string;\n readonly reason: PlayerQueueRejectionReason;\n}\n\nexport interface PlayerQueueBuildResult {\n readonly queue: readonly QueueItem[];\n readonly rejected: readonly PlayerQueueRejection[];\n}\n\n/**\n * Builds a Core queue from consumer records that already passed Host access\n * policy. Candidate order is preserved. The same media item may occur more\n * than once when each occurrence has a unique queueItemId.\n *\n * The result retains only Player items, never the consumer record itself.\n */\nexport function buildPlayerQueue<TSource>(\n candidates: readonly PlayerQueueCandidate<TSource>[],\n adapter: PlayerMediaAdapter<TSource>,\n): PlayerQueueBuildResult {\n const queue: QueueItem[] = [];\n const rejected: PlayerQueueRejection[] = [];\n const seenQueueItemIds = new Set<string>();\n\n candidates.forEach((candidate, candidateIndex) => {\n if (seenQueueItemIds.has(candidate.queueItemId)) {\n rejected.push(Object.freeze({\n candidateIndex,\n queueItemId: candidate.queueItemId,\n reason: \"duplicate-queue-item-id\",\n }));\n return;\n }\n seenQueueItemIds.add(candidate.queueItemId);\n\n const item = adapter.toPlayerMediaItem(candidate.source);\n if (item === null) {\n rejected.push(Object.freeze({\n candidateIndex,\n queueItemId: candidate.queueItemId,\n reason: \"not-playable\",\n }));\n return;\n }\n\n queue.push(Object.freeze({\n queueItemId: candidate.queueItemId,\n item,\n }));\n });\n\n return Object.freeze({\n queue: Object.freeze(queue),\n rejected: Object.freeze(rejected),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/consumer/queue.ts"],"names":[],"mappings":";AAiCO,SAAS,gBAAA,CACd,YACA,OAAA,EACwB;AACxB,EAAA,MAAM,QAAqB,EAAC;AAC5B,EAAA,MAAM,WAAmC,EAAC;AAC1C,EAAA,MAAM,gBAAA,uBAAuB,GAAA,EAAY;AAEzC,EAAA,UAAA,CAAW,OAAA,CAAQ,CAAC,SAAA,EAAW,cAAA,KAAmB;AAChD,IAAA,IAAI,gBAAA,CAAiB,GAAA,CAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC/C,MAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAA,CAAO;AAAA,QAC1B,cAAA;AAAA,QACA,aAAa,SAAA,CAAU,WAAA;AAAA,QACvB,MAAA,EAAQ;AAAA,OACT,CAAC,CAAA;AACF,MAAA;AAAA,IACF;AACA,IAAA,gBAAA,CAAiB,GAAA,CAAI,UAAU,WAAW,CAAA;AAE1C,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,iBAAA,CAAkB,SAAA,CAAU,MAAM,CAAA;AACvD,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAA,CAAO;AAAA,QAC1B,cAAA;AAAA,QACA,aAAa,SAAA,CAAU,WAAA;AAAA,QACvB,MAAA,EAAQ;AAAA,OACT,CAAC,CAAA;AACF,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,OAAO,MAAA,CAAO;AAAA,MACvB,aAAa,SAAA,CAAU,WAAA;AAAA,MACvB;AAAA,KACD,CAAC,CAAA;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACnB,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AAAA,IAC1B,QAAA,EAAU,MAAA,CAAO,MAAA,CAAO,QAAQ;AAAA,GACjC,CAAA;AACH","file":"index.js","sourcesContent":["import type {\r\n PlayerMediaAdapter,\r\n QueueItem,\r\n} from \"../contracts/player\";\r\n\r\nexport interface PlayerQueueCandidate<TSource> {\r\n /** Host-owned stable identity for this occurrence in a queue context. */\r\n readonly queueItemId: string;\r\n readonly source: TSource;\r\n}\r\n\r\nexport type PlayerQueueRejectionReason =\r\n | \"not-playable\"\r\n | \"duplicate-queue-item-id\";\r\n\r\nexport interface PlayerQueueRejection {\r\n readonly candidateIndex: number;\r\n readonly queueItemId: string;\r\n readonly reason: PlayerQueueRejectionReason;\r\n}\r\n\r\nexport interface PlayerQueueBuildResult {\r\n readonly queue: readonly QueueItem[];\r\n readonly rejected: readonly PlayerQueueRejection[];\r\n}\r\n\r\n/**\r\n * Builds a Core queue from consumer records that already passed Host access\r\n * policy. Candidate order is preserved. The same media item may occur more\r\n * than once when each occurrence has a unique queueItemId.\r\n *\r\n * The result retains only Player items, never the consumer record itself.\r\n */\r\nexport function buildPlayerQueue<TSource>(\r\n candidates: readonly PlayerQueueCandidate<TSource>[],\r\n adapter: PlayerMediaAdapter<TSource>,\r\n): PlayerQueueBuildResult {\r\n const queue: QueueItem[] = [];\r\n const rejected: PlayerQueueRejection[] = [];\r\n const seenQueueItemIds = new Set<string>();\r\n\r\n candidates.forEach((candidate, candidateIndex) => {\r\n if (seenQueueItemIds.has(candidate.queueItemId)) {\r\n rejected.push(Object.freeze({\r\n candidateIndex,\r\n queueItemId: candidate.queueItemId,\r\n reason: \"duplicate-queue-item-id\",\r\n }));\r\n return;\r\n }\r\n seenQueueItemIds.add(candidate.queueItemId);\r\n\r\n const item = adapter.toPlayerMediaItem(candidate.source);\r\n if (item === null) {\r\n rejected.push(Object.freeze({\r\n candidateIndex,\r\n queueItemId: candidate.queueItemId,\r\n reason: \"not-playable\",\r\n }));\r\n return;\r\n }\r\n\r\n queue.push(Object.freeze({\r\n queueItemId: candidate.queueItemId,\r\n item,\r\n }));\r\n });\r\n\r\n return Object.freeze({\r\n queue: Object.freeze(queue),\r\n rejected: Object.freeze(rejected),\r\n });\r\n}\r\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { F as FadeCurve, M as MediaKind, P as PauseOptions,
|
|
1
|
+
import { p as PlayerExperienceConfig } from '../player-BxNFzRq4.js';
|
|
2
|
+
export { A as ActivatePlaybackContextOptions, B as BoundPlayerCommands, a as BoundPlayerViewState, F as FadeCurve, M as MediaKind, P as PauseOptions, b as PersistMode, c as PlaybackContextInput, d as PlaybackContextMode, e as PlaybackContextState, f as PlaybackStatus, g as PlaybackTransitionCause, h as PlayerAppearance, i as PlayerCommandResult, j as PlayerCommands, k as PlayerConfigImportResult, l as PlayerDebugController, m as PlayerDebugSnapshot, n as PlayerError, o as PlayerErrorCode, q as PlayerExperienceConfigEnvelope, r as PlayerExperienceConfigStore, s as PlayerExperiencePreset, t as PlayerHostConfig, u as PlayerMediaAdapter, v as PlayerMediaItem, w as PlayerMediaSource, x as PlayerMediaSyncReason, y as PlayerState, z as PlayerStore, C as PlayerViewProps, D as PlayerWaveform, E as PodcastPreviousMode, Q as QueueItem, R as RepeatMode, S as SeekCommitMode, G as SetQueueOptions, H as StopOptions } from '../player-BxNFzRq4.js';
|
|
3
3
|
|
|
4
4
|
interface ConfigValidationSuccess {
|
|
5
5
|
readonly ok: true;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ConfigValidationFailure, ConfigValidationSuccess, PlayerExperienceConfigValidationResult } from './contracts/index.js';
|
|
2
|
-
export { F as FadeCurve, M as MediaKind, P as PauseOptions,
|
|
2
|
+
export { A as ActivatePlaybackContextOptions, B as BoundPlayerCommands, a as BoundPlayerViewState, F as FadeCurve, M as MediaKind, P as PauseOptions, b as PersistMode, c as PlaybackContextInput, d as PlaybackContextMode, e as PlaybackContextState, f as PlaybackStatus, g as PlaybackTransitionCause, h as PlayerAppearance, i as PlayerCommandResult, j as PlayerCommands, k as PlayerConfigImportResult, l as PlayerDebugController, m as PlayerDebugSnapshot, n as PlayerError, o as PlayerErrorCode, p as PlayerExperienceConfig, q as PlayerExperienceConfigEnvelope, r as PlayerExperienceConfigStore, s as PlayerExperiencePreset, t as PlayerHostConfig, u as PlayerMediaAdapter, v as PlayerMediaItem, w as PlayerMediaSource, x as PlayerMediaSyncReason, y as PlayerState, z as PlayerStore, C as PlayerViewProps, D as PlayerWaveform, E as PodcastPreviousMode, Q as QueueItem, R as RepeatMode, S as SeekCommitMode, G as SetQueueOptions, H as StopOptions } from './player-BxNFzRq4.js';
|
|
3
3
|
export { PLAYER_EXPERIENCE_CONFIG_LIMITS, PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION, PREVIEW_PLAYER_EXPERIENCE_CONFIG, createInitialPlayerState, parsePlayerExperienceConfigJson, resolvePlayerExperienceConfig, serializePlayerExperienceConfig, validatePlayerExperienceConfig, validatePlayerExperienceConfigOverride } from './config/index.js';
|
|
4
4
|
export { AudioSampleEncoding, FutureUploadFormat, MEDIA_DELIVERY_CONTRACT_VERSION, MediaAccessClass, MediaDeliveryDescriptor, MediaDeliverySourceReference, MediaLoudnessMetadata, MediaSourceRole, MediaStorageReferences, MediaUploadEvidence, MediaUploadValidationResult, NormalizedUploadIdentity, PlaybackSourceResolveErrorCode, PlaybackSourceResolveReason, PlaybackSourceResolveRequest, PlaybackSourceResolveResult, PlayerDeliveryHostAdapter, PlayerMediaDescriptor, ProcessedMediaMetadata, ResolvedPlaybackSource, SupportedUploadFormat, UploadHeaderFormat, UploadValidationError, UploadValidationErrorCode, WaveformPeaksV1, validateMediaUploadEvidence, validateWaveformPeaks } from './media-delivery/index.js';
|
|
5
5
|
export { GainPort, GainTransition, PlayerPersistencePort, PodcastProgress, PodcastProgressPort } from './ports/index.js';
|
|
6
|
-
export { M as MediaElementPort, a as MediaElementStateSnapshot, P as PlayerMediaEvent, b as PlayerMediaEventListener, c as PlayerMediaEventType } from './media-element-
|
|
6
|
+
export { M as MediaElementPort, a as MediaElementStateSnapshot, P as PlayerMediaEvent, b as PlayerMediaEventListener, c as PlayerMediaEventType } from './media-element-DyMO-PhK.js';
|
package/dist/index.js
CHANGED
|
@@ -264,6 +264,7 @@ function createInitialPlayerState(experienceConfig) {
|
|
|
264
264
|
shuffleEnabled: resolved.value.initialShuffleEnabled,
|
|
265
265
|
repeatMode: resolved.value.initialRepeatMode,
|
|
266
266
|
queue: Object.freeze([]),
|
|
267
|
+
activeContext: null,
|
|
267
268
|
currentQueueIndex: null,
|
|
268
269
|
canSeek: false,
|
|
269
270
|
canGoPrevious: false,
|