@remedia/node 0.0.0-stage → 0.1.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ import type { NativeDiagnosticEvent, NativeStreamInfo } from '../native.js';
2
+ /** Lifecycle state of a playback pipeline, independent of its voice transport state. */
3
+ export type PlaybackState = 'idle' | 'starting' | 'playing' | 'pausing' | 'resuming' | 'paused' | 'stopping' | 'stopped' | 'completed' | 'failed';
4
+ /** Discord transport lifecycle state, including credential negotiation and recovery. */
5
+ export type VoiceConnectionState = 'connecting' | 'negotiatingDave' | 'connected' | 'reconnecting' | 'disconnecting' | 'disconnected' | 'failed';
6
+ /** Metadata and format information for the selected audio stream; unavailable measurements are null. */
7
+ export interface AudioStreamInfo {
8
+ /** Zero-based index of the selected audio stream in the media container. */
9
+ streamIndex: number;
10
+ /** Codec name reported by the selected audio stream. */
11
+ codec: string;
12
+ /** Encoded stream sample rate in Hz, or null when unknown; playback output is normalized separately. */
13
+ sampleRate: number | null;
14
+ /** Encoded stream channel count, or null when unknown. */
15
+ channels: number | null;
16
+ /** Encoded stream channel layout description, or null when unknown. */
17
+ channelLayout: string | null;
18
+ /** Source duration in milliseconds, or null when unknown. */
19
+ durationMs: number | null;
20
+ /** Stream bit rate in bits per second, or null when unknown. */
21
+ bitRate: number | null;
22
+ /** Container or stream metadata keyed by native tag name. */
23
+ metadata: Readonly<Record<string, string>>;
24
+ }
25
+ /** Source and output timeline measurements in milliseconds; unsupported measurements are null. */
26
+ export interface PlaybackPosition {
27
+ /** Original media timeline associated with the latest submitted PCM block. */
28
+ mediaMs: number | null;
29
+ /** `timestamp` when derived from source PTS, `estimated` when inferred, or null. */
30
+ mediaPositionAccuracy: 'timestamp' | 'estimated' | null;
31
+ /**
32
+ * Duration of audio submitted to the output in milliseconds; may differ from source position after filtering.
33
+ */
34
+ submittedMs: number;
35
+ /** Output-reported played duration in milliseconds, or null when the output does not provide it. */
36
+ playedMs: number | null;
37
+ }
38
+ /** Severity of a structured native tracing event. */
39
+ export type DiagnosticSeverity = 'trace' | 'debug' | 'info' | 'warn' | 'error';
40
+ /** Typed diagnostic value; integers remain decimal strings to preserve precision in JavaScript. */
41
+ export type DiagnosticFieldValue = {
42
+ /** Discriminant describing the representation of the diagnostic field. */
43
+ kind: 'string' | 'debug';
44
+ /** Diagnostic field value in the representation selected by kind. */
45
+ value: string;
46
+ } | {
47
+ /** Discriminant describing the representation of the diagnostic field. */
48
+ kind: 'integer';
49
+ /** Diagnostic field value in the representation selected by kind. */
50
+ value: string;
51
+ } | {
52
+ /** Discriminant describing the representation of the diagnostic field. */
53
+ kind: 'boolean';
54
+ /** Diagnostic field value in the representation selected by kind. */
55
+ value: boolean;
56
+ } | {
57
+ /** Discriminant describing the representation of the diagnostic field. */
58
+ kind: 'number';
59
+ /** Diagnostic field value in the representation selected by kind. */
60
+ value: number;
61
+ };
62
+ /** Structured native diagnostic event forwarded through the bounded diagnostic queues. */
63
+ export interface DiagnosticEvent {
64
+ /** Unix timestamp of the diagnostic event in milliseconds. */
65
+ timestamp: number;
66
+ /** Native tracing severity associated with the event. */
67
+ severity: DiagnosticSeverity;
68
+ /** Native tracing target or module that emitted the diagnostic. */
69
+ target: string;
70
+ /** Server or diagnostic event name. */
71
+ event: string;
72
+ /** Human-readable diagnostic or failure description. */
73
+ message: string | null;
74
+ /** Native playback identity, or null for diagnostics without playback context. */
75
+ playbackId: string | null;
76
+ /** Native voice connection identity, or null for diagnostics without voice context. */
77
+ voiceConnectionId: string | null;
78
+ /** Typed structured diagnostic fields; integers are represented as decimal strings. */
79
+ fields: Readonly<Record<string, DiagnosticFieldValue>>;
80
+ }
81
+ /**
82
+ * Callback receiving a structured native diagnostic event.
83
+ * @param event - Structured native diagnostic delivered by the bounded event queue.
84
+ */
85
+ export type DebugListener = (event: DiagnosticEvent) => void;
86
+ /**
87
+ * Normalize native optional stream metadata into explicit nullable public fields.
88
+ * @param info - Native stream metadata to normalize into the public nullable representation.
89
+ * @returns Stream metadata using the public AudioStreamInfo representation.
90
+ */
91
+ export declare function toStreamInfo(info: NativeStreamInfo): AudioStreamInfo;
92
+ /**
93
+ * Normalize a structured native diagnostic; invalid severity or field representations throw.
94
+ * @param event - Event name identifying the typed notification or Gateway voice dispatch.
95
+ * @returns A diagnostic event with typed field values.
96
+ */
97
+ export declare function toDiagnosticEvent(event: NativeDiagnosticEvent): DiagnosticEvent;
package/dist/types.js ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Normalize native optional stream metadata into explicit nullable public fields.
3
+ * @param info - Native stream metadata to normalize into the public nullable representation.
4
+ * @returns Stream metadata using the public AudioStreamInfo representation.
5
+ */
6
+ export function toStreamInfo(info) {
7
+ return {
8
+ streamIndex: info.streamIndex,
9
+ codec: info.codec,
10
+ sampleRate: info.sampleRate ?? null,
11
+ channels: info.channels ?? null,
12
+ channelLayout: info.channelLayout ?? null,
13
+ durationMs: info.durationMs ?? null,
14
+ bitRate: info.bitRate ?? null,
15
+ metadata: info.metadata,
16
+ };
17
+ }
18
+ /**
19
+ * Normalize a structured native diagnostic; invalid severity or field representations throw.
20
+ * @param event - Event name identifying the typed notification or Gateway voice dispatch.
21
+ * @returns A diagnostic event with typed field values.
22
+ */
23
+ export function toDiagnosticEvent(event) {
24
+ const severity = event.severity;
25
+ if (!isDiagnosticSeverity(severity)) {
26
+ throw new Error(`Unknown native diagnostic severity: ${severity}`);
27
+ }
28
+ const fields = {};
29
+ for (const [key, field] of Object.entries(event.fields)) {
30
+ if (field.kind === 'string' || field.kind === 'debug') {
31
+ if (field.stringValue === undefined) {
32
+ throw new Error(`Invalid native diagnostic field: ${key}`);
33
+ }
34
+ fields[key] = { kind: field.kind, value: field.stringValue };
35
+ }
36
+ else if (field.kind === 'integer') {
37
+ if (field.stringValue === undefined) {
38
+ throw new Error(`Invalid native diagnostic field: ${key}`);
39
+ }
40
+ fields[key] = { kind: 'integer', value: field.stringValue };
41
+ }
42
+ else if (field.kind === 'boolean') {
43
+ if (field.boolValue === undefined) {
44
+ throw new Error(`Invalid native diagnostic field: ${key}`);
45
+ }
46
+ fields[key] = { kind: 'boolean', value: field.boolValue };
47
+ }
48
+ else if (field.kind === 'number') {
49
+ if (field.numberValue === undefined) {
50
+ throw new Error(`Invalid native diagnostic field: ${key}`);
51
+ }
52
+ fields[key] = { kind: 'number', value: field.numberValue };
53
+ }
54
+ else {
55
+ throw new Error(`Unknown native diagnostic field kind: ${field.kind}`);
56
+ }
57
+ }
58
+ return {
59
+ timestamp: event.timestamp,
60
+ severity,
61
+ target: event.target,
62
+ event: event.event,
63
+ message: event.message ?? null,
64
+ playbackId: event.playbackId ?? null,
65
+ voiceConnectionId: event.voiceConnectionId ?? null,
66
+ fields,
67
+ };
68
+ }
69
+ function isDiagnosticSeverity(value) {
70
+ return (value === 'trace' ||
71
+ value === 'debug' ||
72
+ value === 'info' ||
73
+ value === 'warn' ||
74
+ value === 'error');
75
+ }
package/native.d.ts ADDED
@@ -0,0 +1,162 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * Which binding artifact the generated loader actually loaded: `'native'` for
6
+ * a native addon, otherwise the `platformArchABI` of the WASI flavor. Every
7
+ * flavor napi-rs can build is listed, because `NAPI_RS_NATIVE_LIBRARY_PATH`
8
+ * can point the loader at a WASI artifact this package does not build itself.
9
+ */
10
+ export declare const __napiBindingTarget: 'native' | 'wasm32-wasi' | 'wasm32-wasip1'
11
+
12
+ export declare class NativeRemedia {
13
+ /** Forward this context's bounded diagnostic queue to one JavaScript callback. */
14
+ setDebugCallback(callback: (arg: NativeDiagnosticEvent) => void): void
15
+ /** Number of events discarded by the observability queue or full N-API callback queue. */
16
+ get droppedDebugEvents(): number
17
+ /** Open a Discord voice transport using session data collected by the caller's Gateway client. */
18
+ connectVoice(options: NativeVoiceOptions): Promise<NativeVoiceSessionIds>
19
+ /**
20
+ * Replace only the Discord transport while retaining the existing
21
+ * playback decoder, filters, volume, pause state, and output timeline.
22
+ */
23
+ replaceVoice(voiceId: string, generation: number, options: NativeVoiceOptions): Promise<void>
24
+ /**
25
+ * Invalidate an in-flight transport replacement before newer Gateway
26
+ * credentials are used. This does not touch the active playback session.
27
+ */
28
+ invalidateVoiceRecovery(voiceId: string, generation: number): void
29
+ /** Close the old Discord transport while leaving the playback session parked for recovery. */
30
+ suspendVoice(voiceId: string, generation: number): Promise<void>
31
+ /** Terminate a playback parked during voice recovery after retries fail. */
32
+ failVoiceRecovery(voiceId: string, message: string): void
33
+ /** Current transport state for one voice session. */
34
+ voiceState(voiceId: string): string
35
+ /** Return the latest transport failure for one voice session. */
36
+ voiceError(voiceId: string): string | null
37
+ /** Stop playback and release the associated voice transport. */
38
+ disconnectVoice(voiceId: string): Promise<void>
39
+ /** Forget a voice session after its transport has been disconnected. */
40
+ releaseVoiceSession(voiceId: string): void
41
+ /** Create a bounded native byte input for one Node.js Readable producer. */
42
+ createByteStream(): string
43
+ /**
44
+ * Copy one Node buffer into the native-owned bounded input, applying
45
+ * backpressure when the decoder has not consumed enough pending bytes.
46
+ */
47
+ writeByteStream(streamId: string, chunk: Buffer): Promise<void>
48
+ /**
49
+ * Signal a clean producer EOF. The stream handle remains valid until it is
50
+ * cancelled so playback can resolve its source if EOF arrives quickly.
51
+ */
52
+ finishByteStream(streamId: string): Promise<void>
53
+ /** Propagate a JavaScript Readable error through FFmpeg to playback state. */
54
+ failByteStream(streamId: string, message: string): Promise<void>
55
+ /**
56
+ * Cancel an input and wake any native readers or writers blocked by its
57
+ * bounded queue. Repeated calls are harmless.
58
+ */
59
+ cancelByteStream(streamId: string): void
60
+ /** Create an isolated, diagnostics-only Remedia context from environment configuration. */
61
+ static create(): Promise<NativeRemedia>
62
+ /**
63
+ * Stop all playback, disconnect all voice transports, flush telemetry, and close the context.
64
+ * This method is safe to call repeatedly.
65
+ */
66
+ close(): Promise<void>
67
+ /** Create a decoder-only session that consumes frames into a no-op output. */
68
+ createPlaybackSession(): string
69
+ /** Start decoding into the null output or the associated Discord Opus sender. */
70
+ play(playbackId: string, source: NativeMediaSource): Promise<NativeStreamInfo>
71
+ /** Request playback pause at the current output frame boundary. */
72
+ pause(playbackId: string): Promise<void>
73
+ /** Resume a paused playback session. */
74
+ resume(playbackId: string): Promise<void>
75
+ /** Seek to a media position in milliseconds. */
76
+ seek(playbackId: string, positionMs: number): Promise<void>
77
+ /** Validate and replace the trusted native FFmpeg audio filtergraph. */
78
+ playbackSetFilterGraph(playbackId: string, graph: string): Promise<void>
79
+ /** Clear the current native audio filtergraph. */
80
+ playbackClearFilterGraph(playbackId: string): Promise<void>
81
+ /** Current configured raw FFmpeg filtergraph, if any. */
82
+ playbackFilterGraph(playbackId: string): string | null
83
+ /** Set normalized linear master gain in the inclusive range 0.0 through 2.0. */
84
+ playbackSetVolume(playbackId: string, gain: number): Promise<void>
85
+ /** Current normalized linear master gain. */
86
+ playbackVolume(playbackId: string): number
87
+ /** Validate a filtergraph before a Discord voice session has a native playback object. */
88
+ validateFilterGraph(graph: string): Promise<void>
89
+ /** Stop playback without disconnecting its Discord voice transport. */
90
+ stop(playbackId: string): Promise<void>
91
+ /** Capture the source generation before an asynchronous wait is scheduled. */
92
+ playbackGeneration(playbackId: string): string
93
+ /** Wait for a generation captured synchronously by the JavaScript wrapper. */
94
+ wait(playbackId: string, generation: string): Promise<string>
95
+ /** Current playback lifecycle state. */
96
+ playbackState(playbackId: string): string
97
+ /** Source media, filtered output submission, and output-reported played positions. */
98
+ playbackPosition(playbackId: string): NativePlaybackPosition
99
+ /** Known media duration in milliseconds. */
100
+ playbackDurationMs(playbackId: string): number | null
101
+ /** Latest decoder, resampler, output, or worker failure. */
102
+ playbackError(playbackId: string): string | null
103
+ /** Stop and forget a standalone playback session. Voice sessions remain connected. */
104
+ disposePlaybackSession(playbackId: string): Promise<void>
105
+ }
106
+
107
+ export interface NativeDiagnosticEvent {
108
+ timestamp: number
109
+ severity: string
110
+ target: string
111
+ event: string
112
+ message?: string
113
+ playbackId?: string
114
+ voiceConnectionId?: string
115
+ fields: Record<string, NativeDiagnosticField>
116
+ }
117
+
118
+ export interface NativeDiagnosticField {
119
+ kind: string
120
+ stringValue?: string
121
+ boolValue?: boolean
122
+ numberValue?: number
123
+ }
124
+
125
+ export interface NativeMediaSource {
126
+ /** Explicitly `file`, `url`, `buffer`, or `stream`. */
127
+ kind: string
128
+ value?: string
129
+ bytes?: Buffer
130
+ }
131
+
132
+ export interface NativePlaybackPosition {
133
+ mediaMs?: number
134
+ mediaPositionAccuracy?: string
135
+ submittedMs: number
136
+ playedMs?: number
137
+ }
138
+
139
+ export interface NativeStreamInfo {
140
+ streamIndex: number
141
+ codec: string
142
+ sampleRate?: number
143
+ channels?: number
144
+ channelLayout?: string
145
+ durationMs?: number
146
+ bitRate?: number
147
+ metadata: Record<string, string>
148
+ }
149
+
150
+ export interface NativeVoiceOptions {
151
+ guildId: string
152
+ channelId: string
153
+ userId: string
154
+ sessionId: string
155
+ token: string
156
+ endpoint: string
157
+ }
158
+
159
+ export interface NativeVoiceSessionIds {
160
+ voiceId: string
161
+ playbackId: string
162
+ }