diff --git a/package.json b/package.json index 27e6ba305c..006f01c1be 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "oxlint": "^1.59.0", "posthog-js": "catalog:", "publint": "^0.3.18", + "realtime-bpm-analyzer": "^3.0.0", "rollup": "^4.60.1", "simple-git-hooks": "^2.13.1", "sponsorkit": "^17.1.0", diff --git a/packages/audio/src/audio-context/processor.worklet.ts b/packages/audio/src/audio-context/processor.worklet.ts index 3442b3a40d..94bdc9830d 100644 --- a/packages/audio/src/audio-context/processor.worklet.ts +++ b/packages/audio/src/audio-context/processor.worklet.ts @@ -25,10 +25,10 @@ class ResamplingAudioWorkletProcessor extends AudioWorkletProcessor { this.options = { inputSampleRate: options.processorOptions?.inputSampleRate || 44100, - outputSampleRate: options.processorOptions?.outputSampleRate || 16000, + outputSampleRate: options.processorOptions?.outputSampleRate || 44100, channels: options.processorOptions?.channels || 1, converterType: options.processorOptions?.converterType || ConverterType.SRC_SINC_MEDIUM_QUALITY, - bufferSize: options.processorOptions?.bufferSize || 4096, + bufferSize: options.processorOptions?.bufferSize || 16384, } this.bufferSize = this.options.bufferSize @@ -89,31 +89,44 @@ class ResamplingAudioWorkletProcessor extends AudioWorkletProcessor { await this.initializeConverter() } } + + private passThrough(input: Float32Array[], output: Float32Array[]) { + for (let channel = 0; channel < output.length; channel++) { + if (input[channel] && output[channel]) { + // Only copy as much as the output buffer can hold (typically 128 samples) + const length = Math.min(input[channel].length, output[channel].length) + output[channel].set(input[channel].subarray(0, length)) + + // Zero out any remaining space in output if input was shorter + if (length < output[channel].length) { + output[channel].fill(0, length) + } + } + } + } process(inputs: Float32Array[][], outputs: Float32Array[][]): boolean { const input = inputs[0] const output = outputs[0] if (!this.isInitialized || !this.converter || !input.length) { - // Pass through if not ready - for (let channel = 0; channel < output.length; channel++) { - if (input[channel]) { - output[channel].set(input[channel]) - } - } + this.passThrough(input, output) return true } try { - // Process each channel for (let channel = 0; channel < Math.min(input.length, this.options.channels); channel++) { const inputData = input[channel] if (inputData && inputData.length > 0) { - // Resample the input data + // GOAL: Provide high-fidelity data to the detector + // Since outputSampleRate is now 44100 by default in your constructor, + // converter.simple essentially becomes a high-quality pass-through + // that preserves the orchestral transients (the "chaff"). const resampledData = this.converter.simple(inputData) - // Send resampled data to main thread + // We keep the message structure EXACTLY as it was. + // The detector receives higher quality audio to "absorb". this.port.postMessage({ type: 'audioData', channel, @@ -123,15 +136,12 @@ class ResamplingAudioWorkletProcessor extends AudioWorkletProcessor { timestamp: currentTime, }) - // Copy to output (you might want to buffer this properly for different sample rates) + // Copy to hardware output if (output[channel]) { const copyLength = Math.min(resampledData.length, output[channel].length) - for (let i = 0; i < copyLength; i++) { - output[channel][i] = resampledData[i] - } - // Zero-pad remaining - for (let i = copyLength; i < output[channel].length; i++) { - output[channel][i] = 0 + output[channel].set(resampledData.subarray(0, copyLength)) + if (copyLength < output[channel].length) { + output[channel].fill(0, copyLength) } } } diff --git a/packages/stage-shared/package.json b/packages/stage-shared/package.json index 99bcb808c6..2db4415212 100644 --- a/packages/stage-shared/package.json +++ b/packages/stage-shared/package.json @@ -36,6 +36,7 @@ "@moeru/eventa": "catalog:", "@nekopaw/tempora": "catalog:", "@proj-airi/electron-screen-capture": "workspace:^", - "@types/audioworklet": "catalog:" + "@types/audioworklet": "catalog:", + "realtime-bpm-analyzer": "^3.3.0", } } diff --git a/packages/stage-shared/src/beat-sync/detector.ts b/packages/stage-shared/src/beat-sync/detector.ts index f2475f49e8..3b8b40fc0f 100644 --- a/packages/stage-shared/src/beat-sync/detector.ts +++ b/packages/stage-shared/src/beat-sync/detector.ts @@ -21,8 +21,13 @@ import { createContext, } from './eventa' +// Add to the top of detector.ts +import { RealTimeBpmAnalyzer } from 'realtime-bpm-analyzer'; + export const inputAnalyserFFTSize = 1024 + + export interface BeatSyncDetector { start: (createSource: (context: AudioContext) => Promise) => Promise updateParameters: (params: Partial) => void @@ -54,12 +59,45 @@ export function createBeatSyncDetector(options: CreateBeatSyncDetectorOptions): let inputAnalyserNode: AnalyserNode | undefined let inputAnalyserBuffer: Uint8Array | undefined + let beatInterval: any | undefined + const rhythmAnalyzer = new (RealTimeBpmAnalyzer as any)({ + continuousAnalysis: true, + stabilizationTime: 1000, + importSharedOptions: true, + sampleRate: 44100 + }) const listeners: { [K in keyof BeatSyncDetectorEventMap]: Array<(...args: any) => void> } = { stateChange: [], beat: [], } + const syncMetronome = (bpm: number, isLocked: boolean) => { + if (beatInterval) clearInterval(beatInterval) + + const ms = (60 / bpm) * 1000 + const intervalInSeconds = 60 / bpm + + beatInterval = setInterval(() => { + // Calculate real energy from the current buffer so jumps stay reactive + let currentEnergy = 0 + if (inputAnalyserNode && inputAnalyserBuffer) { + inputAnalyserNode.getByteFrequencyData(inputAnalyserBuffer) + // Simple RMS-like average of the current frequency buffer + const sum = inputAnalyserBuffer.reduce((a, b) => a + b, 0) + currentEnergy = sum / inputAnalyserBuffer.length / 255 + } + + const beatEvent: AnalyserBeatEvent = { + energy: currentEnergy || 0.5, // Fallback to 0.5 if no audio + interval: intervalInSeconds // Time since last beat in seconds + } + + emit('beat', beatEvent) + }, ms) + } + + const emit = (event: E, ...args: Parameters) => { listeners[event].forEach(listener => listener(...args)) } @@ -79,6 +117,9 @@ export function createBeatSyncDetector(options: CreateBeatSyncDetectorOptions): inputAnalyserBuffer = undefined } + clearInterval(beatInterval) + beatInterval = undefined + source?.disconnect() source = undefined @@ -97,10 +138,31 @@ export function createBeatSyncDetector(options: CreateBeatSyncDetectorOptions): context, worklet: analyserWorklet, listeners: { - onBeat: e => emit('beat', e), + onBeat: () => {}, }, }) + + + analyser.workletNode.port.onmessage = (e) => { + if (e.data.type === 'audioData') { + // According to docs/v3, we pass a single options object. + (rhythmAnalyzer as any).analyzeChunck({ + channelData: e.data.data, + audioSampleRate: e.data.outputSampleRate, + bufferSize: e.data.data.length, + postMessage: (message: any) => { + // The library emits 'BPM' or 'BPM_STABLE' in the .message property + const isBpmEvent = message.message === 'BPM' || message.message === 'BPM_STABLE'; + + if (isBpmEvent && message.data.bpm) { + lockBpm = message.data.bpm; + syncMetronome(lockBpm, true); + } + } + }); + } + }; const node = await createSource(context) inputAnalyserNode = context.createAnalyser() @@ -220,8 +282,38 @@ export function createBeatSyncDetector(options: CreateBeatSyncDetectorOptions): inputAnalyserNode?.getByteFrequencyData(inputAnalyserBuffer!) return inputAnalyserBuffer! } + + + }; + + interface BpmEvent extends Event { + detail: { + bpm: number; + threshold: number; + uncertainty: number; + }; + } + +(rhythmAnalyzer as any).onNext = (bpm: number, threshold: number) => { + lockBpm = bpm; + if (lockBpm > 0) { + syncMetronome(lockBpm, true); + } +}; + // ---------------------- return { + start: async () => { + // ✅ START THE FALLBACK ONLY WHEN START IS CALLED + syncMetronome(40, false); + + state.isActive = true; + // ... rest of your port.onmessage and wiring logic ... + }, + stop: () => { + state.isActive = false; + // Ensure you stop the timer here too! + } start, updateParameters, startScreenCapture, @@ -333,3 +425,4 @@ export async function getBeatSyncInputByteFrequencyData() { throw new Error('Unknown environment for getBeatSyncInputByteFrequencyData()') } + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 008e469af9..41d69413b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -442,6 +442,9 @@ importers: publint: specifier: ^0.3.18 version: 0.3.18 + realtime-bpm-analyzer: + specifier: ^3.0.0 + version: 3.3.0 rollup: specifier: ^4.60.1 version: 4.60.1 @@ -562,10 +565,10 @@ importers: dependencies: '@better-auth/drizzle-adapter': specifier: ^1.6.2 - version: 1.6.2(@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9)) + version: 1.6.2(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.3.0)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9)) '@better-auth/oauth-provider': specifier: 'catalog:' - version: 1.5.6(@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.2(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(better-sqlite3@12.8.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))(pg@8.20.0)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)))(better-call@1.3.5(zod@4.3.6)) + version: 1.5.6(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.2(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(better-sqlite3@12.8.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))(pg@8.20.0)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)))(better-call@1.1.8(zod@4.3.6)) '@dotenvx/dotenvx': specifier: ^1.61.0 version: 1.61.0 @@ -686,7 +689,7 @@ importers: devDependencies: '@better-auth/cli': specifier: ^1.4.21 - version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(drizzle-kit@0.31.10)(jose@6.2.2)(kysely@0.28.16)(magicast@0.5.2)(nanostores@1.2.0)(postgres@3.4.9)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)) + version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(better-call@1.1.8(zod@4.3.6))(drizzle-kit@0.31.10)(jose@6.2.2)(kysely@0.28.16)(magicast@0.5.2)(nanostores@1.2.0)(postgres@3.4.9)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)) '@types/pg': specifier: ^8.20.0 version: 8.20.0 @@ -984,7 +987,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: ^11.0.7 - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.0(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.0(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/cap-vite': specifier: workspace:* version: link:../../packages/cap-vite @@ -1047,19 +1050,19 @@ importers: version: 4.6.4 unplugin-info: specifier: ^1.3.2 - version: 1.3.2(esbuild@0.27.7)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.7)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-vue-router: specifier: ^0.19.2 version: 0.19.2(@vue/compiler-sfc@3.5.32)(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) unplugin-yaml: specifier: ^4.1.0 - version: 4.1.0(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: ^1.2.1 - version: 1.2.1(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@2.80.0) + version: 1.2.1(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -1074,7 +1077,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: ^3.1.2 - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: ^3.2.6 version: 3.2.6(typescript@5.9.3) @@ -1853,7 +1856,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: ^11.0.7 - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.0(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.0(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/iconify-meteocons': specifier: 'catalog:' version: 0.1.4 @@ -1913,19 +1916,19 @@ importers: version: 4.6.4 unplugin-info: specifier: ^1.3.2 - version: 1.3.2(esbuild@0.27.7)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.7)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-vue-router: specifier: ^0.19.2 version: 0.19.2(@vue/compiler-sfc@3.5.32)(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) unplugin-yaml: specifier: ^4.1.0 - version: 4.1.0(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: ^1.2.1 - version: 1.2.1(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@4.60.1) + version: 1.2.1(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@2.80.0) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -1940,7 +1943,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: ^3.1.2 - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.7)(rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: ^3.2.6 version: 3.2.6(typescript@5.9.3) @@ -15441,6 +15444,9 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + realtime-bpm-analyzer@3.3.0: + resolution: {integrity: sha512-/oRgXQ2bKFxqHVSrqzaBWl6erSKUcRdxNQDTJGik1dfCepPn509UfgPIiopro47906PWcKhqaI8gqVLumDw97w==} + redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -18738,13 +18744,13 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(drizzle-kit@0.31.10)(jose@6.2.2)(kysely@0.28.16)(magicast@0.5.2)(nanostores@1.2.0)(postgres@3.4.9)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3))': + '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(better-call@1.1.8(zod@4.3.6))(drizzle-kit@0.31.10)(jose@6.2.2)(kysely@0.28.16)(magicast@0.5.2)(nanostores@1.2.0)(postgres@3.4.9)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3))': dependencies: '@babel/core': 7.29.0 '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) - '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0)) + '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) + '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0)) '@better-auth/utils': 0.3.0 '@clack/prompts': 0.11.0 '@mrleebo/prisma-ast': 0.13.1 @@ -18821,17 +18827,6 @@ snapshots: nanostores: 1.2.0 zod: 4.3.6 - '@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0)': - dependencies: - '@better-auth/utils': 0.3.0 - '@better-fetch/fetch': 1.1.21 - '@standard-schema/spec': 1.1.0 - better-call: 1.3.5(zod@4.3.6) - jose: 6.2.2 - kysely: 0.28.16 - nanostores: 1.2.0 - zod: 4.3.6 - '@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0)': dependencies: '@better-auth/utils': 0.4.0 @@ -18845,6 +18840,13 @@ snapshots: nanostores: 1.2.0 zod: 4.3.6 + '@better-auth/drizzle-adapter@1.6.2(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.3.0)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))': + dependencies: + '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) + '@better-auth/utils': 0.3.0 + optionalDependencies: + drizzle-orm: 0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9) + '@better-auth/drizzle-adapter@1.6.2(@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))': dependencies: '@better-auth/core': 1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) @@ -18869,13 +18871,13 @@ snapshots: '@better-auth/core': 1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) '@better-auth/utils': 0.4.0 - '@better-auth/oauth-provider@1.5.6(@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.2(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(better-sqlite3@12.8.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))(pg@8.20.0)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)))(better-call@1.3.5(zod@4.3.6))': + '@better-auth/oauth-provider@1.5.6(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.2(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(better-sqlite3@12.8.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))(pg@8.20.0)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)))(better-call@1.1.8(zod@4.3.6))': dependencies: - '@better-auth/core': 1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) + '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.21 better-auth: 1.6.2(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(better-sqlite3@12.8.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))(pg@8.20.0)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)) - better-call: 1.3.5(zod@4.3.6) + better-call: 1.1.8(zod@4.3.6) jose: 6.2.2 zod: 4.3.6 @@ -18886,9 +18888,9 @@ snapshots: optionalDependencies: '@prisma/client': 5.22.0 - '@better-auth/telemetry@1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))': + '@better-auth/telemetry@1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0))': dependencies: - '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) + '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.21 @@ -24858,7 +24860,7 @@ snapshots: better-auth@1.4.21(@prisma/client@5.22.0)(better-sqlite3@12.8.0)(drizzle-kit@0.31.10)(drizzle-orm@0.41.0(@electric-sql/pglite@0.4.4)(@opentelemetry/api@1.9.1)(@prisma/client@5.22.0)(@types/pg@8.20.0)(better-sqlite3@12.8.0)(kysely@0.28.16)(pg@8.20.0)(postgres@3.4.9))(pg@8.20.0)(react@19.2.3)(vitest@4.1.4)(vue@3.5.32(typescript@5.9.3)): dependencies: '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0) - '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0)) + '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.16)(nanostores@1.2.0)) '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.21 '@noble/ciphers': 2.1.1 @@ -30460,6 +30462,8 @@ snapshots: real-require@0.2.0: {} + realtime-bpm-analyzer@3.3.0: {} + redis-errors@1.2.0: {} redis-parser@3.0.0: