@gentomiyano/optimized-web-audio-player 0.1.0
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 +159 -0
- package/dist/config/index.d.ts +37 -0
- package/dist/config/index.js +280 -0
- package/dist/config/index.js.map +1 -0
- package/dist/consumer/index.d.ts +27 -0
- package/dist/consumer/index.js +38 -0
- package/dist/consumer/index.js.map +1 -0
- package/dist/consumer-smoke/node_modules/.modules.yaml +30 -0
- package/dist/consumer-smoke/node_modules/.package-map.json +1 -0
- package/dist/consumer-smoke/node_modules/.pnpm/lock.yaml +57 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/LICENSE +21 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/README.md +9 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_mock.development.js +414 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_mock.production.js +406 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_post_task.development.js +150 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler-unstable_post_task.production.js +140 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.development.js +364 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.native.development.js +350 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.native.production.js +330 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/cjs/scheduler.production.js +340 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.js +7 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/index.native.js +7 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/package.json +27 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/unstable_mock.js +7 -0
- package/dist/consumer-smoke/node_modules/.pnpm/scheduler@0.27.0/node_modules/scheduler/unstable_post_task.js +7 -0
- package/dist/consumer-smoke/node_modules/.pnpm-workspace-state-v1.json +29 -0
- package/dist/contracts/index.d.ts +15 -0
- package/dist/contracts/index.js +3 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +368 -0
- package/dist/index.js.map +1 -0
- package/dist/media-delivery/index.d.ts +185 -0
- package/dist/media-delivery/index.js +91 -0
- package/dist/media-delivery/index.js.map +1 -0
- package/dist/media-element-C9dis4aG.d.ts +40 -0
- package/dist/player-DE9Vqztu.d.ts +212 -0
- package/dist/ports/index.d.ts +32 -0
- package/dist/ports/index.js +3 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/react/index.d.ts +86 -0
- package/dist/react/index.js +2393 -0
- package/dist/react/index.js.map +1 -0
- package/dist/testing/index.d.ts +36 -0
- package/dist/testing/index.js +365 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/ui/index.d.ts +143 -0
- package/dist/ui/index.js +1344 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/player-tokens.css +572 -0
- package/dist/ui/player-views.css +654 -0
- package/dist/ui/styles.css +2 -0
- package/package.json +97 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lastValidatedTimestamp": 1785579814223,
|
|
3
|
+
"projects": {},
|
|
4
|
+
"pnpmfiles": [],
|
|
5
|
+
"settings": {
|
|
6
|
+
"autoInstallPeers": true,
|
|
7
|
+
"dedupeDirectDeps": false,
|
|
8
|
+
"dedupeInjectedDeps": true,
|
|
9
|
+
"dedupePeerDependents": true,
|
|
10
|
+
"dedupePeers": false,
|
|
11
|
+
"dev": true,
|
|
12
|
+
"excludeLinksFromLockfile": false,
|
|
13
|
+
"hoistPattern": [
|
|
14
|
+
"*"
|
|
15
|
+
],
|
|
16
|
+
"hoistWorkspacePackages": true,
|
|
17
|
+
"injectWorkspacePackages": false,
|
|
18
|
+
"linkWorkspacePackages": false,
|
|
19
|
+
"minimumReleaseAge": 1440,
|
|
20
|
+
"minimumReleaseAgeIgnoreMissingTime": true,
|
|
21
|
+
"nodeLinker": "isolated",
|
|
22
|
+
"optional": true,
|
|
23
|
+
"peersSuffixMaxLength": 1000,
|
|
24
|
+
"preferWorkspacePackages": false,
|
|
25
|
+
"production": true,
|
|
26
|
+
"publicHoistPattern": []
|
|
27
|
+
},
|
|
28
|
+
"filteredInstall": false
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { l as PlayerExperienceConfig } from '../player-DE9Vqztu.js';
|
|
2
|
+
export { F as FadeCurve, M as MediaKind, P as PauseOptions, a as PersistMode, b as PlaybackStatus, c as PlaybackTransitionCause, d as PlayerAppearance, e as PlayerCommandResult, f as PlayerCommands, g as PlayerConfigImportResult, h as PlayerDebugController, i as PlayerDebugSnapshot, j as PlayerError, k as PlayerErrorCode, m as PlayerExperienceConfigEnvelope, n as PlayerExperienceConfigStore, o as PlayerExperiencePreset, p as PlayerHostConfig, q as PlayerMediaAdapter, r as PlayerMediaItem, s as PlayerMediaSource, t as PlayerMediaSyncReason, u as PlayerState, v as PlayerStore, w as PlayerViewProps, x as PlayerWaveform, y as PodcastPreviousMode, Q as QueueItem, R as RepeatMode, S as SeekCommitMode, z as SetQueueOptions, A as StopOptions } from '../player-DE9Vqztu.js';
|
|
3
|
+
|
|
4
|
+
interface ConfigValidationSuccess {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly value: Readonly<PlayerExperienceConfig>;
|
|
7
|
+
readonly errors: readonly [];
|
|
8
|
+
}
|
|
9
|
+
interface ConfigValidationFailure {
|
|
10
|
+
readonly ok: false;
|
|
11
|
+
readonly errors: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
type PlayerExperienceConfigValidationResult = ConfigValidationSuccess | ConfigValidationFailure;
|
|
14
|
+
|
|
15
|
+
export { type ConfigValidationFailure, type ConfigValidationSuccess, PlayerExperienceConfig, type PlayerExperienceConfigValidationResult };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ConfigValidationFailure, ConfigValidationSuccess, PlayerExperienceConfigValidationResult } from './contracts/index.js';
|
|
2
|
+
export { F as FadeCurve, M as MediaKind, P as PauseOptions, a as PersistMode, b as PlaybackStatus, c as PlaybackTransitionCause, d as PlayerAppearance, e as PlayerCommandResult, f as PlayerCommands, g as PlayerConfigImportResult, h as PlayerDebugController, i as PlayerDebugSnapshot, j as PlayerError, k as PlayerErrorCode, l as PlayerExperienceConfig, m as PlayerExperienceConfigEnvelope, n as PlayerExperienceConfigStore, o as PlayerExperiencePreset, p as PlayerHostConfig, q as PlayerMediaAdapter, r as PlayerMediaItem, s as PlayerMediaSource, t as PlayerMediaSyncReason, u as PlayerState, v as PlayerStore, w as PlayerViewProps, x as PlayerWaveform, y as PodcastPreviousMode, Q as QueueItem, R as RepeatMode, S as SeekCommitMode, z as SetQueueOptions, A as StopOptions } from './player-DE9Vqztu.js';
|
|
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
|
+
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
|
+
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-C9dis4aG.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
// src/config/defaults.ts
|
|
2
|
+
var PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION = 2;
|
|
3
|
+
var PLAYER_EXPERIENCE_CONFIG_LIMITS = Object.freeze({
|
|
4
|
+
fadeMs: Object.freeze({ min: 0, max: 1e4 }),
|
|
5
|
+
previousRestartThresholdSec: Object.freeze({ min: 0, max: 60 }),
|
|
6
|
+
waveformOpacity: Object.freeze({ min: 0, max: 1 }),
|
|
7
|
+
initialVolume: Object.freeze({ min: 0, max: 1 }),
|
|
8
|
+
podcastSkipSec: Object.freeze({ minExclusive: 0, max: 3600 })
|
|
9
|
+
});
|
|
10
|
+
var PREVIEW_PLAYER_EXPERIENCE_CONFIG = Object.freeze({
|
|
11
|
+
playFadeInMs: 300,
|
|
12
|
+
pauseFadeOutMs: 600,
|
|
13
|
+
trackChangeFadeOutMs: 300,
|
|
14
|
+
trackChangeFadeInMs: 300,
|
|
15
|
+
seekFadeMs: 120,
|
|
16
|
+
fadeCurve: "ease-out",
|
|
17
|
+
previousRestartThresholdSec: 3,
|
|
18
|
+
seekCommitMode: "on-release",
|
|
19
|
+
waveformPlayedStateEnabled: true,
|
|
20
|
+
waveformOpacity: 0.72,
|
|
21
|
+
initialShuffleEnabled: false,
|
|
22
|
+
initialRepeatMode: "off",
|
|
23
|
+
initialVolume: 0.8,
|
|
24
|
+
persistMode: "none",
|
|
25
|
+
podcastBackSec: 15,
|
|
26
|
+
podcastForwardSec: 30,
|
|
27
|
+
podcastPreviousMode: "previous-episode"
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// src/config/validation.ts
|
|
31
|
+
var CONFIG_KEYS = [
|
|
32
|
+
"playFadeInMs",
|
|
33
|
+
"pauseFadeOutMs",
|
|
34
|
+
"trackChangeFadeOutMs",
|
|
35
|
+
"trackChangeFadeInMs",
|
|
36
|
+
"seekFadeMs",
|
|
37
|
+
"fadeCurve",
|
|
38
|
+
"previousRestartThresholdSec",
|
|
39
|
+
"seekCommitMode",
|
|
40
|
+
"waveformPlayedStateEnabled",
|
|
41
|
+
"waveformOpacity",
|
|
42
|
+
"initialShuffleEnabled",
|
|
43
|
+
"initialRepeatMode",
|
|
44
|
+
"initialVolume",
|
|
45
|
+
"persistMode",
|
|
46
|
+
"podcastBackSec",
|
|
47
|
+
"podcastForwardSec",
|
|
48
|
+
"podcastPreviousMode"
|
|
49
|
+
];
|
|
50
|
+
var FADE_CURVES = /* @__PURE__ */ new Set([
|
|
51
|
+
"linear",
|
|
52
|
+
"ease-in",
|
|
53
|
+
"ease-out",
|
|
54
|
+
"equal-power"
|
|
55
|
+
]);
|
|
56
|
+
var SEEK_COMMIT_MODES = /* @__PURE__ */ new Set(["live", "on-release"]);
|
|
57
|
+
var REPEAT_MODES = /* @__PURE__ */ new Set(["off", "all", "one"]);
|
|
58
|
+
var PERSIST_MODES = /* @__PURE__ */ new Set(["none", "session", "local"]);
|
|
59
|
+
var PODCAST_PREVIOUS_MODES = /* @__PURE__ */ new Set([
|
|
60
|
+
"previous-episode",
|
|
61
|
+
"restart-episode"
|
|
62
|
+
]);
|
|
63
|
+
function isRecord(value) {
|
|
64
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
65
|
+
}
|
|
66
|
+
function validateFiniteRange(value, path, min, max, errors) {
|
|
67
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
|
|
68
|
+
errors.push(
|
|
69
|
+
`${path} must be a finite number between ${String(min)} and ${String(max)}.`
|
|
70
|
+
);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function validateBoolean(value, path, errors) {
|
|
76
|
+
if (typeof value !== "boolean") {
|
|
77
|
+
errors.push(`${path} must be a boolean.`);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function validateConfigRecord(value, options) {
|
|
83
|
+
if (!isRecord(value)) {
|
|
84
|
+
return { ok: false, errors: ["config must be an object."] };
|
|
85
|
+
}
|
|
86
|
+
const errors = [];
|
|
87
|
+
const allowedKeys = new Set(CONFIG_KEYS);
|
|
88
|
+
for (const key of Object.keys(value)) {
|
|
89
|
+
if (!allowedKeys.has(key)) {
|
|
90
|
+
errors.push(`config.${key} is not supported.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!options.partial) {
|
|
94
|
+
for (const key of CONFIG_KEYS) {
|
|
95
|
+
if (!Object.hasOwn(value, key)) {
|
|
96
|
+
errors.push(`config.${key} is required.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const has = (key) => Object.hasOwn(value, key);
|
|
101
|
+
const fadeLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.fadeMs;
|
|
102
|
+
for (const key of [
|
|
103
|
+
"playFadeInMs",
|
|
104
|
+
"pauseFadeOutMs",
|
|
105
|
+
"trackChangeFadeOutMs",
|
|
106
|
+
"trackChangeFadeInMs",
|
|
107
|
+
"seekFadeMs"
|
|
108
|
+
]) {
|
|
109
|
+
if (has(key)) {
|
|
110
|
+
validateFiniteRange(value[key], `config.${key}`, fadeLimits.min, fadeLimits.max, errors);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const thresholdLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.previousRestartThresholdSec;
|
|
114
|
+
if (has("previousRestartThresholdSec")) {
|
|
115
|
+
validateFiniteRange(
|
|
116
|
+
value.previousRestartThresholdSec,
|
|
117
|
+
"config.previousRestartThresholdSec",
|
|
118
|
+
thresholdLimits.min,
|
|
119
|
+
thresholdLimits.max,
|
|
120
|
+
errors
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const volumeLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.initialVolume;
|
|
124
|
+
if (has("initialVolume")) {
|
|
125
|
+
validateFiniteRange(
|
|
126
|
+
value.initialVolume,
|
|
127
|
+
"config.initialVolume",
|
|
128
|
+
volumeLimits.min,
|
|
129
|
+
volumeLimits.max,
|
|
130
|
+
errors
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
const waveformOpacityLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.waveformOpacity;
|
|
134
|
+
if (has("waveformOpacity")) {
|
|
135
|
+
validateFiniteRange(
|
|
136
|
+
value.waveformOpacity,
|
|
137
|
+
"config.waveformOpacity",
|
|
138
|
+
waveformOpacityLimits.min,
|
|
139
|
+
waveformOpacityLimits.max,
|
|
140
|
+
errors
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
for (const key of [
|
|
144
|
+
"waveformPlayedStateEnabled",
|
|
145
|
+
"initialShuffleEnabled"
|
|
146
|
+
]) {
|
|
147
|
+
if (has(key)) {
|
|
148
|
+
validateBoolean(value[key], `config.${key}`, errors);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const skipLimits = PLAYER_EXPERIENCE_CONFIG_LIMITS.podcastSkipSec;
|
|
152
|
+
for (const key of ["podcastBackSec", "podcastForwardSec"]) {
|
|
153
|
+
if (has(key)) {
|
|
154
|
+
const candidate = value[key];
|
|
155
|
+
const valid = validateFiniteRange(
|
|
156
|
+
candidate,
|
|
157
|
+
`config.${key}`,
|
|
158
|
+
skipLimits.minExclusive,
|
|
159
|
+
skipLimits.max,
|
|
160
|
+
errors
|
|
161
|
+
);
|
|
162
|
+
if (valid && candidate <= skipLimits.minExclusive) {
|
|
163
|
+
errors.push(`config.${key} must be greater than 0.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (has("fadeCurve") && !FADE_CURVES.has(value.fadeCurve)) {
|
|
168
|
+
errors.push("config.fadeCurve is not supported.");
|
|
169
|
+
}
|
|
170
|
+
if (has("seekCommitMode") && !SEEK_COMMIT_MODES.has(value.seekCommitMode)) {
|
|
171
|
+
errors.push("config.seekCommitMode is not supported.");
|
|
172
|
+
}
|
|
173
|
+
if (has("initialRepeatMode") && !REPEAT_MODES.has(value.initialRepeatMode)) {
|
|
174
|
+
errors.push("config.initialRepeatMode is not supported.");
|
|
175
|
+
}
|
|
176
|
+
if (has("persistMode") && !PERSIST_MODES.has(value.persistMode)) {
|
|
177
|
+
errors.push("config.persistMode is not supported.");
|
|
178
|
+
}
|
|
179
|
+
if (has("podcastPreviousMode") && !PODCAST_PREVIOUS_MODES.has(
|
|
180
|
+
value.podcastPreviousMode
|
|
181
|
+
)) {
|
|
182
|
+
errors.push("config.podcastPreviousMode is not supported.");
|
|
183
|
+
}
|
|
184
|
+
if (errors.length > 0) {
|
|
185
|
+
return { ok: false, errors: Object.freeze(errors) };
|
|
186
|
+
}
|
|
187
|
+
const merged = options.partial ? { ...PREVIEW_PLAYER_EXPERIENCE_CONFIG, ...value } : value;
|
|
188
|
+
return {
|
|
189
|
+
ok: true,
|
|
190
|
+
errors: [],
|
|
191
|
+
value: Object.freeze({ ...merged })
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function validatePlayerExperienceConfig(value) {
|
|
195
|
+
return validateConfigRecord(value, { partial: false });
|
|
196
|
+
}
|
|
197
|
+
function validatePlayerExperienceConfigOverride(value) {
|
|
198
|
+
return validateConfigRecord(value, { partial: true });
|
|
199
|
+
}
|
|
200
|
+
function resolvePlayerExperienceConfig(override) {
|
|
201
|
+
return validatePlayerExperienceConfigOverride(override ?? {});
|
|
202
|
+
}
|
|
203
|
+
function parsePlayerExperienceConfigJson(json) {
|
|
204
|
+
let parsed;
|
|
205
|
+
try {
|
|
206
|
+
parsed = JSON.parse(json);
|
|
207
|
+
} catch {
|
|
208
|
+
return { ok: false, errors: ["Config JSON is not valid JSON."] };
|
|
209
|
+
}
|
|
210
|
+
if (!isRecord(parsed)) {
|
|
211
|
+
return { ok: false, errors: ["Config JSON envelope must be an object."] };
|
|
212
|
+
}
|
|
213
|
+
const envelopeKeys = Object.keys(parsed);
|
|
214
|
+
const errors = [];
|
|
215
|
+
for (const key of envelopeKeys) {
|
|
216
|
+
if (key !== "schemaVersion" && key !== "config") {
|
|
217
|
+
errors.push(`envelope.${key} is not supported.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (!Object.hasOwn(parsed, "schemaVersion")) {
|
|
221
|
+
errors.push("envelope.schemaVersion is required.");
|
|
222
|
+
} else if (parsed.schemaVersion !== PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION) {
|
|
223
|
+
errors.push("envelope.schemaVersion is not supported.");
|
|
224
|
+
}
|
|
225
|
+
if (!Object.hasOwn(parsed, "config")) {
|
|
226
|
+
errors.push("envelope.config is required.");
|
|
227
|
+
}
|
|
228
|
+
if (errors.length > 0) {
|
|
229
|
+
return { ok: false, errors: Object.freeze(errors) };
|
|
230
|
+
}
|
|
231
|
+
return validatePlayerExperienceConfig(parsed.config);
|
|
232
|
+
}
|
|
233
|
+
function serializePlayerExperienceConfig(config) {
|
|
234
|
+
const result = validatePlayerExperienceConfig(config);
|
|
235
|
+
if (!result.ok) {
|
|
236
|
+
throw new TypeError(result.errors.join(" "));
|
|
237
|
+
}
|
|
238
|
+
return JSON.stringify(
|
|
239
|
+
{
|
|
240
|
+
schemaVersion: PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION,
|
|
241
|
+
config: result.value
|
|
242
|
+
},
|
|
243
|
+
null,
|
|
244
|
+
2
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/config/initial-state.ts
|
|
249
|
+
function createInitialPlayerState(experienceConfig) {
|
|
250
|
+
const resolved = resolvePlayerExperienceConfig(experienceConfig);
|
|
251
|
+
if (!resolved.ok) {
|
|
252
|
+
throw new TypeError(resolved.errors.join(" "));
|
|
253
|
+
}
|
|
254
|
+
return Object.freeze({
|
|
255
|
+
currentItem: null,
|
|
256
|
+
currentItemId: null,
|
|
257
|
+
currentQueueItemId: null,
|
|
258
|
+
mediaKind: null,
|
|
259
|
+
status: "idle",
|
|
260
|
+
currentTimeSec: 0,
|
|
261
|
+
durationSec: null,
|
|
262
|
+
volume: resolved.value.initialVolume,
|
|
263
|
+
muted: false,
|
|
264
|
+
shuffleEnabled: resolved.value.initialShuffleEnabled,
|
|
265
|
+
repeatMode: resolved.value.initialRepeatMode,
|
|
266
|
+
queue: Object.freeze([]),
|
|
267
|
+
currentQueueIndex: null,
|
|
268
|
+
canSeek: false,
|
|
269
|
+
canGoPrevious: false,
|
|
270
|
+
canGoNext: false,
|
|
271
|
+
needsUserGesture: false,
|
|
272
|
+
lastTransitionCause: null,
|
|
273
|
+
error: null,
|
|
274
|
+
experienceConfig: resolved.value
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/media-delivery/validation.ts
|
|
279
|
+
var EXTENSION_FORMATS = /* @__PURE__ */ new Map([
|
|
280
|
+
[".wav", "wav"],
|
|
281
|
+
[".mp3", "mp3"]
|
|
282
|
+
]);
|
|
283
|
+
var MIME_FORMATS = /* @__PURE__ */ new Map([
|
|
284
|
+
["audio/wave", "wav"],
|
|
285
|
+
["audio/wav", "wav"],
|
|
286
|
+
["audio/x-wav", "wav"],
|
|
287
|
+
["audio/vnd.wave", "wav"],
|
|
288
|
+
["audio/mpeg", "mp3"],
|
|
289
|
+
["audio/mp3", "mp3"]
|
|
290
|
+
]);
|
|
291
|
+
function normalizeExtension(extension) {
|
|
292
|
+
const normalized = extension.trim().toLowerCase();
|
|
293
|
+
return normalized.startsWith(".") ? normalized : `.${normalized}`;
|
|
294
|
+
}
|
|
295
|
+
function normalizeMimeType(mimeType) {
|
|
296
|
+
return mimeType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
297
|
+
}
|
|
298
|
+
function validateMediaUploadEvidence(evidence) {
|
|
299
|
+
const extension = normalizeExtension(evidence.extension);
|
|
300
|
+
const mimeType = normalizeMimeType(evidence.declaredMimeType);
|
|
301
|
+
const extensionFormat = EXTENSION_FORMATS.get(extension);
|
|
302
|
+
const mimeFormat = MIME_FORMATS.get(mimeType);
|
|
303
|
+
const headerFormat = evidence.detectedHeaderFormat === "unknown" ? void 0 : evidence.detectedHeaderFormat;
|
|
304
|
+
const errors = [];
|
|
305
|
+
if (extensionFormat === void 0) {
|
|
306
|
+
errors.push({
|
|
307
|
+
code: "unsupported-extension",
|
|
308
|
+
field: "extension"
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (mimeFormat === void 0) {
|
|
312
|
+
errors.push({
|
|
313
|
+
code: "unsupported-mime-type",
|
|
314
|
+
field: "declaredMimeType"
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
if (headerFormat === void 0) {
|
|
318
|
+
errors.push({
|
|
319
|
+
code: "unrecognized-header",
|
|
320
|
+
field: "detectedHeaderFormat"
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (extensionFormat !== void 0 && headerFormat !== void 0 && extensionFormat !== headerFormat) {
|
|
324
|
+
errors.push({
|
|
325
|
+
code: "extension-header-mismatch",
|
|
326
|
+
field: "extension"
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
if (mimeFormat !== void 0 && headerFormat !== void 0 && mimeFormat !== headerFormat) {
|
|
330
|
+
errors.push({
|
|
331
|
+
code: "mime-header-mismatch",
|
|
332
|
+
field: "declaredMimeType"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
if (errors.length > 0 || extensionFormat === void 0 || mimeFormat === void 0 || headerFormat === void 0) {
|
|
336
|
+
return Object.freeze({
|
|
337
|
+
ok: false,
|
|
338
|
+
errors: Object.freeze(errors)
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return Object.freeze({
|
|
342
|
+
ok: true,
|
|
343
|
+
value: Object.freeze({
|
|
344
|
+
format: headerFormat,
|
|
345
|
+
extension: headerFormat === "wav" ? ".wav" : ".mp3",
|
|
346
|
+
mimeType: headerFormat === "wav" ? "audio/wave" : "audio/mpeg"
|
|
347
|
+
}),
|
|
348
|
+
errors: []
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function validateWaveformPeaks(waveform) {
|
|
352
|
+
const errors = [];
|
|
353
|
+
if (waveform.peaks.length === 0) {
|
|
354
|
+
errors.push("waveform.peaks must contain at least one value.");
|
|
355
|
+
}
|
|
356
|
+
waveform.peaks.forEach((peak, index) => {
|
|
357
|
+
if (!Number.isFinite(peak) || peak < 0 || peak > 1) {
|
|
358
|
+
errors.push(
|
|
359
|
+
`waveform.peaks[${String(index)}] must be a finite number between 0 and 1.`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
return Object.freeze(errors);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export { PLAYER_EXPERIENCE_CONFIG_LIMITS, PLAYER_EXPERIENCE_CONFIG_SCHEMA_VERSION, PREVIEW_PLAYER_EXPERIENCE_CONFIG, createInitialPlayerState, parsePlayerExperienceConfigJson, resolvePlayerExperienceConfig, serializePlayerExperienceConfig, validateMediaUploadEvidence, validatePlayerExperienceConfig, validatePlayerExperienceConfigOverride, validateWaveformPeaks };
|
|
367
|
+
//# sourceMappingURL=index.js.map
|
|
368
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/defaults.ts","../src/config/validation.ts","../src/config/initial-state.ts","../src/media-delivery/validation.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","import type {\n MediaUploadEvidence,\n MediaUploadValidationResult,\n SupportedUploadFormat,\n UploadValidationError,\n WaveformPeaksV1,\n} from \"./contracts\";\n\nconst EXTENSION_FORMATS = new Map<string, SupportedUploadFormat>([\n [\".wav\", \"wav\"],\n [\".mp3\", \"mp3\"],\n]);\n\nconst MIME_FORMATS = new Map<string, SupportedUploadFormat>([\n [\"audio/wave\", \"wav\"],\n [\"audio/wav\", \"wav\"],\n [\"audio/x-wav\", \"wav\"],\n [\"audio/vnd.wave\", \"wav\"],\n [\"audio/mpeg\", \"mp3\"],\n [\"audio/mp3\", \"mp3\"],\n]);\n\nfunction normalizeExtension(extension: string): string {\n const normalized = extension.trim().toLowerCase();\n return normalized.startsWith(\".\") ? normalized : `.${normalized}`;\n}\n\nfunction normalizeMimeType(mimeType: string): string {\n return mimeType.split(\";\", 1)[0]?.trim().toLowerCase() ?? \"\";\n}\n\nexport function validateMediaUploadEvidence(\n evidence: MediaUploadEvidence,\n): MediaUploadValidationResult {\n const extension = normalizeExtension(evidence.extension);\n const mimeType = normalizeMimeType(evidence.declaredMimeType);\n const extensionFormat = EXTENSION_FORMATS.get(extension);\n const mimeFormat = MIME_FORMATS.get(mimeType);\n const headerFormat =\n evidence.detectedHeaderFormat === \"unknown\"\n ? undefined\n : evidence.detectedHeaderFormat;\n const errors: UploadValidationError[] = [];\n\n if (extensionFormat === undefined) {\n errors.push({\n code: \"unsupported-extension\",\n field: \"extension\",\n });\n }\n if (mimeFormat === undefined) {\n errors.push({\n code: \"unsupported-mime-type\",\n field: \"declaredMimeType\",\n });\n }\n if (headerFormat === undefined) {\n errors.push({\n code: \"unrecognized-header\",\n field: \"detectedHeaderFormat\",\n });\n }\n if (\n extensionFormat !== undefined &&\n headerFormat !== undefined &&\n extensionFormat !== headerFormat\n ) {\n errors.push({\n code: \"extension-header-mismatch\",\n field: \"extension\",\n });\n }\n if (\n mimeFormat !== undefined &&\n headerFormat !== undefined &&\n mimeFormat !== headerFormat\n ) {\n errors.push({\n code: \"mime-header-mismatch\",\n field: \"declaredMimeType\",\n });\n }\n\n if (\n errors.length > 0 ||\n extensionFormat === undefined ||\n mimeFormat === undefined ||\n headerFormat === undefined\n ) {\n return Object.freeze({\n ok: false,\n errors: Object.freeze(errors),\n });\n }\n\n return Object.freeze({\n ok: true,\n value: Object.freeze({\n format: headerFormat,\n extension: headerFormat === \"wav\" ? \".wav\" : \".mp3\",\n mimeType: headerFormat === \"wav\" ? \"audio/wave\" : \"audio/mpeg\",\n }),\n errors: [] as const,\n });\n}\n\nexport function validateWaveformPeaks(\n waveform: WaveformPeaksV1,\n): readonly string[] {\n const errors: string[] = [];\n\n if (waveform.peaks.length === 0) {\n errors.push(\"waveform.peaks must contain at least one value.\");\n }\n\n waveform.peaks.forEach((peak, index) => {\n if (!Number.isFinite(peak) || peak < 0 || peak > 1) {\n errors.push(\n `waveform.peaks[${String(index)}] must be a finite number between 0 and 1.`,\n );\n }\n });\n\n return Object.freeze(errors);\n}\n"]}
|