@ikonai/sdk 0.0.11 → 0.0.13

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.
Files changed (35) hide show
  1. package/assets/audio-capture-worker-DbFwKRTJ.js +14727 -0
  2. package/assets/audio-worker-BFOOU5sk.js +14916 -0
  3. package/assets/index-CvFH-Dkf.js +650 -0
  4. package/assets/protocol-worker-DIwctOUY.js +15181 -0
  5. package/assets/video-capture-worker-CJhKrZYf.js +14631 -0
  6. package/assets/video-worker-BhoZnZ1o.js +14659 -0
  7. package/channel/channel-manager.d.ts +14 -4
  8. package/client/ikon-client-config.d.ts +94 -2
  9. package/client/ikon-client.d.ts +104 -4
  10. package/connection/urls.d.ts +13 -0
  11. package/functions/function-registry.d.ts +1 -1
  12. package/functions/index.d.ts +2 -1
  13. package/functions/media-capture-functions.d.ts +9 -0
  14. package/functions/types.d.ts +5 -0
  15. package/index.d.ts +8 -4
  16. package/index.js +11394 -8981
  17. package/media/capabilities.d.ts +13 -0
  18. package/media/ikon-audio-capture.d.ts +34 -0
  19. package/media/ikon-audio-playback.d.ts +157 -0
  20. package/media/ikon-image-capture.d.ts +24 -0
  21. package/media/ikon-media-capture.d.ts +11 -0
  22. package/media/ikon-media.d.ts +27 -0
  23. package/media/ikon-video-capture.d.ts +33 -0
  24. package/media/ikon-video-playback.d.ts +70 -0
  25. package/media/index.d.ts +14 -0
  26. package/media/ring-buffer.d.ts +21 -0
  27. package/package.json +1 -1
  28. package/utils/deferred.d.ts +6 -0
  29. package/utils/opcode-names.d.ts +1 -0
  30. package/utils/user-gesture.d.ts +7 -0
  31. package/worker/audio-capture-worker.d.ts +1 -0
  32. package/worker/audio-worker.d.ts +1 -0
  33. package/worker/protocol-worker.d.ts +1 -0
  34. package/worker/video-capture-worker.d.ts +10 -0
  35. package/worker/video-worker.d.ts +1 -0
