@ephia/dova-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/EphiaBinding-BvRmlqqC.d.ts +36 -0
- package/dist/EphiaFloatingButton-CxiF86VW.d.ts +65 -0
- package/dist/EphiaTextarea-B4_CAVUg.d.ts +183 -0
- package/dist/NativeBinding-ChG0GeSz.d.ts +53 -0
- package/dist/TargetBinding-BKGQwUMc.d.ts +89 -0
- package/dist/TiptapBinding-B-agfV2H.d.ts +45 -0
- package/dist/Transport-zdeA4Pou.d.ts +63 -0
- package/dist/audio-state-kZ3KSvux.d.ts +39 -0
- package/dist/chunk-35AJK2IO.js +1 -0
- package/dist/chunk-35AJK2IO.js.map +1 -0
- package/dist/chunk-3LXZODL4.js +886 -0
- package/dist/chunk-3LXZODL4.js.map +1 -0
- package/dist/chunk-5IK5TLSK.js +67 -0
- package/dist/chunk-5IK5TLSK.js.map +1 -0
- package/dist/chunk-7E43RY75.js +9 -0
- package/dist/chunk-7E43RY75.js.map +1 -0
- package/dist/chunk-A5UEXJ5R.js +183 -0
- package/dist/chunk-A5UEXJ5R.js.map +1 -0
- package/dist/chunk-AEE554FT.js +51 -0
- package/dist/chunk-AEE554FT.js.map +1 -0
- package/dist/chunk-DIEWY3IT.js +1332 -0
- package/dist/chunk-DIEWY3IT.js.map +1 -0
- package/dist/chunk-EGIAN7FH.js +18 -0
- package/dist/chunk-EGIAN7FH.js.map +1 -0
- package/dist/chunk-EMOEAPVU.js +486 -0
- package/dist/chunk-EMOEAPVU.js.map +1 -0
- package/dist/chunk-IDC7FHIZ.js +40 -0
- package/dist/chunk-IDC7FHIZ.js.map +1 -0
- package/dist/chunk-ITJFN3VM.js +601 -0
- package/dist/chunk-ITJFN3VM.js.map +1 -0
- package/dist/chunk-K24GNU27.js +22 -0
- package/dist/chunk-K24GNU27.js.map +1 -0
- package/dist/chunk-LXMCRXXF.js +778 -0
- package/dist/chunk-LXMCRXXF.js.map +1 -0
- package/dist/chunk-MJCEOOLW.js +122 -0
- package/dist/chunk-MJCEOOLW.js.map +1 -0
- package/dist/chunk-N7U5M3VZ.js +33 -0
- package/dist/chunk-N7U5M3VZ.js.map +1 -0
- package/dist/chunk-PSYX674B.js +27 -0
- package/dist/chunk-PSYX674B.js.map +1 -0
- package/dist/chunk-RFQRV7ML.js +33 -0
- package/dist/chunk-RFQRV7ML.js.map +1 -0
- package/dist/chunk-THNHRV2B.js +18 -0
- package/dist/chunk-THNHRV2B.js.map +1 -0
- package/dist/chunk-VSLGR64U.js +62 -0
- package/dist/chunk-VSLGR64U.js.map +1 -0
- package/dist/chunk-W2ZP674X.js +346 -0
- package/dist/chunk-W2ZP674X.js.map +1 -0
- package/dist/chunk-YWZUMUYE.js +695 -0
- package/dist/chunk-YWZUMUYE.js.map +1 -0
- package/dist/client-options-Uo6jXO8k.d.ts +64 -0
- package/dist/connection-state-Bk33YprE.d.ts +32 -0
- package/dist/core/bindings/index.d.ts +24 -0
- package/dist/core/bindings/index.js +1025 -0
- package/dist/core/bindings/index.js.map +1 -0
- package/dist/core/index.d.ts +383 -0
- package/dist/core/index.js +1284 -0
- package/dist/core/index.js.map +1 -0
- package/dist/createEphiaClient-BhdZ183V.d.ts +69 -0
- package/dist/devices/speechmike/index.d.ts +148 -0
- package/dist/devices/speechmike/index.js +40 -0
- package/dist/devices/speechmike/index.js.map +1 -0
- package/dist/headless/index.d.ts +10 -0
- package/dist/headless/index.js +25 -0
- package/dist/headless/index.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +38 -0
- package/dist/react/index.js +70 -0
- package/dist/react/index.js.map +1 -0
- package/dist/rich-editor/index.d.ts +46 -0
- package/dist/rich-editor/index.js +13 -0
- package/dist/rich-editor/index.js.map +1 -0
- package/dist/schema-B2ycPlNB.d.ts +87 -0
- package/dist/session-APaXR48R.d.ts +12 -0
- package/dist/shared/index.d.ts +16 -0
- package/dist/shared/index.js +30 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/style.css +1093 -0
- package/dist/testing/index.d.ts +84 -0
- package/dist/testing/index.js +36 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types-D5SXPSwR.d.ts +32 -0
- package/dist/ui/index.d.ts +30 -0
- package/dist/ui/index.js +34 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/useEphiaSpeechMike-CjD7DWnh.d.ts +64 -0
- package/package.json +110 -0
- package/src/core/audio/audio-worklet-source.ts +30 -0
- package/src/core/audio/index.ts +3 -0
- package/src/core/audio/voice-level-meter.test.ts +27 -0
- package/src/core/audio/voice-level-meter.ts +270 -0
- package/src/core/bindings/EphiaBinding.ts +41 -0
- package/src/core/bindings/SegmentBindingBridge.test.ts +422 -0
- package/src/core/bindings/SegmentBindingBridge.ts +377 -0
- package/src/core/bindings/TargetBinding.ts +142 -0
- package/src/core/bindings/adapters/NativeAdapter.test.ts +85 -0
- package/src/core/bindings/adapters/NativeAdapter.ts +216 -0
- package/src/core/bindings/adapters/ProseMirrorAdapter.ts +231 -0
- package/src/core/bindings/adapters/index.ts +2 -0
- package/src/core/bindings/binding-factory.ts +78 -0
- package/src/core/bindings/detect-editor-type.ts +87 -0
- package/src/core/bindings/index.ts +13 -0
- package/src/core/bindings/insertion-boundary.test.ts +38 -0
- package/src/core/bindings/insertion-boundary.ts +26 -0
- package/src/core/bindings/native/NativeBinding.test.ts +277 -0
- package/src/core/bindings/native/NativeBinding.ts +239 -0
- package/src/core/bindings/resolver.ts +18 -0
- package/src/core/bindings/targets/codemirror.binding.ts +293 -0
- package/src/core/bindings/targets/contenteditable.binding.ts +452 -0
- package/src/core/bindings/targets/index.ts +10 -0
- package/src/core/bindings/targets/monaco.binding.ts +315 -0
- package/src/core/bindings/targets/tiptap.binding.test.ts +417 -0
- package/src/core/bindings/targets/tiptap.binding.ts +1192 -0
- package/src/core/bindings/tiptap/TiptapBinding.test.ts +63 -0
- package/src/core/bindings/tiptap/TiptapBinding.ts +464 -0
- package/src/core/bindings/types.ts +41 -0
- package/src/core/client/EphiaAudioClient.ts +654 -0
- package/src/core/client/audio-capture.ts +263 -0
- package/src/core/client/client-options.ts +39 -0
- package/src/core/client/client-state.ts +18 -0
- package/src/core/client/constants.ts +23 -0
- package/src/core/client/session-api.ts +415 -0
- package/src/core/connection/connection-state.ts +78 -0
- package/src/core/connection/index.ts +6 -0
- package/src/core/index.ts +47 -0
- package/src/core/operations/textToDocumentOperations.test.ts +69 -0
- package/src/core/operations/textToDocumentOperations.ts +92 -0
- package/src/core/runtime/DictationRuntime.test.ts +578 -0
- package/src/core/runtime/DictationRuntime.ts +434 -0
- package/src/core/runtime/TranscriptApplier.test.ts +355 -0
- package/src/core/runtime/TranscriptApplier.ts +229 -0
- package/src/core/runtime/index.ts +18 -0
- package/src/core/session/index.ts +2 -0
- package/src/core/session/session-machine.test.ts +16 -0
- package/src/core/session/session-machine.ts +59 -0
- package/src/core/targets/EditorContextCollector.ts +71 -0
- package/src/core/targets/TargetManager.test.ts +194 -0
- package/src/core/targets/TargetManager.ts +194 -0
- package/src/core/targets/index.ts +10 -0
- package/src/core/text-processing/index.ts +11 -0
- package/src/core/text-processing/overlap.test.ts +35 -0
- package/src/core/text-processing/overlap.ts +101 -0
- package/src/core/text-processing/voice-formatting.normalizer.test.ts +132 -0
- package/src/core/text-processing/voice-formatting.normalizer.ts +284 -0
- package/src/core/transcript/client-transcript.reducer.ts +366 -0
- package/src/core/transcript/client-transcript.state.ts +25 -0
- package/src/core/transcript/index.ts +19 -0
- package/src/core/transcript/transcript.assembler.test.ts +205 -0
- package/src/core/transcript/transcript.assembler.ts +152 -0
- package/src/core/transcript/transcript.reducer.test.ts +199 -0
- package/src/core/transcript/transcript.reducer.ts +771 -0
- package/src/core/transcript/transcript.state.ts +123 -0
- package/src/core/transport/LiveKitTransport.publish.test.ts +226 -0
- package/src/core/transport/LiveKitTransport.ts +459 -0
- package/src/core/transport/MockTransport.ts +231 -0
- package/src/core/transport/Transport.ts +82 -0
- package/src/debug/sdk-debug-collector.ts +79 -0
- package/src/devices/index.ts +2 -0
- package/src/devices/speechmike/__tests__/EphiaSpeechMikeProvider.test.tsx +99 -0
- package/src/devices/speechmike/__tests__/speechmike-audio-resolver.test.ts +96 -0
- package/src/devices/speechmike/__tests__/speechmike-button-router.test.ts +66 -0
- package/src/devices/speechmike/__tests__/speechmike-device-manager.test.ts +201 -0
- package/src/devices/speechmike/__tests__/speechmike-led-controller.test.ts +68 -0
- package/src/devices/speechmike/browser.ts +80 -0
- package/src/devices/speechmike/constants.ts +74 -0
- package/src/devices/speechmike/dictation-support-loader.ts +81 -0
- package/src/devices/speechmike/index.ts +11 -0
- package/src/devices/speechmike/react/EphiaSpeechMikeContext.ts +34 -0
- package/src/devices/speechmike/react/EphiaSpeechMikeProvider.tsx +287 -0
- package/src/devices/speechmike/react/useEphiaSpeechMike.ts +26 -0
- package/src/devices/speechmike/speechmike-audio-resolver.ts +58 -0
- package/src/devices/speechmike/speechmike-button-router.ts +73 -0
- package/src/devices/speechmike/speechmike-device-manager.ts +461 -0
- package/src/devices/speechmike/speechmike-led-controller.ts +78 -0
- package/src/devices/speechmike/types.ts +96 -0
- package/src/dictation_support.d.ts +31 -0
- package/src/global.d.ts +10 -0
- package/src/headless/createEphiaClient.ts +220 -0
- package/src/headless/index.ts +18 -0
- package/src/index.ts +89 -0
- package/src/react/EphiaAuto.tsx +87 -0
- package/src/react/components/EphiaDictationButton.tsx +88 -0
- package/src/react/components/EphiaStatusBar.tsx +59 -0
- package/src/react/components/EphiaTextarea.tsx +295 -0
- package/src/react/ephia-react.css +318 -0
- package/src/react/hooks/targets/index.ts +3 -0
- package/src/react/hooks/targets/useEphiaCodemirror.ts +35 -0
- package/src/react/hooks/targets/useEphiaMonaco.ts +35 -0
- package/src/react/hooks/targets/useEphiaTiptap.ts +23 -0
- package/src/react/hooks/useEphia.lifecycle.test.tsx +389 -0
- package/src/react/hooks/useEphia.ts +367 -0
- package/src/react/hooks/useEphiaDiscardTarget.ts +53 -0
- package/src/react/hooks/useEphiaServerEvent.ts +33 -0
- package/src/react/hooks/useEphiaTarget.ts +47 -0
- package/src/react/hooks/useEphiaTranscript.ts +22 -0
- package/src/react/index.ts +58 -0
- package/src/react/provider/EphiaContext.ts +63 -0
- package/src/react/provider/EphiaInternalContext.ts +32 -0
- package/src/react/provider/EphiaProvider.tsx +373 -0
- package/src/react/registry/binding-factory.ts +7 -0
- package/src/react/registry/detect-editor-type.ts +2 -0
- package/src/react/registry/events.ts +37 -0
- package/src/react/registry/registries/CodeMirrorInstanceRegistry.ts +24 -0
- package/src/react/registry/registries/MonacoInstanceRegistry.ts +23 -0
- package/src/react/registry/registries/TargetRegistry.ts +327 -0
- package/src/react/registry/registries/TiptapInstanceRegistry.ts +43 -0
- package/src/react/registry/registries/index.ts +5 -0
- package/src/react/store/create-ephia-store.ts +36 -0
- package/src/react/store/types.ts +41 -0
- package/src/react/utils/flash-range.ts +24 -0
- package/src/react/utils/index.ts +1 -0
- package/src/rich-editor/adapters/tiptap.test.ts +86 -0
- package/src/rich-editor/adapters/tiptap.ts +23 -0
- package/src/rich-editor/index.ts +3 -0
- package/src/rich-editor/types.ts +24 -0
- package/src/rich-editor/use-ephia-rich-editor.test.tsx +202 -0
- package/src/rich-editor/use-ephia-rich-editor.ts +47 -0
- package/src/shared/config/endpoint.test.ts +45 -0
- package/src/shared/config/endpoint.ts +39 -0
- package/src/shared/config/schema.ts +32 -0
- package/src/shared/effective-text.ts +13 -0
- package/src/shared/errors/EphiaSdkError.ts +54 -0
- package/src/shared/errors/messages.ts +40 -0
- package/src/shared/index.ts +27 -0
- package/src/shared/state/audio-state.ts +45 -0
- package/src/shared/state/index.ts +2 -0
- package/src/shared/store/document-store.ts +32 -0
- package/src/shared/store/index.ts +2 -0
- package/src/shared/types/editors.ts +28 -0
- package/src/shared/types/session.ts +12 -0
- package/src/style.css +2 -0
- package/src/testing/index.tsx +60 -0
- package/src/ui/assets/ephia-logo.svg +4 -0
- package/src/ui/components/EphiaLogo.tsx +77 -0
- package/src/ui/index.ts +24 -0
- package/src/ui/primitives/Button.tsx +53 -0
- package/src/ui/primitives/Spinner.tsx +21 -0
- package/src/ui/primitives/index.ts +5 -0
- package/src/ui/recorder/EphiaFloatingButton.tsx +489 -0
- package/src/ui/recorder/MinimalProcessingBars.tsx +122 -0
- package/src/ui/recorder/StandardIntensityVisualizer.tsx +148 -0
- package/src/ui/recorder/appearance.ts +9 -0
- package/src/ui/recorder/index.ts +8 -0
- package/src/ui/theme.css +775 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/transport/LiveKitTransport.ts","../src/debug/sdk-debug-collector.ts","../src/core/client/client-state.ts","../src/core/session/session-machine.ts","../src/core/runtime/TranscriptApplier.ts","../src/core/targets/TargetManager.ts"],"sourcesContent":["/**\n * Transport LiveKit — implémentation du Transport Ephia pour l'agent ephia-transcribe-agent.\n *\n * Wire V2 :\n * • SDK → backend : ephia.client.control\n * • backend → SDK : ephia.server.event\n */\n\nimport {\n Room,\n RoomEvent,\n DataPacket_Kind,\n LocalAudioTrack,\n LocalTrackPublication,\n ConnectionState,\n ConnectionQuality,\n MediaDeviceFailure,\n AudioPresets,\n Track,\n} from \"livekit-client\";\nimport type { ReconnectContext, ReconnectPolicy } from \"livekit-client\";\nimport type {\n EphiaAudioEvent,\n EphiaClientMessage,\n EphiaServerEvent,\n} from \"ephia-protocol\";\nimport { ephiaWireServerEventSchema, PROTOCOL_VERSION } from \"ephia-protocol\";\nimport { EphiaSdkError } from \"../../shared/errors/EphiaSdkError\";\nimport { dbgRawEvent, dbgState } from \"../../debug/sdk-debug-collector\";\nimport type {\n Transport,\n TransportConnectParams,\n TransportState,\n} from \"./Transport\";\n\nconst TOPIC_CLIENT_CONTROL = \"ephia.client.control\";\nconst TOPIC_SERVER_EVENT = \"ephia.server.event\";\nconst TRACK_MIC = \"ephia-microphone\";\n\nconst textDecoder = new TextDecoder();\nconst textEncoder = new TextEncoder();\n\n// P1 backend : parsing défensif des champs additifs optionnels du payload\n// JSON brut (snake_case) — le backend peut omettre ces champs (versions\n// antérieures, fallback) sans que le transport ne plante.\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nconst RECONNECT_POLICY: ReconnectPolicy = {\n nextRetryDelayInMs(ctx: ReconnectContext): number | null {\n if (ctx.retryCount >= 10) return null;\n const base = Math.min(300 * 2 ** ctx.retryCount, 30_000);\n const jitter = Math.floor(Math.random() * 1000);\n return base + jitter;\n },\n};\n\nfunction uuid(): string {\n const c = globalThis.crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\nexport class LiveKitTransport implements Transport {\n private room: Room | null = null;\n private _eventCallbacks: Set<(event: EphiaAudioEvent) => void> = new Set();\n private _serverEventCallbacks: Set<(event: EphiaServerEvent) => void> = new Set();\n private _stateCallbacks: Set<(state: TransportState) => void> = new Set();\n private _errorCallbacks: Set<(error: EphiaSdkError) => void> = new Set();\n private _state: TransportState = {\n status: \"idle\",\n localAudioPublished: false,\n localAudioMuted: false,\n reconnectCount: 0,\n };\n private _localAudioTrack: LocalTrackPublication | null = null;\n private _krispProcessor: { destroy?: () => void } | null = null;\n\n private _sessionId = \"\";\n private _clientSeq = 0;\n\n async connect(params: TransportConnectParams): Promise<void> {\n if (!this.room) {\n this.room = new Room(this._roomOptions());\n } else if (\n this.room.state === ConnectionState.Connected ||\n this.room.state === ConnectionState.Reconnecting ||\n this.room.state === ConnectionState.Connecting\n ) {\n await this.disconnect(\"reconnect\");\n this.room = new Room(this._roomOptions());\n } else {\n this.room.removeAllListeners();\n }\n\n this._sessionId = (params as any).sessionId ?? params.roomName;\n this._clientSeq = 0;\n\n this._setState({ status: \"connecting\", roomName: params.roomName });\n this._attachRoomListeners(params);\n await this.room.connect(params.livekitUrl, params.token);\n\n if (this.room.state !== ConnectionState.Connected) {\n throw new EphiaSdkError(\n \"transport.connect_failed\",\n `LiveKit room not connected: ${this.room.state}`\n );\n }\n\n this._setState({\n status: \"connected\",\n roomName: this.room.name,\n participantIdentity: this.room.localParticipant?.identity,\n });\n\n }\n\n async prepareConnection(url: string, token?: string): Promise<void> {\n if (!this.room) {\n this.room = new Room(this._roomOptions());\n }\n await this.room.prepareConnection(url, token);\n }\n\n async disconnect(_reason?: string): Promise<void> {\n if (this.room) {\n const room = this.room;\n await room.disconnect();\n room.removeAllListeners();\n this.room = null;\n }\n this._localAudioTrack = null;\n this._setState({\n status: \"disconnected\",\n localAudioPublished: false,\n localAudioMuted: false,\n });\n }\n\n async sendMessage(message: EphiaClientMessage): Promise<void> {\n if (!this.room) {\n throw new EphiaSdkError(\"transport.not_connected\", \"Cannot send message: room is null\");\n }\n if (this.room.state !== ConnectionState.Connected) {\n throw new EphiaSdkError(\"transport.not_connected\", `Cannot send message: room state is ${this.room.state}`);\n }\n\n const wire = this._toWireClientEvent(message);\n await this.room.localParticipant.publishData(textEncoder.encode(JSON.stringify(wire)), {\n reliable: true,\n topic: TOPIC_CLIENT_CONTROL,\n });\n }\n\n onEvent(callback: (event: EphiaAudioEvent) => void): () => void {\n this._eventCallbacks.add(callback);\n return () => this._eventCallbacks.delete(callback);\n }\n\n onServerEvent(callback: (event: EphiaServerEvent) => void): () => void {\n this._serverEventCallbacks.add(callback);\n return () => this._serverEventCallbacks.delete(callback);\n }\n\n onTransportState(callback: (state: TransportState) => void): () => void {\n this._stateCallbacks.add(callback);\n return () => this._stateCallbacks.delete(callback);\n }\n\n onError(callback: (error: EphiaSdkError) => void): () => void {\n this._errorCallbacks.add(callback);\n return () => this._errorCallbacks.delete(callback);\n }\n\n getState(): TransportState {\n return { ...this._state };\n }\n\n getLocalAudioPublication(): LocalTrackPublication | null {\n return this._localAudioTrack;\n }\n\n async publishAudio(track: MediaStreamTrack, options?: { enableNoiseFilter?: boolean }): Promise<void> {\n if (this._localAudioTrack) {\n await this.unpublishAudio();\n }\n if (!this.room) {\n throw new EphiaSdkError(\"transport.connect_failed\", \"Cannot publish audio: room is null\");\n }\n if (this.room.state !== ConnectionState.Connected) {\n throw new EphiaSdkError(\"transport.connect_failed\", `Room not connected: ${this.room.state}`);\n }\n\n this._localAudioTrack = await this.room.localParticipant.publishTrack(track, {\n name: TRACK_MIC,\n source: Track.Source.Microphone,\n audioPreset: AudioPresets.speech,\n } as any);\n\n this._setState({ localAudioMuted: false });\n\n const tryKrisp = options?.enableNoiseFilter === true;\n if (tryKrisp && this._localAudioTrack?.track instanceof LocalAudioTrack) {\n try {\n const { KrispNoiseFilter, isKrispNoiseFilterSupported } = await import(\"@livekit/krisp-noise-filter\");\n if (isKrispNoiseFilterSupported()) {\n const processor = KrispNoiseFilter();\n await this._localAudioTrack.track.setProcessor(processor);\n this._krispProcessor = processor;\n await track.applyConstraints({\n echoCancellation: false,\n noiseSuppression: false,\n autoGainControl: false,\n });\n this._setState({ krispActive: true });\n }\n } catch (err) {\n console.warn(\"[LiveKitTransport] Krisp initialization failed — fallback WebRTC natif\", err);\n }\n }\n }\n\n async unpublishAudio(): Promise<void> {\n if (this._localAudioTrack && this.room) {\n if (this._krispProcessor && this._localAudioTrack.track instanceof LocalAudioTrack) {\n try {\n await this._localAudioTrack.track.stopProcessor();\n } catch { /* ignore */ }\n this._krispProcessor = null;\n }\n await this.room.localParticipant.unpublishTrack(this._localAudioTrack.track!);\n this._localAudioTrack = null;\n this._setState({ krispActive: false, localAudioMuted: false });\n }\n }\n\n async performRpc(method: string, payload: unknown, timeout = 3000): Promise<string> {\n if (!this.room) {\n throw new EphiaSdkError(\"transport.not_connected\", \"Cannot perform RPC: room not available\");\n }\n return this.room.localParticipant.performRpc({\n destinationIdentity: \"ephia-transcribe-agent\",\n method,\n payload: JSON.stringify(payload),\n responseTimeout: timeout,\n });\n }\n\n async switchActiveDevice(kind: MediaDeviceKind, deviceId: string): Promise<boolean> {\n if (!this.room) {\n throw new EphiaSdkError(\"transport.not_connected\", \"Cannot switch device: room not available\");\n }\n return this.room.switchActiveDevice(kind, deviceId);\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private _toWireClientEvent(message: EphiaClientMessage): Record<string, unknown> {\n const msgType = (message as { type: string }).type;\n return {\n protocolVersion: PROTOCOL_VERSION,\n type: msgType,\n eventId: uuid(),\n sessionId: this._sessionId,\n clientSeq: ++this._clientSeq,\n sentAt: new Date().toISOString(),\n payload: this._toWireClientPayload(message),\n };\n }\n\n private _toWireClientPayload(message: EphiaClientMessage): Record<string, unknown> {\n const msgType = (message as { type: string }).type;\n const payload = (message as { payload?: unknown }).payload;\n const raw = payload && typeof payload === \"object\" && !Array.isArray(payload)\n ? (payload as Record<string, unknown>)\n : {};\n\n if (msgType === \"session.reset\") {\n const scope = raw.scope === \"target\" ? \"target\" : \"global\";\n const targetId = asString(raw.targetId) ?? asString(raw.target_id);\n const reason = asString(raw.reason) ?? \"user_explicit\";\n\n if (scope === \"target\" && !targetId) {\n throw new EphiaSdkError(\n \"protocol.invalid_event\",\n \"session.reset target scope requires targetId\"\n );\n }\n\n return scope === \"target\" && targetId\n ? { scope: \"target\", targetId, reason }\n : { scope: \"global\", reason };\n }\n\n if (msgType === \"session.target.changed\") {\n return { targetId: asString(raw.targetId) ?? asString(raw.target_id) ?? \"\" };\n }\n\n if (msgType === \"session.editor_context.update\") {\n return {\n targetId: asString(raw.targetId) ?? asString(raw.target_id) ?? \"\",\n documentEmpty: raw.documentEmpty ?? raw.document_empty ?? false,\n insertionMode: raw.insertionMode ?? raw.insertion_mode ?? \"append\",\n leftContext: raw.leftContext ?? raw.left_context ?? \"\",\n rightContext: raw.rightContext ?? raw.right_context ?? \"\",\n selectedText: raw.selectedText ?? raw.selected_text,\n cursorOffset: raw.cursorOffset ?? raw.cursor_offset,\n };\n }\n\n return raw;\n }\n\n private _attachRoomListeners(_params: TransportConnectParams): void {\n if (!this.room) return;\n\n this.room.on(RoomEvent.ConnectionStateChanged, (lkState) => {\n dbgRawEvent(\"livekit\", \"RoomEvent.ConnectionStateChanged\", { state: lkState });\n switch (lkState) {\n case ConnectionState.Connected:\n this._setState({ status: \"connected\" });\n break;\n case ConnectionState.Disconnected:\n this._setState({ status: \"disconnected\" });\n break;\n case ConnectionState.Reconnecting:\n this._setState({\n status: \"reconnecting\",\n reconnectCount: this._state.reconnectCount + 1,\n });\n break;\n }\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.Reconnecting, () => {\n dbgRawEvent(\"livekit\", \"RoomEvent.Reconnecting\", {});\n this._setState({\n status: \"reconnecting\",\n reconnectCount: this._state.reconnectCount + 1,\n });\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.Reconnected, () => {\n dbgRawEvent(\"livekit\", \"RoomEvent.Reconnected\", {});\n this._setState({ status: \"reconnected\" });\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.ConnectionQualityChanged, (quality) => {\n dbgRawEvent(\"livekit\", \"RoomEvent.ConnectionQualityChanged\", { quality });\n const mapped =\n quality === ConnectionQuality.Excellent ? \"excellent\" :\n quality === ConnectionQuality.Good ? \"good\" :\n quality === ConnectionQuality.Poor ? \"poor\" : \"unknown\";\n this._setState({ connectionQuality: mapped });\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.LocalTrackPublished, () => {\n dbgRawEvent(\"livekit\", \"RoomEvent.LocalTrackPublished\", {});\n this._setState({ localAudioPublished: true });\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.LocalTrackUnpublished, () => {\n dbgRawEvent(\"livekit\", \"RoomEvent.LocalTrackUnpublished\", {});\n this._setState({ localAudioPublished: false });\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.LocalAudioSilenceDetected, () => {\n dbgRawEvent(\"livekit\", \"RoomEvent.LocalAudioSilenceDetected\", {});\n this._setState({ localAudioMuted: true });\n dbgState(\"livekit.transport\", this._state);\n });\n\n this.room.on(RoomEvent.MediaDevicesError, (err) => {\n dbgRawEvent(\"livekit\", \"RoomEvent.MediaDevicesError\", { error: err instanceof Error ? err.message : String(err) });\n const failure = MediaDeviceFailure.getFailure(err);\n switch (failure) {\n case MediaDeviceFailure.PermissionDenied:\n this._emitError(\"audio.permission_denied\", \"Autorisation micro refusée — vérifier les paramètres du navigateur\");\n break;\n case MediaDeviceFailure.NotFound:\n this._emitError(\"audio.no_input_device\", \"Aucun microphone détecté\");\n break;\n case MediaDeviceFailure.DeviceInUse:\n this._emitError(\"audio.device_in_use\", \"Micro occupé par une autre application\");\n break;\n default:\n this._emitError(\"audio.no_input_device\", err instanceof Error ? err.message : String(err));\n }\n });\n\n // Écoute le topic serveur unique du wire V2.\n this.room.on(RoomEvent.DataReceived, (payload, _participant, kind, topic) => {\n const isReliable = kind === DataPacket_Kind.RELIABLE;\n const isLossy = kind === DataPacket_Kind.LOSSY;\n\n if (topic !== TOPIC_SERVER_EVENT) return;\n if (!isReliable && !isLossy) return;\n\n try {\n const data = JSON.parse(textDecoder.decode(payload)) as Record<string, unknown>;\n dbgRawEvent(\"livekit\", \"RoomEvent.DataReceived\", { topic, kind, data });\n const parsed = ephiaWireServerEventSchema.safeParse(data);\n if (!parsed.success) {\n console.warn(\"[LiveKitTransport] invalid server event schema (ignored):\", parsed.error.issues);\n return;\n }\n this._handleWireServerEvent(parsed.data);\n } catch (err) {\n console.warn(\"[LiveKitTransport] failed to parse data channel payload\", err);\n }\n });\n }\n\n private _handleWireServerEvent(event: EphiaServerEvent): void {\n this._serverEventCallbacks.forEach((cb) => cb(event));\n }\n\n private _roomOptions() {\n return {\n adaptiveStream: false,\n dynacast: false,\n disconnectOnPageLeave: false,\n reconnectPolicy: RECONNECT_POLICY,\n audioCaptureDefaults: {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n };\n }\n\n private _setState(partial: Partial<TransportState>): void {\n const next = { ...this._state, ...partial };\n let changed = false;\n for (const key in partial) {\n if ((next as any)[key] !== (this._state as any)[key]) {\n changed = true;\n break;\n }\n }\n if (!changed) return;\n this._state = next;\n this._stateCallbacks.forEach((cb) => cb(this._state));\n }\n\n private _emitError(code: EphiaSdkError[\"code\"], message: string): void {\n const err = new EphiaSdkError(code, message);\n this._errorCallbacks.forEach((cb) => cb(err));\n }\n}\n","/**\n * Collecteur de debug interne pour l'Ephia SDK.\n * Active uniquement en environnement de développement (Next.js dev).\n * Expose window.__ephia_sdk_debug__ pour inspection externe.\n */\n\nexport interface SdkDebugEntry {\n ts: number;\n source: string;\n type: string;\n payload: unknown;\n}\n\nexport interface SdkDebugStateSnapshot {\n ts: number;\n source: string;\n state: unknown;\n}\n\nexport interface SdkDebugCollector {\n events: SdkDebugEntry[];\n rawEvents: SdkDebugEntry[];\n states: Record<string, SdkDebugStateSnapshot>;\n pushEvent(source: string, type: string, payload: unknown): void;\n pushRawEvent(source: string, type: string, payload: unknown): void;\n setState(source: string, state: unknown): void;\n}\n\nfunction isDev(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return process.env.NODE_ENV === \"development\";\n } catch {\n return false;\n }\n}\n\nlet collector: SdkDebugCollector | null = null;\n\nexport function getSdkDebugCollector(): SdkDebugCollector | null {\n if (!isDev()) return null;\n if (!collector) {\n collector = {\n events: [],\n rawEvents: [],\n states: {},\n pushEvent(source, type, payload) {\n const entry: SdkDebugEntry = { ts: performance.now(), source, type, payload };\n this.events.push(entry);\n if (this.events.length > 3000) this.events.shift();\n },\n pushRawEvent(source, type, payload) {\n const entry: SdkDebugEntry = { ts: performance.now(), source, type, payload };\n this.rawEvents.push(entry);\n if (this.rawEvents.length > 3000) this.rawEvents.shift();\n },\n setState(source, state) {\n this.states[source] = { ts: performance.now(), source, state };\n },\n };\n (window as unknown as Record<string, unknown>).__ephia_sdk_debug__ = collector;\n }\n return collector;\n}\n\n/** Helper: pousse un event brut sans importer le collector explicitement. */\nexport function dbgRawEvent(source: string, type: string, payload: unknown): void {\n getSdkDebugCollector()?.pushRawEvent(source, type, payload);\n}\n\n/** Helper: pousse un event parsé. */\nexport function dbgEvent(source: string, type: string, payload: unknown): void {\n getSdkDebugCollector()?.pushEvent(source, type, payload);\n}\n\n/** Helper: met à jour un snapshot state. */\nexport function dbgState(source: string, state: unknown): void {\n getSdkDebugCollector()?.setState(source, state);\n}\n","/** Alias de EphiaSessionStatus — tous les statuts sont des états valides de la machine. */\nexport type EphiaClientStatus = import(\"../session/session-machine\").EphiaSessionStatus;\n\nexport interface EphiaClientState {\n status: EphiaClientStatus;\n sessionId: string | null;\n roomName: string | null;\n error: { code: string; message: string } | null;\n isMicEnabled: boolean;\n}\n\nexport const initialClientState: EphiaClientState = {\n status: \"idle\",\n sessionId: null,\n roomName: null,\n error: null,\n isMicEnabled: false,\n};\n","/**\n * Machine à états stricte pour les sessions Ephia Audio.\n *\n * Interdit les transitions invalides (ex: recording -> creating_session).\n */\n\nimport { EphiaSdkError } from \"../../shared/errors/EphiaSdkError\";\n\nexport type EphiaSessionStatus =\n | \"idle\"\n | \"creating_session\"\n | \"connecting_transport\"\n | \"ready\"\n | \"recording\"\n | \"paused\"\n | \"finalizing\"\n | \"ended\"\n | \"error\"\n | \"disposed\";\n\nconst ALLOWED_TRANSITIONS: Record<EphiaSessionStatus, EphiaSessionStatus[]> = {\n idle: [\"creating_session\", \"disposed\"],\n creating_session: [\"connecting_transport\", \"error\", \"disposed\"],\n connecting_transport: [\"ready\", \"error\", \"disposed\"],\n ready: [\"recording\", \"finalizing\", \"error\", \"disposed\"],\n recording: [\"paused\", \"finalizing\", \"error\", \"disposed\"],\n paused: [\"recording\", \"finalizing\", \"error\", \"disposed\"],\n // \"paused\" : stop() passe par finalizing pendant le drain backend, puis retombe en paused\n finalizing: [\"paused\", \"ended\", \"error\", \"disposed\"],\n ended: [\"idle\", \"disposed\"],\n error: [\"idle\", \"disposed\"],\n disposed: [],\n};\n\nexport function transitionSessionStatus(\n current: EphiaSessionStatus,\n next: EphiaSessionStatus\n): EphiaSessionStatus {\n if (current === next) return current;\n\n const allowed = ALLOWED_TRANSITIONS[current] ?? [];\n if (!allowed.includes(next)) {\n throw new EphiaSdkError(\n \"client.invalid_state\",\n `Transition interdite: ${current} -> ${next}`,\n { current, next, allowed }\n );\n }\n return next;\n}\n\nexport function canTransition(\n current: EphiaSessionStatus,\n next: EphiaSessionStatus\n): boolean {\n if (current === next) return true;\n const allowed = ALLOWED_TRANSITIONS[current] ?? [];\n return allowed.includes(next);\n}\n","import type { EphiaServerEvent, SegmentOperationPayload } from \"ephia-protocol\";\nimport type { EphiaBinding } from \"../bindings/EphiaBinding\";\nimport type { TargetManager } from \"../targets/TargetManager\";\n\nexport type TranscriptApplierOptions = {\n targetManager: Pick<TargetManager, \"get\" | \"getActiveTarget\" | \"getActiveTargetId\">;\n warn?: (message: string, details?: Record<string, unknown>) => void;\n debug?: (message: string, details?: Record<string, unknown>) => void;\n};\n\nconst MAX_SEEN_EVENTS = 1000;\nconst MAX_PENDING_EVENTS = 50;\n\n/**\n * Applique les events serveur V2 aux bindings éditeurs enregistrés.\n *\n * Règles de stricte V2 :\n * - targetId obligatoire sur segment.operation\n * - eventId déjà vu → ignore\n * - segmentRevision inférieure ou égale à la dernière appliquée → ignore\n * - upsert/absorb atomique (upsert puis remove absorbed)\n * - segment.preview ignoré si un canonical a déjà été appliqué pour ce segment\n * - event/revision non marqués comme vus si le binding n'est pas encore disponible\n */\nexport class TranscriptApplier {\n private readonly targetManager: TranscriptApplierOptions[\"targetManager\"];\n private readonly warn: NonNullable<TranscriptApplierOptions[\"warn\"]>;\n private readonly debug: NonNullable<TranscriptApplierOptions[\"debug\"]>;\n\n /** eventId → déjà traité (idempotence). */\n private readonly seenEventIds = new Set<string>();\n private readonly seenEventQueue: string[] = [];\n\n /** segmentId → dernière segmentRevision appliquée. */\n private readonly latestRevisionBySegment = new Map<string, number>();\n\n /** segmentId → targetId (cache défensif). */\n private readonly segmentTargetMap = new Map<string, string>();\n\n /** segmentIds pour lesquels un canonical a été appliqué avec succès. */\n private readonly canonicalSegments = new Set<string>();\n\n /** Events en attente de montage de leur cible (targetId → events[]). */\n private readonly pendingEvents = new Map<string, EphiaServerEvent[]>();\n\n constructor(options: TranscriptApplierOptions) {\n this.targetManager = options.targetManager;\n this.warn = options.warn ?? ((message, details) => console.warn(message, details));\n this.debug = options.debug ?? (() => {});\n }\n\n handleEvent(event: EphiaServerEvent): void {\n // Check for duplicate WITHOUT marking yet — we only mark after successful application.\n if (this.seenEventIds.has(event.eventId)) {\n this.debug(\"duplicate event ignored\", { eventId: event.eventId, type: event.type });\n return;\n }\n\n if (event.type === \"segment.preview\") {\n // Ignore previews that arrive after a canonical was already applied.\n if (this.canonicalSegments.has(event.payload.segmentId)) {\n this.debug(\"late preview ignored after canonical\", {\n segmentId: event.payload.segmentId,\n });\n this._markEventSeen(event.eventId);\n return;\n }\n\n const binding = this.resolveBinding(event.payload.targetId, event.payload.segmentId);\n // If binding not found, enqueue event for later replay.\n if (!binding) {\n this._enqueuePendingEvent(event, event.payload.targetId);\n return;\n }\n\n this._markEventSeen(event.eventId);\n binding.previewSegment?.({ id: event.payload.segmentId, text: event.payload.text });\n return;\n }\n\n if (event.type === \"segment.operation\") {\n const applied = this.applyOperation(event.payload, event);\n // Only mark as seen if the event was processed (binding found).\n // If binding missing, event is enqueued for retry.\n if (applied) this._markEventSeen(event.eventId);\n }\n }\n\n /**\n * Rejoue les events en attente pour une cible donnée.\n * À appeler après l'enregistrement d'une nouvelle target.\n */\n flushPendingEvents(targetId: string): void {\n const pending = this.pendingEvents.get(targetId);\n if (!pending || pending.length === 0) return;\n\n this.debug(\"flushing pending events\", { targetId, count: pending.length });\n\n const toRetry = [...pending];\n this.pendingEvents.delete(targetId);\n\n for (const event of toRetry) {\n this.handleEvent(event);\n }\n }\n\n private applyOperation(payload: SegmentOperationPayload, event?: EphiaServerEvent): boolean {\n // Resolve binding FIRST — before marking the revision. If binding is absent,\n // return false so the event stays unmarked and can be enqueued for retry.\n const binding = this.resolveBinding(payload.targetId, payload.segmentId);\n if (!binding) {\n if (event && payload.targetId) {\n this._enqueuePendingEvent(event, payload.targetId);\n }\n return false;\n }\n\n if (!this.shouldApplySegmentRevision(payload)) {\n // Revision already surpassed — event is stale but binding exists; mark as seen.\n return true;\n }\n\n if (payload.operation === \"noop\") return true;\n\n if (payload.operation === \"delete\") {\n binding.removeSegment(payload.segmentId);\n if (payload.absorbedSegmentIds?.length) {\n binding.removeSegments(payload.absorbedSegmentIds);\n }\n return true;\n }\n\n // \"upsert\" and \"absorb\" both upsert the primary segment then remove absorbed.\n binding.upsertSegment({\n id: payload.segmentId,\n text: payload.textForInsertion || payload.text,\n stage: payload.stage,\n source: payload.source,\n });\n\n if (payload.stage === \"canonical\") {\n this.canonicalSegments.add(payload.segmentId);\n }\n\n if (payload.absorbedSegmentIds?.length) {\n binding.removeSegments(payload.absorbedSegmentIds);\n }\n\n return true;\n }\n\n private shouldApplySegmentRevision(payload: SegmentOperationPayload): boolean {\n const previous = this.latestRevisionBySegment.get(payload.segmentId);\n const current = payload.segmentRevision;\n\n if (previous !== undefined && current <= previous) {\n this.debug(\"stale segment revision ignored\", {\n segmentId: payload.segmentId,\n previous,\n current,\n });\n return false;\n }\n\n this.latestRevisionBySegment.set(payload.segmentId, current);\n return true;\n }\n\n private resolveBinding(targetId: string | undefined, segmentId: string): EphiaBinding | null {\n if (targetId) {\n const target = this.targetManager.get(targetId);\n const binding = this.asEphiaBinding(target?.binding);\n if (!binding) {\n this.warn(\"[ephia] target not found; transcript event ignored\", {\n targetId,\n segmentId,\n });\n return null;\n }\n this.segmentTargetMap.set(segmentId, targetId);\n return binding;\n }\n\n const mappedTargetId = this.segmentTargetMap.get(segmentId);\n if (mappedTargetId) {\n return this.asEphiaBinding(this.targetManager.get(mappedTargetId)?.binding);\n }\n\n this.warn(\"[ephia] segment.operation missing targetId; event ignored\", {\n segmentId,\n });\n return null;\n }\n\n private _markEventSeen(eventId: string): void {\n this.seenEventIds.add(eventId);\n this.seenEventQueue.push(eventId);\n if (this.seenEventQueue.length > MAX_SEEN_EVENTS) {\n const oldest = this.seenEventQueue.shift();\n if (oldest) this.seenEventIds.delete(oldest);\n }\n }\n\n private asEphiaBinding(binding: unknown): EphiaBinding | null {\n if (!binding) return null;\n if (typeof binding === \"object\" && \"upsertSegment\" in binding) return binding as EphiaBinding;\n return null;\n }\n\n private _enqueuePendingEvent(event: EphiaServerEvent, targetId?: string): void {\n if (!targetId) return;\n\n const queue = this.pendingEvents.get(targetId) ?? [];\n if (queue.length >= MAX_PENDING_EVENTS) {\n this.debug(\"pending events queue full, dropping oldest\", { targetId });\n queue.shift();\n }\n\n queue.push(event);\n this.pendingEvents.set(targetId, queue);\n\n this.debug(\"event enqueued for target\", {\n targetId,\n eventId: event.eventId,\n type: event.type,\n queueSize: queue.length,\n });\n }\n}\n","import type { EphiaClientMessage, ResetReason } from \"ephia-protocol\";\nimport type { EphiaBinding } from \"../bindings/EphiaBinding\";\n\ntype WireClientMessage = EphiaClientMessage | {\n type: string;\n payload?: Record<string, unknown>;\n};\n\nexport type ManagedTarget = {\n id: string;\n binding: EphiaBinding | null;\n element?: HTMLElement;\n mode?: string;\n};\n\n/** Internal stored target — extends ManagedTarget with a registration token. */\ntype StoredTarget = ManagedTarget & { token: symbol };\n\nexport type TargetManagerOptions = {\n sendMessage: (message: WireClientMessage) => Promise<void>;\n flushDelayMs?: number;\n onActiveTargetChange?: (targetId: string | null) => void;\n onTargetRegistered?: (targetId: string) => void;\n};\n\nexport class TargetManager {\n private readonly targets = new Map<string, StoredTarget>();\n private readonly sendMessage: TargetManagerOptions[\"sendMessage\"];\n private readonly flushDelayMs: number;\n private readonly onActiveTargetChange?: (targetId: string | null) => void;\n private readonly onTargetRegistered?: (targetId: string) => void;\n private activeTargetId: string | null = null;\n private flushTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(options: TargetManagerOptions) {\n this.sendMessage = options.sendMessage;\n this.flushDelayMs = options.flushDelayMs ?? 80;\n this.onActiveTargetChange = options.onActiveTargetChange;\n this.onTargetRegistered = options.onTargetRegistered;\n }\n\n /**\n * Register a target and return its registration token.\n * Pass the token to unregister() to prevent stale cleanups from accidentally\n * removing a newer registration with the same id (React StrictMode / remounts).\n */\n register(target: ManagedTarget): symbol {\n const token = Symbol(target.id);\n this.targets.set(target.id, { ...target, token });\n if (this.activeTargetId === null && target.mode !== \"read\") {\n this.setActive(target.id);\n }\n this.onTargetRegistered?.(target.id);\n return token;\n }\n\n /**\n * Unregister a target.\n * When `token` is provided, only unregisters if the stored token matches\n * (prevents a stale cleanup from removing a newer registration).\n * Returns true if the target was removed, false if skipped.\n */\n unregister(targetId: string, token?: symbol): boolean {\n const current = this.targets.get(targetId);\n if (!current) return false;\n\n if (token !== undefined && current.token !== token) {\n // Token mismatch — a newer registration owns this id, skip.\n return false;\n }\n\n this.targets.delete(targetId);\n this.clearFlush(targetId);\n if (this.activeTargetId === targetId) {\n const next = this.firstWritableTargetId();\n this.activeTargetId = null;\n if (next) {\n this.setActive(next);\n } else {\n this.onActiveTargetChange?.(null);\n }\n }\n return true;\n }\n\n get(targetId: string): ManagedTarget | undefined {\n return this.targets.get(targetId);\n }\n\n getActiveTargetId(): string | null {\n return this.activeTargetId;\n }\n\n getActiveTarget(): ManagedTarget | undefined {\n return this.activeTargetId ? this.targets.get(this.activeTargetId) : undefined;\n }\n\n setActive(targetId: string): void {\n if (!this.targets.has(targetId)) return;\n if (this.activeTargetId === targetId) {\n this.scheduleEditorContextFlush(targetId);\n return;\n }\n this.activeTargetId = targetId;\n this.onActiveTargetChange?.(targetId);\n this.send({\n type: \"session.target.changed\",\n payload: { targetId },\n });\n this.scheduleEditorContextFlush(targetId);\n }\n\n syncActiveTarget(): void {\n if (!this.activeTargetId) return;\n this.send({\n type: \"session.target.changed\",\n payload: { targetId: this.activeTargetId },\n });\n this.flushEditorContext(this.activeTargetId);\n }\n\n /** Resync native bindings from current DOM before dictation (manual edits between sessions). */\n prepareForDictation(targetId = this.activeTargetId): void {\n if (!targetId) return;\n const binding = this.targets.get(targetId)?.binding;\n if (binding?.kind === \"native\" && \"prepareForDictation\" in binding) {\n (binding as { prepareForDictation: () => void }).prepareForDictation();\n }\n }\n\n flushEditorContext(targetId = this.activeTargetId): void {\n if (!targetId) return;\n this.clearFlush(targetId);\n const target = this.targets.get(targetId);\n if (!target?.binding) return;\n const context = target.binding.getEditorContext(target.id);\n this.send({\n type: \"session.editor_context.update\",\n payload: context,\n });\n }\n\n scheduleEditorContextFlush(targetId = this.activeTargetId): void {\n if (!targetId) return;\n this.clearFlush(targetId);\n const timer = setTimeout(() => {\n this.flushTimers.delete(targetId);\n this.flushEditorContext(targetId);\n }, this.flushDelayMs);\n this.flushTimers.set(targetId, timer);\n }\n\n reset(\n scope: \"global\" | \"target\",\n targetId = this.activeTargetId,\n reason: ResetReason = \"user_explicit\"\n ): void {\n this.send({\n type: \"session.reset\",\n payload:\n scope === \"target\" && targetId\n ? { scope: \"target\", targetId, reason }\n : { scope: \"global\", reason },\n });\n }\n\n dispose(): void {\n for (const targetId of this.flushTimers.keys()) {\n this.clearFlush(targetId);\n }\n this.targets.clear();\n this.activeTargetId = null;\n }\n\n private firstWritableTargetId(): string | null {\n for (const target of this.targets.values()) {\n if (target.mode !== \"read\") return target.id;\n }\n return null;\n }\n\n private clearFlush(targetId: string): void {\n const timer = this.flushTimers.get(targetId);\n if (timer) clearTimeout(timer);\n this.flushTimers.delete(targetId);\n }\n\n private send(message: WireClientMessage): void {\n this.sendMessage(message).catch(() => {\n // The manager can be mounted before the LiveKit room is connected.\n // A session-active resync flushes the latest active target/context.\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EphiaAudioClient
|
|
3
|
+
} from "./chunk-DIEWY3IT.js";
|
|
4
|
+
import {
|
|
5
|
+
TargetManager,
|
|
6
|
+
TranscriptApplier
|
|
7
|
+
} from "./chunk-LXMCRXXF.js";
|
|
8
|
+
|
|
9
|
+
// src/headless/createEphiaClient.ts
|
|
10
|
+
function createEphiaClient(options) {
|
|
11
|
+
const audioClient = new EphiaAudioClient({
|
|
12
|
+
apiUrl: options.apiUrl,
|
|
13
|
+
apiKey: options.apiKey,
|
|
14
|
+
bearerToken: options.bearerToken,
|
|
15
|
+
clientType: options.clientType,
|
|
16
|
+
sessionOptions: options.language ? { language: options.language } : void 0,
|
|
17
|
+
transport: options.transport
|
|
18
|
+
});
|
|
19
|
+
const targetManager = new TargetManager({
|
|
20
|
+
sendMessage: (message) => audioClient.sendMessage(message)
|
|
21
|
+
});
|
|
22
|
+
const applier = new TranscriptApplier({ targetManager });
|
|
23
|
+
const unsubServerEvent = audioClient.onServerEvent((event) => applier.handleEvent(event));
|
|
24
|
+
function register(target) {
|
|
25
|
+
target.binding.attach();
|
|
26
|
+
const token = targetManager.register({ id: target.id, binding: target.binding, mode: target.mode ?? "write" });
|
|
27
|
+
return () => {
|
|
28
|
+
const removed = targetManager.unregister(target.id, token);
|
|
29
|
+
if (removed) target.binding.detach();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const initialTargets = options.targets ?? [];
|
|
33
|
+
if (options.binding) {
|
|
34
|
+
initialTargets.push({ id: "default", binding: options.binding, mode: "write" });
|
|
35
|
+
}
|
|
36
|
+
const cleanupTargets = initialTargets.map(register);
|
|
37
|
+
async function sendWireMessage(type, payload) {
|
|
38
|
+
await audioClient.sendMessage({ type, payload });
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
on(type, handler) {
|
|
42
|
+
return audioClient.onServerEvent((event) => {
|
|
43
|
+
if (event.type === type) {
|
|
44
|
+
handler(event);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
async start(targetIdOrOptions, maybeOptions) {
|
|
49
|
+
const targetId = typeof targetIdOrOptions === "string" ? targetIdOrOptions : void 0;
|
|
50
|
+
const startOptions = typeof targetIdOrOptions === "string" ? maybeOptions : targetIdOrOptions;
|
|
51
|
+
if (targetId) {
|
|
52
|
+
targetManager.setActive(targetId);
|
|
53
|
+
}
|
|
54
|
+
const activeTargetId = targetId ?? targetManager.getActiveTargetId() ?? void 0;
|
|
55
|
+
targetManager.prepareForDictation(activeTargetId);
|
|
56
|
+
const result = await audioClient.start(
|
|
57
|
+
targetId ? { ...startOptions, initialTargetId: targetId } : startOptions
|
|
58
|
+
);
|
|
59
|
+
targetManager.syncActiveTarget();
|
|
60
|
+
targetManager.flushEditorContext(
|
|
61
|
+
targetId ?? targetManager.getActiveTargetId() ?? void 0
|
|
62
|
+
);
|
|
63
|
+
return result;
|
|
64
|
+
},
|
|
65
|
+
async stop() {
|
|
66
|
+
await audioClient.stop();
|
|
67
|
+
},
|
|
68
|
+
async pause() {
|
|
69
|
+
await audioClient.stop();
|
|
70
|
+
},
|
|
71
|
+
async resume(resumeOptions) {
|
|
72
|
+
const status = audioClient.getState().status;
|
|
73
|
+
if (status === "paused") {
|
|
74
|
+
targetManager.prepareForDictation(targetManager.getActiveTargetId() ?? void 0);
|
|
75
|
+
await audioClient.start(resumeOptions);
|
|
76
|
+
targetManager.syncActiveTarget();
|
|
77
|
+
targetManager.flushEditorContext(targetManager.getActiveTargetId() ?? void 0);
|
|
78
|
+
} else {
|
|
79
|
+
throw new Error("[ephia] resume() requires a paused session; call start() to create a new session");
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
async endSession() {
|
|
83
|
+
await audioClient.endSession();
|
|
84
|
+
},
|
|
85
|
+
async warmupMic() {
|
|
86
|
+
await audioClient.warmupMic();
|
|
87
|
+
},
|
|
88
|
+
registerTarget(target) {
|
|
89
|
+
return register(target);
|
|
90
|
+
},
|
|
91
|
+
async setActiveTarget(targetId) {
|
|
92
|
+
targetManager.setActive(targetId);
|
|
93
|
+
},
|
|
94
|
+
async sendEditorContext(context) {
|
|
95
|
+
await sendWireMessage("session.editor_context.update", context);
|
|
96
|
+
},
|
|
97
|
+
async resetTarget(targetId, reason) {
|
|
98
|
+
await sendWireMessage("session.reset", {
|
|
99
|
+
scope: "target",
|
|
100
|
+
targetId,
|
|
101
|
+
reason: reason ?? "user_explicit"
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
async resetSession(reason) {
|
|
105
|
+
await sendWireMessage("session.reset", {
|
|
106
|
+
scope: "global",
|
|
107
|
+
reason: reason ?? "user_explicit"
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
async dispose() {
|
|
111
|
+
unsubServerEvent();
|
|
112
|
+
cleanupTargets.forEach((cleanup) => cleanup());
|
|
113
|
+
targetManager.dispose();
|
|
114
|
+
await audioClient.dispose();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
createEphiaClient
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=chunk-MJCEOOLW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/headless/createEphiaClient.ts"],"sourcesContent":["import { EphiaAudioClient } from \"../core/client/EphiaAudioClient\";\nimport type { EphiaAudioClientOptions, EphiaStartOptions, EphiaStartResult } from \"../core/client/EphiaAudioClient\";\nimport { TranscriptApplier } from \"../core/runtime/TranscriptApplier\";\nimport { TargetManager } from \"../core/targets/TargetManager\";\nimport type { EphiaBinding } from \"../core/bindings/EphiaBinding\";\nimport type { EditorContext, EphiaServerEvent, ResetReason } from \"ephia-protocol\";\nimport type { Transport } from \"../core/transport/Transport\";\n\nexport type EphiaHeadlessTarget = {\n id: string;\n binding: EphiaBinding;\n mode?: string;\n};\n\nexport type EphiaHeadlessOptions = {\n apiUrl?: string;\n apiKey?: string;\n bearerToken?: string;\n clientType?: string;\n language?: string;\n /** Pre-registered targets. Additional targets can be registered via registerTarget(). */\n targets?: EphiaHeadlessTarget[];\n /** Single target shorthand; equivalent to targets: [{ id: \"default\", binding }]. */\n binding?: EphiaBinding;\n /** Transport override for testing. */\n transport?: Transport;\n};\n\nexport type EphiaHeadlessClient = {\n /**\n * Subscribe to a typed V2 server event. Returns an unsubscriber.\n *\n * @example\n * const unsub = client.on(\"segment.operation\", (event) => { ... });\n * // later:\n * unsub();\n */\n on<T extends EphiaServerEvent[\"type\"]>(\n type: T,\n handler: (event: Extract<EphiaServerEvent, { type: T }>) => void,\n ): () => void;\n\n start(targetId?: string, options?: EphiaStartOptions): Promise<EphiaStartResult>;\n start(options?: EphiaStartOptions): Promise<EphiaStartResult>;\n stop(): Promise<void>;\n pause(): Promise<void>;\n resume(options?: EphiaStartOptions): Promise<void>;\n endSession(): Promise<void>;\n warmupMic(): Promise<void>;\n\n /**\n * Registers a new target binding. Returns an unsubscriber.\n */\n registerTarget(target: EphiaHeadlessTarget): () => void;\n\n /**\n * Notifies the backend that a specific target is now active.\n * Sends session.target.changed + editor_context.update.\n */\n setActiveTarget(targetId: string): Promise<void>;\n\n /**\n * Sends a manual editor context snapshot to the backend.\n * Use when the backend needs cursor/text state without a target switch.\n */\n sendEditorContext(context: EditorContext): Promise<void>;\n\n /**\n * Resets the dictation context for a specific target (clears pending segments).\n */\n resetTarget(targetId: string, reason?: ResetReason): Promise<void>;\n\n /**\n * Resets the global session context (all targets).\n */\n resetSession(reason?: ResetReason): Promise<void>;\n\n dispose(): Promise<void>;\n};\n\nexport function createEphiaClient(options: EphiaHeadlessOptions): EphiaHeadlessClient {\n const audioClient = new EphiaAudioClient({\n apiUrl: options.apiUrl,\n apiKey: options.apiKey,\n bearerToken: options.bearerToken,\n clientType: options.clientType,\n sessionOptions: options.language ? { language: options.language } : undefined,\n transport: options.transport,\n } as EphiaAudioClientOptions);\n\n const targetManager = new TargetManager({\n sendMessage: (message) => audioClient.sendMessage(message as any),\n });\n\n const applier = new TranscriptApplier({ targetManager });\n const unsubServerEvent = audioClient.onServerEvent((event) => applier.handleEvent(event));\n\n function register(target: EphiaHeadlessTarget): () => void {\n target.binding.attach();\n const token = targetManager.register({ id: target.id, binding: target.binding, mode: target.mode ?? \"write\" });\n return () => {\n const removed = targetManager.unregister(target.id, token);\n if (removed) target.binding.detach();\n };\n }\n\n const initialTargets = options.targets ?? [];\n if (options.binding) {\n initialTargets.push({ id: \"default\", binding: options.binding, mode: \"write\" });\n }\n const cleanupTargets = initialTargets.map(register);\n\n async function sendWireMessage(type: string, payload?: Record<string, unknown>): Promise<void> {\n await audioClient.sendMessage({ type, payload } as any);\n }\n\n return {\n on<T extends EphiaServerEvent[\"type\"]>(\n type: T,\n handler: (event: Extract<EphiaServerEvent, { type: T }>) => void,\n ): () => void {\n return audioClient.onServerEvent((event) => {\n if (event.type === type) {\n handler(event as Extract<EphiaServerEvent, { type: T }>);\n }\n });\n },\n\n async start(\n targetIdOrOptions?: string | EphiaStartOptions,\n maybeOptions?: EphiaStartOptions,\n ): Promise<EphiaStartResult> {\n const targetId =\n typeof targetIdOrOptions === \"string\" ? targetIdOrOptions : undefined;\n const startOptions =\n typeof targetIdOrOptions === \"string\" ? maybeOptions : targetIdOrOptions;\n\n if (targetId) {\n targetManager.setActive(targetId);\n }\n\n const activeTargetId = targetId ?? targetManager.getActiveTargetId() ?? undefined;\n targetManager.prepareForDictation(activeTargetId);\n\n const result = await audioClient.start(\n targetId ? { ...startOptions, initialTargetId: targetId } : startOptions,\n );\n\n // Mirror what the React hook does: resync target + editor context after start.\n targetManager.syncActiveTarget();\n targetManager.flushEditorContext(\n targetId ?? targetManager.getActiveTargetId() ?? undefined,\n );\n\n return result;\n },\n\n async stop(): Promise<void> {\n await audioClient.stop();\n },\n\n async pause(): Promise<void> {\n await audioClient.stop();\n },\n\n async resume(resumeOptions?: EphiaStartOptions): Promise<void> {\n const status = audioClient.getState().status;\n if (status === \"paused\") {\n targetManager.prepareForDictation(targetManager.getActiveTargetId() ?? undefined);\n await audioClient.start(resumeOptions);\n targetManager.syncActiveTarget();\n targetManager.flushEditorContext(targetManager.getActiveTargetId() ?? undefined);\n } else {\n throw new Error(\"[ephia] resume() requires a paused session; call start() to create a new session\");\n }\n },\n\n async endSession(): Promise<void> {\n await audioClient.endSession();\n },\n\n async warmupMic(): Promise<void> {\n await audioClient.warmupMic();\n },\n\n registerTarget(target: EphiaHeadlessTarget): () => void {\n return register(target);\n },\n\n async setActiveTarget(targetId: string): Promise<void> {\n targetManager.setActive(targetId);\n },\n\n async sendEditorContext(context: EditorContext): Promise<void> {\n await sendWireMessage(\"session.editor_context.update\", context as unknown as Record<string, unknown>);\n },\n\n async resetTarget(targetId: string, reason?: ResetReason): Promise<void> {\n await sendWireMessage(\"session.reset\", {\n scope: \"target\",\n targetId,\n reason: reason ?? \"user_explicit\",\n });\n },\n\n async resetSession(reason?: ResetReason): Promise<void> {\n await sendWireMessage(\"session.reset\", {\n scope: \"global\",\n reason: reason ?? \"user_explicit\",\n });\n },\n\n async dispose(): Promise<void> {\n unsubServerEvent();\n cleanupTargets.forEach((cleanup) => cleanup());\n targetManager.dispose();\n await audioClient.dispose();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/shared/errors/EphiaSdkError.ts
|
|
2
|
+
var EphiaSdkError = class extends Error {
|
|
3
|
+
constructor(code, message, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.details = details;
|
|
7
|
+
this.name = "EphiaSdkError";
|
|
8
|
+
}
|
|
9
|
+
code;
|
|
10
|
+
details;
|
|
11
|
+
toJSON() {
|
|
12
|
+
return {
|
|
13
|
+
code: this.code,
|
|
14
|
+
message: this.message,
|
|
15
|
+
details: this.details
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
function toEphiaError(error) {
|
|
20
|
+
if (error instanceof EphiaSdkError) {
|
|
21
|
+
return { code: error.code, message: error.message };
|
|
22
|
+
}
|
|
23
|
+
if (error instanceof Error) {
|
|
24
|
+
return { code: "client.start_failed", message: error.message };
|
|
25
|
+
}
|
|
26
|
+
return { code: "client.start_failed", message: String(error) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
EphiaSdkError,
|
|
31
|
+
toEphiaError
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-N7U5M3VZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/errors/EphiaSdkError.ts"],"sourcesContent":["/**\n * Erreurs typées du SDK Ephia.\n */\n\nexport type EphiaSdkErrorCode =\n | \"config.invalid\"\n | \"session.create_failed\"\n | \"session.token_missing\"\n | \"transport.connect_failed\"\n | \"transport.reconnecting\"\n | \"transport.disconnected\"\n | \"transport.not_connected\"\n | \"audio.permission_denied\"\n | \"audio.no_input_device\"\n | \"audio.device_in_use\"\n | \"audio.track_publish_failed\"\n | \"audio.silence_detected\"\n | \"protocol.invalid_event\"\n | \"protocol.seq_gap\"\n | \"protocol.seq_gap_sync_failed\"\n | \"client.invalid_state\"\n | \"client.start_failed\"\n | \"client.finalization_timeout\"\n | \"binding.target_not_found\"\n | \"binding.unsupported_editor\";\n\nexport class EphiaSdkError extends Error {\n constructor(\n public code: EphiaSdkErrorCode,\n message: string,\n public details?: Record<string, unknown>\n ) {\n super(message);\n this.name = \"EphiaSdkError\";\n }\n\n toJSON(): Record<string, unknown> {\n return {\n code: this.code,\n message: this.message,\n details: this.details,\n };\n }\n}\n\nexport function toEphiaError(error: unknown): { code: string; message: string } {\n if (error instanceof EphiaSdkError) {\n return { code: error.code, message: error.message };\n }\n if (error instanceof Error) {\n return { code: \"client.start_failed\", message: error.message };\n }\n return { code: \"client.start_failed\", message: String(error) };\n}\n"],"mappings":";AA0BO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YACS,MACP,SACO,SACP;AACA,UAAM,OAAO;AAJN;AAEA;AAGP,SAAK,OAAO;AAAA,EACd;AAAA,EANS;AAAA,EAEA;AAAA,EAMT,SAAkC;AAChC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAEO,SAAS,aAAa,OAAmD;AAC9E,MAAI,iBAAiB,eAAe;AAClC,WAAO,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM,QAAQ;AAAA,EACpD;AACA,MAAI,iBAAiB,OAAO;AAC1B,WAAO,EAAE,MAAM,uBAAuB,SAAS,MAAM,QAAQ;AAAA,EAC/D;AACA,SAAO,EAAE,MAAM,uBAAuB,SAAS,OAAO,KAAK,EAAE;AAC/D;","names":[]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/shared/config/endpoint.ts
|
|
2
|
+
var DEFAULT_EPHIA_SDK_API_URL = "https://api.ephia.app";
|
|
3
|
+
function normalizeApiUrl(raw) {
|
|
4
|
+
return raw.trim().replace(/\/+$/, "").replace(/\/api\/v\d+$/, "");
|
|
5
|
+
}
|
|
6
|
+
function readEnvEndpoint() {
|
|
7
|
+
const env = typeof globalThis !== "undefined" && globalThis.process?.env;
|
|
8
|
+
if (!env) return void 0;
|
|
9
|
+
const fromEnv = env["EPHIA_SDK_ENDPOINT"]?.trim();
|
|
10
|
+
if (fromEnv) return fromEnv;
|
|
11
|
+
const fromNextPublic = env["NEXT_PUBLIC_EPHIA_SDK_ENDPOINT"]?.trim();
|
|
12
|
+
if (fromNextPublic) return fromNextPublic;
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
function resolveEphiaSdkApiUrl(explicitApiUrl) {
|
|
16
|
+
const explicit = explicitApiUrl?.trim();
|
|
17
|
+
if (explicit) return normalizeApiUrl(explicit);
|
|
18
|
+
const fromEnv = readEnvEndpoint();
|
|
19
|
+
if (fromEnv) return normalizeApiUrl(fromEnv);
|
|
20
|
+
return DEFAULT_EPHIA_SDK_API_URL;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
DEFAULT_EPHIA_SDK_API_URL,
|
|
25
|
+
resolveEphiaSdkApiUrl
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-PSYX674B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/config/endpoint.ts"],"sourcesContent":["/** URL API Ephia par défaut (production). */\nexport const DEFAULT_EPHIA_SDK_API_URL = \"https://api.ephia.app\";\n\nfunction normalizeApiUrl(raw: string): string {\n return raw.trim().replace(/\\/+$/, \"\").replace(/\\/api\\/v\\d+$/, \"\");\n}\n\nfunction readEnvEndpoint(): string | undefined {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (typeof globalThis !== \"undefined\" && (globalThis as any).process?.env) as Record<string, string | undefined> | undefined;\n if (!env) return undefined;\n\n const fromEnv = env[\"EPHIA_SDK_ENDPOINT\"]?.trim();\n if (fromEnv) return fromEnv;\n\n // Next.js n'expose côté client que les variables NEXT_PUBLIC_*.\n const fromNextPublic = env[\"NEXT_PUBLIC_EPHIA_SDK_ENDPOINT\"]?.trim();\n if (fromNextPublic) return fromNextPublic;\n\n return undefined;\n}\n\n/**\n * Résout l'URL API utilisée par le SDK.\n *\n * Priorité :\n * 1. `apiUrl` explicite (ex. localhost en dev)\n * 2. `EPHIA_SDK_ENDPOINT` ou `NEXT_PUBLIC_EPHIA_SDK_ENDPOINT`\n * 3. {@link DEFAULT_EPHIA_SDK_API_URL}\n */\nexport function resolveEphiaSdkApiUrl(explicitApiUrl?: string | null): string {\n const explicit = explicitApiUrl?.trim();\n if (explicit) return normalizeApiUrl(explicit);\n\n const fromEnv = readEnvEndpoint();\n if (fromEnv) return normalizeApiUrl(fromEnv);\n\n return DEFAULT_EPHIA_SDK_API_URL;\n}\n"],"mappings":";AACO,IAAM,4BAA4B;AAEzC,SAAS,gBAAgB,KAAqB;AAC5C,SAAO,IAAI,KAAK,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,gBAAgB,EAAE;AAClE;AAEA,SAAS,kBAAsC;AAE7C,QAAM,MAAO,OAAO,eAAe,eAAgB,WAAmB,SAAS;AAC/E,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU,IAAI,oBAAoB,GAAG,KAAK;AAChD,MAAI,QAAS,QAAO;AAGpB,QAAM,iBAAiB,IAAI,gCAAgC,GAAG,KAAK;AACnE,MAAI,eAAgB,QAAO;AAE3B,SAAO;AACT;AAUO,SAAS,sBAAsB,gBAAwC;AAC5E,QAAM,WAAW,gBAAgB,KAAK;AACtC,MAAI,SAAU,QAAO,gBAAgB,QAAQ;AAE7C,QAAM,UAAU,gBAAgB;AAChC,MAAI,QAAS,QAAO,gBAAgB,OAAO;AAE3C,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/core/connection/connection-state.ts
|
|
2
|
+
var initialConnectionState = {
|
|
3
|
+
status: "idle",
|
|
4
|
+
quality: "unknown",
|
|
5
|
+
localAudioPublished: false,
|
|
6
|
+
localAudioMuted: false,
|
|
7
|
+
reconnectCount: 0,
|
|
8
|
+
dataChannelReady: false
|
|
9
|
+
};
|
|
10
|
+
function mapTransportStateToConnectionState(ts, prev) {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
const next = {
|
|
13
|
+
status: ts.status,
|
|
14
|
+
quality: ts.connectionQuality ?? "unknown",
|
|
15
|
+
roomName: ts.roomName,
|
|
16
|
+
participantIdentity: ts.participantIdentity,
|
|
17
|
+
reconnectCount: ts.reconnectCount,
|
|
18
|
+
localAudioPublished: ts.localAudioPublished,
|
|
19
|
+
localAudioMuted: ts.localAudioMuted,
|
|
20
|
+
dataChannelReady: ts.status === "connected" || ts.status === "reconnected",
|
|
21
|
+
lastError: ts.lastError ? { code: ts.lastError.code, message: ts.lastError.message } : void 0,
|
|
22
|
+
lastConnectedAt: ts.status === "connected" ? now : prev?.lastConnectedAt,
|
|
23
|
+
lastReconnectedAt: ts.status === "reconnected" ? now : prev?.lastReconnectedAt,
|
|
24
|
+
lastDisconnectedAt: ts.status === "disconnected" ? now : prev?.lastDisconnectedAt
|
|
25
|
+
};
|
|
26
|
+
return next;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
initialConnectionState,
|
|
31
|
+
mapTransportStateToConnectionState
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-RFQRV7ML.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/connection/connection-state.ts"],"sourcesContent":["/**\n * État de connexion transport + qualité.\n *\n * Source de vérité pour les composants UI de connexion.\n * Alimenté par LiveKitTransport via un mapper depuis TransportState.\n */\n\nimport type { TransportState } from \"../transport/Transport\";\n\nexport type EphiaConnectionStatus =\n | \"idle\"\n | \"connecting\"\n | \"connected\"\n | \"reconnecting\"\n | \"reconnected\"\n | \"disconnected\"\n | \"error\";\n\nexport type EphiaConnectionQuality = \"unknown\" | \"poor\" | \"good\" | \"excellent\";\n\nexport type EphiaConnectionState = {\n status: EphiaConnectionStatus;\n quality: EphiaConnectionQuality;\n\n roomName?: string;\n participantIdentity?: string;\n\n reconnectCount: number;\n lastConnectedAt?: number;\n lastReconnectedAt?: number;\n lastDisconnectedAt?: number;\n\n localAudioPublished: boolean;\n localAudioMuted: boolean;\n\n dataChannelReady: boolean;\n\n lastError?: {\n code: string;\n message: string;\n };\n};\n\nexport const initialConnectionState: EphiaConnectionState = {\n status: \"idle\",\n quality: \"unknown\",\n localAudioPublished: false,\n localAudioMuted: false,\n reconnectCount: 0,\n dataChannelReady: false,\n};\n\nexport function mapTransportStateToConnectionState(\n ts: TransportState,\n prev?: EphiaConnectionState\n): EphiaConnectionState {\n const now = Date.now();\n const next: EphiaConnectionState = {\n status: ts.status,\n quality: ts.connectionQuality ?? \"unknown\",\n roomName: ts.roomName,\n participantIdentity: ts.participantIdentity,\n reconnectCount: ts.reconnectCount,\n localAudioPublished: ts.localAudioPublished,\n localAudioMuted: ts.localAudioMuted,\n dataChannelReady: ts.status === \"connected\" || ts.status === \"reconnected\",\n lastError: ts.lastError\n ? { code: ts.lastError.code, message: ts.lastError.message }\n : undefined,\n lastConnectedAt:\n ts.status === \"connected\" ? now : prev?.lastConnectedAt,\n lastReconnectedAt:\n ts.status === \"reconnected\" ? now : prev?.lastReconnectedAt,\n lastDisconnectedAt:\n ts.status === \"disconnected\" ? now : prev?.lastDisconnectedAt,\n };\n return next;\n}\n"],"mappings":";AA2CO,IAAM,yBAA+C;AAAA,EAC1D,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,kBAAkB;AACpB;AAEO,SAAS,mCACd,IACA,MACsB;AACtB,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,OAA6B;AAAA,IACjC,QAAQ,GAAG;AAAA,IACX,SAAS,GAAG,qBAAqB;AAAA,IACjC,UAAU,GAAG;AAAA,IACb,qBAAqB,GAAG;AAAA,IACxB,gBAAgB,GAAG;AAAA,IACnB,qBAAqB,GAAG;AAAA,IACxB,iBAAiB,GAAG;AAAA,IACpB,kBAAkB,GAAG,WAAW,eAAe,GAAG,WAAW;AAAA,IAC7D,WAAW,GAAG,YACV,EAAE,MAAM,GAAG,UAAU,MAAM,SAAS,GAAG,UAAU,QAAQ,IACzD;AAAA,IACJ,iBACE,GAAG,WAAW,cAAc,MAAM,MAAM;AAAA,IAC1C,mBACE,GAAG,WAAW,gBAAgB,MAAM,MAAM;AAAA,IAC5C,oBACE,GAAG,WAAW,iBAAiB,MAAM,MAAM;AAAA,EAC/C;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/react/provider/EphiaInternalContext.ts
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
var EphiaInternalContext = createContext(null);
|
|
4
|
+
function useEphiaInternal() {
|
|
5
|
+
const ctx = useContext(EphiaInternalContext);
|
|
6
|
+
if (!ctx) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"[ephia-audio] useEphiaInternal() doit \xEAtre utilis\xE9 dans un <EphiaProvider>. V\xE9rifiez que <EphiaProvider> est bien dans votre layout racine."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
return ctx;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
EphiaInternalContext,
|
|
16
|
+
useEphiaInternal
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=chunk-THNHRV2B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/provider/EphiaInternalContext.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { EphiaStore } from \"../store/create-ephia-store\";\nimport type { EphiaAudioClient } from \"../../core/client/EphiaAudioClient\";\nimport type { TargetManager } from \"../../core/targets/TargetManager\";\n\n/**\n * Internal context used by SDK hooks that need direct access to the audio client\n * and target manager. This context is intentionally NOT exported from the public\n * package API so that consumers cannot bypass the V2 orchestration layer.\n */\nexport interface EphiaInternalContextValue {\n store: EphiaStore;\n clientRef: React.MutableRefObject<EphiaAudioClient | null>;\n targetManagerRef: React.MutableRefObject<TargetManager | null>;\n /** Incrémenté à chaque (re)création du client audio — pour ré-abonner les hooks tardifs. */\n clientEpoch: number;\n}\n\nexport const EphiaInternalContext = createContext<EphiaInternalContextValue | null>(null);\n\nexport function useEphiaInternal(): EphiaInternalContextValue {\n const ctx = useContext(EphiaInternalContext);\n if (!ctx) {\n throw new Error(\n \"[ephia-audio] useEphiaInternal() doit être utilisé dans un <EphiaProvider>. \" +\n \"Vérifiez que <EphiaProvider> est bien dans votre layout racine.\"\n );\n }\n return ctx;\n}\n"],"mappings":";AAEA,SAAS,eAAe,kBAAkB;AAkBnC,IAAM,uBAAuB,cAAgD,IAAI;AAEjF,SAAS,mBAA8C;AAC5D,QAAM,MAAM,WAAW,oBAAoB;AAC3C,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/core/operations/textToDocumentOperations.ts
|
|
2
|
+
function localUuid() {
|
|
3
|
+
const c = globalThis.crypto;
|
|
4
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
5
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
6
|
+
}
|
|
7
|
+
function textToDocumentOperations(text, options) {
|
|
8
|
+
const position = options?.position ?? "cursor";
|
|
9
|
+
const operations = [];
|
|
10
|
+
for (const part of text.split(/(\n+)/g)) {
|
|
11
|
+
if (!part) continue;
|
|
12
|
+
if (part[0] === "\n") {
|
|
13
|
+
if (part.length > 1) {
|
|
14
|
+
operations.push({
|
|
15
|
+
id: localUuid(),
|
|
16
|
+
type: "paragraph_break",
|
|
17
|
+
position
|
|
18
|
+
});
|
|
19
|
+
} else {
|
|
20
|
+
operations.push({
|
|
21
|
+
id: localUuid(),
|
|
22
|
+
type: "line_break",
|
|
23
|
+
position
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
operations.push({
|
|
28
|
+
id: localUuid(),
|
|
29
|
+
type: "insert_text",
|
|
30
|
+
position,
|
|
31
|
+
text: part
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return operations;
|
|
36
|
+
}
|
|
37
|
+
function documentOperationsToPlainText(operations) {
|
|
38
|
+
let out = "";
|
|
39
|
+
for (const operation of operations) {
|
|
40
|
+
switch (operation.type) {
|
|
41
|
+
case "insert_text":
|
|
42
|
+
case "insert":
|
|
43
|
+
out += operation.text;
|
|
44
|
+
break;
|
|
45
|
+
case "line_break":
|
|
46
|
+
out += "\n";
|
|
47
|
+
break;
|
|
48
|
+
case "paragraph_break":
|
|
49
|
+
out += "\n\n";
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
textToDocumentOperations,
|
|
60
|
+
documentOperationsToPlainText
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=chunk-VSLGR64U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/operations/textToDocumentOperations.ts"],"sourcesContent":["/**\n * P3 (post-P0 dictée) — conversion texte ↔ DocumentOperation[].\n *\n * Découpe un texte contenant des \\n / \\n\\n structurels (déjà décidés par le\n * backend — cf. merge_internal_layout côté ephia_transcribe_agent) en une\n * séquence d'opérations neutres, consommable aussi bien par un binding riche\n * (TipTap, cf. P4) qu'un consommateur plain-text/headless (documentOperationsToPlainText).\n */\nimport type {\n DocumentOperation,\n InsertTextOperation,\n LineBreakOperation,\n ParagraphBreakOperation,\n} from \"ephia-protocol\";\n\nfunction localUuid(): string {\n const c = globalThis.crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\nexport interface TextToDocumentOperationsOptions {\n position?: number | \"cursor\" | \"start\" | \"end\";\n}\n\n/**\n * \\n → LineBreakOperation\n * \\n\\n (ou plus) → ParagraphBreakOperation\n * reste → InsertTextOperation\n */\nexport function textToDocumentOperations(\n text: string,\n options?: TextToDocumentOperationsOptions\n): DocumentOperation[] {\n const position = options?.position ?? \"cursor\";\n const operations: DocumentOperation[] = [];\n\n for (const part of text.split(/(\\n+)/g)) {\n if (!part) continue;\n if (part[0] === \"\\n\") {\n if (part.length > 1) {\n operations.push({\n id: localUuid(),\n type: \"paragraph_break\",\n position,\n } as ParagraphBreakOperation);\n } else {\n operations.push({\n id: localUuid(),\n type: \"line_break\",\n position,\n } as LineBreakOperation);\n }\n } else {\n operations.push({\n id: localUuid(),\n type: \"insert_text\",\n position,\n text: part,\n } as InsertTextOperation);\n }\n }\n\n return operations;\n}\n\n/** Fonction inverse — round-trip sans perte avec textToDocumentOperations(),\n * indispensable pour les consommateurs plain-text/headless qui n'ont pas de\n * binding riche capable d'interpréter line_break/paragraph_break nativement. */\nexport function documentOperationsToPlainText(operations: DocumentOperation[]): string {\n let out = \"\";\n for (const operation of operations) {\n switch (operation.type) {\n case \"insert_text\":\n case \"insert\":\n out += operation.text;\n break;\n case \"line_break\":\n out += \"\\n\";\n break;\n case \"paragraph_break\":\n out += \"\\n\\n\";\n break;\n default:\n // Les opérations de remplacement/section/highlight n'ont pas de\n // représentation texte-plat universelle — ignorées ici, gérées par\n // le store headless qui les interprète (cf. P3, plan post-P0).\n break;\n }\n }\n return out;\n}\n"],"mappings":";AAeA,SAAS,YAAoB;AAC3B,QAAM,IAAI,WAAW;AACrB,MAAI,GAAG,WAAY,QAAO,EAAE,WAAW;AACvC,SAAO,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAC1E;AAWO,SAAS,yBACd,MACA,SACqB;AACrB,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,aAAkC,CAAC;AAEzC,aAAW,QAAQ,KAAK,MAAM,QAAQ,GAAG;AACvC,QAAI,CAAC,KAAM;AACX,QAAI,KAAK,CAAC,MAAM,MAAM;AACpB,UAAI,KAAK,SAAS,GAAG;AACnB,mBAAW,KAAK;AAAA,UACd,IAAI,UAAU;AAAA,UACd,MAAM;AAAA,UACN;AAAA,QACF,CAA4B;AAAA,MAC9B,OAAO;AACL,mBAAW,KAAK;AAAA,UACd,IAAI,UAAU;AAAA,UACd,MAAM;AAAA,UACN;AAAA,QACF,CAAuB;AAAA,MACzB;AAAA,IACF,OAAO;AACL,iBAAW,KAAK;AAAA,QACd,IAAI,UAAU;AAAA,QACd,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,MACR,CAAwB;AAAA,IAC1B;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,8BAA8B,YAAyC;AACrF,MAAI,MAAM;AACV,aAAW,aAAa,YAAY;AAClC,YAAQ,UAAU,MAAM;AAAA,MACtB,KAAK;AAAA,MACL,KAAK;AACH,eAAO,UAAU;AACjB;AAAA,MACF,KAAK;AACH,eAAO;AACP;AAAA,MACF,KAAK;AACH,eAAO;AACP;AAAA,MACF;AAIE;AAAA,IACJ;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|