@vocalstack/js-sdk 1.0.13 → 2.0.0-alpha.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/LICENSE +21 -21
- package/README.md +112 -69
- package/SKILL.md +252 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.js +74 -0
- package/dist/file.d.ts +2 -0
- package/dist/file.js +38 -0
- package/dist/http.d.ts +75 -0
- package/dist/http.js +122 -0
- package/dist/index.d.ts +56 -2
- package/dist/index.js +65 -17
- package/dist/mic.d.ts +75 -0
- package/dist/mic.js +258 -0
- package/dist/realtime.d.ts +132 -0
- package/dist/realtime.js +450 -0
- package/dist/resources.d.ts +521 -0
- package/dist/resources.js +787 -0
- package/dist/subscription.d.ts +55 -0
- package/dist/subscription.js +164 -0
- package/dist/ws.d.ts +12 -0
- package/dist/ws.js +15 -0
- package/package.json +32 -36
- package/skills/vocalstack/SKILL.md +252 -0
- package/dist/RestApi.d.ts +0 -21
- package/dist/RestApi.js +0 -94
- package/dist/VocalStackApi.d.ts +0 -20
- package/dist/VocalStackApi.js +0 -51
- package/dist/WebsocketApi.d.ts +0 -38
- package/dist/WebsocketApi.js +0 -150
- package/dist/apis/Billing.d.ts +0 -59
- package/dist/apis/Billing.js +0 -21
- package/dist/apis/LiveTranscription.d.ts +0 -58
- package/dist/apis/LiveTranscription.js +0 -55
- package/dist/apis/Polyglot.d.ts +0 -113
- package/dist/apis/Polyglot.js +0 -68
- package/dist/apis/Security.d.ts +0 -25
- package/dist/apis/Security.js +0 -35
- package/dist/apis/Transcriptions.d.ts +0 -61
- package/dist/apis/Transcriptions.js +0 -43
- package/dist/apis/UrlTranscription.d.ts +0 -59
- package/dist/apis/UrlTranscription.js +0 -53
- package/dist/apis/index.d.ts +0 -6
- package/dist/apis/index.js +0 -22
- package/dist/esm/ApiVersion.d.ts +0 -2
- package/dist/esm/ApiVersion.js +0 -28
- package/dist/esm/RestApi.d.ts +0 -21
- package/dist/esm/RestApi.js +0 -91
- package/dist/esm/VocalStackApi.d.ts +0 -19
- package/dist/esm/VocalStackApi.js +0 -46
- package/dist/esm/WebsocketApi.d.ts +0 -38
- package/dist/esm/WebsocketApi.js +0 -146
- package/dist/esm/apis/Billing.d.ts +0 -59
- package/dist/esm/apis/Billing.js +0 -17
- package/dist/esm/apis/LiveTranscription.d.ts +0 -58
- package/dist/esm/apis/LiveTranscription.js +0 -51
- package/dist/esm/apis/Polyglot.d.ts +0 -113
- package/dist/esm/apis/Polyglot.js +0 -64
- package/dist/esm/apis/Security.d.ts +0 -25
- package/dist/esm/apis/Security.js +0 -31
- package/dist/esm/apis/Transcriptions.d.ts +0 -61
- package/dist/esm/apis/Transcriptions.js +0 -39
- package/dist/esm/apis/UrlTranscription.d.ts +0 -59
- package/dist/esm/apis/UrlTranscription.js +0 -49
- package/dist/esm/apis/index.d.ts +0 -6
- package/dist/esm/apis/index.js +0 -6
- package/dist/esm/generics.zod.d.ts +0 -77
- package/dist/esm/generics.zod.js +0 -45
- package/dist/esm/index.d.ts +0 -2
- package/dist/esm/index.js +0 -2
- package/dist/esm/version.d.ts +0 -1
- package/dist/esm/version.js +0 -1
- package/dist/generics.zod.d.ts +0 -77
- package/dist/generics.zod.js +0 -48
package/dist/file.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export async function* fileChunks(input) {
|
|
2
|
+
if (typeof input === 'string') {
|
|
3
|
+
const fsSpecifier = 'node:' + 'fs';
|
|
4
|
+
const { createReadStream } = (await import(/* @vite-ignore */ fsSpecifier));
|
|
5
|
+
for await (const chunk of createReadStream(input)) {
|
|
6
|
+
yield chunk;
|
|
7
|
+
}
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (input instanceof Uint8Array) {
|
|
11
|
+
yield input;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (input instanceof ArrayBuffer) {
|
|
15
|
+
yield new Uint8Array(input);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (typeof Blob !== 'undefined' && input instanceof Blob) {
|
|
19
|
+
const reader = input.stream().getReader();
|
|
20
|
+
try {
|
|
21
|
+
for (;;) {
|
|
22
|
+
const { done, value } = await reader.read();
|
|
23
|
+
if (done)
|
|
24
|
+
return;
|
|
25
|
+
if (value)
|
|
26
|
+
yield value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
reader.releaseLock();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (input && typeof input === 'object' && Symbol.asyncIterator in input) {
|
|
34
|
+
yield* input;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
throw new TypeError('transcribeFile: expected a path, Blob, byte array, or async iterable');
|
|
38
|
+
}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export type Query = Record<string, string | number | boolean | undefined | null>;
|
|
2
|
+
export interface ClientOptions {
|
|
3
|
+
/** API origin; `https://api.vocalstack.com` when absent. */
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
/** An API key (`vs_key_…`), on a server: keys are refused from web
|
|
6
|
+
* pages. Absent (and no `token`) = anonymous (a listener). */
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
/** In a browser instead: a signed-in person's access token or a
|
|
9
|
+
* short-lived browser token (`vs_bt_…`), or a getter so a refreshed one
|
|
10
|
+
* is picked up per request. Not with `apiKey`. */
|
|
11
|
+
token?: string | (() => string | undefined | null | Promise<string | undefined | null>);
|
|
12
|
+
/** Act for an organization the user belongs to (`X-Acting-Principal`). */
|
|
13
|
+
actingPrincipal?: string;
|
|
14
|
+
/** A stable per-device id (correlation only, `X-Device-Id`). */
|
|
15
|
+
deviceId?: string;
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
/** How often a `429 rate_limited`, or a `503 unavailable` on a call that
|
|
18
|
+
* is safe to repeat (GET, HEAD, DELETE, or one with an `Idempotency-Key`),
|
|
19
|
+
* is retried after its `Retry-After` (at most 60 s each). Default 2; 0
|
|
20
|
+
* turns it off. */
|
|
21
|
+
maxRetries?: number;
|
|
22
|
+
/** Default deadline for a realtime socket to report ready, in ms. */
|
|
23
|
+
readyDeadlineMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Per-call options every method takes last. */
|
|
26
|
+
export interface CallOptions {
|
|
27
|
+
/** Sent as `Idempotency-Key` on the creates that honour it. */
|
|
28
|
+
idempotencyKey?: string;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
/** How a listener reads something that is not theirs: a private
|
|
32
|
+
* broadcast's listener token, or a share link's code. */
|
|
33
|
+
export interface ReadAs {
|
|
34
|
+
listenerToken?: string;
|
|
35
|
+
shareCode?: string;
|
|
36
|
+
}
|
|
37
|
+
/** A listener-side read's query: the wire spells both as the options do
|
|
38
|
+
* (`?listenerToken=`, `?shareCode=`), so this only picks them out. */
|
|
39
|
+
export declare function listenerQuery(as: ReadAs | undefined): Query;
|
|
40
|
+
export interface RequestInit2 extends CallOptions {
|
|
41
|
+
query?: Query;
|
|
42
|
+
body?: unknown;
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
/** Send no credential even when the client has one. */
|
|
45
|
+
anonymous?: boolean;
|
|
46
|
+
/** Never retry, whatever the answer (a single-use code). */
|
|
47
|
+
noRetry?: boolean;
|
|
48
|
+
/** Read a 400 as OAuth's `{ error, error_description }`. */
|
|
49
|
+
oauthErrors?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare const DEFAULT_BASE_URL = "https://api.vocalstack.com";
|
|
52
|
+
export declare function qs(q: Query | undefined): string;
|
|
53
|
+
export declare const seg: (s: string | number) => string;
|
|
54
|
+
/** Whether a failed call is retried after its `Retry-After` (up to
|
|
55
|
+
* `maxRetries` times): `429 rate_limited` always (nothing ran), and
|
|
56
|
+
* `503 unavailable` (the database is waking) only when repeating the call
|
|
57
|
+
* cannot do the work twice: GET, HEAD and DELETE, or a call carrying an
|
|
58
|
+
* `Idempotency-Key`. */
|
|
59
|
+
export declare function retries(status: number, code: string, method: string, idempotencyKey?: string): boolean;
|
|
60
|
+
/** The one HTTP path every resource method goes through. */
|
|
61
|
+
export declare class Transport {
|
|
62
|
+
#private;
|
|
63
|
+
readonly baseUrl: string;
|
|
64
|
+
readonly opts: ClientOptions;
|
|
65
|
+
constructor(opts: ClientOptions);
|
|
66
|
+
/** What goes in `Authorization: Bearer`: the API key, else the token. */
|
|
67
|
+
bearer(): Promise<string | undefined>;
|
|
68
|
+
url(path: string, query?: Query): string;
|
|
69
|
+
wsUrl(path: string, query?: Query): string;
|
|
70
|
+
/** The raw response, after error mapping. Redirects are not followed
|
|
71
|
+
* (`redirect: 'manual'`), so a 302 comes back as itself. */
|
|
72
|
+
raw(method: string, path: string, init?: RequestInit2): Promise<Response>;
|
|
73
|
+
/** A JSON call; an empty body resolves to `undefined`. */
|
|
74
|
+
json<T>(method: string, path: string, init?: RequestInit2): Promise<T>;
|
|
75
|
+
}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { fromEnvelope, fromOAuth } from './errors.js';
|
|
2
|
+
/** A listener-side read's query: the wire spells both as the options do
|
|
3
|
+
* (`?listenerToken=`, `?shareCode=`), so this only picks them out. */
|
|
4
|
+
export function listenerQuery(as) {
|
|
5
|
+
return { listenerToken: as?.listenerToken, shareCode: as?.shareCode };
|
|
6
|
+
}
|
|
7
|
+
export const DEFAULT_BASE_URL = 'https://api.vocalstack.com';
|
|
8
|
+
export function qs(q) {
|
|
9
|
+
if (!q)
|
|
10
|
+
return '';
|
|
11
|
+
const parts = [];
|
|
12
|
+
for (const [k, v] of Object.entries(q)) {
|
|
13
|
+
if (v === undefined || v === null || v === '')
|
|
14
|
+
continue;
|
|
15
|
+
parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
16
|
+
}
|
|
17
|
+
return parts.length ? `?${parts.join('&')}` : '';
|
|
18
|
+
}
|
|
19
|
+
export const seg = (s) => encodeURIComponent(String(s));
|
|
20
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21
|
+
/** The wait before retrying `503 unavailable` when it names none. */
|
|
22
|
+
const UNAVAILABLE_WAIT_S = 2;
|
|
23
|
+
/** Whether a failed call is retried after its `Retry-After` (up to
|
|
24
|
+
* `maxRetries` times): `429 rate_limited` always (nothing ran), and
|
|
25
|
+
* `503 unavailable` (the database is waking) only when repeating the call
|
|
26
|
+
* cannot do the work twice: GET, HEAD and DELETE, or a call carrying an
|
|
27
|
+
* `Idempotency-Key`. */
|
|
28
|
+
export function retries(status, code, method, idempotencyKey) {
|
|
29
|
+
if (status === 429)
|
|
30
|
+
return true;
|
|
31
|
+
if (status !== 503 || code !== 'unavailable')
|
|
32
|
+
return false;
|
|
33
|
+
const m = method.toUpperCase();
|
|
34
|
+
return m === 'GET' || m === 'HEAD' || m === 'DELETE' || !!idempotencyKey;
|
|
35
|
+
}
|
|
36
|
+
/** The one HTTP path every resource method goes through. */
|
|
37
|
+
export class Transport {
|
|
38
|
+
baseUrl;
|
|
39
|
+
opts;
|
|
40
|
+
#fetch;
|
|
41
|
+
constructor(opts) {
|
|
42
|
+
this.baseUrl = (opts.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
43
|
+
if (opts.apiKey && opts.token)
|
|
44
|
+
throw new TypeError('VocalStack: pass apiKey (a server) or token (a browser), not both');
|
|
45
|
+
this.opts = opts;
|
|
46
|
+
this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
47
|
+
}
|
|
48
|
+
/** What goes in `Authorization: Bearer`: the API key, else the token. */
|
|
49
|
+
async bearer() {
|
|
50
|
+
if (this.opts.apiKey)
|
|
51
|
+
return this.opts.apiKey;
|
|
52
|
+
const t = this.opts.token;
|
|
53
|
+
const v = typeof t === 'function' ? await t() : t;
|
|
54
|
+
return v ?? undefined;
|
|
55
|
+
}
|
|
56
|
+
url(path, query) {
|
|
57
|
+
return `${this.baseUrl}${path}${qs(query)}`;
|
|
58
|
+
}
|
|
59
|
+
wsUrl(path, query) {
|
|
60
|
+
return `${this.baseUrl.replace(/^http/, 'ws')}${path}${qs(query)}`;
|
|
61
|
+
}
|
|
62
|
+
/** The raw response, after error mapping. Redirects are not followed
|
|
63
|
+
* (`redirect: 'manual'`), so a 302 comes back as itself. */
|
|
64
|
+
async raw(method, path, init = {}) {
|
|
65
|
+
const headers = { accept: 'application/json', ...init.headers };
|
|
66
|
+
const form = typeof FormData !== 'undefined' && init.body instanceof FormData;
|
|
67
|
+
const urlencoded = init.body instanceof URLSearchParams;
|
|
68
|
+
if (urlencoded)
|
|
69
|
+
headers['content-type'] = 'application/x-www-form-urlencoded';
|
|
70
|
+
else if (init.body !== undefined && !form)
|
|
71
|
+
headers['content-type'] = 'application/json';
|
|
72
|
+
const token = init.anonymous ? undefined : await this.bearer();
|
|
73
|
+
if (token)
|
|
74
|
+
headers.authorization = `Bearer ${token}`;
|
|
75
|
+
if (this.opts.actingPrincipal)
|
|
76
|
+
headers['x-acting-principal'] = this.opts.actingPrincipal;
|
|
77
|
+
if (this.opts.deviceId)
|
|
78
|
+
headers['x-device-id'] = this.opts.deviceId;
|
|
79
|
+
if (init.idempotencyKey)
|
|
80
|
+
headers['idempotency-key'] = init.idempotencyKey;
|
|
81
|
+
const body = init.body === undefined ? undefined : form ? init.body : urlencoded ? String(init.body) : JSON.stringify(init.body);
|
|
82
|
+
const maxRetries = init.noRetry ? 0 : (this.opts.maxRetries ?? 2);
|
|
83
|
+
for (let attempt = 0;; attempt++) {
|
|
84
|
+
const res = await this.#fetch(this.url(path, init.query), {
|
|
85
|
+
method,
|
|
86
|
+
headers,
|
|
87
|
+
body,
|
|
88
|
+
redirect: 'manual',
|
|
89
|
+
signal: init.signal,
|
|
90
|
+
});
|
|
91
|
+
if (res.ok || (res.status >= 300 && res.status < 400))
|
|
92
|
+
return res;
|
|
93
|
+
const text = await res.text().catch(() => '');
|
|
94
|
+
let json = null;
|
|
95
|
+
try {
|
|
96
|
+
json = text ? JSON.parse(text) : null;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
json = null;
|
|
100
|
+
}
|
|
101
|
+
const err = (init.oauthErrors ? fromOAuth : fromEnvelope)(json, res.status, res.headers);
|
|
102
|
+
if (retries(res.status, err.code, method, init.idempotencyKey) && attempt < maxRetries) {
|
|
103
|
+
// `unavailable` (a waking database) asks for Retry-After: 2; a
|
|
104
|
+
// missing header means the same.
|
|
105
|
+
const wait = err.retryAfter ?? (err.code === 'unavailable' ? UNAVAILABLE_WAIT_S : undefined);
|
|
106
|
+
if (wait !== undefined && wait <= 60) {
|
|
107
|
+
await sleep(wait * 1000);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** A JSON call; an empty body resolves to `undefined`. */
|
|
115
|
+
async json(method, path, init = {}) {
|
|
116
|
+
const res = await this.raw(method, path, init);
|
|
117
|
+
if (res.status >= 300 && res.status < 400)
|
|
118
|
+
return { location: res.headers.get('location') };
|
|
119
|
+
const text = await res.text();
|
|
120
|
+
return (text ? JSON.parse(text) : undefined);
|
|
121
|
+
}
|
|
122
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The VocalStack SDK: one client, `VocalStack`, grouped by resource.
|
|
3
|
+
*
|
|
4
|
+
* import { VocalStack } from '@vocalstack/js-sdk';
|
|
5
|
+
* const vs = new VocalStack({ apiKey: process.env.VOCALSTACK_API_KEY });
|
|
6
|
+
*
|
|
7
|
+
* const tx = await vs.transcriptions.createFromUrl('https://example.com/talk.mp3');
|
|
8
|
+
* const job = await vs.jobs.wait(tx.job!.recordingId);
|
|
9
|
+
* const { sentences } = await vs.recordings.transcript(job.recordingId);
|
|
10
|
+
*
|
|
11
|
+
* What is what (the API's glossary): a realtime session is a signed token
|
|
12
|
+
* plus one socket, nothing stored (`vs.realtime`); a broadcast is the link
|
|
13
|
+
* listeners open; a transcription is a file, link, microphone or stream
|
|
14
|
+
* transcribed and stored; a recording is one take inside either; a job is
|
|
15
|
+
* a file or link recording's transcription work.
|
|
16
|
+
*/
|
|
17
|
+
import { type ClientOptions } from './http.js';
|
|
18
|
+
import { Realtime } from './realtime.js';
|
|
19
|
+
import { Batches, Billing, Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Transcriptions, Voices } from './resources.js';
|
|
20
|
+
export type { ClientOptions, CallOptions, ReadAs, Query } from './http.js';
|
|
21
|
+
export { DEFAULT_BASE_URL } from './http.js';
|
|
22
|
+
export { VocalStackError } from './errors.js';
|
|
23
|
+
export type { ConnectTokenErrorCode, VocalStackErrorOptions } from './errors.js';
|
|
24
|
+
export { RealtimeSession } from './realtime.js';
|
|
25
|
+
export type { ConnectParams, SessionEvent } from './realtime.js';
|
|
26
|
+
export type { FileInput } from './file.js';
|
|
27
|
+
export { Realtime, applySentence } from './realtime.js';
|
|
28
|
+
export type { ConnectOptions, TranscribeOptions, RealtimeTranscript } from './realtime.js';
|
|
29
|
+
export { Subscription } from './subscription.js';
|
|
30
|
+
export type { SubscribeOptions, SubscriptionEvent } from './subscription.js';
|
|
31
|
+
export type { WsLike } from './ws.js';
|
|
32
|
+
export { Batches, Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Transcriptions, Voices, } from './resources.js';
|
|
33
|
+
export type { AddBatchFileInput, ConnectToken, ConnectTokenInput, CreateTranscriptionInput, LifecycleFilter, Me, PageOptions, RecordingPageOptions, SearchOptions, Upload, UsageChargesOptions, UsageOptions } from './resources.js';
|
|
34
|
+
export type * from '@vocalstack/contract';
|
|
35
|
+
export type VocalStackOptions = ClientOptions;
|
|
36
|
+
export declare class VocalStack {
|
|
37
|
+
readonly baseUrl: string;
|
|
38
|
+
readonly realtime: Realtime;
|
|
39
|
+
readonly broadcasts: Broadcasts;
|
|
40
|
+
readonly transcriptions: Transcriptions;
|
|
41
|
+
readonly batches: Batches;
|
|
42
|
+
readonly recordings: Recordings;
|
|
43
|
+
readonly jobs: Jobs;
|
|
44
|
+
readonly corrections: Corrections;
|
|
45
|
+
readonly shareLinks: ShareLinks;
|
|
46
|
+
readonly voices: Voices;
|
|
47
|
+
readonly search: Search;
|
|
48
|
+
readonly me: MeApi;
|
|
49
|
+
readonly connect: Connect;
|
|
50
|
+
readonly billing: Billing;
|
|
51
|
+
readonly languages: Languages;
|
|
52
|
+
readonly rtmp: Rtmp;
|
|
53
|
+
readonly events: Events;
|
|
54
|
+
readonly meta: Meta;
|
|
55
|
+
constructor(opts?: ClientOptions);
|
|
56
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The VocalStack SDK: one client, `VocalStack`, grouped by resource.
|
|
3
|
+
*
|
|
4
|
+
* import { VocalStack } from '@vocalstack/js-sdk';
|
|
5
|
+
* const vs = new VocalStack({ apiKey: process.env.VOCALSTACK_API_KEY });
|
|
6
|
+
*
|
|
7
|
+
* const tx = await vs.transcriptions.createFromUrl('https://example.com/talk.mp3');
|
|
8
|
+
* const job = await vs.jobs.wait(tx.job!.recordingId);
|
|
9
|
+
* const { sentences } = await vs.recordings.transcript(job.recordingId);
|
|
10
|
+
*
|
|
11
|
+
* What is what (the API's glossary): a realtime session is a signed token
|
|
12
|
+
* plus one socket, nothing stored (`vs.realtime`); a broadcast is the link
|
|
13
|
+
* listeners open; a transcription is a file, link, microphone or stream
|
|
14
|
+
* transcribed and stored; a recording is one take inside either; a job is
|
|
15
|
+
* a file or link recording's transcription work.
|
|
16
|
+
*/
|
|
17
|
+
import { Transport } from './http.js';
|
|
18
|
+
import { Realtime } from './realtime.js';
|
|
19
|
+
import { Batches, Billing, Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Transcriptions, Voices, } from './resources.js';
|
|
20
|
+
export { DEFAULT_BASE_URL } from './http.js';
|
|
21
|
+
export { VocalStackError } from './errors.js';
|
|
22
|
+
export { RealtimeSession } from './realtime.js';
|
|
23
|
+
export { Realtime, applySentence } from './realtime.js';
|
|
24
|
+
export { Subscription } from './subscription.js';
|
|
25
|
+
export { Batches, Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Transcriptions, Voices, } from './resources.js';
|
|
26
|
+
export class VocalStack {
|
|
27
|
+
baseUrl;
|
|
28
|
+
realtime;
|
|
29
|
+
broadcasts;
|
|
30
|
+
transcriptions;
|
|
31
|
+
batches;
|
|
32
|
+
recordings;
|
|
33
|
+
jobs;
|
|
34
|
+
corrections;
|
|
35
|
+
shareLinks;
|
|
36
|
+
voices;
|
|
37
|
+
search;
|
|
38
|
+
me;
|
|
39
|
+
connect;
|
|
40
|
+
billing;
|
|
41
|
+
languages;
|
|
42
|
+
rtmp;
|
|
43
|
+
events;
|
|
44
|
+
meta;
|
|
45
|
+
constructor(opts = {}) {
|
|
46
|
+
const t = new Transport(opts);
|
|
47
|
+
this.baseUrl = t.baseUrl;
|
|
48
|
+
this.realtime = new Realtime(t);
|
|
49
|
+
this.broadcasts = new Broadcasts(t);
|
|
50
|
+
this.transcriptions = new Transcriptions(t);
|
|
51
|
+
this.batches = new Batches(t);
|
|
52
|
+
this.recordings = new Recordings(t);
|
|
53
|
+
this.jobs = new Jobs(t);
|
|
54
|
+
this.corrections = new Corrections(t);
|
|
55
|
+
this.shareLinks = new ShareLinks(t);
|
|
56
|
+
this.voices = new Voices(t);
|
|
57
|
+
this.search = new Search(t);
|
|
58
|
+
this.me = new MeApi(t);
|
|
59
|
+
this.connect = new Connect(t);
|
|
60
|
+
this.billing = new Billing(t);
|
|
61
|
+
this.languages = new Languages(t);
|
|
62
|
+
this.rtmp = new Rtmp(t);
|
|
63
|
+
this.events = new Events(t);
|
|
64
|
+
this.meta = new Meta(t);
|
|
7
65
|
}
|
|
8
|
-
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./apis/index"), exports);
|
|
18
|
-
__exportStar(require("./generics.zod"), exports);
|
|
66
|
+
}
|
package/dist/mic.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { RealtimeSession } from './realtime.js';
|
|
2
|
+
/** Why capture stopped producing audio (see `MicCaptureOptions.onStall`). */
|
|
3
|
+
export type MicStallReason =
|
|
4
|
+
/** The input track ended: device unplugged, headset switched, permission revoked. */
|
|
5
|
+
'track_ended'
|
|
6
|
+
/** The track is muted by the OS/hardware (no samples until it unmutes). */
|
|
7
|
+
| 'track_muted'
|
|
8
|
+
/** The AudioContext was suspended (phone call, screen lock, route change). */
|
|
9
|
+
| 'context_suspended'
|
|
10
|
+
/** The AudioContext was closed underneath us. */
|
|
11
|
+
| 'context_closed'
|
|
12
|
+
/** Seconds of exact-zero samples: a silently denied or hardware-muted mic. */
|
|
13
|
+
| 'silent';
|
|
14
|
+
export interface MicCaptureOptions {
|
|
15
|
+
chunkSeconds?: number;
|
|
16
|
+
sampleRate?: number;
|
|
17
|
+
audio?: MediaTrackConstraints;
|
|
18
|
+
/**
|
|
19
|
+
* Called every ~0.5s with (pcm, wall): cumulative output samples
|
|
20
|
+
* produced and seconds since capture start. Wire it to
|
|
21
|
+
* `session.sendClock` so the server can detect capture loss (a
|
|
22
|
+
* suspended context or dead track produces no samples while wall
|
|
23
|
+
* time advances) and mark the transcript with honest gaps. Driven
|
|
24
|
+
* by a timer, not by sample arrival, so it keeps reporting while
|
|
25
|
+
* capture is stalled — that is the case it exists for.
|
|
26
|
+
*/
|
|
27
|
+
onClock?: (pcm: number, wall: number) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Capture stopped producing audio. Recovery is attempted
|
|
30
|
+
* automatically (context resume, track re-acquire); `onRecover`
|
|
31
|
+
* fires when samples flow again. Use it to show a "microphone
|
|
32
|
+
* lost" state instead of "Listening".
|
|
33
|
+
*/
|
|
34
|
+
onStall?: (reason: MicStallReason) => void;
|
|
35
|
+
/** Audio is flowing again after a stall. */
|
|
36
|
+
onRecover?: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Exact-zero run length (seconds) that flags a silently denied or
|
|
39
|
+
* hardware-muted mic (real mics always carry a noise floor). Default
|
|
40
|
+
* 3; 0 disables.
|
|
41
|
+
*/
|
|
42
|
+
silentSeconds?: number;
|
|
43
|
+
}
|
|
44
|
+
/** The input device the capture is reading, as the browser reports it. */
|
|
45
|
+
export interface MicDevice {
|
|
46
|
+
/** The device's name, e.g. "MacBook Pro Microphone" ("" before permission is granted). */
|
|
47
|
+
label: string;
|
|
48
|
+
/** Its native rate (the capture resamples to `sampleRate`). */
|
|
49
|
+
sampleRate?: number;
|
|
50
|
+
channelCount?: number;
|
|
51
|
+
noiseSuppression?: boolean;
|
|
52
|
+
echoCancellation?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface MicCapture {
|
|
55
|
+
stop(): Promise<void>;
|
|
56
|
+
readonly sampleRate: number;
|
|
57
|
+
/** The microphone being read now (a re-acquired track may be another one). */
|
|
58
|
+
readonly device: MicDevice;
|
|
59
|
+
/** Hold the capture open but stop sending what the microphone hears.
|
|
60
|
+
*
|
|
61
|
+
* A side conversation should not have to end the session, and pausing
|
|
62
|
+
* is not the same thing: pausing closes the stream and the listeners
|
|
63
|
+
* lose it. Muting keeps every listener connected and the recording
|
|
64
|
+
* running, and simply says nothing.
|
|
65
|
+
*
|
|
66
|
+
* Muted chunks are emitted as SILENCE rather than dropped. The server
|
|
67
|
+
* measures media time by the audio it receives, so dropping would
|
|
68
|
+
* compress the timeline and every later timestamp would be wrong;
|
|
69
|
+
* silence keeps the clock honest and transcribes to nothing, which is
|
|
70
|
+
* exactly what happened. */
|
|
71
|
+
setMuted(muted: boolean): void;
|
|
72
|
+
readonly muted: boolean;
|
|
73
|
+
}
|
|
74
|
+
export declare function captureMicrophone(onChunk: (pcm: Float32Array) => void, opts?: MicCaptureOptions): Promise<MicCapture>;
|
|
75
|
+
export declare function streamMicrophone(session: RealtimeSession, opts?: MicCaptureOptions): Promise<MicCapture>;
|