@@ -0,0 +1,13 @@
1
+ /**
2
+ * SharedArrayBuffer is the most efficient way to move continuous PCM/video data between threads.
3
+ *
4
+ * Browsers gate it behind cross-origin isolation (COOP/COEP) to prevent side-channel attacks.
5
+ * This helper centralizes the runtime checks so pipelines can prefer SAB but always fall back.
6
+ */
7
+ export declare function isSharedArrayBufferSupported(): boolean;
8
+ /**
9
+ * AudioWorklet is the preferred audio rendering path because it runs on the browser's audio thread.
10
+ *
11
+ * When unavailable (older browsers or restrictive environments), the SDK must fall back to main-thread audio.
12
+ */
13
+ export declare function isAudioWorkletSupported(ctx: BaseAudioContext): boolean;
@@ -0,0 +1,34 @@
1
+ import { IkonClient } from '../client/ikon-client';
2
+ export interface IkonAudioCaptureRequest {
3
+ userGesture?: boolean;
4
+ constraints?: MediaTrackConstraints;
5
+ options?: {
6
+ bitrate?: number;
7
+ frameDurationMs?: number;
8
+ };
9
+ }
10
+ export interface IkonAudioCaptureHandle {
11
+ captureId: string;
12
+ stop(): Promise<void>;
13
+ }
14
+ export declare class IkonAudioCapture {
15
+ private readonly client;
16
+ private worker;
17
+ private sendPort;
18
+ private workletModuleUrl;
19
+ private workletReady;
20
+ private workletContext;
21
+ private audioContext;
22
+ private nextTrackId;
23
+ private captures;
24
+ constructor(client: IkonClient);
25
+ private allocateTrackId;
26
+ private ensureWorker;
27
+ private ensureSendPort;
28
+ private ensureAudioContext;
29
+ private acquireMicrophoneStream;
30
+ private ensureWorklet;
31
+ startMic(request?: IkonAudioCaptureRequest): Promise<IkonAudioCaptureHandle>;
32
+ stop(captureId: string): Promise<boolean>;
33
+ dispose(): void;
34
+ }
@@ -0,0 +1,157 @@
1
+ import { IkonClient } from '../client/ikon-client';
2
+ export interface IkonAudioOutputConfig {
3
+ /**
4
+ * Target output sample rate.
5
+ *
6
+ * The browser ultimately controls the AudioContext sample rate, so this is best-effort.
7
+ * The decode worker resamples into the actual output rate used by the playback graph.
8
+ */
9
+ sampleRate?: number;
10
+ /**
11
+ * Target output channel count.
12
+ *
13
+ * This is used to normalize decoded streams into a single mixing format.
14
+ */
15
+ channels?: number;
16
+ /**
17
+ * Startup buffer size (seconds).
18
+ *
19
+ * This is the minimum buffered audio required before playback begins after a reset.
20
+ */
21
+ minBufferSizeSeconds?: number;
22
+ /**
23
+ * Maximum buffered audio (seconds).
24
+ *
25
+ * Larger buffers improve robustness under jitter but increase latency.
26
+ */
27
+ maxBufferSizeSeconds?: number;
28
+ }
29
+ export interface IkonAudioPlaybackConfig {
30
+ /**
31
+ * Output format used by the mixing graph.
32
+ */
33
+ output?: IkonAudioOutputConfig;
34
+ /**
35
+ * Called when the browser requires a user gesture to start audio playback.
36
+ */
37
+ onAudioUnlockRequired?: () => void;
38
+ /**
39
+ * Worker and transport preferences.
40
+ *
41
+ * The SDK prefers:
42
+ * - decode in a worker
43
+ * - audio rendering in an AudioWorklet
44
+ * - PCM transfer through SharedArrayBuffer when cross-origin isolated
45
+ *
46
+ * All of these have fallbacks for restrictive embed environments.
47
+ */
48
+ threading?: {
49
+ decodeWorker?: 'auto' | 'disabled';
50
+ preferSharedArrayBuffer?: boolean;
51
+ preferAudioWorklet?: boolean;
52
+ preferWebCodecs?: boolean;
53
+ };
54
+ }
55
+ /**
56
+ * Audio playback pipeline for Ikon protocol audio streams.
57
+ *
58
+ * The key separation of responsibilities is:
59
+ * - protocol I/O: handled by IkonClient (optionally in ProtocolWorker)
60
+ * - decoding: done in a dedicated AudioWorker
61
+ * - rendering: done in AudioWorklet when available
62
+ *
63
+ * This keeps frequent audio frames off the UI thread and avoids forcing the SDK
64
+ * to invent a parallel “audio message model” alongside the protocol.
65
+ */
66
+ export declare class IkonAudioPlayback {
67
+ private readonly client;
68
+ private readonly config;
69
+ private enabled;
70
+ private prepared;
71
+ private stateUnsubscribe;
72
+ private unlockRequiredSignaled;
73
+ private readonly isIos;
74
+ private recreateOnNextResume;
75
+ private boundVisibilityChange;
76
+ private unlockHandler;
77
+ private awaitingUnlock;
78
+ private recoveryTask;
79
+ /** Timeout for AudioContext.resume() - normally resolves instantly, but some browsers hang indefinitely */
80
+ private static readonly RESUME_TIMEOUT_MS;
81
+ private audioContext;
82
+ private audioWorkletNode;
83
+ private scriptProcessorNode;
84
+ private workletModuleUrl;
85
+ private workletReady;
86
+ private pendingWorkletMessages;
87
+ private audioWorker;
88
+ private protocolPort;
89
+ private readonly fallbackQueues;
90
+ private readonly activeStreams;
91
+ constructor(client: IkonClient, config?: IkonAudioPlaybackConfig);
92
+ /**
93
+ * Prepare the audio pipeline without attempting to start playback.
94
+ *
95
+ * This is meant to reduce time-to-first-audio by:
96
+ * - creating the AudioContext (it may remain suspended)
97
+ * - loading the AudioWorklet module (if supported)
98
+ * - starting the decode worker when the client is connected
99
+ *
100
+ * Prewarming does not trigger user-gesture prompts and does not call resume().
101
+ */
102
+ prepare(): Promise<void>;
103
+ /**
104
+ * Enable or disable audio playback.
105
+ *
106
+ * Browsers may require a user gesture to start audio. When that happens, the SDK
107
+ * keeps the decode pipeline alive but the audio graph remains suspended until
108
+ * the app calls `enable({ userGesture: true })` or resumes the AudioContext.
109
+ */
110
+ setEnabled(enabled: boolean, options?: {
111
+ userGesture?: boolean;
112
+ }): Promise<void>;
113
+ /**
114
+ * Convenience wrapper for `setEnabled(true)`.
115
+ */
116
+ enable(options?: {
117
+ userGesture?: boolean;
118
+ }): Promise<void>;
119
+ /**
120
+ * Convenience wrapper for `setEnabled(false)`.
121
+ */
122
+ disable(): Promise<void>;
123
+ /**
124
+ * Dispose of resources. After calling this, the instance should not be reused.
125
+ */
126
+ dispose(): void;
127
+ private resolveOutputConfig;
128
+ private ensureAudioGraph;
129
+ private attachAudioContextHealthHandlers;
130
+ private attachVisibilityHandler;
131
+ private removeVisibilityHandler;
132
+ private getContextState;
133
+ private addUnlockHandler;
134
+ private removeUnlockHandler;
135
+ /**
136
+ * Wraps AudioContext.resume() with a timeout to prevent indefinite hanging.
137
+ * Some browsers return a Promise that never resolves in certain edge cases.
138
+ */
139
+ private resumeWithTimeout;
140
+ private signalUnlockRequired;
141
+ private ensureAudioWorklet;
142
+ private ensureScriptProcessor;
143
+ private readFromFallbackQueue;
144
+ private resumeAudioContextIfPossible;
145
+ private recoverAudioContext;
146
+ private recoverAudioContextInternal;
147
+ private recreateAudioGraph;
148
+ private rebindWorkerTransportIfNeeded;
149
+ private ensureWorkers;
150
+ private onAudioWorkerMessage;
151
+ private postToWorklet;
152
+ private stopAudioWorklet;
153
+ private stopScriptProcessor;
154
+ private stopAudioGraph;
155
+ private stopWorkers;
156
+ private ensureStateSubscription;
157
+ }
@@ -0,0 +1,24 @@
1
+ export interface IkonImageCaptureRequest {
2
+ userGesture?: boolean;
3
+ constraints?: MediaTrackConstraints;
4
+ mime?: 'image/jpeg' | 'image/png';
5
+ quality?: number;
6
+ width?: number;
7
+ height?: number;
8
+ }
9
+ export interface IkonImageCaptureResult {
10
+ mime: string;
11
+ width: number;
12
+ height: number;
13
+ data: Uint8Array;
14
+ }
15
+ export declare class IkonImageCapture {
16
+ private readonly requestUserGesture;
17
+ constructor(requestUserGesture: (req: {
18
+ kind: 'image';
19
+ resume: () => void;
20
+ cancel?: () => void;
21
+ }) => void);
22
+ private acquireCameraStream;
23
+ captureFromCamera(request?: IkonImageCaptureRequest): Promise<IkonImageCaptureResult>;
24
+ }
@@ -0,0 +1,11 @@
1
+ import { IkonClient } from '../client/ikon-client';
2
+ import { IkonAudioCapture } from './ikon-audio-capture';
3
+ import { IkonImageCapture } from './ikon-image-capture';
4
+ import { IkonVideoCapture } from './ikon-video-capture';
5
+ export declare class IkonMediaCapture {
6
+ readonly audio: IkonAudioCapture;
7
+ readonly video: IkonVideoCapture;
8
+ readonly image: IkonImageCapture;
9
+ constructor(client: IkonClient);
10
+ dispose(): void;
11
+ }
@@ -0,0 +1,27 @@
1
+ import { IkonClient } from '../client/ikon-client';
2
+ import { IkonAudioPlayback, IkonAudioPlaybackConfig } from './ikon-audio-playback';
3
+ import { IkonVideoPlayback, IkonVideoPlaybackConfig } from './ikon-video-playback';
4
+ export interface IkonMediaConfig {
5
+ audio?: IkonAudioPlaybackConfig;
6
+ video?: IkonVideoPlaybackConfig;
7
+ }
8
+ /**
9
+ * Convenience wrapper for SDK-provided media pipelines.
10
+ *
11
+ * The SDK keeps protocol messages as the source of truth and moves heavy work
12
+ * (decode, parsing, buffering) into dedicated workers when possible.
13
+ *
14
+ * This wrapper is optional; applications can instantiate `IkonAudioPlayback` and
15
+ * `IkonVideoPlayback` directly when they want tighter control.
16
+ */
17
+ export declare class IkonMedia {
18
+ readonly audio: IkonAudioPlayback;
19
+ readonly video: IkonVideoPlayback;
20
+ constructor(client: IkonClient, config?: IkonMediaConfig);
21
+ /**
22
+ * Releases media resources (workers, audio graph, decoders).
23
+ *
24
+ * This does not disconnect the underlying `IkonClient`.
25
+ */
26
+ dispose(): void;
27
+ }
@@ -0,0 +1,33 @@
1
+ import { IkonClient } from '../client/ikon-client';
2
+ import { VideoCaptureWorkerOptions } from '../worker/video-capture-worker';
3
+ export type VideoCaptureSource = 'camera' | 'screen';
4
+ export interface IkonVideoCaptureRequest {
5
+ source: VideoCaptureSource;
6
+ options?: VideoCaptureWorkerOptions;
7
+ userGesture?: boolean;
8
+ constraints?: MediaTrackConstraints;
9
+ }
10
+ export interface IkonVideoCaptureHandle {
11
+ captureId: string;
12
+ stop(): Promise<void>;
13
+ }
14
+ export declare class IkonVideoCapture {
15
+ private readonly client;
16
+ private worker;
17
+ private protocolPort;
18
+ private sendPort;
19
+ private nextTrackId;
20
+ private captures;
21
+ constructor(client: IkonClient);
22
+ private allocateTrackId;
23
+ private ensureWorker;
24
+ private ensurePorts;
25
+ private getMediaStream;
26
+ private acquireStreamWithGestureFallback;
27
+ start(request: IkonVideoCaptureRequest): Promise<IkonVideoCaptureHandle>;
28
+ startCamera(options?: Omit<IkonVideoCaptureRequest, 'source'>): Promise<IkonVideoCaptureHandle>;
29
+ startScreen(options?: Omit<IkonVideoCaptureRequest, 'source'>): Promise<IkonVideoCaptureHandle>;
30
+ stop(captureId: string): Promise<boolean>;
31
+ dispose(): void;
32
+ private pumpFrames;
33
+ }
@@ -0,0 +1,70 @@
1
+ import { IkonClient } from '../client/ikon-client';
2
+ export interface IkonVideoPlaybackConfig {
3
+ /**
4
+ * Worker and rendering preferences.
5
+ *
6
+ * The preferred path is decode + render inside a dedicated worker using OffscreenCanvas,
7
+ * which avoids scheduling heavy work on the UI thread during high-frequency streaming.
8
+ */
9
+ threading?: {
10
+ decodeWorker?: 'auto' | 'disabled';
11
+ preferOffscreenCanvas?: boolean;
12
+ };
13
+ }
14
+ /**
15
+ * Video playback pipeline for Ikon protocol video streams.
16
+ *
17
+ * This implementation is intentionally canvas-based and renderer-agnostic.
18
+ * Applications decide how to position and size the canvas; the SDK focuses on
19
+ * decoding and presenting frames efficiently.
20
+ */
21
+ export declare class IkonVideoPlayback {
22
+ private readonly client;
23
+ private readonly config;
24
+ private enabled;
25
+ private prepared;
26
+ private stateUnsubscribe;
27
+ private worker;
28
+ private protocolPort;
29
+ private readonly surfaces;
30
+ constructor(client: IkonClient, config?: IkonVideoPlaybackConfig);
31
+ /**
32
+ * Prepare the video pipeline. This can reduce time-to-first-frame by creating
33
+ * the worker and attaching the protocol subscription early.
34
+ *
35
+ * Video still requires an explicit canvas surface to actually present frames.
36
+ */
37
+ prepare(): Promise<void>;
38
+ /**
39
+ * Attach a canvas for a specific video stream id.
40
+ *
41
+ * When OffscreenCanvas is available, the SDK transfers the rendering surface to a worker
42
+ * so the UI thread only participates in layout and input handling.
43
+ */
44
+ attachCanvas(streamId: string, canvas: HTMLCanvasElement): Promise<void>;
45
+ /**
46
+ * Detach the canvas for the given stream id.
47
+ */
48
+ detachCanvas(streamId: string): void;
49
+ /**
50
+ * Enable or disable video playback.
51
+ */
52
+ setEnabled(enabled: boolean): Promise<void>;
53
+ /**
54
+ * Dispose of resources. After calling this, the instance should not be reused.
55
+ */
56
+ dispose(): void;
57
+ private canUseOffscreenCanvas;
58
+ private hasDemand;
59
+ private ensureWorker;
60
+ private onWorkerMessage;
61
+ private syncAllStreamsToWorker;
62
+ private syncStreamToWorker;
63
+ private requestIdrForStream;
64
+ private parseStreamId;
65
+ private detachProtocolPort;
66
+ private ensureProtocolPort;
67
+ private maybeStopWorker;
68
+ private stopWorker;
69
+ private ensureStateSubscription;
70
+ }
@@ -0,0 +1,14 @@
1
+ export { IkonMedia } from './ikon-media';
2
+ export type { IkonMediaConfig } from './ikon-media';
3
+ export { IkonAudioPlayback } from './ikon-audio-playback';
4
+ export type { IkonAudioPlaybackConfig, IkonAudioOutputConfig } from './ikon-audio-playback';
5
+ export { IkonVideoPlayback } from './ikon-video-playback';
6
+ export type { IkonVideoPlaybackConfig } from './ikon-video-playback';
7
+ export { isSharedArrayBufferSupported, isAudioWorkletSupported } from './capabilities';
8
+ export { IkonMediaCapture } from './ikon-media-capture';
9
+ export { IkonVideoCapture } from './ikon-video-capture';
10
+ export type { IkonVideoCaptureHandle, IkonVideoCaptureRequest, VideoCaptureSource } from './ikon-video-capture';
11
+ export { IkonAudioCapture } from './ikon-audio-capture';
12
+ export type { IkonAudioCaptureHandle, IkonAudioCaptureRequest } from './ikon-audio-capture';
13
+ export { IkonImageCapture } from './ikon-image-capture';
14
+ export type { IkonImageCaptureRequest, IkonImageCaptureResult } from './ikon-image-capture';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SharedArrayBuffer-backed ring buffer for real-time PCM transport.
3
+ *
4
+ * The design goal is predictable, low-latency reads on the AudioWorklet thread.
5
+ * The ring buffer stores a fixed-size Float32 sample array plus atomic read/write indices.
6
+ */
7
+ export declare class RingBuffer<TArray extends Float32Array = Float32Array> {
8
+ static getStorageForCapacity(capacity: number, type: typeof Float32Array): SharedArrayBuffer;
9
+ private readonly readWrite;
10
+ private readonly storage;
11
+ readonly buf: SharedArrayBuffer;
12
+ constructor(sharedArrayBuffer: SharedArrayBuffer, type: {
13
+ new (buffer: ArrayBufferLike, byteOffset: number, length: number): TArray;
14
+ });
15
+ get capacity(): number;
16
+ flush(): void;
17
+ availableRead(): number;
18
+ availableWrite(): number;
19
+ push(input: Float32Array): number;
20
+ pop(target: Float32Array): number;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikonai/sdk",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -0,0 +1,6 @@
1
+ export interface Deferred<T> {
2
+ promise: Promise<T>;
3
+ resolve: (value: T) => void;
4
+ reject: (error: Error) => void;
5
+ }
6
+ export declare function createDeferred<T>(): Deferred<T>;
@@ -0,0 +1 @@
1
+ export declare function getOpcodeName(opcode: number): string;
@@ -0,0 +1,7 @@
1
+ export type UserGestureKind = 'microphone' | 'camera' | 'screen' | 'image';
2
+ export interface UserGestureRequest {
3
+ kind: UserGestureKind;
4
+ resume: () => void;
5
+ cancel?: () => void;
6
+ }
7
+ export declare function isPermissionOrGestureError(error: unknown): boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ type CodecPreference = 'h264' | 'vp8' | 'hevc';
2
+ export type VideoCaptureWorkerOptions = {
3
+ preferredCodecs?: CodecPreference[];
4
+ width?: number;
5
+ height?: number;
6
+ framerate?: number;
7
+ bitrate?: number;
8
+ keyFrameIntervalFrames?: number;
9
+ };
10
+ export {};
@@ -0,0 +1 @@
1
+ export {};