@jswork/howler-audio-manager 1.0.1 → 1.0.2
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 +64 -0
- package/dist/index.cjs.js +137 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.esm.js +146 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +166 -3
- package/dist/index.umd.js.map +1 -1
- package/package.json +46 -31
- package/src/index.ts +2 -0
- package/src/manager.ts +4 -0
- package/src/react.tsx +98 -0
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/afei/github/howler-audio-manager/dist/index.esm.js","../src/manager.ts"],"names":["Howl","Howler","AudioManager","options","inject","effectCache","Map","urlAudioCache","preloadPromises","activeUrlHowl","globalVolume","listeners","HowlConstructor","HowlClass","HowlerInstance","HowlerObj","defaultVolume","setGlobalVolume","hasEffect","key","has","loadEffect","url","get","unload","howl","src","preload","on","emit","type","_id","error","set","loadEffects","map","Object","keys","forEach","typedKey","isNaN","Number","playEffect","console","warn","String","playing","stop","volume","off","once","soundId","onStart","onEnd","onStop","onError","play","stopEffect","removeEffect","delete","preloadUrl","existing","state","Promise","resolve","pending","createUrlHowl","promise","reject","settle","then","preloadUrls","urls","all","playUrl","isSingleton","singleton","loop","stopUrl","event","handler","Set","add","destroy","payload","err","mute","muted","stopAll","clear","default"],"mappings":"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","sourcesContent":["/*!\n * name: @jswork/howler-audio-manager\n * description: Howler.js audio manager, enum-driven sounds for H5.\n * homepage: https://js.work\n * version: 1.0.1\n * date: 2026-10-20 18:54:06\n * license: MIT\n */\n\n\n// src/manager.ts\nimport { Howl, Howler } from \"howler\";\nvar AudioManager = class {\n constructor(options, inject) {\n this.effectCache = /* @__PURE__ */ new Map();\n this.urlAudioCache = /* @__PURE__ */ new Map();\n this.preloadPromises = /* @__PURE__ */ new Map();\n this.activeUrlHowl = null;\n this.globalVolume = 1;\n this.listeners = /* @__PURE__ */ new Map();\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === \"number\") {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n hasEffect(key) {\n return this.effectCache.has(key);\n }\n loadEffect(key, url) {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key).unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"effect\", key, url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.emit(\"error\", { type: \"effect\", key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n loadEffects(map) {\n Object.keys(map).forEach((key) => {\n const typedKey = isNaN(Number(key)) ? key : Number(key);\n this.loadEffect(typedKey, map[key]);\n });\n }\n playEffect(key, options) {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return void 0;\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"effect\", key, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"effect\", key, soundId });\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"effect\", key, soundId });\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"effect\", key, soundId, error });\n });\n return howl.play();\n }\n stopEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n removeEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n preloadUrl(url) {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === \"loaded\") {\n return Promise.resolve();\n }\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n const howl = existing && existing.state() !== \"unloaded\" ? existing : this.createUrlHowl(url);\n const promise = new Promise((resolve, reject) => {\n howl.once(\"load\", () => {\n resolve();\n });\n howl.once(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n return promise;\n }\n preloadUrls(urls) {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n createUrlHowl(url) {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"url\", url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n this.emit(\"error\", { type: \"url\", url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n playUrl(url, options) {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n const isSingleton = options?.singleton !== false;\n if (isSingleton && this.activeUrlHowl && this.activeUrlHowl !== howl && this.activeUrlHowl.playing()) {\n this.activeUrlHowl.stop();\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (options?.loop !== void 0) {\n howl.loop(options.loop);\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"url\", url, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"url\", url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n return howl.play();\n }\n stopUrl(url) {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n on(event, handler) {\n const set = this.listeners.get(event) ?? /* @__PURE__ */ new Set();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n emit(event, payload) {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n setGlobalVolume(volume) {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n mute(muted) {\n this.HowlerInstance.mute(muted);\n }\n stopAll() {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n destroy() {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n};\nexport {\n AudioManager,\n AudioManager as default\n};\n","import { Howl, Howler } from 'howler';\nimport type {\n AudioManagerOptions,\n PlayOptions,\n PlayUrlOptions,\n AudioEventType,\n AudioEventPayload,\n EventSubscription,\n SoundResourceMap\n} from './types';\n\nexport class AudioManager<K extends string | number = string> {\n private effectCache = new Map<K, Howl>();\n private urlAudioCache = new Map<string, Howl>();\n private preloadPromises = new Map<string, Promise<void>>();\n private activeUrlHowl: Howl | null = null;\n private globalVolume = 1.0;\n private listeners = new Map<AudioEventType, Set<(payload: AudioEventPayload<K>) => void>>();\n\n private HowlConstructor: typeof Howl;\n private HowlerInstance: typeof Howler;\n\n constructor(\n options?: AudioManagerOptions,\n inject?: { HowlClass?: typeof Howl; HowlerObj?: typeof Howler }\n ) {\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === 'number') {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n\n hasEffect(key: K): boolean {\n return this.effectCache.has(key);\n }\n\n loadEffect(key: K, url: string): void {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key)!.unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'effect', key, url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.emit('error', { type: 'effect', key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n\n loadEffects(map: SoundResourceMap<K>): void {\n Object.keys(map).forEach((key) => {\n const typedKey = (isNaN(Number(key)) ? key : Number(key)) as K;\n this.loadEffect(typedKey, map[key as any]);\n });\n }\n\n playEffect(key: K, options?: PlayOptions): number | undefined {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return undefined;\n }\n\n if (howl.playing()) {\n howl.stop();\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'effect', key, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'effect', key, soundId });\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'effect', key, soundId });\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'effect', key, soundId, error });\n });\n\n return howl.play();\n }\n\n stopEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n\n removeEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n\n preloadUrl(url: string): Promise<void> {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === 'loaded') {\n return Promise.resolve();\n }\n\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n\n // A howl in a terminal failed state (unloaded after error) must be replaced\n // so a fresh instance can retry the load.\n const howl = existing && existing.state() !== 'unloaded' ? existing : this.createUrlHowl(url);\n\n const promise = new Promise<void>((resolve, reject) => {\n howl.once('load', () => {\n resolve();\n });\n\n howl.once('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n\n return promise;\n }\n\n preloadUrls(urls: string[]): Promise<void[]> {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n\n private createUrlHowl(url: string): Howl {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'url', url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n this.emit('error', { type: 'url', url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n\n playUrl(url: string, options?: PlayUrlOptions): number {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n\n // Singleton mutex: stop currently active URL audio if singleton !== false\n const isSingleton = options?.singleton !== false;\n if (\n isSingleton &&\n this.activeUrlHowl &&\n this.activeUrlHowl !== howl &&\n this.activeUrlHowl.playing()\n ) {\n this.activeUrlHowl.stop();\n }\n\n // Overlap prevention on the same audio instance\n if (howl.playing()) {\n howl.stop();\n }\n\n if (options?.loop !== undefined) {\n howl.loop(options.loop);\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'url', url, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'url', url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n return howl.play();\n }\n\n stopUrl(url: string): void {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n\n on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription {\n const set = this.listeners.get(event) ?? new Set<(payload: AudioEventPayload<K>) => void>();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n\n protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n\n setGlobalVolume(volume: number): void {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n\n mute(muted: boolean): void {\n this.HowlerInstance.mute(muted);\n }\n\n stopAll(): void {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n\n destroy(): void {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/afei/github/howler-audio-manager/dist/index.esm.js","../src/manager.ts","../src/react.tsx"],"names":["Howl","Howler","AudioManager","options","inject","effectCache","Map","urlAudioCache","preloadPromises","activeUrlHowl","globalVolume","listeners","HowlConstructor","HowlClass","HowlerInstance","HowlerObj","defaultVolume","setGlobalVolume","hasEffect","key","has","loadEffect","url","get","unload","howl","src","preload","on","emit","type","_id","error","set","loadEffects","map","Object","keys","forEach","typedKey","isNaN","Number","playEffect","console","warn","String","playing","stop","volume","off","once","soundId","onStart","onEnd","onStop","onError","play","stopEffect","removeEffect","delete","preloadUrl","existing","state","Promise","resolve","pending","createUrlHowl","promise","reject","settle","then","preloadUrls","urls","all","playUrl","isSingleton","singleton","loop","stopUrl","event","handler","Set","add","destroy","payload","err","getGlobalVolume","mute","muted","stopAll","clear","createContext","useCallback","useContext","useEffect","useRef","useState","jsx","AudioManagerContext","AudioManagerProvider","manager","children","Provider","value","useAudio","Error","useAudioEvent","handlerRef","current","subscription","useGlobalVolume","setVolumeState","setVolume","next","usePreload","urlsKey","JSON","stringify","status","setState","list","parse","length","cancelled","default"],"mappings":"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","sourcesContent":["/*!\n * name: @jswork/howler-audio-manager\n * description: Howler.js audio manager, enum-driven sounds for H5.\n * homepage: https://js.work\n * version: 1.0.2\n * date: 2026-10-20 20:03:06\n * license: MIT\n */\n\n\n// src/manager.ts\nimport { Howl, Howler } from \"howler\";\nvar AudioManager = class {\n constructor(options, inject) {\n this.effectCache = /* @__PURE__ */ new Map();\n this.urlAudioCache = /* @__PURE__ */ new Map();\n this.preloadPromises = /* @__PURE__ */ new Map();\n this.activeUrlHowl = null;\n this.globalVolume = 1;\n this.listeners = /* @__PURE__ */ new Map();\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === \"number\") {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n hasEffect(key) {\n return this.effectCache.has(key);\n }\n loadEffect(key, url) {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key).unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"effect\", key, url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.emit(\"error\", { type: \"effect\", key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n loadEffects(map) {\n Object.keys(map).forEach((key) => {\n const typedKey = isNaN(Number(key)) ? key : Number(key);\n this.loadEffect(typedKey, map[key]);\n });\n }\n playEffect(key, options) {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return void 0;\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"effect\", key, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"effect\", key, soundId });\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"effect\", key, soundId });\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"effect\", key, soundId, error });\n });\n return howl.play();\n }\n stopEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n removeEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n preloadUrl(url) {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === \"loaded\") {\n return Promise.resolve();\n }\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n const howl = existing && existing.state() !== \"unloaded\" ? existing : this.createUrlHowl(url);\n const promise = new Promise((resolve, reject) => {\n howl.once(\"load\", () => {\n resolve();\n });\n howl.once(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n return promise;\n }\n preloadUrls(urls) {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n createUrlHowl(url) {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"url\", url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n this.emit(\"error\", { type: \"url\", url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n playUrl(url, options) {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n const isSingleton = options?.singleton !== false;\n if (isSingleton && this.activeUrlHowl && this.activeUrlHowl !== howl && this.activeUrlHowl.playing()) {\n this.activeUrlHowl.stop();\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (options?.loop !== void 0) {\n howl.loop(options.loop);\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"url\", url, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"url\", url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n return howl.play();\n }\n stopUrl(url) {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n on(event, handler) {\n const set = this.listeners.get(event) ?? /* @__PURE__ */ new Set();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n emit(event, payload) {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n getGlobalVolume() {\n return this.globalVolume;\n }\n setGlobalVolume(volume) {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n mute(muted) {\n this.HowlerInstance.mute(muted);\n }\n stopAll() {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n destroy() {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n};\n\n// src/react.tsx\nimport { createContext, useCallback, useContext, useEffect, useRef, useState } from \"react\";\nimport { jsx } from \"react/jsx-runtime\";\nvar AudioManagerContext = createContext(null);\nfunction AudioManagerProvider({\n manager,\n children\n}) {\n return /* @__PURE__ */ jsx(AudioManagerContext.Provider, { value: manager, children });\n}\nfunction useAudio() {\n const manager = useContext(AudioManagerContext);\n if (!manager) {\n throw new Error(\"useAudio must be used within an <AudioManagerProvider>.\");\n }\n return manager;\n}\nfunction useAudioEvent(event, handler) {\n const manager = useAudio();\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n useEffect(() => {\n const subscription = manager.on(event, (payload) => handlerRef.current(payload));\n return () => subscription.destroy();\n }, [manager, event]);\n}\nfunction useGlobalVolume() {\n const manager = useAudio();\n const [volume, setVolumeState] = useState(() => manager.getGlobalVolume());\n const setVolume = useCallback(\n (next) => {\n manager.setGlobalVolume(next);\n setVolumeState(next);\n },\n [manager]\n );\n return [volume, setVolume];\n}\nfunction usePreload(urls) {\n const manager = useAudio();\n const urlsKey = JSON.stringify(urls);\n const [state, setState] = useState({ status: \"idle\", error: null });\n useEffect(() => {\n const list = JSON.parse(urlsKey);\n if (list.length === 0) {\n setState({ status: \"idle\", error: null });\n return;\n }\n let cancelled = false;\n setState({ status: \"loading\", error: null });\n manager.preloadUrls(list).then(\n () => {\n if (!cancelled) setState({ status: \"loaded\", error: null });\n },\n (error) => {\n if (!cancelled) setState({ status: \"error\", error });\n }\n );\n return () => {\n cancelled = true;\n };\n }, [manager, urlsKey]);\n return state;\n}\nexport {\n AudioManager,\n AudioManagerProvider,\n AudioManager as default,\n useAudio,\n useAudioEvent,\n useGlobalVolume,\n usePreload\n};\n","import { Howl, Howler } from 'howler';\nimport type {\n AudioManagerOptions,\n PlayOptions,\n PlayUrlOptions,\n AudioEventType,\n AudioEventPayload,\n EventSubscription,\n SoundResourceMap\n} from './types';\n\nexport class AudioManager<K extends string | number = string> {\n private effectCache = new Map<K, Howl>();\n private urlAudioCache = new Map<string, Howl>();\n private preloadPromises = new Map<string, Promise<void>>();\n private activeUrlHowl: Howl | null = null;\n private globalVolume = 1.0;\n private listeners = new Map<AudioEventType, Set<(payload: AudioEventPayload<K>) => void>>();\n\n private HowlConstructor: typeof Howl;\n private HowlerInstance: typeof Howler;\n\n constructor(\n options?: AudioManagerOptions,\n inject?: { HowlClass?: typeof Howl; HowlerObj?: typeof Howler }\n ) {\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === 'number') {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n\n hasEffect(key: K): boolean {\n return this.effectCache.has(key);\n }\n\n loadEffect(key: K, url: string): void {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key)!.unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'effect', key, url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.emit('error', { type: 'effect', key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n\n loadEffects(map: SoundResourceMap<K>): void {\n Object.keys(map).forEach((key) => {\n const typedKey = (isNaN(Number(key)) ? key : Number(key)) as K;\n this.loadEffect(typedKey, map[key as any]);\n });\n }\n\n playEffect(key: K, options?: PlayOptions): number | undefined {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return undefined;\n }\n\n if (howl.playing()) {\n howl.stop();\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'effect', key, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'effect', key, soundId });\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'effect', key, soundId });\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'effect', key, soundId, error });\n });\n\n return howl.play();\n }\n\n stopEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n\n removeEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n\n preloadUrl(url: string): Promise<void> {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === 'loaded') {\n return Promise.resolve();\n }\n\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n\n // A howl in a terminal failed state (unloaded after error) must be replaced\n // so a fresh instance can retry the load.\n const howl = existing && existing.state() !== 'unloaded' ? existing : this.createUrlHowl(url);\n\n const promise = new Promise<void>((resolve, reject) => {\n howl.once('load', () => {\n resolve();\n });\n\n howl.once('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n\n return promise;\n }\n\n preloadUrls(urls: string[]): Promise<void[]> {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n\n private createUrlHowl(url: string): Howl {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'url', url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n this.emit('error', { type: 'url', url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n\n playUrl(url: string, options?: PlayUrlOptions): number {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n\n // Singleton mutex: stop currently active URL audio if singleton !== false\n const isSingleton = options?.singleton !== false;\n if (\n isSingleton &&\n this.activeUrlHowl &&\n this.activeUrlHowl !== howl &&\n this.activeUrlHowl.playing()\n ) {\n this.activeUrlHowl.stop();\n }\n\n // Overlap prevention on the same audio instance\n if (howl.playing()) {\n howl.stop();\n }\n\n if (options?.loop !== undefined) {\n howl.loop(options.loop);\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'url', url, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'url', url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n return howl.play();\n }\n\n stopUrl(url: string): void {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n\n on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription {\n const set = this.listeners.get(event) ?? new Set<(payload: AudioEventPayload<K>) => void>();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n\n protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n\n getGlobalVolume(): number {\n return this.globalVolume;\n }\n\n setGlobalVolume(volume: number): void {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n\n mute(muted: boolean): void {\n this.HowlerInstance.mute(muted);\n }\n\n stopAll(): void {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n\n destroy(): void {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n}\n","import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { AudioManager } from './manager';\nimport type { AudioEventType, AudioEventPayload } from './types';\n\nconst AudioManagerContext = createContext<AudioManager<any> | null>(null);\n\nexport interface AudioManagerProviderProps<K extends string | number = string> {\n manager: AudioManager<K>;\n children?: ReactNode;\n}\n\nexport function AudioManagerProvider<K extends string | number = string>({\n manager,\n children\n}: AudioManagerProviderProps<K>) {\n return (\n <AudioManagerContext.Provider value={manager}>\n {children}\n </AudioManagerContext.Provider>\n );\n}\n\nexport function useAudio<K extends string | number = string>(): AudioManager<K> {\n const manager = useContext(AudioManagerContext);\n if (!manager) {\n throw new Error('useAudio must be used within an <AudioManagerProvider>.');\n }\n return manager as unknown as AudioManager<K>;\n}\n\nexport function useAudioEvent<K extends string | number = string>(\n event: AudioEventType,\n handler: (payload: AudioEventPayload<K>) => void\n): void {\n const manager = useAudio<K>();\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const subscription = manager.on(event, (payload) => handlerRef.current(payload));\n return () => subscription.destroy();\n }, [manager, event]);\n}\n\nexport function useGlobalVolume(): [number, (volume: number) => void] {\n const manager = useAudio();\n const [volume, setVolumeState] = useState(() => manager.getGlobalVolume());\n\n const setVolume = useCallback(\n (next: number) => {\n manager.setGlobalVolume(next);\n setVolumeState(next);\n },\n [manager]\n );\n\n return [volume, setVolume];\n}\n\nexport interface PreloadState {\n status: 'idle' | 'loading' | 'loaded' | 'error';\n error: unknown;\n}\n\nexport function usePreload(urls: string[]): PreloadState {\n const manager = useAudio();\n const urlsKey = JSON.stringify(urls);\n const [state, setState] = useState<PreloadState>({ status: 'idle', error: null });\n\n useEffect(() => {\n const list: string[] = JSON.parse(urlsKey);\n if (list.length === 0) {\n setState({ status: 'idle', error: null });\n return;\n }\n\n let cancelled = false;\n setState({ status: 'loading', error: null });\n\n manager.preloadUrls(list).then(\n () => {\n if (!cancelled) setState({ status: 'loaded', error: null });\n },\n (error) => {\n if (!cancelled) setState({ status: 'error', error });\n }\n );\n\n return () => {\n cancelled = true;\n };\n }, [manager, urlsKey]);\n\n return state;\n}\n\n\n"]}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
function _array_like_to_array(arr, len) {
|
|
2
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
3
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
4
|
+
return arr2;
|
|
5
|
+
}
|
|
6
|
+
function _array_with_holes(arr) {
|
|
7
|
+
if (Array.isArray(arr)) return arr;
|
|
8
|
+
}
|
|
1
9
|
function _class_call_check(instance, Constructor) {
|
|
2
10
|
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
|
3
11
|
}
|
|
@@ -15,16 +23,54 @@ function _create_class(Constructor, protoProps, staticProps) {
|
|
|
15
23
|
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
16
24
|
return Constructor;
|
|
17
25
|
}
|
|
26
|
+
function _iterable_to_array_limit(arr, i) {
|
|
27
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
28
|
+
if (_i == null) return;
|
|
29
|
+
var _arr = [];
|
|
30
|
+
var _n = true;
|
|
31
|
+
var _d = false;
|
|
32
|
+
var _s, _e;
|
|
33
|
+
try {
|
|
34
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
35
|
+
_arr.push(_s.value);
|
|
36
|
+
if (i && _arr.length === i) break;
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
_d = true;
|
|
40
|
+
_e = err;
|
|
41
|
+
} finally{
|
|
42
|
+
try {
|
|
43
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
44
|
+
} finally{
|
|
45
|
+
if (_d) throw _e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return _arr;
|
|
49
|
+
}
|
|
50
|
+
function _non_iterable_rest() {
|
|
51
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
52
|
+
}
|
|
53
|
+
function _sliced_to_array(arr, i) {
|
|
54
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
55
|
+
}
|
|
18
56
|
function _type_of(obj) {
|
|
19
57
|
"@swc/helpers - typeof";
|
|
20
58
|
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
21
59
|
}
|
|
60
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
61
|
+
if (!o) return;
|
|
62
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
63
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
64
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
65
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
66
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
67
|
+
}
|
|
22
68
|
/*!
|
|
23
69
|
* name: @jswork/howler-audio-manager
|
|
24
70
|
* description: Howler.js audio manager, enum-driven sounds for H5.
|
|
25
71
|
* homepage: https://js.work
|
|
26
|
-
* version: 1.0.
|
|
27
|
-
* date: 2026-10-20
|
|
72
|
+
* version: 1.0.2
|
|
73
|
+
* date: 2026-10-20 20:03:06
|
|
28
74
|
* license: MIT
|
|
29
75
|
*/ (function(g, f) {
|
|
30
76
|
if ((typeof exports === "undefined" ? "undefined" : _type_of(exports)) == "object" && (typeof module === "undefined" ? "undefined" : _type_of(module)) < "u") {
|
|
@@ -93,8 +139,23 @@ function _type_of(obj) {
|
|
|
93
139
|
AudioManager: function AudioManager1() {
|
|
94
140
|
return AudioManager;
|
|
95
141
|
},
|
|
142
|
+
AudioManagerProvider: function AudioManagerProvider1() {
|
|
143
|
+
return AudioManagerProvider;
|
|
144
|
+
},
|
|
96
145
|
default: function _default() {
|
|
97
146
|
return AudioManager;
|
|
147
|
+
},
|
|
148
|
+
useAudio: function useAudio1() {
|
|
149
|
+
return useAudio;
|
|
150
|
+
},
|
|
151
|
+
useAudioEvent: function useAudioEvent1() {
|
|
152
|
+
return useAudioEvent;
|
|
153
|
+
},
|
|
154
|
+
useGlobalVolume: function useGlobalVolume1() {
|
|
155
|
+
return useGlobalVolume;
|
|
156
|
+
},
|
|
157
|
+
usePreload: function usePreload1() {
|
|
158
|
+
return usePreload;
|
|
98
159
|
}
|
|
99
160
|
});
|
|
100
161
|
module1.exports = __toCommonJS(index_exports);
|
|
@@ -436,6 +497,12 @@ function _type_of(obj) {
|
|
|
436
497
|
}
|
|
437
498
|
}
|
|
438
499
|
},
|
|
500
|
+
{
|
|
501
|
+
key: "getGlobalVolume",
|
|
502
|
+
value: function getGlobalVolume() {
|
|
503
|
+
return this.globalVolume;
|
|
504
|
+
}
|
|
505
|
+
},
|
|
439
506
|
{
|
|
440
507
|
key: "setGlobalVolume",
|
|
441
508
|
value: function setGlobalVolume(volume) {
|
|
@@ -475,9 +542,105 @@ function _type_of(obj) {
|
|
|
475
542
|
]);
|
|
476
543
|
return AudioManager;
|
|
477
544
|
}();
|
|
545
|
+
// src/react.tsx
|
|
546
|
+
var import_react = require("react");
|
|
547
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
548
|
+
var AudioManagerContext = (0, import_react.createContext)(null);
|
|
549
|
+
function AudioManagerProvider(param) {
|
|
550
|
+
var manager = param.manager, children = param.children;
|
|
551
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AudioManagerContext.Provider, {
|
|
552
|
+
value: manager,
|
|
553
|
+
children: children
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
function useAudio() {
|
|
557
|
+
var manager = (0, import_react.useContext)(AudioManagerContext);
|
|
558
|
+
if (!manager) {
|
|
559
|
+
throw new Error("useAudio must be used within an <AudioManagerProvider>.");
|
|
560
|
+
}
|
|
561
|
+
return manager;
|
|
562
|
+
}
|
|
563
|
+
function useAudioEvent(event, handler) {
|
|
564
|
+
var manager = useAudio();
|
|
565
|
+
var handlerRef = (0, import_react.useRef)(handler);
|
|
566
|
+
handlerRef.current = handler;
|
|
567
|
+
(0, import_react.useEffect)(function() {
|
|
568
|
+
var subscription = manager.on(event, function(payload) {
|
|
569
|
+
return handlerRef.current(payload);
|
|
570
|
+
});
|
|
571
|
+
return function() {
|
|
572
|
+
return subscription.destroy();
|
|
573
|
+
};
|
|
574
|
+
}, [
|
|
575
|
+
manager,
|
|
576
|
+
event
|
|
577
|
+
]);
|
|
578
|
+
}
|
|
579
|
+
function useGlobalVolume() {
|
|
580
|
+
var manager = useAudio();
|
|
581
|
+
var _ref = _sliced_to_array((0, import_react.useState)(function() {
|
|
582
|
+
return manager.getGlobalVolume();
|
|
583
|
+
}), 2), volume = _ref[0], setVolumeState = _ref[1];
|
|
584
|
+
var setVolume = (0, import_react.useCallback)(function(next) {
|
|
585
|
+
manager.setGlobalVolume(next);
|
|
586
|
+
setVolumeState(next);
|
|
587
|
+
}, [
|
|
588
|
+
manager
|
|
589
|
+
]);
|
|
590
|
+
return [
|
|
591
|
+
volume,
|
|
592
|
+
setVolume
|
|
593
|
+
];
|
|
594
|
+
}
|
|
595
|
+
function usePreload(urls) {
|
|
596
|
+
var manager = useAudio();
|
|
597
|
+
var urlsKey = JSON.stringify(urls);
|
|
598
|
+
var _ref = _sliced_to_array((0, import_react.useState)({
|
|
599
|
+
status: "idle",
|
|
600
|
+
error: null
|
|
601
|
+
}), 2), state = _ref[0], setState = _ref[1];
|
|
602
|
+
(0, import_react.useEffect)(function() {
|
|
603
|
+
var list = JSON.parse(urlsKey);
|
|
604
|
+
if (list.length === 0) {
|
|
605
|
+
setState({
|
|
606
|
+
status: "idle",
|
|
607
|
+
error: null
|
|
608
|
+
});
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
var cancelled = false;
|
|
612
|
+
setState({
|
|
613
|
+
status: "loading",
|
|
614
|
+
error: null
|
|
615
|
+
});
|
|
616
|
+
manager.preloadUrls(list).then(function() {
|
|
617
|
+
if (!cancelled) setState({
|
|
618
|
+
status: "loaded",
|
|
619
|
+
error: null
|
|
620
|
+
});
|
|
621
|
+
}, function(error) {
|
|
622
|
+
if (!cancelled) setState({
|
|
623
|
+
status: "error",
|
|
624
|
+
error: error
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
return function() {
|
|
628
|
+
cancelled = true;
|
|
629
|
+
};
|
|
630
|
+
}, [
|
|
631
|
+
manager,
|
|
632
|
+
urlsKey
|
|
633
|
+
]);
|
|
634
|
+
return state;
|
|
635
|
+
}
|
|
478
636
|
// Annotate the CommonJS export names for ESM import in node:
|
|
479
637
|
0 && (module1.exports = {
|
|
480
|
-
AudioManager: AudioManager
|
|
638
|
+
AudioManager: AudioManager,
|
|
639
|
+
AudioManagerProvider: AudioManagerProvider,
|
|
640
|
+
useAudio: useAudio,
|
|
641
|
+
useAudioEvent: useAudioEvent,
|
|
642
|
+
useGlobalVolume: useGlobalVolume,
|
|
643
|
+
usePreload: usePreload
|
|
481
644
|
});
|
|
482
645
|
if (__exports != exports1) module1.exports = exports1;
|
|
483
646
|
return module1.exports;
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/afei/github/howler-audio-manager/dist/index.umd.js","../src/index.ts","../src/manager.ts"],"names":["g","f","exports","module","define","amd","globalThis","self","__exports","__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","index_exports","AudioManager","default","import_howler","require","options","inject","effectCache","Map","urlAudioCache","preloadPromises","activeUrlHowl","globalVolume","listeners","HowlConstructor","HowlClass","Howl","HowlerInstance","HowlerObj","Howler","defaultVolume","setGlobalVolume","hasEffect","has","loadEffect","url","unload","howl","src","preload","on","emit","type","_id","error","set","loadEffects","map","keys","forEach","typedKey","isNaN","Number","playEffect","console","warn","String","playing","stop","volume","off","once","soundId","onStart","onEnd","onStop","onError","play","stopEffect","removeEffect","delete","preloadUrl","existing","state","Promise","resolve","pending","createUrlHowl","promise","reject","settle","then","preloadUrls","urls","playUrl","isSingleton","singleton","loop","stopUrl","event","handler","Set","add","destroy","payload","err","mute","muted","stopAll","clear"],"mappings":"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","sourcesContent":["/*!\n * name: @jswork/howler-audio-manager\n * description: Howler.js audio manager, enum-driven sounds for H5.\n * homepage: https://js.work\n * version: 1.0.1\n * date: 2026-10-20 18:54:06\n * license: MIT\n */\n(function(g,f){if(typeof exports==\"object\"&&typeof module<\"u\"){module.exports=f()}else if(\"function\"==typeof define && define.amd){define(\"urlLiteral\",f)}else {g[\"urlLiteral\"]=f()}}(typeof globalThis < \"u\" ? globalThis : typeof self < \"u\" ? self : this,function(){var exports={};var __exports=exports;var module={exports};\n\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/index.ts\nvar index_exports = {};\n__export(index_exports, {\n AudioManager: () => AudioManager,\n default: () => AudioManager\n});\nmodule.exports = __toCommonJS(index_exports);\n\n// src/manager.ts\nvar import_howler = require(\"howler\");\nvar AudioManager = class {\n constructor(options, inject) {\n this.effectCache = /* @__PURE__ */ new Map();\n this.urlAudioCache = /* @__PURE__ */ new Map();\n this.preloadPromises = /* @__PURE__ */ new Map();\n this.activeUrlHowl = null;\n this.globalVolume = 1;\n this.listeners = /* @__PURE__ */ new Map();\n this.HowlConstructor = inject?.HowlClass ?? import_howler.Howl;\n this.HowlerInstance = inject?.HowlerObj ?? import_howler.Howler;\n if (typeof options?.defaultVolume === \"number\") {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n hasEffect(key) {\n return this.effectCache.has(key);\n }\n loadEffect(key, url) {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key).unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"effect\", key, url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.emit(\"error\", { type: \"effect\", key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n loadEffects(map) {\n Object.keys(map).forEach((key) => {\n const typedKey = isNaN(Number(key)) ? key : Number(key);\n this.loadEffect(typedKey, map[key]);\n });\n }\n playEffect(key, options) {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return void 0;\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"effect\", key, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"effect\", key, soundId });\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"effect\", key, soundId });\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"effect\", key, soundId, error });\n });\n return howl.play();\n }\n stopEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n removeEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n preloadUrl(url) {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === \"loaded\") {\n return Promise.resolve();\n }\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n const howl = existing && existing.state() !== \"unloaded\" ? existing : this.createUrlHowl(url);\n const promise = new Promise((resolve, reject) => {\n howl.once(\"load\", () => {\n resolve();\n });\n howl.once(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n return promise;\n }\n preloadUrls(urls) {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n createUrlHowl(url) {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"url\", url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n this.emit(\"error\", { type: \"url\", url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n playUrl(url, options) {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n const isSingleton = options?.singleton !== false;\n if (isSingleton && this.activeUrlHowl && this.activeUrlHowl !== howl && this.activeUrlHowl.playing()) {\n this.activeUrlHowl.stop();\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (options?.loop !== void 0) {\n howl.loop(options.loop);\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"url\", url, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"url\", url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n return howl.play();\n }\n stopUrl(url) {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n on(event, handler) {\n const set = this.listeners.get(event) ?? /* @__PURE__ */ new Set();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n emit(event, payload) {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n setGlobalVolume(volume) {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n mute(muted) {\n this.HowlerInstance.mute(muted);\n }\n stopAll() {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n destroy() {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n AudioManager\n});\n\nif(__exports != exports)module.exports = exports;return module.exports}));\n","export * from './types';\nexport { AudioManager } from './manager';\nexport { AudioManager as default } from './manager';\n","import { Howl, Howler } from 'howler';\nimport type {\n AudioManagerOptions,\n PlayOptions,\n PlayUrlOptions,\n AudioEventType,\n AudioEventPayload,\n EventSubscription,\n SoundResourceMap\n} from './types';\n\nexport class AudioManager<K extends string | number = string> {\n private effectCache = new Map<K, Howl>();\n private urlAudioCache = new Map<string, Howl>();\n private preloadPromises = new Map<string, Promise<void>>();\n private activeUrlHowl: Howl | null = null;\n private globalVolume = 1.0;\n private listeners = new Map<AudioEventType, Set<(payload: AudioEventPayload<K>) => void>>();\n\n private HowlConstructor: typeof Howl;\n private HowlerInstance: typeof Howler;\n\n constructor(\n options?: AudioManagerOptions,\n inject?: { HowlClass?: typeof Howl; HowlerObj?: typeof Howler }\n ) {\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === 'number') {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n\n hasEffect(key: K): boolean {\n return this.effectCache.has(key);\n }\n\n loadEffect(key: K, url: string): void {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key)!.unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'effect', key, url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.emit('error', { type: 'effect', key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n\n loadEffects(map: SoundResourceMap<K>): void {\n Object.keys(map).forEach((key) => {\n const typedKey = (isNaN(Number(key)) ? key : Number(key)) as K;\n this.loadEffect(typedKey, map[key as any]);\n });\n }\n\n playEffect(key: K, options?: PlayOptions): number | undefined {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return undefined;\n }\n\n if (howl.playing()) {\n howl.stop();\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'effect', key, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'effect', key, soundId });\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'effect', key, soundId });\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'effect', key, soundId, error });\n });\n\n return howl.play();\n }\n\n stopEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n\n removeEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n\n preloadUrl(url: string): Promise<void> {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === 'loaded') {\n return Promise.resolve();\n }\n\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n\n // A howl in a terminal failed state (unloaded after error) must be replaced\n // so a fresh instance can retry the load.\n const howl = existing && existing.state() !== 'unloaded' ? existing : this.createUrlHowl(url);\n\n const promise = new Promise<void>((resolve, reject) => {\n howl.once('load', () => {\n resolve();\n });\n\n howl.once('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n\n return promise;\n }\n\n preloadUrls(urls: string[]): Promise<void[]> {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n\n private createUrlHowl(url: string): Howl {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'url', url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n this.emit('error', { type: 'url', url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n\n playUrl(url: string, options?: PlayUrlOptions): number {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n\n // Singleton mutex: stop currently active URL audio if singleton !== false\n const isSingleton = options?.singleton !== false;\n if (\n isSingleton &&\n this.activeUrlHowl &&\n this.activeUrlHowl !== howl &&\n this.activeUrlHowl.playing()\n ) {\n this.activeUrlHowl.stop();\n }\n\n // Overlap prevention on the same audio instance\n if (howl.playing()) {\n howl.stop();\n }\n\n if (options?.loop !== undefined) {\n howl.loop(options.loop);\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'url', url, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'url', url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n return howl.play();\n }\n\n stopUrl(url: string): void {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n\n on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription {\n const set = this.listeners.get(event) ?? new Set<(payload: AudioEventPayload<K>) => void>();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n\n protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n\n setGlobalVolume(volume: number): void {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n\n mute(muted: boolean): void {\n this.HowlerInstance.mute(muted);\n }\n\n stopAll(): void {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n\n destroy(): void {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/afei/github/howler-audio-manager/dist/index.umd.js","../src/index.ts","../src/manager.ts","../src/react.tsx"],"names":["g","f","exports","module","define","amd","globalThis","self","__exports","__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","index_exports","AudioManager","AudioManagerProvider","default","useAudio","useAudioEvent","useGlobalVolume","usePreload","import_howler","require","options","inject","effectCache","Map","urlAudioCache","preloadPromises","activeUrlHowl","globalVolume","listeners","HowlConstructor","HowlClass","Howl","HowlerInstance","HowlerObj","Howler","defaultVolume","setGlobalVolume","hasEffect","has","loadEffect","url","unload","howl","src","preload","on","emit","type","_id","error","set","loadEffects","map","keys","forEach","typedKey","isNaN","Number","playEffect","console","warn","String","playing","stop","volume","off","once","soundId","onStart","onEnd","onStop","onError","play","stopEffect","removeEffect","delete","preloadUrl","existing","state","Promise","resolve","pending","createUrlHowl","promise","reject","settle","then","preloadUrls","urls","playUrl","isSingleton","singleton","loop","stopUrl","event","handler","Set","add","destroy","payload","err","getGlobalVolume","mute","muted","stopAll","clear","import_react","import_jsx_runtime","AudioManagerContext","createContext","manager","children","jsx","Provider","useContext","Error","handlerRef","useRef","current","useEffect","subscription","useState","setVolumeState","setVolume","useCallback","next","urlsKey","JSON","stringify","status","setState","list","parse","length","cancelled"],"mappings":"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","sourcesContent":["/*!\n * name: @jswork/howler-audio-manager\n * description: Howler.js audio manager, enum-driven sounds for H5.\n * homepage: https://js.work\n * version: 1.0.2\n * date: 2026-10-20 20:03:06\n * license: MIT\n */\n(function(g,f){if(typeof exports==\"object\"&&typeof module<\"u\"){module.exports=f()}else if(\"function\"==typeof define && define.amd){define(\"urlLiteral\",f)}else {g[\"urlLiteral\"]=f()}}(typeof globalThis < \"u\" ? globalThis : typeof self < \"u\" ? self : this,function(){var exports={};var __exports=exports;var module={exports};\n\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/index.ts\nvar index_exports = {};\n__export(index_exports, {\n AudioManager: () => AudioManager,\n AudioManagerProvider: () => AudioManagerProvider,\n default: () => AudioManager,\n useAudio: () => useAudio,\n useAudioEvent: () => useAudioEvent,\n useGlobalVolume: () => useGlobalVolume,\n usePreload: () => usePreload\n});\nmodule.exports = __toCommonJS(index_exports);\n\n// src/manager.ts\nvar import_howler = require(\"howler\");\nvar AudioManager = class {\n constructor(options, inject) {\n this.effectCache = /* @__PURE__ */ new Map();\n this.urlAudioCache = /* @__PURE__ */ new Map();\n this.preloadPromises = /* @__PURE__ */ new Map();\n this.activeUrlHowl = null;\n this.globalVolume = 1;\n this.listeners = /* @__PURE__ */ new Map();\n this.HowlConstructor = inject?.HowlClass ?? import_howler.Howl;\n this.HowlerInstance = inject?.HowlerObj ?? import_howler.Howler;\n if (typeof options?.defaultVolume === \"number\") {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n hasEffect(key) {\n return this.effectCache.has(key);\n }\n loadEffect(key, url) {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key).unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"effect\", key, url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.emit(\"error\", { type: \"effect\", key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n loadEffects(map) {\n Object.keys(map).forEach((key) => {\n const typedKey = isNaN(Number(key)) ? key : Number(key);\n this.loadEffect(typedKey, map[key]);\n });\n }\n playEffect(key, options) {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return void 0;\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"effect\", key, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"effect\", key, soundId });\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"effect\", key, soundId });\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"effect\", key, soundId, error });\n });\n return howl.play();\n }\n stopEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n removeEffect(key) {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n preloadUrl(url) {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === \"loaded\") {\n return Promise.resolve();\n }\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n const howl = existing && existing.state() !== \"unloaded\" ? existing : this.createUrlHowl(url);\n const promise = new Promise((resolve, reject) => {\n howl.once(\"load\", () => {\n resolve();\n });\n howl.once(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n return promise;\n }\n preloadUrls(urls) {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n createUrlHowl(url) {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on(\"load\", () => {\n this.emit(\"load\", { type: \"url\", url });\n });\n howl.on(\"loaderror\", (_id, error) => {\n this.urlAudioCache.delete(url);\n this.emit(\"error\", { type: \"url\", url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n playUrl(url, options) {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n const isSingleton = options?.singleton !== false;\n if (isSingleton && this.activeUrlHowl && this.activeUrlHowl !== howl && this.activeUrlHowl.playing()) {\n this.activeUrlHowl.stop();\n }\n if (howl.playing()) {\n howl.stop();\n }\n if (options?.loop !== void 0) {\n howl.loop(options.loop);\n }\n if (typeof options?.volume === \"number\") {\n howl.volume(options.volume);\n }\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n howl.off(\"play\");\n howl.off(\"end\");\n howl.off(\"stop\");\n howl.off(\"playerror\");\n howl.once(\"play\", (soundId) => {\n options?.onStart?.(soundId);\n this.emit(\"play\", { type: \"url\", url, soundId });\n });\n howl.once(\"end\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit(\"end\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"stop\", (soundId) => {\n if (typeof options?.volume === \"number\") {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit(\"stop\", { type: \"url\", url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n howl.once(\"playerror\", (soundId, error) => {\n options?.onError?.(soundId, error);\n this.emit(\"error\", { type: \"url\", url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n return howl.play();\n }\n stopUrl(url) {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n on(event, handler) {\n const set = this.listeners.get(event) ?? /* @__PURE__ */ new Set();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n emit(event, payload) {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n getGlobalVolume() {\n return this.globalVolume;\n }\n setGlobalVolume(volume) {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n mute(muted) {\n this.HowlerInstance.mute(muted);\n }\n stopAll() {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n destroy() {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n};\n\n// src/react.tsx\nvar import_react = require(\"react\");\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nvar AudioManagerContext = (0, import_react.createContext)(null);\nfunction AudioManagerProvider({\n manager,\n children\n}) {\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AudioManagerContext.Provider, { value: manager, children });\n}\nfunction useAudio() {\n const manager = (0, import_react.useContext)(AudioManagerContext);\n if (!manager) {\n throw new Error(\"useAudio must be used within an <AudioManagerProvider>.\");\n }\n return manager;\n}\nfunction useAudioEvent(event, handler) {\n const manager = useAudio();\n const handlerRef = (0, import_react.useRef)(handler);\n handlerRef.current = handler;\n (0, import_react.useEffect)(() => {\n const subscription = manager.on(event, (payload) => handlerRef.current(payload));\n return () => subscription.destroy();\n }, [manager, event]);\n}\nfunction useGlobalVolume() {\n const manager = useAudio();\n const [volume, setVolumeState] = (0, import_react.useState)(() => manager.getGlobalVolume());\n const setVolume = (0, import_react.useCallback)(\n (next) => {\n manager.setGlobalVolume(next);\n setVolumeState(next);\n },\n [manager]\n );\n return [volume, setVolume];\n}\nfunction usePreload(urls) {\n const manager = useAudio();\n const urlsKey = JSON.stringify(urls);\n const [state, setState] = (0, import_react.useState)({ status: \"idle\", error: null });\n (0, import_react.useEffect)(() => {\n const list = JSON.parse(urlsKey);\n if (list.length === 0) {\n setState({ status: \"idle\", error: null });\n return;\n }\n let cancelled = false;\n setState({ status: \"loading\", error: null });\n manager.preloadUrls(list).then(\n () => {\n if (!cancelled) setState({ status: \"loaded\", error: null });\n },\n (error) => {\n if (!cancelled) setState({ status: \"error\", error });\n }\n );\n return () => {\n cancelled = true;\n };\n }, [manager, urlsKey]);\n return state;\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n AudioManager,\n AudioManagerProvider,\n useAudio,\n useAudioEvent,\n useGlobalVolume,\n usePreload\n});\n\nif(__exports != exports)module.exports = exports;return module.exports}));\n","export * from './types';\nexport { AudioManager } from './manager';\nexport { AudioManager as default } from './manager';\nexport * from './react';\n\n","import { Howl, Howler } from 'howler';\nimport type {\n AudioManagerOptions,\n PlayOptions,\n PlayUrlOptions,\n AudioEventType,\n AudioEventPayload,\n EventSubscription,\n SoundResourceMap\n} from './types';\n\nexport class AudioManager<K extends string | number = string> {\n private effectCache = new Map<K, Howl>();\n private urlAudioCache = new Map<string, Howl>();\n private preloadPromises = new Map<string, Promise<void>>();\n private activeUrlHowl: Howl | null = null;\n private globalVolume = 1.0;\n private listeners = new Map<AudioEventType, Set<(payload: AudioEventPayload<K>) => void>>();\n\n private HowlConstructor: typeof Howl;\n private HowlerInstance: typeof Howler;\n\n constructor(\n options?: AudioManagerOptions,\n inject?: { HowlClass?: typeof Howl; HowlerObj?: typeof Howler }\n ) {\n this.HowlConstructor = inject?.HowlClass ?? Howl;\n this.HowlerInstance = inject?.HowlerObj ?? Howler;\n if (typeof options?.defaultVolume === 'number') {\n this.setGlobalVolume(options.defaultVolume);\n }\n }\n\n hasEffect(key: K): boolean {\n return this.effectCache.has(key);\n }\n\n loadEffect(key: K, url: string): void {\n if (this.effectCache.has(key)) {\n this.effectCache.get(key)!.unload();\n }\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'effect', key, url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.emit('error', { type: 'effect', key, url, error });\n });\n this.effectCache.set(key, howl);\n }\n\n loadEffects(map: SoundResourceMap<K>): void {\n Object.keys(map).forEach((key) => {\n const typedKey = (isNaN(Number(key)) ? key : Number(key)) as K;\n this.loadEffect(typedKey, map[key as any]);\n });\n }\n\n playEffect(key: K, options?: PlayOptions): number | undefined {\n const howl = this.effectCache.get(key);\n if (!howl) {\n console.warn(`[AudioManager] Effect not found for key: ${String(key)}.`);\n return undefined;\n }\n\n if (howl.playing()) {\n howl.stop();\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'effect', key, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'effect', key, soundId });\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'effect', key, soundId });\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'effect', key, soundId, error });\n });\n\n return howl.play();\n }\n\n stopEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.stop();\n }\n }\n\n removeEffect(key: K): void {\n const howl = this.effectCache.get(key);\n if (howl) {\n howl.unload();\n this.effectCache.delete(key);\n }\n }\n\n preloadUrl(url: string): Promise<void> {\n const existing = this.urlAudioCache.get(url);\n if (existing && existing.state() === 'loaded') {\n return Promise.resolve();\n }\n\n const pending = this.preloadPromises.get(url);\n if (pending) {\n return pending;\n }\n\n // A howl in a terminal failed state (unloaded after error) must be replaced\n // so a fresh instance can retry the load.\n const howl = existing && existing.state() !== 'unloaded' ? existing : this.createUrlHowl(url);\n\n const promise = new Promise<void>((resolve, reject) => {\n howl.once('load', () => {\n resolve();\n });\n\n howl.once('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n reject(error);\n });\n });\n\n this.preloadPromises.set(url, promise);\n const settle = () => {\n if (this.preloadPromises.get(url) === promise) {\n this.preloadPromises.delete(url);\n }\n };\n promise.then(settle, settle);\n\n return promise;\n }\n\n preloadUrls(urls: string[]): Promise<void[]> {\n return Promise.all(urls.map((url) => this.preloadUrl(url)));\n }\n\n private createUrlHowl(url: string): Howl {\n const howl = new this.HowlConstructor({\n src: [url],\n preload: true\n });\n howl.on('load', () => {\n this.emit('load', { type: 'url', url });\n });\n howl.on('loaderror', (_id: number, error: unknown) => {\n this.urlAudioCache.delete(url);\n this.emit('error', { type: 'url', url, error });\n });\n this.urlAudioCache.set(url, howl);\n return howl;\n }\n\n playUrl(url: string, options?: PlayUrlOptions): number {\n const howl = this.urlAudioCache.get(url) || this.createUrlHowl(url);\n\n // Singleton mutex: stop currently active URL audio if singleton !== false\n const isSingleton = options?.singleton !== false;\n if (\n isSingleton &&\n this.activeUrlHowl &&\n this.activeUrlHowl !== howl &&\n this.activeUrlHowl.playing()\n ) {\n this.activeUrlHowl.stop();\n }\n\n // Overlap prevention on the same audio instance\n if (howl.playing()) {\n howl.stop();\n }\n\n if (options?.loop !== undefined) {\n howl.loop(options.loop);\n }\n\n if (typeof options?.volume === 'number') {\n howl.volume(options.volume);\n }\n\n if (isSingleton) {\n this.activeUrlHowl = howl;\n }\n\n // Clear handlers from previous plays to avoid stale/duplicate callbacks.\n howl.off('play');\n howl.off('end');\n howl.off('stop');\n howl.off('playerror');\n\n howl.once('play', (soundId: number) => {\n options?.onStart?.(soundId);\n this.emit('play', { type: 'url', url, soundId });\n });\n\n howl.once('end', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onEnd?.(soundId);\n this.emit('end', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl && !howl.loop()) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('stop', (soundId: number) => {\n if (typeof options?.volume === 'number') {\n howl.volume(1);\n }\n options?.onStop?.(soundId);\n this.emit('stop', { type: 'url', url, soundId });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n howl.once('playerror', (soundId: number, error: unknown) => {\n options?.onError?.(soundId, error);\n this.emit('error', { type: 'url', url, soundId, error });\n if (this.activeUrlHowl === howl) {\n this.activeUrlHowl = null;\n }\n });\n\n return howl.play();\n }\n\n stopUrl(url: string): void {\n const howl = this.urlAudioCache.get(url);\n if (howl) {\n howl.stop();\n }\n }\n\n on(event: AudioEventType, handler: (payload: AudioEventPayload<K>) => void): EventSubscription {\n const set = this.listeners.get(event) ?? new Set<(payload: AudioEventPayload<K>) => void>();\n this.listeners.set(event, set);\n set.add(handler);\n return {\n destroy: () => {\n set.delete(handler);\n }\n };\n }\n\n protected emit(event: AudioEventType, payload: AudioEventPayload<K>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(`[AudioManager] Error in event listener for ${event}:`, err);\n }\n });\n }\n }\n\n getGlobalVolume(): number {\n return this.globalVolume;\n }\n\n setGlobalVolume(volume: number): void {\n this.globalVolume = volume;\n this.HowlerInstance.volume(volume);\n }\n\n mute(muted: boolean): void {\n this.HowlerInstance.mute(muted);\n }\n\n stopAll(): void {\n this.HowlerInstance.stop();\n this.activeUrlHowl = null;\n }\n\n destroy(): void {\n this.effectCache.forEach((howl) => howl.unload());\n this.urlAudioCache.forEach((howl) => howl.unload());\n this.effectCache.clear();\n this.urlAudioCache.clear();\n this.preloadPromises.clear();\n this.listeners.clear();\n this.activeUrlHowl = null;\n }\n}\n","import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { AudioManager } from './manager';\nimport type { AudioEventType, AudioEventPayload } from './types';\n\nconst AudioManagerContext = createContext<AudioManager<any> | null>(null);\n\nexport interface AudioManagerProviderProps<K extends string | number = string> {\n manager: AudioManager<K>;\n children?: ReactNode;\n}\n\nexport function AudioManagerProvider<K extends string | number = string>({\n manager,\n children\n}: AudioManagerProviderProps<K>) {\n return (\n <AudioManagerContext.Provider value={manager}>\n {children}\n </AudioManagerContext.Provider>\n );\n}\n\nexport function useAudio<K extends string | number = string>(): AudioManager<K> {\n const manager = useContext(AudioManagerContext);\n if (!manager) {\n throw new Error('useAudio must be used within an <AudioManagerProvider>.');\n }\n return manager as unknown as AudioManager<K>;\n}\n\nexport function useAudioEvent<K extends string | number = string>(\n event: AudioEventType,\n handler: (payload: AudioEventPayload<K>) => void\n): void {\n const manager = useAudio<K>();\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const subscription = manager.on(event, (payload) => handlerRef.current(payload));\n return () => subscription.destroy();\n }, [manager, event]);\n}\n\nexport function useGlobalVolume(): [number, (volume: number) => void] {\n const manager = useAudio();\n const [volume, setVolumeState] = useState(() => manager.getGlobalVolume());\n\n const setVolume = useCallback(\n (next: number) => {\n manager.setGlobalVolume(next);\n setVolumeState(next);\n },\n [manager]\n );\n\n return [volume, setVolume];\n}\n\nexport interface PreloadState {\n status: 'idle' | 'loading' | 'loaded' | 'error';\n error: unknown;\n}\n\nexport function usePreload(urls: string[]): PreloadState {\n const manager = useAudio();\n const urlsKey = JSON.stringify(urls);\n const [state, setState] = useState<PreloadState>({ status: 'idle', error: null });\n\n useEffect(() => {\n const list: string[] = JSON.parse(urlsKey);\n if (list.length === 0) {\n setState({ status: 'idle', error: null });\n return;\n }\n\n let cancelled = false;\n setState({ status: 'loading', error: null });\n\n manager.preloadUrls(list).then(\n () => {\n if (!cancelled) setState({ status: 'loaded', error: null });\n },\n (error) => {\n if (!cancelled) setState({ status: 'error', error });\n }\n );\n\n return () => {\n cancelled = true;\n };\n }, [manager, urlsKey]);\n\n return state;\n}\n\n\n"]}
|
package/package.json
CHANGED
|
@@ -1,53 +1,68 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@jswork/howler-audio-manager",
|
|
3
|
-
"version": "1.0.1",
|
|
4
2
|
"description": "Howler.js audio manager, enum-driven sounds for H5.",
|
|
5
|
-
"npmClient": "pnpm",
|
|
6
|
-
"main": "dist/index.cjs.js",
|
|
7
|
-
"module": "dist/index.esm.js",
|
|
8
|
-
"types": "dist/index.d.ts",
|
|
9
|
-
"files": [
|
|
10
|
-
"src",
|
|
11
|
-
"dist"
|
|
12
|
-
],
|
|
13
|
-
"prettier": "@jswork/style-guide/prettier",
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsup",
|
|
16
|
-
"test": "bun test",
|
|
17
|
-
"release": "release-it"
|
|
18
|
-
},
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"peerDependencies": {
|
|
21
|
-
"howler": "^2.2.4"
|
|
22
|
-
},
|
|
23
|
-
"peerDependenciesMeta": {
|
|
24
|
-
"howler": {
|
|
25
|
-
"optional": false
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
3
|
"devDependencies": {
|
|
4
|
+
"@happy-dom/global-registrator": "^21.0.3",
|
|
29
5
|
"@jswork/style-guide": "^1.0.3",
|
|
30
6
|
"@jswork/tsup-banner": "^1.0.4",
|
|
31
7
|
"@swc/core": "^1.5.5",
|
|
8
|
+
"@testing-library/react": "^16.3.3",
|
|
32
9
|
"@types/howler": "^2.2.12",
|
|
33
10
|
"@types/jest": "^29.5.12",
|
|
34
11
|
"@types/node": "^20.12.11",
|
|
12
|
+
"@types/react": "^18.3.31",
|
|
13
|
+
"@types/react-dom": "^18.3.7",
|
|
35
14
|
"esbuild-plugin-replace": "^1.4.0",
|
|
36
15
|
"esbuild-plugin-umd-wrapper": "^3.0.0",
|
|
37
16
|
"howler": "^2.2.4",
|
|
38
17
|
"prettier": "^3.3.3",
|
|
18
|
+
"react": "^18.3.1",
|
|
19
|
+
"react-dom": "^18.3.1",
|
|
39
20
|
"tsup": "^8.2.4",
|
|
40
21
|
"typescript": "^4.9.5"
|
|
41
22
|
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
46
27
|
"homepage": "https://js.work",
|
|
47
28
|
"keywords": [
|
|
48
29
|
"howler",
|
|
49
30
|
"audio-manager",
|
|
50
31
|
"h5-audio",
|
|
51
|
-
"sound-effects"
|
|
52
|
-
|
|
32
|
+
"sound-effects",
|
|
33
|
+
"audio-management",
|
|
34
|
+
"howlerjs",
|
|
35
|
+
"dual-caching",
|
|
36
|
+
"audio-playback",
|
|
37
|
+
"memory-leak-prevention"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"main": "dist/index.cjs.js",
|
|
41
|
+
"module": "dist/index.esm.js",
|
|
42
|
+
"name": "@jswork/howler-audio-manager",
|
|
43
|
+
"npmClient": "pnpm",
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"howler": "^2.2.4",
|
|
46
|
+
"react": ">=16.8.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"howler": {
|
|
50
|
+
"optional": false
|
|
51
|
+
},
|
|
52
|
+
"react": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"prettier": "@jswork/style-guide/prettier",
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public",
|
|
59
|
+
"registry": "https://registry.npmjs.org"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsup",
|
|
63
|
+
"release": "release-it",
|
|
64
|
+
"test": "bun test"
|
|
65
|
+
},
|
|
66
|
+
"types": "dist/index.d.ts",
|
|
67
|
+
"version": "1.0.2"
|
|
53
68
|
}
|