naijalingo 0.1.2 → 0.2.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 CHANGED
@@ -74,6 +74,19 @@ const client = new NaijaLingo({ apiKey: "YOUR_API_KEY" });
74
74
 
75
75
  ## API Reference
76
76
 
77
+ ### Speech-to-Text
78
+
79
+ ```ts
80
+ const transcript = await client.stt.transcribe(
81
+ "https://cdn.example.com/pidgin-sample.wav",
82
+ { language: "pcm", durationSeconds: 8.1 },
83
+ );
84
+ console.log(transcript.text, transcript.language, transcript.duration);
85
+ ```
86
+
87
+ The audio URL must be publicly reachable and your API key must have the `stt`
88
+ scope. Supported language hints are `yo`, `ha`, `ig`, `pcm`, and `en`.
89
+
77
90
  ### Text-to-Speech
78
91
 
79
92
  ```ts
@@ -153,6 +166,9 @@ await greeting.save("cloned_voice_greeting.wav");
153
166
 
154
167
  // `speaker: audio.voiceId` is equivalent.
155
168
 
169
+ // Permanently delete the cloned voice when consent is withdrawn.
170
+ await client.tts.deleteVoice(audio.voiceId);
171
+
156
172
  // From a Buffer
157
173
  import { readFileSync } from "node:fs";
158
174
  const buf = readFileSync("reference.wav");
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  NaijaLingoError: () => NaijaLingoError,
31
31
  NotFoundError: () => NotFoundError,
32
32
  RateLimitError: () => RateLimitError,
33
+ STT: () => STT,
33
34
  ServerError: () => ServerError,
34
35
  TTS: () => TTS,
35
36
  VERSION: () => VERSION
@@ -197,6 +198,13 @@ var BaseClient = class {
197
198
  });
198
199
  return new Uint8Array(await resp.arrayBuffer());
199
200
  }
201
+ async postJson(path, body) {
202
+ const resp = await this.request("POST", path, {
203
+ headers: { "Content-Type": "application/json" },
204
+ body: JSON.stringify(body)
205
+ });
206
+ return await resp.json();
207
+ }
200
208
  async *postStream(path, body) {
201
209
  let lastError;
202
210
  for (let attempt = 0; attempt < 3; attempt++) {
@@ -260,6 +268,9 @@ var BaseClient = class {
260
268
  // Let fetch set multipart boundary — do not set Content-Type
261
269
  });
262
270
  }
271
+ async delete(path) {
272
+ await this.request("DELETE", path);
273
+ }
263
274
  };
264
275
  function sleep(ms) {
265
276
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -641,6 +652,14 @@ var TTS = class {
641
652
  const data = await this.client.getJson(`/v1/speakers/${speakerId}`);
642
653
  return speakerFromDict(data);
643
654
  }
655
+ /** Permanently delete a cloned voice owned by this API key. */
656
+ async deleteVoice(voiceId) {
657
+ const normalizedVoiceId = voiceId.trim();
658
+ if (!normalizedVoiceId) {
659
+ throw new Error("voiceId is required");
660
+ }
661
+ await this.client.delete(`/v1/voices/${encodeURIComponent(normalizedVoiceId)}`);
662
+ }
644
663
  async listLanguages() {
645
664
  const data = await this.client.getJson("/v1/languages");
646
665
  return languageListFromDict(data);
@@ -698,14 +717,34 @@ function guessMime(filename) {
698
717
  return "application/octet-stream";
699
718
  }
700
719
 
720
+ // src/stt.ts
721
+ var STT = class {
722
+ constructor(client) {
723
+ this.client = client;
724
+ }
725
+ client;
726
+ async transcribe(fileUrl, options = {}) {
727
+ const body = { file_url: fileUrl };
728
+ if (options.language) body.language = options.language;
729
+ if (options.durationSeconds != null) body.duration_seconds = options.durationSeconds;
730
+ let response = await this.client.postJson("/v1/audio/transcriptions", body);
731
+ while (typeof response.result === "object" && response.result !== null && !Array.isArray(response.result)) {
732
+ response = response.result;
733
+ }
734
+ return response;
735
+ }
736
+ };
737
+
701
738
  // src/index.ts
702
739
  var VERSION = "0.1.2";
703
740
  var NaijaLingo = class {
704
741
  _client;
705
742
  tts;
743
+ stt;
706
744
  constructor(options = {}) {
707
745
  this._client = new BaseClient(options);
708
746
  this.tts = new TTS(this._client);
747
+ this.stt = new STT(this._client);
709
748
  }
710
749
  /** List available TTS models. GET /v1/models */
711
750
  async listModels() {
@@ -735,6 +774,7 @@ var NaijaLingo = class {
735
774
  NaijaLingoError,
736
775
  NotFoundError,
737
776
  RateLimitError,
777
+ STT,
738
778
  ServerError,
739
779
  TTS,
740
780
  VERSION
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/tts.ts","../src/types.ts","../src/voice.ts"],"sourcesContent":["/**\n * 9jaLingo Node.js SDK\n *\n * Official SDK for the 9jaLingo API — Nigerian language Text-to-Speech.\n *\n * @example\n * ```ts\n * import { NaijaLingo } from \"naijalingo\";\n *\n * const client = new NaijaLingo(); // uses NAIJALINGO_API_KEY\n * const audio = await client.tts.generate(\"Bawo ni!\", {\n * voice: \"adeola_yo\",\n * lang: \"yo\",\n * });\n * await audio.save(\"greeting.wav\");\n * ```\n */\n\nimport { BaseClient, type ClientOptions } from \"./client.js\";\nimport { TTS } from \"./tts.js\";\nimport {\n apiInfoFromDict,\n modelListFromDict,\n serviceInfoFromDict,\n type APIInfo,\n type ModelList,\n type ServiceInfo,\n} from \"./types.js\";\n\nexport const VERSION = \"0.1.2\";\n\nexport {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nexport {\n AudioResponse,\n AudioStream,\n CloneResponse,\n TTS,\n type CloneOptions,\n type GenerateOptions,\n type ListSpeakersOptions,\n type StreamOptions,\n} from \"./tts.js\";\n\nexport type {\n APIInfo,\n HealthStatus,\n Language,\n LanguageList,\n Model,\n ModelList,\n ResponseFormat,\n ServiceInfo,\n Speaker,\n SpeakerList,\n} from \"./types.js\";\n\nexport type { ClientOptions };\n\nexport class NaijaLingo {\n private readonly _client: BaseClient;\n readonly tts: TTS;\n\n constructor(options: ClientOptions = {}) {\n this._client = new BaseClient(options);\n this.tts = new TTS(this._client);\n }\n\n /** List available TTS models. GET /v1/models */\n async listModels(): Promise<ModelList> {\n const data = await this._client.getJson(\"/v1/models\");\n return modelListFromDict(data);\n }\n\n /** Root API information. GET / */\n async apiInfo(): Promise<APIInfo> {\n const data = await this._client.getJson(\"/\");\n return apiInfoFromDict(data);\n }\n\n /** v1 service metadata. GET /v1 */\n async serviceInfo(): Promise<ServiceInfo> {\n const data = await this._client.getJson(\"/v1\");\n return serviceInfoFromDict(data);\n }\n}\n","/** Base exception for all 9jaLingo SDK errors. */\nexport class NaijaLingoError extends Error {\n readonly statusCode?: number;\n readonly response?: unknown;\n\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message);\n this.name = \"NaijaLingoError\";\n this.statusCode = statusCode;\n this.response = response;\n }\n}\n\n/** Raised when the API key is missing or invalid. */\nexport class AuthenticationError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** Raised when rate limit is exceeded. */\nexport class RateLimitError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"RateLimitError\";\n }\n}\n\n/** Raised when a requested resource is not found. */\nexport class NotFoundError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"NotFoundError\";\n }\n}\n\n/** Raised when the API returns a 5xx error. */\nexport class ServerError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ServerError\";\n }\n}\n\n/** Raised when the inference GPU is waking from scale-to-zero. */\nexport class InferenceCapacityError extends ServerError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"InferenceCapacityError\";\n }\n}\n\n/** Raised when unable to connect to the API. */\nexport class ConnectionError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ConnectionError\";\n }\n}\n","import {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.9jalingo.org\";\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ClientOptions {\n apiKey?: string;\n baseUrl?: string;\n /** Request timeout in milliseconds (default 300000). */\n timeout?: number;\n}\n\nexport class BaseClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeout: number;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: ClientOptions = {}) {\n this.apiKey =\n options.apiKey ?? process.env.NAIJALINGO_API_KEY ?? \"\";\n const base =\n options.baseUrl ??\n process.env.NAIJALINGO_BASE_URL ??\n DEFAULT_BASE_URL;\n this.baseUrl = base.replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n this.defaultHeaders = {\n \"User-Agent\": \"naijalingo-js/0.1.2\",\n };\n if (this.apiKey) {\n this.defaultHeaders[\"X-API-Key\"] = this.apiKey;\n }\n }\n\n private async request(\n method: string,\n path: string,\n init: RequestInit & { timeoutMs?: number } = {},\n ): Promise<Response> {\n const url = `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`;\n const timeoutMs = init.timeoutMs ?? this.timeout;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(url, {\n ...init,\n method,\n headers: {\n ...this.defaultHeaders,\n ...(init.headers as Record<string, string> | undefined),\n },\n signal: controller.signal,\n });\n clearTimeout(timer);\n\n if (response.ok) {\n return response;\n }\n await this.raiseForStatus(response);\n // unreachable\n return response;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) {\n throw err;\n }\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n // Only retry connect-style failures, not aborts from long reads\n if (isAbort) {\n throw new ConnectionError(\n `Request timed out after ${timeoutMs}ms: ${String(err)}`,\n );\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n private async raiseForStatus(response: Response): Promise<never> {\n const status = response.status;\n let detail: string;\n let body: unknown;\n try {\n body = await response.json();\n detail =\n typeof body === \"object\" &&\n body !== null &&\n \"detail\" in body\n ? String((body as { detail: unknown }).detail)\n : JSON.stringify(body);\n } catch {\n detail = await response.text();\n }\n\n const normalizedDetail = detail.toLowerCase();\n if (status === 503 && (\n normalizedDetail.includes(\"inference capacity\")\n || normalizedDetail.includes(\"inference component has no capacity\")\n || normalizedDetail.includes(\"capacity is starting\")\n )) {\n throw new InferenceCapacityError(detail, status, body);\n }\n if (status === 401 || status === 403) {\n throw new AuthenticationError(detail, status, body);\n }\n if (status === 404) {\n throw new NotFoundError(detail, status, body);\n }\n if (status === 429) {\n throw new RateLimitError(detail, status, body);\n }\n if (status >= 500) {\n throw new ServerError(detail, status, body);\n }\n throw new NaijaLingoError(detail, status, body);\n }\n\n async getJson(\n path: string,\n params?: Record<string, string | undefined | null>,\n ): Promise<Record<string, unknown>> {\n let url = path;\n if (params) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v != null) qs.set(k, v);\n }\n const s = qs.toString();\n if (s) url = `${path}?${s}`;\n }\n const resp = await this.request(\"GET\", url);\n return (await resp.json()) as Record<string, unknown>;\n }\n\n async postBytes(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Uint8Array> {\n const resp = await this.request(\"POST\", path, {\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return new Uint8Array(await resp.arrayBuffer());\n }\n\n async *postStream(\n path: string,\n body: Record<string, unknown>,\n ): AsyncGenerator<Uint8Array, void, unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const response = await fetch(\n `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`,\n {\n method: \"POST\",\n headers: {\n ...this.defaultHeaders,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n },\n );\n clearTimeout(timer);\n\n if (!response.ok) {\n await this.raiseForStatus(response);\n }\n if (!response.body) {\n throw new ConnectionError(\"No response body for stream\");\n }\n\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value && value.length > 0) {\n yield value;\n }\n }\n } finally {\n reader.releaseLock();\n }\n return;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) throw err;\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n if (isAbort) {\n throw new ConnectionError(`Request timed out: ${String(err)}`);\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n async postMultipart(\n path: string,\n form: FormData,\n timeoutMs?: number,\n ): Promise<Response> {\n return this.request(\"POST\", path, {\n body: form,\n timeoutMs: timeoutMs ?? Math.max(this.timeout, 600_000),\n // Let fetch set multipart boundary — do not set Content-Type\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { basename, dirname } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport { BaseClient } from \"./client.js\";\nimport {\n type HealthStatus,\n type LanguageList,\n type ResponseFormat,\n type Speaker,\n type SpeakerList,\n healthStatusFromDict,\n languageListFromDict,\n speakerFromDict,\n speakerListFromDict,\n} from \"./types.js\";\nimport {\n resolveCloneLangAndSpeaker,\n resolveTtsVoiceAndLang,\n} from \"./voice.js\";\n\nconst DEFAULT_MODEL_NAME = \"9jalingo-tts-1\";\n\nexport type GenerateOptions = {\n voice?: string;\n speaker?: string;\n lang?: string;\n language?: string;\n modelName?: string;\n speakerEmbedding?: number[];\n responseFormat?: ResponseFormat;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n enableLongForm?: boolean;\n maxChunkDuration?: number;\n silenceDuration?: number;\n};\n\nexport type StreamOptions = Omit<\n GenerateOptions,\n \"modelName\" | \"responseFormat\"\n>;\n\nexport type CloneOptions = {\n voice?: string;\n lang?: string;\n speaker?: string;\n modelName?: string;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n responseFormat?: ResponseFormat;\n};\n\nexport type ListSpeakersOptions = {\n language?: string;\n gender?: string;\n domain?: string;\n};\n\n/** Audio data returned from a generation request. */\nexport class AudioResponse {\n readonly content: Uint8Array;\n readonly mediaType: string;\n\n constructor(content: Uint8Array, mediaType = \"audio/wav\") {\n this.content = content;\n this.mediaType = mediaType;\n }\n\n /** Write the audio to a file. */\n async save(path: string): Promise<string> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, this.content);\n return path;\n }\n\n get length(): number {\n return this.content.length;\n }\n\n toString(): string {\n return `AudioResponse(${this.content.length} bytes, ${this.mediaType})`;\n }\n}\n\n/** Audio and reusable voice metadata returned from a clone request. */\nexport class CloneResponse extends AudioResponse {\n readonly voiceId: string | null;\n readonly voiceCode: string | null;\n readonly voiceName: string | null;\n readonly cloneId: string | null;\n readonly jobId: string | null;\n\n constructor(\n content: Uint8Array,\n mediaType: string,\n metadata: {\n voiceId: string | null;\n voiceCode: string | null;\n voiceName: string | null;\n cloneId: string | null;\n jobId: string | null;\n },\n ) {\n super(content, mediaType);\n this.voiceId = metadata.voiceId;\n this.voiceCode = metadata.voiceCode;\n this.voiceName = metadata.voiceName;\n this.cloneId = metadata.cloneId;\n this.jobId = metadata.jobId;\n }\n}\n\n/** Streaming audio response — yields byte chunks as they arrive. */\nexport class AudioStream implements AsyncIterable<Uint8Array> {\n private readonly chunks: AsyncIterable<Uint8Array>;\n private collected: Uint8Array | null = null;\n\n constructor(chunks: AsyncIterable<Uint8Array>) {\n this.chunks = chunks;\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n for await (const chunk of this.chunks) {\n yield chunk;\n }\n }\n\n /** Consume the entire stream and return an AudioResponse. */\n async collect(): Promise<AudioResponse> {\n if (this.collected == null) {\n const parts: Uint8Array[] = [];\n let total = 0;\n for await (const chunk of this.chunks) {\n parts.push(chunk);\n total += chunk.length;\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n this.collected = out;\n }\n return new AudioResponse(this.collected, \"audio/wav\");\n }\n}\n\nexport class TTS {\n private readonly client: BaseClient;\n\n constructor(client: BaseClient) {\n this.client = client;\n }\n\n /** Generate speech from text. */\n async generate(\n text: string,\n options: GenerateOptions = {},\n ): Promise<AudioResponse> {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const responseFormat = options.responseFormat ?? \"wav\";\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n model: options.modelName ?? DEFAULT_MODEL_NAME,\n speaker_embedding: options.speakerEmbedding,\n response_format: responseFormat,\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n const content = await this.client.postBytes(\"/v1/audio/speech\", body);\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new AudioResponse(content, mediaType);\n }\n\n /** Stream speech generation — audio arrives as it's produced. */\n stream(text: string, options: StreamOptions = {}): AudioStream {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n speaker_embedding: options.speakerEmbedding,\n response_format: \"wav\",\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n return new AudioStream(this.client.postStream(\"/v1/audio/speech/stream\", body));\n }\n\n /**\n * Generate speech using a cloned voice from a reference audio file.\n * `audioFile` may be a filesystem path, Buffer, Uint8Array, Blob, or File.\n */\n async clone(\n text: string,\n audioFile: string | Buffer | Uint8Array | Blob,\n options: CloneOptions = {},\n ): Promise<CloneResponse> {\n const voice = options.voice ?? \"pcm\";\n const [resolvedLang, resolvedSpeaker] = resolveCloneLangAndSpeaker(\n voice,\n { lang: options.lang, speaker: options.speaker },\n );\n const responseFormat = options.responseFormat ?? \"wav\";\n\n const form = new FormData();\n form.append(\"text\", text);\n form.append(\"lang\", resolvedLang);\n form.append(\"model_name\", options.modelName ?? DEFAULT_MODEL_NAME);\n form.append(\"response_format\", responseFormat);\n if (resolvedSpeaker) {\n form.append(\"voice\", resolvedSpeaker);\n }\n if (options.temperature != null) {\n form.append(\"temperature\", String(options.temperature));\n }\n if (options.topP != null) {\n form.append(\"top_p\", String(options.topP));\n }\n if (options.repetitionPenalty != null) {\n form.append(\"repetition_penalty\", String(options.repetitionPenalty));\n }\n\n const { blob, filename } = await toAudioBlob(audioFile);\n form.append(\"audio\", blob, filename);\n\n const response = await this.client.postMultipart(\"/v1/audio/clone\", form);\n const content = new Uint8Array(await response.arrayBuffer());\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new CloneResponse(content, mediaType, {\n voiceId: response.headers.get(\"X-Voice-ID\"),\n voiceCode: response.headers.get(\"X-Voice-Code\"),\n voiceName: response.headers.get(\"X-Voice-Name\"),\n cloneId: response.headers.get(\"X-Clone-ID\"),\n jobId: response.headers.get(\"X-Job-ID\"),\n });\n }\n\n async listSpeakers(\n options: ListSpeakersOptions = {},\n ): Promise<SpeakerList> {\n const data = await this.client.getJson(\"/v1/speakers\", {\n language: options.language,\n gender: options.gender,\n domain: options.domain,\n });\n return speakerListFromDict(data);\n }\n\n async getSpeaker(speakerId: string): Promise<Speaker> {\n const data = await this.client.getJson(`/v1/speakers/${speakerId}`);\n return speakerFromDict(data);\n }\n\n async listLanguages(): Promise<LanguageList> {\n const data = await this.client.getJson(\"/v1/languages\");\n return languageListFromDict(data);\n }\n\n async health(): Promise<HealthStatus> {\n const data = await this.client.getJson(\"/v1/health\");\n return healthStatusFromDict(data);\n }\n}\n\nconst MEDIA_TYPES: Record<string, string> = {\n wav: \"audio/wav\",\n pcm: \"application/octet-stream\",\n flac: \"audio/flac\",\n aac: \"audio/aac\",\n ogg: \"audio/ogg\",\n mp3: \"audio/mpeg\",\n alac: \"audio/alac\",\n};\n\nfunction buildBody(\n kwargs: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(kwargs)) {\n if (v !== undefined && v !== null) out[k] = v;\n }\n return out;\n}\n\nasync function toAudioBlob(\n audioFile: string | Buffer | Uint8Array | Blob,\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof audioFile === \"string\") {\n const bytes = readFileSync(audioFile);\n const filename = basename(audioFile);\n const mime = guessMime(filename);\n return {\n blob: new Blob([bytes], { type: mime }),\n filename,\n };\n }\n if (audioFile instanceof Blob) {\n return {\n blob: audioFile,\n filename: \"audio.wav\",\n };\n }\n // Buffer | Uint8Array\n const bytes =\n audioFile instanceof Uint8Array\n ? audioFile\n : new Uint8Array(audioFile);\n return {\n blob: new Blob([bytes], { type: \"application/octet-stream\" }),\n filename: \"audio.wav\",\n };\n}\n\nfunction guessMime(filename: string): string {\n const lower = filename.toLowerCase();\n if (lower.endsWith(\".wav\")) return \"audio/wav\";\n if (lower.endsWith(\".mp3\")) return \"audio/mpeg\";\n if (lower.endsWith(\".flac\")) return \"audio/flac\";\n if (lower.endsWith(\".ogg\")) return \"audio/ogg\";\n if (lower.endsWith(\".aac\") || lower.endsWith(\".m4a\")) return \"audio/aac\";\n return \"application/octet-stream\";\n}\n","/** A speaker voice identity. */\nexport interface Speaker {\n id: string;\n name: string;\n language: string;\n gender?: string | null;\n domain?: string | null;\n}\n\nexport function speakerFromDict(data: Record<string, unknown>): Speaker {\n return {\n id: String(data.id ?? data.speaker_id ?? \"\"),\n name: String(data.name ?? data.id ?? \"\"),\n language: String(data.language ?? \"\"),\n gender: (data.gender as string | null | undefined) ?? null,\n domain: (data.domain as string | null | undefined) ?? null,\n };\n}\n\n/** List of speakers with metadata. */\nexport interface SpeakerList {\n speakers: Speaker[];\n total: number;\n byLanguage: Record<string, number>;\n [Symbol.iterator](): Iterator<Speaker>;\n length: number;\n}\n\nexport function speakerListFromDict(data: Record<string, unknown>): SpeakerList {\n const raw = (data.speakers as Record<string, unknown>[] | undefined) ?? [];\n const speakers = raw.map(speakerFromDict);\n const total = typeof data.total === \"number\" ? data.total : speakers.length;\n const byLanguage =\n (data.by_language as Record<string, number> | undefined) ?? {};\n return {\n speakers,\n total,\n byLanguage,\n get length() {\n return total;\n },\n [Symbol.iterator]() {\n return speakers[Symbol.iterator]();\n },\n };\n}\n\n/** A supported language. */\nexport interface Language {\n code: string;\n name: string;\n speakerCount: number;\n}\n\nexport function languageFromDict(data: Record<string, unknown>): Language {\n return {\n code: String(data.code ?? \"\"),\n name: String(data.name ?? \"\"),\n speakerCount: Number(data.speaker_count ?? 0),\n };\n}\n\n/** List of supported languages and domains. */\nexport interface LanguageList {\n languages: Language[];\n domains: string[];\n}\n\nexport function languageListFromDict(data: Record<string, unknown>): LanguageList {\n const rawLangs = data.languages;\n let languages: Language[];\n\n if (rawLangs && typeof rawLangs === \"object\" && !Array.isArray(rawLangs)) {\n languages = Object.entries(rawLangs as Record<string, unknown>).map(\n ([code, value]) => {\n if (value && typeof value === \"object\") {\n const v = value as Record<string, unknown>;\n return {\n code,\n name: String(v.name ?? code),\n speakerCount: Number(v.speaker_count ?? 0),\n };\n }\n return { code, name: String(value), speakerCount: 0 };\n },\n );\n } else {\n const arr = (rawLangs as Record<string, unknown>[] | undefined) ?? [];\n languages = arr.map(languageFromDict);\n }\n\n return {\n languages,\n domains: (data.domains as string[] | undefined) ?? [],\n };\n}\n\n/** API health check result. */\nexport interface HealthStatus {\n status: string;\n engineReady: boolean;\n codecReady: boolean;\n totalSpeakers: number;\n languages: string[];\n voiceCloningAvailable: boolean;\n speakerProjectionLoaded: boolean;\n}\n\nexport function healthStatusFromDict(data: Record<string, unknown>): HealthStatus {\n const supported = data.supported_languages;\n let languages: string[] = [];\n if (Array.isArray(data.languages)) {\n languages = data.languages as string[];\n } else if (supported && typeof supported === \"object\") {\n languages = Object.keys(supported as Record<string, unknown>);\n }\n\n return {\n status: String(data.status ?? \"unknown\"),\n engineReady: Boolean(data.engine_ready ?? data.tts_initialized ?? false),\n codecReady: Boolean(data.codec_ready ?? false),\n totalSpeakers: Number(data.total_speakers ?? data.speakers_loaded ?? 0),\n languages,\n voiceCloningAvailable: Boolean(data.voice_cloning_available ?? false),\n speakerProjectionLoaded: Boolean(data.speaker_projection_loaded ?? false),\n };\n}\n\n/** An available TTS model. */\nexport interface Model {\n id: string;\n object: string;\n ownedBy: string;\n}\n\nexport function modelFromDict(data: Record<string, unknown>): Model {\n return {\n id: String(data.id ?? \"\"),\n object: String(data.object ?? \"model\"),\n ownedBy: String(data.owned_by ?? \"9jalingo\"),\n };\n}\n\n/** List of available models. */\nexport interface ModelList {\n models: Model[];\n object: string;\n [Symbol.iterator](): Iterator<Model>;\n length: number;\n}\n\nexport function modelListFromDict(data: Record<string, unknown>): ModelList {\n const raw = (data.data as Record<string, unknown>[] | undefined) ?? [];\n const models = raw.map(modelFromDict);\n return {\n models,\n object: String(data.object ?? \"list\"),\n get length() {\n return models.length;\n },\n [Symbol.iterator]() {\n return models[Symbol.iterator]();\n },\n };\n}\n\n/** Root API information returned by GET /. */\nexport interface APIInfo {\n name: string;\n version: string;\n description: string;\n endpoints: Record<string, string>;\n}\n\nexport function apiInfoFromDict(data: Record<string, unknown>): APIInfo {\n return {\n name: String(data.name ?? \"\"),\n version: String(data.version ?? \"\"),\n description: String(data.description ?? \"\"),\n endpoints: (data.endpoints as Record<string, string> | undefined) ?? {},\n };\n}\n\n/** Service metadata returned by GET /v1. */\nexport interface ServiceInfo {\n object: string;\n name: string;\n modelsUrl: string;\n speechUrl: string;\n speechStreamUrl: string;\n}\n\nexport function serviceInfoFromDict(data: Record<string, unknown>): ServiceInfo {\n return {\n object: String(data.object ?? \"service\"),\n name: String(data.name ?? \"\"),\n modelsUrl: String(data.models_url ?? \"\"),\n speechUrl: String(data.speech_url ?? \"\"),\n speechStreamUrl: String(data.speech_stream_url ?? \"\"),\n };\n}\n\nexport type ResponseFormat =\n | \"wav\"\n | \"pcm\"\n | \"flac\"\n | \"aac\"\n | \"ogg\"\n | \"mp3\"\n | \"alac\";\n","const LANG_ALIASES: Record<string, string> = {\n yo: \"yo\",\n yor: \"yo\",\n yoruba: \"yo\",\n ig: \"ig\",\n ibo: \"ig\",\n igbo: \"ig\",\n ha: \"ha\",\n hau: \"ha\",\n hausa: \"ha\",\n pcm: \"pcm\",\n pidgin: \"pcm\",\n};\n\nexport function normalizeLang(code: string): string | null {\n return LANG_ALIASES[code.trim().toLowerCase()] ?? null;\n}\n\n/**\n * Resolve speech API fields: voice/speaker + lang/language.\n * Returns [speakerId, langCode]. Bare language codes must be passed via lang/language.\n */\nexport function resolveTtsVoiceAndLang(options: {\n voice?: string | null;\n speaker?: string | null;\n lang?: string | null;\n language?: string | null;\n}): [string | null, string | null] {\n let resolvedLang: string | null = null;\n\n if (options.lang != null) {\n resolvedLang = normalizeLang(options.lang);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.lang}'. Use ha, ig, yo, or pcm.`,\n );\n }\n } else if (options.language != null) {\n resolvedLang = normalizeLang(options.language);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.language}'. Use ha, ig, yo, or pcm.`,\n );\n }\n }\n\n // speaker takes precedence over voice\n const candidate =\n options.speaker != null ? options.speaker : options.voice;\n if (candidate == null || !String(candidate).trim()) {\n return [null, resolvedLang];\n }\n\n const resolved = String(candidate).trim();\n const normalized = resolved.toLowerCase();\n if (normalized in LANG_ALIASES) {\n const langCode = LANG_ALIASES[normalized];\n throw new Error(\n `'${resolved}' is a language code, not a speaker ID. ` +\n `Pass speaker/voice='ada_pcm' (or another speaker ID) and ` +\n `lang='${langCode}' for the language. ` +\n `Browse speakers with: client.tts.listSpeakers({ language: '${langCode}' })`,\n );\n }\n\n if (resolvedLang == null && resolved.includes(\"_\")) {\n const suffix = resolved.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n\n return [resolved, resolvedLang];\n}\n\n/**\n * Resolve clone form fields.\n * voice may be a language code (\"pcm\") or a speaker ID with a language suffix.\n */\nexport function resolveCloneLangAndSpeaker(\n voice: string,\n options: { lang?: string | null; speaker?: string | null } = {},\n): [string, string | null] {\n let resolvedLang =\n options.lang != null ? normalizeLang(options.lang) : null;\n let resolvedSpeaker = options.speaker?.trim() || null;\n\n const voiceStr = (voice || \"\").trim();\n const voiceNorm = voiceStr.toLowerCase();\n\n if (voiceNorm in LANG_ALIASES) {\n if (resolvedLang == null) {\n resolvedLang = LANG_ALIASES[voiceNorm];\n }\n } else if (voiceStr.includes(\"_\")) {\n if (resolvedSpeaker == null) {\n resolvedSpeaker = voiceStr;\n }\n if (resolvedLang == null) {\n const suffix = voiceStr.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n } else if (voiceStr && resolvedLang == null) {\n throw new Error(\n `'${voice}' is not a language code (ha/ig/yo/pcm) or a speaker ID ` +\n `with a language suffix (e.g. 'daniel_pcm'). ` +\n `Pass voice='pcm' or lang='pcm'.`,\n );\n }\n\n if (!resolvedLang) {\n throw new Error(\n \"Clone requires a language. Pass voice='pcm' (or 'ha'/'ig'/'yo'), \" +\n \"lang='pcm', or a speaker ID ending in _pcm/_ha/_ig/_yo.\",\n );\n }\n\n return [resolvedLang, resolvedSpeaker];\n}\n"],"mappings":"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","names":["bytes"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/tts.ts","../src/types.ts","../src/voice.ts","../src/stt.ts"],"sourcesContent":["/**\n * 9jaLingo Node.js SDK\n *\n * Official SDK for the 9jaLingo API — Nigerian language Text-to-Speech.\n *\n * @example\n * ```ts\n * import { NaijaLingo } from \"naijalingo\";\n *\n * const client = new NaijaLingo(); // uses NAIJALINGO_API_KEY\n * const audio = await client.tts.generate(\"Bawo ni!\", {\n * voice: \"adeola_yo\",\n * lang: \"yo\",\n * });\n * await audio.save(\"greeting.wav\");\n * ```\n */\n\nimport { BaseClient, type ClientOptions } from \"./client.js\";\nimport { TTS } from \"./tts.js\";\nimport { STT } from \"./stt.js\";\nimport {\n apiInfoFromDict,\n modelListFromDict,\n serviceInfoFromDict,\n type APIInfo,\n type ModelList,\n type ServiceInfo,\n} from \"./types.js\";\n\nexport const VERSION = \"0.1.2\";\n\nexport {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nexport {\n AudioResponse,\n AudioStream,\n CloneResponse,\n TTS,\n type CloneOptions,\n type GenerateOptions,\n type ListSpeakersOptions,\n type StreamOptions,\n} from \"./tts.js\";\nexport { STT, type TranscribeOptions, type Transcription } from \"./stt.js\";\n\nexport type {\n APIInfo,\n HealthStatus,\n Language,\n LanguageList,\n Model,\n ModelList,\n ResponseFormat,\n ServiceInfo,\n Speaker,\n SpeakerList,\n} from \"./types.js\";\n\nexport type { ClientOptions };\n\nexport class NaijaLingo {\n private readonly _client: BaseClient;\n readonly tts: TTS;\n readonly stt: STT;\n\n constructor(options: ClientOptions = {}) {\n this._client = new BaseClient(options);\n this.tts = new TTS(this._client);\n this.stt = new STT(this._client);\n }\n\n /** List available TTS models. GET /v1/models */\n async listModels(): Promise<ModelList> {\n const data = await this._client.getJson(\"/v1/models\");\n return modelListFromDict(data);\n }\n\n /** Root API information. GET / */\n async apiInfo(): Promise<APIInfo> {\n const data = await this._client.getJson(\"/\");\n return apiInfoFromDict(data);\n }\n\n /** v1 service metadata. GET /v1 */\n async serviceInfo(): Promise<ServiceInfo> {\n const data = await this._client.getJson(\"/v1\");\n return serviceInfoFromDict(data);\n }\n}\n","/** Base exception for all 9jaLingo SDK errors. */\nexport class NaijaLingoError extends Error {\n readonly statusCode?: number;\n readonly response?: unknown;\n\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message);\n this.name = \"NaijaLingoError\";\n this.statusCode = statusCode;\n this.response = response;\n }\n}\n\n/** Raised when the API key is missing or invalid. */\nexport class AuthenticationError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** Raised when rate limit is exceeded. */\nexport class RateLimitError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"RateLimitError\";\n }\n}\n\n/** Raised when a requested resource is not found. */\nexport class NotFoundError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"NotFoundError\";\n }\n}\n\n/** Raised when the API returns a 5xx error. */\nexport class ServerError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ServerError\";\n }\n}\n\n/** Raised when the inference GPU is waking from scale-to-zero. */\nexport class InferenceCapacityError extends ServerError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"InferenceCapacityError\";\n }\n}\n\n/** Raised when unable to connect to the API. */\nexport class ConnectionError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ConnectionError\";\n }\n}\n","import {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.9jalingo.org\";\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ClientOptions {\n apiKey?: string;\n baseUrl?: string;\n /** Request timeout in milliseconds (default 300000). */\n timeout?: number;\n}\n\nexport class BaseClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeout: number;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: ClientOptions = {}) {\n this.apiKey =\n options.apiKey ?? process.env.NAIJALINGO_API_KEY ?? \"\";\n const base =\n options.baseUrl ??\n process.env.NAIJALINGO_BASE_URL ??\n DEFAULT_BASE_URL;\n this.baseUrl = base.replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n this.defaultHeaders = {\n \"User-Agent\": \"naijalingo-js/0.1.2\",\n };\n if (this.apiKey) {\n this.defaultHeaders[\"X-API-Key\"] = this.apiKey;\n }\n }\n\n private async request(\n method: string,\n path: string,\n init: RequestInit & { timeoutMs?: number } = {},\n ): Promise<Response> {\n const url = `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`;\n const timeoutMs = init.timeoutMs ?? this.timeout;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(url, {\n ...init,\n method,\n headers: {\n ...this.defaultHeaders,\n ...(init.headers as Record<string, string> | undefined),\n },\n signal: controller.signal,\n });\n clearTimeout(timer);\n\n if (response.ok) {\n return response;\n }\n await this.raiseForStatus(response);\n // unreachable\n return response;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) {\n throw err;\n }\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n // Only retry connect-style failures, not aborts from long reads\n if (isAbort) {\n throw new ConnectionError(\n `Request timed out after ${timeoutMs}ms: ${String(err)}`,\n );\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n private async raiseForStatus(response: Response): Promise<never> {\n const status = response.status;\n let detail: string;\n let body: unknown;\n try {\n body = await response.json();\n detail =\n typeof body === \"object\" &&\n body !== null &&\n \"detail\" in body\n ? String((body as { detail: unknown }).detail)\n : JSON.stringify(body);\n } catch {\n detail = await response.text();\n }\n\n const normalizedDetail = detail.toLowerCase();\n if (status === 503 && (\n normalizedDetail.includes(\"inference capacity\")\n || normalizedDetail.includes(\"inference component has no capacity\")\n || normalizedDetail.includes(\"capacity is starting\")\n )) {\n throw new InferenceCapacityError(detail, status, body);\n }\n if (status === 401 || status === 403) {\n throw new AuthenticationError(detail, status, body);\n }\n if (status === 404) {\n throw new NotFoundError(detail, status, body);\n }\n if (status === 429) {\n throw new RateLimitError(detail, status, body);\n }\n if (status >= 500) {\n throw new ServerError(detail, status, body);\n }\n throw new NaijaLingoError(detail, status, body);\n }\n\n async getJson(\n path: string,\n params?: Record<string, string | undefined | null>,\n ): Promise<Record<string, unknown>> {\n let url = path;\n if (params) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v != null) qs.set(k, v);\n }\n const s = qs.toString();\n if (s) url = `${path}?${s}`;\n }\n const resp = await this.request(\"GET\", url);\n return (await resp.json()) as Record<string, unknown>;\n }\n\n async postBytes(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Uint8Array> {\n const resp = await this.request(\"POST\", path, {\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return new Uint8Array(await resp.arrayBuffer());\n }\n\n async postJson(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>> {\n const resp = await this.request(\"POST\", path, {\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return (await resp.json()) as Record<string, unknown>;\n }\n\n async *postStream(\n path: string,\n body: Record<string, unknown>,\n ): AsyncGenerator<Uint8Array, void, unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const response = await fetch(\n `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`,\n {\n method: \"POST\",\n headers: {\n ...this.defaultHeaders,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n },\n );\n clearTimeout(timer);\n\n if (!response.ok) {\n await this.raiseForStatus(response);\n }\n if (!response.body) {\n throw new ConnectionError(\"No response body for stream\");\n }\n\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value && value.length > 0) {\n yield value;\n }\n }\n } finally {\n reader.releaseLock();\n }\n return;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) throw err;\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n if (isAbort) {\n throw new ConnectionError(`Request timed out: ${String(err)}`);\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n async postMultipart(\n path: string,\n form: FormData,\n timeoutMs?: number,\n ): Promise<Response> {\n return this.request(\"POST\", path, {\n body: form,\n timeoutMs: timeoutMs ?? Math.max(this.timeout, 600_000),\n // Let fetch set multipart boundary — do not set Content-Type\n });\n }\n\n async delete(path: string): Promise<void> {\n await this.request(\"DELETE\", path);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { basename, dirname } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport { BaseClient } from \"./client.js\";\nimport {\n type HealthStatus,\n type LanguageList,\n type ResponseFormat,\n type Speaker,\n type SpeakerList,\n healthStatusFromDict,\n languageListFromDict,\n speakerFromDict,\n speakerListFromDict,\n} from \"./types.js\";\nimport {\n resolveCloneLangAndSpeaker,\n resolveTtsVoiceAndLang,\n} from \"./voice.js\";\n\nconst DEFAULT_MODEL_NAME = \"9jalingo-tts-1\";\n\nexport type GenerateOptions = {\n voice?: string;\n speaker?: string;\n lang?: string;\n language?: string;\n modelName?: string;\n speakerEmbedding?: number[];\n responseFormat?: ResponseFormat;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n enableLongForm?: boolean;\n maxChunkDuration?: number;\n silenceDuration?: number;\n};\n\nexport type StreamOptions = Omit<\n GenerateOptions,\n \"modelName\" | \"responseFormat\"\n>;\n\nexport type CloneOptions = {\n voice?: string;\n lang?: string;\n speaker?: string;\n modelName?: string;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n responseFormat?: ResponseFormat;\n};\n\nexport type ListSpeakersOptions = {\n language?: string;\n gender?: string;\n domain?: string;\n};\n\n/** Audio data returned from a generation request. */\nexport class AudioResponse {\n readonly content: Uint8Array;\n readonly mediaType: string;\n\n constructor(content: Uint8Array, mediaType = \"audio/wav\") {\n this.content = content;\n this.mediaType = mediaType;\n }\n\n /** Write the audio to a file. */\n async save(path: string): Promise<string> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, this.content);\n return path;\n }\n\n get length(): number {\n return this.content.length;\n }\n\n toString(): string {\n return `AudioResponse(${this.content.length} bytes, ${this.mediaType})`;\n }\n}\n\n/** Audio and reusable voice metadata returned from a clone request. */\nexport class CloneResponse extends AudioResponse {\n readonly voiceId: string | null;\n readonly voiceCode: string | null;\n readonly voiceName: string | null;\n readonly cloneId: string | null;\n readonly jobId: string | null;\n\n constructor(\n content: Uint8Array,\n mediaType: string,\n metadata: {\n voiceId: string | null;\n voiceCode: string | null;\n voiceName: string | null;\n cloneId: string | null;\n jobId: string | null;\n },\n ) {\n super(content, mediaType);\n this.voiceId = metadata.voiceId;\n this.voiceCode = metadata.voiceCode;\n this.voiceName = metadata.voiceName;\n this.cloneId = metadata.cloneId;\n this.jobId = metadata.jobId;\n }\n}\n\n/** Streaming audio response — yields byte chunks as they arrive. */\nexport class AudioStream implements AsyncIterable<Uint8Array> {\n private readonly chunks: AsyncIterable<Uint8Array>;\n private collected: Uint8Array | null = null;\n\n constructor(chunks: AsyncIterable<Uint8Array>) {\n this.chunks = chunks;\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n for await (const chunk of this.chunks) {\n yield chunk;\n }\n }\n\n /** Consume the entire stream and return an AudioResponse. */\n async collect(): Promise<AudioResponse> {\n if (this.collected == null) {\n const parts: Uint8Array[] = [];\n let total = 0;\n for await (const chunk of this.chunks) {\n parts.push(chunk);\n total += chunk.length;\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n this.collected = out;\n }\n return new AudioResponse(this.collected, \"audio/wav\");\n }\n}\n\nexport class TTS {\n private readonly client: BaseClient;\n\n constructor(client: BaseClient) {\n this.client = client;\n }\n\n /** Generate speech from text. */\n async generate(\n text: string,\n options: GenerateOptions = {},\n ): Promise<AudioResponse> {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const responseFormat = options.responseFormat ?? \"wav\";\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n model: options.modelName ?? DEFAULT_MODEL_NAME,\n speaker_embedding: options.speakerEmbedding,\n response_format: responseFormat,\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n const content = await this.client.postBytes(\"/v1/audio/speech\", body);\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new AudioResponse(content, mediaType);\n }\n\n /** Stream speech generation — audio arrives as it's produced. */\n stream(text: string, options: StreamOptions = {}): AudioStream {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n speaker_embedding: options.speakerEmbedding,\n response_format: \"wav\",\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n return new AudioStream(this.client.postStream(\"/v1/audio/speech/stream\", body));\n }\n\n /**\n * Generate speech using a cloned voice from a reference audio file.\n * `audioFile` may be a filesystem path, Buffer, Uint8Array, Blob, or File.\n */\n async clone(\n text: string,\n audioFile: string | Buffer | Uint8Array | Blob,\n options: CloneOptions = {},\n ): Promise<CloneResponse> {\n const voice = options.voice ?? \"pcm\";\n const [resolvedLang, resolvedSpeaker] = resolveCloneLangAndSpeaker(\n voice,\n { lang: options.lang, speaker: options.speaker },\n );\n const responseFormat = options.responseFormat ?? \"wav\";\n\n const form = new FormData();\n form.append(\"text\", text);\n form.append(\"lang\", resolvedLang);\n form.append(\"model_name\", options.modelName ?? DEFAULT_MODEL_NAME);\n form.append(\"response_format\", responseFormat);\n if (resolvedSpeaker) {\n form.append(\"voice\", resolvedSpeaker);\n }\n if (options.temperature != null) {\n form.append(\"temperature\", String(options.temperature));\n }\n if (options.topP != null) {\n form.append(\"top_p\", String(options.topP));\n }\n if (options.repetitionPenalty != null) {\n form.append(\"repetition_penalty\", String(options.repetitionPenalty));\n }\n\n const { blob, filename } = await toAudioBlob(audioFile);\n form.append(\"audio\", blob, filename);\n\n const response = await this.client.postMultipart(\"/v1/audio/clone\", form);\n const content = new Uint8Array(await response.arrayBuffer());\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new CloneResponse(content, mediaType, {\n voiceId: response.headers.get(\"X-Voice-ID\"),\n voiceCode: response.headers.get(\"X-Voice-Code\"),\n voiceName: response.headers.get(\"X-Voice-Name\"),\n cloneId: response.headers.get(\"X-Clone-ID\"),\n jobId: response.headers.get(\"X-Job-ID\"),\n });\n }\n\n async listSpeakers(\n options: ListSpeakersOptions = {},\n ): Promise<SpeakerList> {\n const data = await this.client.getJson(\"/v1/speakers\", {\n language: options.language,\n gender: options.gender,\n domain: options.domain,\n });\n return speakerListFromDict(data);\n }\n\n async getSpeaker(speakerId: string): Promise<Speaker> {\n const data = await this.client.getJson(`/v1/speakers/${speakerId}`);\n return speakerFromDict(data);\n }\n\n /** Permanently delete a cloned voice owned by this API key. */\n async deleteVoice(voiceId: string): Promise<void> {\n const normalizedVoiceId = voiceId.trim();\n if (!normalizedVoiceId) {\n throw new Error(\"voiceId is required\");\n }\n await this.client.delete(`/v1/voices/${encodeURIComponent(normalizedVoiceId)}`);\n }\n\n async listLanguages(): Promise<LanguageList> {\n const data = await this.client.getJson(\"/v1/languages\");\n return languageListFromDict(data);\n }\n\n async health(): Promise<HealthStatus> {\n const data = await this.client.getJson(\"/v1/health\");\n return healthStatusFromDict(data);\n }\n}\n\nconst MEDIA_TYPES: Record<string, string> = {\n wav: \"audio/wav\",\n pcm: \"application/octet-stream\",\n flac: \"audio/flac\",\n aac: \"audio/aac\",\n ogg: \"audio/ogg\",\n mp3: \"audio/mpeg\",\n alac: \"audio/alac\",\n};\n\nfunction buildBody(\n kwargs: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(kwargs)) {\n if (v !== undefined && v !== null) out[k] = v;\n }\n return out;\n}\n\nasync function toAudioBlob(\n audioFile: string | Buffer | Uint8Array | Blob,\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof audioFile === \"string\") {\n const bytes = readFileSync(audioFile);\n const filename = basename(audioFile);\n const mime = guessMime(filename);\n return {\n blob: new Blob([bytes], { type: mime }),\n filename,\n };\n }\n if (audioFile instanceof Blob) {\n return {\n blob: audioFile,\n filename: \"audio.wav\",\n };\n }\n // Buffer | Uint8Array\n const bytes =\n audioFile instanceof Uint8Array\n ? audioFile\n : new Uint8Array(audioFile);\n return {\n blob: new Blob([bytes], { type: \"application/octet-stream\" }),\n filename: \"audio.wav\",\n };\n}\n\nfunction guessMime(filename: string): string {\n const lower = filename.toLowerCase();\n if (lower.endsWith(\".wav\")) return \"audio/wav\";\n if (lower.endsWith(\".mp3\")) return \"audio/mpeg\";\n if (lower.endsWith(\".flac\")) return \"audio/flac\";\n if (lower.endsWith(\".ogg\")) return \"audio/ogg\";\n if (lower.endsWith(\".aac\") || lower.endsWith(\".m4a\")) return \"audio/aac\";\n return \"application/octet-stream\";\n}\n","/** A speaker voice identity. */\nexport interface Speaker {\n id: string;\n name: string;\n language: string;\n gender?: string | null;\n domain?: string | null;\n}\n\nexport function speakerFromDict(data: Record<string, unknown>): Speaker {\n return {\n id: String(data.id ?? data.speaker_id ?? \"\"),\n name: String(data.name ?? data.id ?? \"\"),\n language: String(data.language ?? \"\"),\n gender: (data.gender as string | null | undefined) ?? null,\n domain: (data.domain as string | null | undefined) ?? null,\n };\n}\n\n/** List of speakers with metadata. */\nexport interface SpeakerList {\n speakers: Speaker[];\n total: number;\n byLanguage: Record<string, number>;\n [Symbol.iterator](): Iterator<Speaker>;\n length: number;\n}\n\nexport function speakerListFromDict(data: Record<string, unknown>): SpeakerList {\n const raw = (data.speakers as Record<string, unknown>[] | undefined) ?? [];\n const speakers = raw.map(speakerFromDict);\n const total = typeof data.total === \"number\" ? data.total : speakers.length;\n const byLanguage =\n (data.by_language as Record<string, number> | undefined) ?? {};\n return {\n speakers,\n total,\n byLanguage,\n get length() {\n return total;\n },\n [Symbol.iterator]() {\n return speakers[Symbol.iterator]();\n },\n };\n}\n\n/** A supported language. */\nexport interface Language {\n code: string;\n name: string;\n speakerCount: number;\n}\n\nexport function languageFromDict(data: Record<string, unknown>): Language {\n return {\n code: String(data.code ?? \"\"),\n name: String(data.name ?? \"\"),\n speakerCount: Number(data.speaker_count ?? 0),\n };\n}\n\n/** List of supported languages and domains. */\nexport interface LanguageList {\n languages: Language[];\n domains: string[];\n}\n\nexport function languageListFromDict(data: Record<string, unknown>): LanguageList {\n const rawLangs = data.languages;\n let languages: Language[];\n\n if (rawLangs && typeof rawLangs === \"object\" && !Array.isArray(rawLangs)) {\n languages = Object.entries(rawLangs as Record<string, unknown>).map(\n ([code, value]) => {\n if (value && typeof value === \"object\") {\n const v = value as Record<string, unknown>;\n return {\n code,\n name: String(v.name ?? code),\n speakerCount: Number(v.speaker_count ?? 0),\n };\n }\n return { code, name: String(value), speakerCount: 0 };\n },\n );\n } else {\n const arr = (rawLangs as Record<string, unknown>[] | undefined) ?? [];\n languages = arr.map(languageFromDict);\n }\n\n return {\n languages,\n domains: (data.domains as string[] | undefined) ?? [],\n };\n}\n\n/** API health check result. */\nexport interface HealthStatus {\n status: string;\n engineReady: boolean;\n codecReady: boolean;\n totalSpeakers: number;\n languages: string[];\n voiceCloningAvailable: boolean;\n speakerProjectionLoaded: boolean;\n}\n\nexport function healthStatusFromDict(data: Record<string, unknown>): HealthStatus {\n const supported = data.supported_languages;\n let languages: string[] = [];\n if (Array.isArray(data.languages)) {\n languages = data.languages as string[];\n } else if (supported && typeof supported === \"object\") {\n languages = Object.keys(supported as Record<string, unknown>);\n }\n\n return {\n status: String(data.status ?? \"unknown\"),\n engineReady: Boolean(data.engine_ready ?? data.tts_initialized ?? false),\n codecReady: Boolean(data.codec_ready ?? false),\n totalSpeakers: Number(data.total_speakers ?? data.speakers_loaded ?? 0),\n languages,\n voiceCloningAvailable: Boolean(data.voice_cloning_available ?? false),\n speakerProjectionLoaded: Boolean(data.speaker_projection_loaded ?? false),\n };\n}\n\n/** An available TTS model. */\nexport interface Model {\n id: string;\n object: string;\n ownedBy: string;\n}\n\nexport function modelFromDict(data: Record<string, unknown>): Model {\n return {\n id: String(data.id ?? \"\"),\n object: String(data.object ?? \"model\"),\n ownedBy: String(data.owned_by ?? \"9jalingo\"),\n };\n}\n\n/** List of available models. */\nexport interface ModelList {\n models: Model[];\n object: string;\n [Symbol.iterator](): Iterator<Model>;\n length: number;\n}\n\nexport function modelListFromDict(data: Record<string, unknown>): ModelList {\n const raw = (data.data as Record<string, unknown>[] | undefined) ?? [];\n const models = raw.map(modelFromDict);\n return {\n models,\n object: String(data.object ?? \"list\"),\n get length() {\n return models.length;\n },\n [Symbol.iterator]() {\n return models[Symbol.iterator]();\n },\n };\n}\n\n/** Root API information returned by GET /. */\nexport interface APIInfo {\n name: string;\n version: string;\n description: string;\n endpoints: Record<string, string>;\n}\n\nexport function apiInfoFromDict(data: Record<string, unknown>): APIInfo {\n return {\n name: String(data.name ?? \"\"),\n version: String(data.version ?? \"\"),\n description: String(data.description ?? \"\"),\n endpoints: (data.endpoints as Record<string, string> | undefined) ?? {},\n };\n}\n\n/** Service metadata returned by GET /v1. */\nexport interface ServiceInfo {\n object: string;\n name: string;\n modelsUrl: string;\n speechUrl: string;\n speechStreamUrl: string;\n}\n\nexport function serviceInfoFromDict(data: Record<string, unknown>): ServiceInfo {\n return {\n object: String(data.object ?? \"service\"),\n name: String(data.name ?? \"\"),\n modelsUrl: String(data.models_url ?? \"\"),\n speechUrl: String(data.speech_url ?? \"\"),\n speechStreamUrl: String(data.speech_stream_url ?? \"\"),\n };\n}\n\nexport type ResponseFormat =\n | \"wav\"\n | \"pcm\"\n | \"flac\"\n | \"aac\"\n | \"ogg\"\n | \"mp3\"\n | \"alac\";\n","const LANG_ALIASES: Record<string, string> = {\n yo: \"yo\",\n yor: \"yo\",\n yoruba: \"yo\",\n ig: \"ig\",\n ibo: \"ig\",\n igbo: \"ig\",\n ha: \"ha\",\n hau: \"ha\",\n hausa: \"ha\",\n pcm: \"pcm\",\n pidgin: \"pcm\",\n};\n\nexport function normalizeLang(code: string): string | null {\n return LANG_ALIASES[code.trim().toLowerCase()] ?? null;\n}\n\n/**\n * Resolve speech API fields: voice/speaker + lang/language.\n * Returns [speakerId, langCode]. Bare language codes must be passed via lang/language.\n */\nexport function resolveTtsVoiceAndLang(options: {\n voice?: string | null;\n speaker?: string | null;\n lang?: string | null;\n language?: string | null;\n}): [string | null, string | null] {\n let resolvedLang: string | null = null;\n\n if (options.lang != null) {\n resolvedLang = normalizeLang(options.lang);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.lang}'. Use ha, ig, yo, or pcm.`,\n );\n }\n } else if (options.language != null) {\n resolvedLang = normalizeLang(options.language);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.language}'. Use ha, ig, yo, or pcm.`,\n );\n }\n }\n\n // speaker takes precedence over voice\n const candidate =\n options.speaker != null ? options.speaker : options.voice;\n if (candidate == null || !String(candidate).trim()) {\n return [null, resolvedLang];\n }\n\n const resolved = String(candidate).trim();\n const normalized = resolved.toLowerCase();\n if (normalized in LANG_ALIASES) {\n const langCode = LANG_ALIASES[normalized];\n throw new Error(\n `'${resolved}' is a language code, not a speaker ID. ` +\n `Pass speaker/voice='ada_pcm' (or another speaker ID) and ` +\n `lang='${langCode}' for the language. ` +\n `Browse speakers with: client.tts.listSpeakers({ language: '${langCode}' })`,\n );\n }\n\n if (resolvedLang == null && resolved.includes(\"_\")) {\n const suffix = resolved.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n\n return [resolved, resolvedLang];\n}\n\n/**\n * Resolve clone form fields.\n * voice may be a language code (\"pcm\") or a speaker ID with a language suffix.\n */\nexport function resolveCloneLangAndSpeaker(\n voice: string,\n options: { lang?: string | null; speaker?: string | null } = {},\n): [string, string | null] {\n let resolvedLang =\n options.lang != null ? normalizeLang(options.lang) : null;\n let resolvedSpeaker = options.speaker?.trim() || null;\n\n const voiceStr = (voice || \"\").trim();\n const voiceNorm = voiceStr.toLowerCase();\n\n if (voiceNorm in LANG_ALIASES) {\n if (resolvedLang == null) {\n resolvedLang = LANG_ALIASES[voiceNorm];\n }\n } else if (voiceStr.includes(\"_\")) {\n if (resolvedSpeaker == null) {\n resolvedSpeaker = voiceStr;\n }\n if (resolvedLang == null) {\n const suffix = voiceStr.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n } else if (voiceStr && resolvedLang == null) {\n throw new Error(\n `'${voice}' is not a language code (ha/ig/yo/pcm) or a speaker ID ` +\n `with a language suffix (e.g. 'daniel_pcm'). ` +\n `Pass voice='pcm' or lang='pcm'.`,\n );\n }\n\n if (!resolvedLang) {\n throw new Error(\n \"Clone requires a language. Pass voice='pcm' (or 'ha'/'ig'/'yo'), \" +\n \"lang='pcm', or a speaker ID ending in _pcm/_ha/_ig/_yo.\",\n );\n }\n\n return [resolvedLang, resolvedSpeaker];\n}\n","import { BaseClient } from \"./client.js\";\r\n\r\nexport type TranscribeOptions = {\r\n language?: string;\r\n durationSeconds?: number;\r\n};\r\n\r\nexport type Transcription = {\r\n text: string;\r\n language?: string;\r\n duration?: number;\r\n model?: string;\r\n};\r\n\r\nexport class STT {\r\n constructor(private readonly client: BaseClient) {}\r\n\r\n async transcribe(fileUrl: string, options: TranscribeOptions = {}): Promise<Transcription> {\r\n const body: Record<string, unknown> = { file_url: fileUrl };\r\n if (options.language) body.language = options.language;\r\n if (options.durationSeconds != null) body.duration_seconds = options.durationSeconds;\r\n let response: Record<string, unknown> = await this.client.postJson(\"/v1/audio/transcriptions\", body);\r\n while (typeof response.result === \"object\" && response.result !== null && !Array.isArray(response.result)) {\r\n response = response.result as Record<string, unknown>;\r\n }\r\n return response as Transcription;\r\n }\r\n}"],"mappings":"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","names":["bytes"]}
package/dist/index.d.cts CHANGED
@@ -14,8 +14,10 @@ declare class BaseClient {
14
14
  private raiseForStatus;
15
15
  getJson(path: string, params?: Record<string, string | undefined | null>): Promise<Record<string, unknown>>;
16
16
  postBytes(path: string, body: Record<string, unknown>): Promise<Uint8Array>;
17
+ postJson(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
17
18
  postStream(path: string, body: Record<string, unknown>): AsyncGenerator<Uint8Array, void, unknown>;
18
19
  postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<Response>;
20
+ delete(path: string): Promise<void>;
19
21
  }
20
22
 
21
23
  /** A speaker voice identity. */
@@ -164,10 +166,28 @@ declare class TTS {
164
166
  clone(text: string, audioFile: string | Buffer | Uint8Array | Blob, options?: CloneOptions): Promise<CloneResponse>;
165
167
  listSpeakers(options?: ListSpeakersOptions): Promise<SpeakerList>;
166
168
  getSpeaker(speakerId: string): Promise<Speaker>;
169
+ /** Permanently delete a cloned voice owned by this API key. */
170
+ deleteVoice(voiceId: string): Promise<void>;
167
171
  listLanguages(): Promise<LanguageList>;
168
172
  health(): Promise<HealthStatus>;
169
173
  }
170
174
 
175
+ type TranscribeOptions = {
176
+ language?: string;
177
+ durationSeconds?: number;
178
+ };
179
+ type Transcription = {
180
+ text: string;
181
+ language?: string;
182
+ duration?: number;
183
+ model?: string;
184
+ };
185
+ declare class STT {
186
+ private readonly client;
187
+ constructor(client: BaseClient);
188
+ transcribe(fileUrl: string, options?: TranscribeOptions): Promise<Transcription>;
189
+ }
190
+
171
191
  /** Base exception for all 9jaLingo SDK errors. */
172
192
  declare class NaijaLingoError extends Error {
173
193
  readonly statusCode?: number;
@@ -222,6 +242,7 @@ declare const VERSION = "0.1.2";
222
242
  declare class NaijaLingo {
223
243
  private readonly _client;
224
244
  readonly tts: TTS;
245
+ readonly stt: STT;
225
246
  constructor(options?: ClientOptions);
226
247
  /** List available TTS models. GET /v1/models */
227
248
  listModels(): Promise<ModelList>;
@@ -231,4 +252,4 @@ declare class NaijaLingo {
231
252
  serviceInfo(): Promise<ServiceInfo>;
232
253
  }
233
254
 
234
- export { type APIInfo, AudioResponse, AudioStream, AuthenticationError, type ClientOptions, type CloneOptions, CloneResponse, ConnectionError, type GenerateOptions, type HealthStatus, InferenceCapacityError, type Language, type LanguageList, type ListSpeakersOptions, type Model, type ModelList, NaijaLingo, NaijaLingoError, NotFoundError, RateLimitError, type ResponseFormat, ServerError, type ServiceInfo, type Speaker, type SpeakerList, type StreamOptions, TTS, VERSION };
255
+ export { type APIInfo, AudioResponse, AudioStream, AuthenticationError, type ClientOptions, type CloneOptions, CloneResponse, ConnectionError, type GenerateOptions, type HealthStatus, InferenceCapacityError, type Language, type LanguageList, type ListSpeakersOptions, type Model, type ModelList, NaijaLingo, NaijaLingoError, NotFoundError, RateLimitError, type ResponseFormat, STT, ServerError, type ServiceInfo, type Speaker, type SpeakerList, type StreamOptions, TTS, type TranscribeOptions, type Transcription, VERSION };
package/dist/index.d.ts CHANGED
@@ -14,8 +14,10 @@ declare class BaseClient {
14
14
  private raiseForStatus;
15
15
  getJson(path: string, params?: Record<string, string | undefined | null>): Promise<Record<string, unknown>>;
16
16
  postBytes(path: string, body: Record<string, unknown>): Promise<Uint8Array>;
17
+ postJson(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
17
18
  postStream(path: string, body: Record<string, unknown>): AsyncGenerator<Uint8Array, void, unknown>;
18
19
  postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<Response>;
20
+ delete(path: string): Promise<void>;
19
21
  }
20
22
 
21
23
  /** A speaker voice identity. */
@@ -164,10 +166,28 @@ declare class TTS {
164
166
  clone(text: string, audioFile: string | Buffer | Uint8Array | Blob, options?: CloneOptions): Promise<CloneResponse>;
165
167
  listSpeakers(options?: ListSpeakersOptions): Promise<SpeakerList>;
166
168
  getSpeaker(speakerId: string): Promise<Speaker>;
169
+ /** Permanently delete a cloned voice owned by this API key. */
170
+ deleteVoice(voiceId: string): Promise<void>;
167
171
  listLanguages(): Promise<LanguageList>;
168
172
  health(): Promise<HealthStatus>;
169
173
  }
170
174
 
175
+ type TranscribeOptions = {
176
+ language?: string;
177
+ durationSeconds?: number;
178
+ };
179
+ type Transcription = {
180
+ text: string;
181
+ language?: string;
182
+ duration?: number;
183
+ model?: string;
184
+ };
185
+ declare class STT {
186
+ private readonly client;
187
+ constructor(client: BaseClient);
188
+ transcribe(fileUrl: string, options?: TranscribeOptions): Promise<Transcription>;
189
+ }
190
+
171
191
  /** Base exception for all 9jaLingo SDK errors. */
172
192
  declare class NaijaLingoError extends Error {
173
193
  readonly statusCode?: number;
@@ -222,6 +242,7 @@ declare const VERSION = "0.1.2";
222
242
  declare class NaijaLingo {
223
243
  private readonly _client;
224
244
  readonly tts: TTS;
245
+ readonly stt: STT;
225
246
  constructor(options?: ClientOptions);
226
247
  /** List available TTS models. GET /v1/models */
227
248
  listModels(): Promise<ModelList>;
@@ -231,4 +252,4 @@ declare class NaijaLingo {
231
252
  serviceInfo(): Promise<ServiceInfo>;
232
253
  }
233
254
 
234
- export { type APIInfo, AudioResponse, AudioStream, AuthenticationError, type ClientOptions, type CloneOptions, CloneResponse, ConnectionError, type GenerateOptions, type HealthStatus, InferenceCapacityError, type Language, type LanguageList, type ListSpeakersOptions, type Model, type ModelList, NaijaLingo, NaijaLingoError, NotFoundError, RateLimitError, type ResponseFormat, ServerError, type ServiceInfo, type Speaker, type SpeakerList, type StreamOptions, TTS, VERSION };
255
+ export { type APIInfo, AudioResponse, AudioStream, AuthenticationError, type ClientOptions, type CloneOptions, CloneResponse, ConnectionError, type GenerateOptions, type HealthStatus, InferenceCapacityError, type Language, type LanguageList, type ListSpeakersOptions, type Model, type ModelList, NaijaLingo, NaijaLingoError, NotFoundError, RateLimitError, type ResponseFormat, STT, ServerError, type ServiceInfo, type Speaker, type SpeakerList, type StreamOptions, TTS, type TranscribeOptions, type Transcription, VERSION };
package/dist/index.js CHANGED
@@ -159,6 +159,13 @@ var BaseClient = class {
159
159
  });
160
160
  return new Uint8Array(await resp.arrayBuffer());
161
161
  }
162
+ async postJson(path, body) {
163
+ const resp = await this.request("POST", path, {
164
+ headers: { "Content-Type": "application/json" },
165
+ body: JSON.stringify(body)
166
+ });
167
+ return await resp.json();
168
+ }
162
169
  async *postStream(path, body) {
163
170
  let lastError;
164
171
  for (let attempt = 0; attempt < 3; attempt++) {
@@ -222,6 +229,9 @@ var BaseClient = class {
222
229
  // Let fetch set multipart boundary — do not set Content-Type
223
230
  });
224
231
  }
232
+ async delete(path) {
233
+ await this.request("DELETE", path);
234
+ }
225
235
  };
226
236
  function sleep(ms) {
227
237
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -603,6 +613,14 @@ var TTS = class {
603
613
  const data = await this.client.getJson(`/v1/speakers/${speakerId}`);
604
614
  return speakerFromDict(data);
605
615
  }
616
+ /** Permanently delete a cloned voice owned by this API key. */
617
+ async deleteVoice(voiceId) {
618
+ const normalizedVoiceId = voiceId.trim();
619
+ if (!normalizedVoiceId) {
620
+ throw new Error("voiceId is required");
621
+ }
622
+ await this.client.delete(`/v1/voices/${encodeURIComponent(normalizedVoiceId)}`);
623
+ }
606
624
  async listLanguages() {
607
625
  const data = await this.client.getJson("/v1/languages");
608
626
  return languageListFromDict(data);
@@ -660,14 +678,34 @@ function guessMime(filename) {
660
678
  return "application/octet-stream";
661
679
  }
662
680
 
681
+ // src/stt.ts
682
+ var STT = class {
683
+ constructor(client) {
684
+ this.client = client;
685
+ }
686
+ client;
687
+ async transcribe(fileUrl, options = {}) {
688
+ const body = { file_url: fileUrl };
689
+ if (options.language) body.language = options.language;
690
+ if (options.durationSeconds != null) body.duration_seconds = options.durationSeconds;
691
+ let response = await this.client.postJson("/v1/audio/transcriptions", body);
692
+ while (typeof response.result === "object" && response.result !== null && !Array.isArray(response.result)) {
693
+ response = response.result;
694
+ }
695
+ return response;
696
+ }
697
+ };
698
+
663
699
  // src/index.ts
664
700
  var VERSION = "0.1.2";
665
701
  var NaijaLingo = class {
666
702
  _client;
667
703
  tts;
704
+ stt;
668
705
  constructor(options = {}) {
669
706
  this._client = new BaseClient(options);
670
707
  this.tts = new TTS(this._client);
708
+ this.stt = new STT(this._client);
671
709
  }
672
710
  /** List available TTS models. GET /v1/models */
673
711
  async listModels() {
@@ -696,6 +734,7 @@ export {
696
734
  NaijaLingoError,
697
735
  NotFoundError,
698
736
  RateLimitError,
737
+ STT,
699
738
  ServerError,
700
739
  TTS,
701
740
  VERSION
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/tts.ts","../src/types.ts","../src/voice.ts","../src/index.ts"],"sourcesContent":["/** Base exception for all 9jaLingo SDK errors. */\nexport class NaijaLingoError extends Error {\n readonly statusCode?: number;\n readonly response?: unknown;\n\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message);\n this.name = \"NaijaLingoError\";\n this.statusCode = statusCode;\n this.response = response;\n }\n}\n\n/** Raised when the API key is missing or invalid. */\nexport class AuthenticationError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** Raised when rate limit is exceeded. */\nexport class RateLimitError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"RateLimitError\";\n }\n}\n\n/** Raised when a requested resource is not found. */\nexport class NotFoundError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"NotFoundError\";\n }\n}\n\n/** Raised when the API returns a 5xx error. */\nexport class ServerError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ServerError\";\n }\n}\n\n/** Raised when the inference GPU is waking from scale-to-zero. */\nexport class InferenceCapacityError extends ServerError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"InferenceCapacityError\";\n }\n}\n\n/** Raised when unable to connect to the API. */\nexport class ConnectionError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ConnectionError\";\n }\n}\n","import {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.9jalingo.org\";\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ClientOptions {\n apiKey?: string;\n baseUrl?: string;\n /** Request timeout in milliseconds (default 300000). */\n timeout?: number;\n}\n\nexport class BaseClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeout: number;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: ClientOptions = {}) {\n this.apiKey =\n options.apiKey ?? process.env.NAIJALINGO_API_KEY ?? \"\";\n const base =\n options.baseUrl ??\n process.env.NAIJALINGO_BASE_URL ??\n DEFAULT_BASE_URL;\n this.baseUrl = base.replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n this.defaultHeaders = {\n \"User-Agent\": \"naijalingo-js/0.1.2\",\n };\n if (this.apiKey) {\n this.defaultHeaders[\"X-API-Key\"] = this.apiKey;\n }\n }\n\n private async request(\n method: string,\n path: string,\n init: RequestInit & { timeoutMs?: number } = {},\n ): Promise<Response> {\n const url = `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`;\n const timeoutMs = init.timeoutMs ?? this.timeout;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(url, {\n ...init,\n method,\n headers: {\n ...this.defaultHeaders,\n ...(init.headers as Record<string, string> | undefined),\n },\n signal: controller.signal,\n });\n clearTimeout(timer);\n\n if (response.ok) {\n return response;\n }\n await this.raiseForStatus(response);\n // unreachable\n return response;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) {\n throw err;\n }\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n // Only retry connect-style failures, not aborts from long reads\n if (isAbort) {\n throw new ConnectionError(\n `Request timed out after ${timeoutMs}ms: ${String(err)}`,\n );\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n private async raiseForStatus(response: Response): Promise<never> {\n const status = response.status;\n let detail: string;\n let body: unknown;\n try {\n body = await response.json();\n detail =\n typeof body === \"object\" &&\n body !== null &&\n \"detail\" in body\n ? String((body as { detail: unknown }).detail)\n : JSON.stringify(body);\n } catch {\n detail = await response.text();\n }\n\n const normalizedDetail = detail.toLowerCase();\n if (status === 503 && (\n normalizedDetail.includes(\"inference capacity\")\n || normalizedDetail.includes(\"inference component has no capacity\")\n || normalizedDetail.includes(\"capacity is starting\")\n )) {\n throw new InferenceCapacityError(detail, status, body);\n }\n if (status === 401 || status === 403) {\n throw new AuthenticationError(detail, status, body);\n }\n if (status === 404) {\n throw new NotFoundError(detail, status, body);\n }\n if (status === 429) {\n throw new RateLimitError(detail, status, body);\n }\n if (status >= 500) {\n throw new ServerError(detail, status, body);\n }\n throw new NaijaLingoError(detail, status, body);\n }\n\n async getJson(\n path: string,\n params?: Record<string, string | undefined | null>,\n ): Promise<Record<string, unknown>> {\n let url = path;\n if (params) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v != null) qs.set(k, v);\n }\n const s = qs.toString();\n if (s) url = `${path}?${s}`;\n }\n const resp = await this.request(\"GET\", url);\n return (await resp.json()) as Record<string, unknown>;\n }\n\n async postBytes(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Uint8Array> {\n const resp = await this.request(\"POST\", path, {\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return new Uint8Array(await resp.arrayBuffer());\n }\n\n async *postStream(\n path: string,\n body: Record<string, unknown>,\n ): AsyncGenerator<Uint8Array, void, unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const response = await fetch(\n `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`,\n {\n method: \"POST\",\n headers: {\n ...this.defaultHeaders,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n },\n );\n clearTimeout(timer);\n\n if (!response.ok) {\n await this.raiseForStatus(response);\n }\n if (!response.body) {\n throw new ConnectionError(\"No response body for stream\");\n }\n\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value && value.length > 0) {\n yield value;\n }\n }\n } finally {\n reader.releaseLock();\n }\n return;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) throw err;\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n if (isAbort) {\n throw new ConnectionError(`Request timed out: ${String(err)}`);\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n async postMultipart(\n path: string,\n form: FormData,\n timeoutMs?: number,\n ): Promise<Response> {\n return this.request(\"POST\", path, {\n body: form,\n timeoutMs: timeoutMs ?? Math.max(this.timeout, 600_000),\n // Let fetch set multipart boundary — do not set Content-Type\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { basename, dirname } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport { BaseClient } from \"./client.js\";\nimport {\n type HealthStatus,\n type LanguageList,\n type ResponseFormat,\n type Speaker,\n type SpeakerList,\n healthStatusFromDict,\n languageListFromDict,\n speakerFromDict,\n speakerListFromDict,\n} from \"./types.js\";\nimport {\n resolveCloneLangAndSpeaker,\n resolveTtsVoiceAndLang,\n} from \"./voice.js\";\n\nconst DEFAULT_MODEL_NAME = \"9jalingo-tts-1\";\n\nexport type GenerateOptions = {\n voice?: string;\n speaker?: string;\n lang?: string;\n language?: string;\n modelName?: string;\n speakerEmbedding?: number[];\n responseFormat?: ResponseFormat;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n enableLongForm?: boolean;\n maxChunkDuration?: number;\n silenceDuration?: number;\n};\n\nexport type StreamOptions = Omit<\n GenerateOptions,\n \"modelName\" | \"responseFormat\"\n>;\n\nexport type CloneOptions = {\n voice?: string;\n lang?: string;\n speaker?: string;\n modelName?: string;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n responseFormat?: ResponseFormat;\n};\n\nexport type ListSpeakersOptions = {\n language?: string;\n gender?: string;\n domain?: string;\n};\n\n/** Audio data returned from a generation request. */\nexport class AudioResponse {\n readonly content: Uint8Array;\n readonly mediaType: string;\n\n constructor(content: Uint8Array, mediaType = \"audio/wav\") {\n this.content = content;\n this.mediaType = mediaType;\n }\n\n /** Write the audio to a file. */\n async save(path: string): Promise<string> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, this.content);\n return path;\n }\n\n get length(): number {\n return this.content.length;\n }\n\n toString(): string {\n return `AudioResponse(${this.content.length} bytes, ${this.mediaType})`;\n }\n}\n\n/** Audio and reusable voice metadata returned from a clone request. */\nexport class CloneResponse extends AudioResponse {\n readonly voiceId: string | null;\n readonly voiceCode: string | null;\n readonly voiceName: string | null;\n readonly cloneId: string | null;\n readonly jobId: string | null;\n\n constructor(\n content: Uint8Array,\n mediaType: string,\n metadata: {\n voiceId: string | null;\n voiceCode: string | null;\n voiceName: string | null;\n cloneId: string | null;\n jobId: string | null;\n },\n ) {\n super(content, mediaType);\n this.voiceId = metadata.voiceId;\n this.voiceCode = metadata.voiceCode;\n this.voiceName = metadata.voiceName;\n this.cloneId = metadata.cloneId;\n this.jobId = metadata.jobId;\n }\n}\n\n/** Streaming audio response — yields byte chunks as they arrive. */\nexport class AudioStream implements AsyncIterable<Uint8Array> {\n private readonly chunks: AsyncIterable<Uint8Array>;\n private collected: Uint8Array | null = null;\n\n constructor(chunks: AsyncIterable<Uint8Array>) {\n this.chunks = chunks;\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n for await (const chunk of this.chunks) {\n yield chunk;\n }\n }\n\n /** Consume the entire stream and return an AudioResponse. */\n async collect(): Promise<AudioResponse> {\n if (this.collected == null) {\n const parts: Uint8Array[] = [];\n let total = 0;\n for await (const chunk of this.chunks) {\n parts.push(chunk);\n total += chunk.length;\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n this.collected = out;\n }\n return new AudioResponse(this.collected, \"audio/wav\");\n }\n}\n\nexport class TTS {\n private readonly client: BaseClient;\n\n constructor(client: BaseClient) {\n this.client = client;\n }\n\n /** Generate speech from text. */\n async generate(\n text: string,\n options: GenerateOptions = {},\n ): Promise<AudioResponse> {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const responseFormat = options.responseFormat ?? \"wav\";\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n model: options.modelName ?? DEFAULT_MODEL_NAME,\n speaker_embedding: options.speakerEmbedding,\n response_format: responseFormat,\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n const content = await this.client.postBytes(\"/v1/audio/speech\", body);\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new AudioResponse(content, mediaType);\n }\n\n /** Stream speech generation — audio arrives as it's produced. */\n stream(text: string, options: StreamOptions = {}): AudioStream {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n speaker_embedding: options.speakerEmbedding,\n response_format: \"wav\",\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n return new AudioStream(this.client.postStream(\"/v1/audio/speech/stream\", body));\n }\n\n /**\n * Generate speech using a cloned voice from a reference audio file.\n * `audioFile` may be a filesystem path, Buffer, Uint8Array, Blob, or File.\n */\n async clone(\n text: string,\n audioFile: string | Buffer | Uint8Array | Blob,\n options: CloneOptions = {},\n ): Promise<CloneResponse> {\n const voice = options.voice ?? \"pcm\";\n const [resolvedLang, resolvedSpeaker] = resolveCloneLangAndSpeaker(\n voice,\n { lang: options.lang, speaker: options.speaker },\n );\n const responseFormat = options.responseFormat ?? \"wav\";\n\n const form = new FormData();\n form.append(\"text\", text);\n form.append(\"lang\", resolvedLang);\n form.append(\"model_name\", options.modelName ?? DEFAULT_MODEL_NAME);\n form.append(\"response_format\", responseFormat);\n if (resolvedSpeaker) {\n form.append(\"voice\", resolvedSpeaker);\n }\n if (options.temperature != null) {\n form.append(\"temperature\", String(options.temperature));\n }\n if (options.topP != null) {\n form.append(\"top_p\", String(options.topP));\n }\n if (options.repetitionPenalty != null) {\n form.append(\"repetition_penalty\", String(options.repetitionPenalty));\n }\n\n const { blob, filename } = await toAudioBlob(audioFile);\n form.append(\"audio\", blob, filename);\n\n const response = await this.client.postMultipart(\"/v1/audio/clone\", form);\n const content = new Uint8Array(await response.arrayBuffer());\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new CloneResponse(content, mediaType, {\n voiceId: response.headers.get(\"X-Voice-ID\"),\n voiceCode: response.headers.get(\"X-Voice-Code\"),\n voiceName: response.headers.get(\"X-Voice-Name\"),\n cloneId: response.headers.get(\"X-Clone-ID\"),\n jobId: response.headers.get(\"X-Job-ID\"),\n });\n }\n\n async listSpeakers(\n options: ListSpeakersOptions = {},\n ): Promise<SpeakerList> {\n const data = await this.client.getJson(\"/v1/speakers\", {\n language: options.language,\n gender: options.gender,\n domain: options.domain,\n });\n return speakerListFromDict(data);\n }\n\n async getSpeaker(speakerId: string): Promise<Speaker> {\n const data = await this.client.getJson(`/v1/speakers/${speakerId}`);\n return speakerFromDict(data);\n }\n\n async listLanguages(): Promise<LanguageList> {\n const data = await this.client.getJson(\"/v1/languages\");\n return languageListFromDict(data);\n }\n\n async health(): Promise<HealthStatus> {\n const data = await this.client.getJson(\"/v1/health\");\n return healthStatusFromDict(data);\n }\n}\n\nconst MEDIA_TYPES: Record<string, string> = {\n wav: \"audio/wav\",\n pcm: \"application/octet-stream\",\n flac: \"audio/flac\",\n aac: \"audio/aac\",\n ogg: \"audio/ogg\",\n mp3: \"audio/mpeg\",\n alac: \"audio/alac\",\n};\n\nfunction buildBody(\n kwargs: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(kwargs)) {\n if (v !== undefined && v !== null) out[k] = v;\n }\n return out;\n}\n\nasync function toAudioBlob(\n audioFile: string | Buffer | Uint8Array | Blob,\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof audioFile === \"string\") {\n const bytes = readFileSync(audioFile);\n const filename = basename(audioFile);\n const mime = guessMime(filename);\n return {\n blob: new Blob([bytes], { type: mime }),\n filename,\n };\n }\n if (audioFile instanceof Blob) {\n return {\n blob: audioFile,\n filename: \"audio.wav\",\n };\n }\n // Buffer | Uint8Array\n const bytes =\n audioFile instanceof Uint8Array\n ? audioFile\n : new Uint8Array(audioFile);\n return {\n blob: new Blob([bytes], { type: \"application/octet-stream\" }),\n filename: \"audio.wav\",\n };\n}\n\nfunction guessMime(filename: string): string {\n const lower = filename.toLowerCase();\n if (lower.endsWith(\".wav\")) return \"audio/wav\";\n if (lower.endsWith(\".mp3\")) return \"audio/mpeg\";\n if (lower.endsWith(\".flac\")) return \"audio/flac\";\n if (lower.endsWith(\".ogg\")) return \"audio/ogg\";\n if (lower.endsWith(\".aac\") || lower.endsWith(\".m4a\")) return \"audio/aac\";\n return \"application/octet-stream\";\n}\n","/** A speaker voice identity. */\nexport interface Speaker {\n id: string;\n name: string;\n language: string;\n gender?: string | null;\n domain?: string | null;\n}\n\nexport function speakerFromDict(data: Record<string, unknown>): Speaker {\n return {\n id: String(data.id ?? data.speaker_id ?? \"\"),\n name: String(data.name ?? data.id ?? \"\"),\n language: String(data.language ?? \"\"),\n gender: (data.gender as string | null | undefined) ?? null,\n domain: (data.domain as string | null | undefined) ?? null,\n };\n}\n\n/** List of speakers with metadata. */\nexport interface SpeakerList {\n speakers: Speaker[];\n total: number;\n byLanguage: Record<string, number>;\n [Symbol.iterator](): Iterator<Speaker>;\n length: number;\n}\n\nexport function speakerListFromDict(data: Record<string, unknown>): SpeakerList {\n const raw = (data.speakers as Record<string, unknown>[] | undefined) ?? [];\n const speakers = raw.map(speakerFromDict);\n const total = typeof data.total === \"number\" ? data.total : speakers.length;\n const byLanguage =\n (data.by_language as Record<string, number> | undefined) ?? {};\n return {\n speakers,\n total,\n byLanguage,\n get length() {\n return total;\n },\n [Symbol.iterator]() {\n return speakers[Symbol.iterator]();\n },\n };\n}\n\n/** A supported language. */\nexport interface Language {\n code: string;\n name: string;\n speakerCount: number;\n}\n\nexport function languageFromDict(data: Record<string, unknown>): Language {\n return {\n code: String(data.code ?? \"\"),\n name: String(data.name ?? \"\"),\n speakerCount: Number(data.speaker_count ?? 0),\n };\n}\n\n/** List of supported languages and domains. */\nexport interface LanguageList {\n languages: Language[];\n domains: string[];\n}\n\nexport function languageListFromDict(data: Record<string, unknown>): LanguageList {\n const rawLangs = data.languages;\n let languages: Language[];\n\n if (rawLangs && typeof rawLangs === \"object\" && !Array.isArray(rawLangs)) {\n languages = Object.entries(rawLangs as Record<string, unknown>).map(\n ([code, value]) => {\n if (value && typeof value === \"object\") {\n const v = value as Record<string, unknown>;\n return {\n code,\n name: String(v.name ?? code),\n speakerCount: Number(v.speaker_count ?? 0),\n };\n }\n return { code, name: String(value), speakerCount: 0 };\n },\n );\n } else {\n const arr = (rawLangs as Record<string, unknown>[] | undefined) ?? [];\n languages = arr.map(languageFromDict);\n }\n\n return {\n languages,\n domains: (data.domains as string[] | undefined) ?? [],\n };\n}\n\n/** API health check result. */\nexport interface HealthStatus {\n status: string;\n engineReady: boolean;\n codecReady: boolean;\n totalSpeakers: number;\n languages: string[];\n voiceCloningAvailable: boolean;\n speakerProjectionLoaded: boolean;\n}\n\nexport function healthStatusFromDict(data: Record<string, unknown>): HealthStatus {\n const supported = data.supported_languages;\n let languages: string[] = [];\n if (Array.isArray(data.languages)) {\n languages = data.languages as string[];\n } else if (supported && typeof supported === \"object\") {\n languages = Object.keys(supported as Record<string, unknown>);\n }\n\n return {\n status: String(data.status ?? \"unknown\"),\n engineReady: Boolean(data.engine_ready ?? data.tts_initialized ?? false),\n codecReady: Boolean(data.codec_ready ?? false),\n totalSpeakers: Number(data.total_speakers ?? data.speakers_loaded ?? 0),\n languages,\n voiceCloningAvailable: Boolean(data.voice_cloning_available ?? false),\n speakerProjectionLoaded: Boolean(data.speaker_projection_loaded ?? false),\n };\n}\n\n/** An available TTS model. */\nexport interface Model {\n id: string;\n object: string;\n ownedBy: string;\n}\n\nexport function modelFromDict(data: Record<string, unknown>): Model {\n return {\n id: String(data.id ?? \"\"),\n object: String(data.object ?? \"model\"),\n ownedBy: String(data.owned_by ?? \"9jalingo\"),\n };\n}\n\n/** List of available models. */\nexport interface ModelList {\n models: Model[];\n object: string;\n [Symbol.iterator](): Iterator<Model>;\n length: number;\n}\n\nexport function modelListFromDict(data: Record<string, unknown>): ModelList {\n const raw = (data.data as Record<string, unknown>[] | undefined) ?? [];\n const models = raw.map(modelFromDict);\n return {\n models,\n object: String(data.object ?? \"list\"),\n get length() {\n return models.length;\n },\n [Symbol.iterator]() {\n return models[Symbol.iterator]();\n },\n };\n}\n\n/** Root API information returned by GET /. */\nexport interface APIInfo {\n name: string;\n version: string;\n description: string;\n endpoints: Record<string, string>;\n}\n\nexport function apiInfoFromDict(data: Record<string, unknown>): APIInfo {\n return {\n name: String(data.name ?? \"\"),\n version: String(data.version ?? \"\"),\n description: String(data.description ?? \"\"),\n endpoints: (data.endpoints as Record<string, string> | undefined) ?? {},\n };\n}\n\n/** Service metadata returned by GET /v1. */\nexport interface ServiceInfo {\n object: string;\n name: string;\n modelsUrl: string;\n speechUrl: string;\n speechStreamUrl: string;\n}\n\nexport function serviceInfoFromDict(data: Record<string, unknown>): ServiceInfo {\n return {\n object: String(data.object ?? \"service\"),\n name: String(data.name ?? \"\"),\n modelsUrl: String(data.models_url ?? \"\"),\n speechUrl: String(data.speech_url ?? \"\"),\n speechStreamUrl: String(data.speech_stream_url ?? \"\"),\n };\n}\n\nexport type ResponseFormat =\n | \"wav\"\n | \"pcm\"\n | \"flac\"\n | \"aac\"\n | \"ogg\"\n | \"mp3\"\n | \"alac\";\n","const LANG_ALIASES: Record<string, string> = {\n yo: \"yo\",\n yor: \"yo\",\n yoruba: \"yo\",\n ig: \"ig\",\n ibo: \"ig\",\n igbo: \"ig\",\n ha: \"ha\",\n hau: \"ha\",\n hausa: \"ha\",\n pcm: \"pcm\",\n pidgin: \"pcm\",\n};\n\nexport function normalizeLang(code: string): string | null {\n return LANG_ALIASES[code.trim().toLowerCase()] ?? null;\n}\n\n/**\n * Resolve speech API fields: voice/speaker + lang/language.\n * Returns [speakerId, langCode]. Bare language codes must be passed via lang/language.\n */\nexport function resolveTtsVoiceAndLang(options: {\n voice?: string | null;\n speaker?: string | null;\n lang?: string | null;\n language?: string | null;\n}): [string | null, string | null] {\n let resolvedLang: string | null = null;\n\n if (options.lang != null) {\n resolvedLang = normalizeLang(options.lang);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.lang}'. Use ha, ig, yo, or pcm.`,\n );\n }\n } else if (options.language != null) {\n resolvedLang = normalizeLang(options.language);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.language}'. Use ha, ig, yo, or pcm.`,\n );\n }\n }\n\n // speaker takes precedence over voice\n const candidate =\n options.speaker != null ? options.speaker : options.voice;\n if (candidate == null || !String(candidate).trim()) {\n return [null, resolvedLang];\n }\n\n const resolved = String(candidate).trim();\n const normalized = resolved.toLowerCase();\n if (normalized in LANG_ALIASES) {\n const langCode = LANG_ALIASES[normalized];\n throw new Error(\n `'${resolved}' is a language code, not a speaker ID. ` +\n `Pass speaker/voice='ada_pcm' (or another speaker ID) and ` +\n `lang='${langCode}' for the language. ` +\n `Browse speakers with: client.tts.listSpeakers({ language: '${langCode}' })`,\n );\n }\n\n if (resolvedLang == null && resolved.includes(\"_\")) {\n const suffix = resolved.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n\n return [resolved, resolvedLang];\n}\n\n/**\n * Resolve clone form fields.\n * voice may be a language code (\"pcm\") or a speaker ID with a language suffix.\n */\nexport function resolveCloneLangAndSpeaker(\n voice: string,\n options: { lang?: string | null; speaker?: string | null } = {},\n): [string, string | null] {\n let resolvedLang =\n options.lang != null ? normalizeLang(options.lang) : null;\n let resolvedSpeaker = options.speaker?.trim() || null;\n\n const voiceStr = (voice || \"\").trim();\n const voiceNorm = voiceStr.toLowerCase();\n\n if (voiceNorm in LANG_ALIASES) {\n if (resolvedLang == null) {\n resolvedLang = LANG_ALIASES[voiceNorm];\n }\n } else if (voiceStr.includes(\"_\")) {\n if (resolvedSpeaker == null) {\n resolvedSpeaker = voiceStr;\n }\n if (resolvedLang == null) {\n const suffix = voiceStr.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n } else if (voiceStr && resolvedLang == null) {\n throw new Error(\n `'${voice}' is not a language code (ha/ig/yo/pcm) or a speaker ID ` +\n `with a language suffix (e.g. 'daniel_pcm'). ` +\n `Pass voice='pcm' or lang='pcm'.`,\n );\n }\n\n if (!resolvedLang) {\n throw new Error(\n \"Clone requires a language. Pass voice='pcm' (or 'ha'/'ig'/'yo'), \" +\n \"lang='pcm', or a speaker ID ending in _pcm/_ha/_ig/_yo.\",\n );\n }\n\n return [resolvedLang, resolvedSpeaker];\n}\n","/**\n * 9jaLingo Node.js SDK\n *\n * Official SDK for the 9jaLingo API — Nigerian language Text-to-Speech.\n *\n * @example\n * ```ts\n * import { NaijaLingo } from \"naijalingo\";\n *\n * const client = new NaijaLingo(); // uses NAIJALINGO_API_KEY\n * const audio = await client.tts.generate(\"Bawo ni!\", {\n * voice: \"adeola_yo\",\n * lang: \"yo\",\n * });\n * await audio.save(\"greeting.wav\");\n * ```\n */\n\nimport { BaseClient, type ClientOptions } from \"./client.js\";\nimport { TTS } from \"./tts.js\";\nimport {\n apiInfoFromDict,\n modelListFromDict,\n serviceInfoFromDict,\n type APIInfo,\n type ModelList,\n type ServiceInfo,\n} from \"./types.js\";\n\nexport const VERSION = \"0.1.2\";\n\nexport {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nexport {\n AudioResponse,\n AudioStream,\n CloneResponse,\n TTS,\n type CloneOptions,\n type GenerateOptions,\n type ListSpeakersOptions,\n type StreamOptions,\n} from \"./tts.js\";\n\nexport type {\n APIInfo,\n HealthStatus,\n Language,\n LanguageList,\n Model,\n ModelList,\n ResponseFormat,\n ServiceInfo,\n Speaker,\n SpeakerList,\n} from \"./types.js\";\n\nexport type { ClientOptions };\n\nexport class NaijaLingo {\n private readonly _client: BaseClient;\n readonly tts: TTS;\n\n constructor(options: ClientOptions = {}) {\n this._client = new BaseClient(options);\n this.tts = new TTS(this._client);\n }\n\n /** List available TTS models. GET /v1/models */\n async listModels(): Promise<ModelList> {\n const data = await this._client.getJson(\"/v1/models\");\n return modelListFromDict(data);\n }\n\n /** Root API information. GET / */\n async apiInfo(): Promise<APIInfo> {\n const data = await this._client.getJson(\"/\");\n return apiInfoFromDict(data);\n }\n\n /** v1 service metadata. GET /v1 */\n async serviceInfo(): Promise<ServiceInfo> {\n const data = await this._client.getJson(\"/v1\");\n return serviceInfoFromDict(data);\n }\n}\n"],"mappings":"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","names":["bytes"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/tts.ts","../src/types.ts","../src/voice.ts","../src/stt.ts","../src/index.ts"],"sourcesContent":["/** Base exception for all 9jaLingo SDK errors. */\nexport class NaijaLingoError extends Error {\n readonly statusCode?: number;\n readonly response?: unknown;\n\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message);\n this.name = \"NaijaLingoError\";\n this.statusCode = statusCode;\n this.response = response;\n }\n}\n\n/** Raised when the API key is missing or invalid. */\nexport class AuthenticationError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** Raised when rate limit is exceeded. */\nexport class RateLimitError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"RateLimitError\";\n }\n}\n\n/** Raised when a requested resource is not found. */\nexport class NotFoundError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"NotFoundError\";\n }\n}\n\n/** Raised when the API returns a 5xx error. */\nexport class ServerError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ServerError\";\n }\n}\n\n/** Raised when the inference GPU is waking from scale-to-zero. */\nexport class InferenceCapacityError extends ServerError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"InferenceCapacityError\";\n }\n}\n\n/** Raised when unable to connect to the API. */\nexport class ConnectionError extends NaijaLingoError {\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message, statusCode, response);\n this.name = \"ConnectionError\";\n }\n}\n","import {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.9jalingo.org\";\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ClientOptions {\n apiKey?: string;\n baseUrl?: string;\n /** Request timeout in milliseconds (default 300000). */\n timeout?: number;\n}\n\nexport class BaseClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeout: number;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: ClientOptions = {}) {\n this.apiKey =\n options.apiKey ?? process.env.NAIJALINGO_API_KEY ?? \"\";\n const base =\n options.baseUrl ??\n process.env.NAIJALINGO_BASE_URL ??\n DEFAULT_BASE_URL;\n this.baseUrl = base.replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n this.defaultHeaders = {\n \"User-Agent\": \"naijalingo-js/0.1.2\",\n };\n if (this.apiKey) {\n this.defaultHeaders[\"X-API-Key\"] = this.apiKey;\n }\n }\n\n private async request(\n method: string,\n path: string,\n init: RequestInit & { timeoutMs?: number } = {},\n ): Promise<Response> {\n const url = `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`;\n const timeoutMs = init.timeoutMs ?? this.timeout;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(url, {\n ...init,\n method,\n headers: {\n ...this.defaultHeaders,\n ...(init.headers as Record<string, string> | undefined),\n },\n signal: controller.signal,\n });\n clearTimeout(timer);\n\n if (response.ok) {\n return response;\n }\n await this.raiseForStatus(response);\n // unreachable\n return response;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) {\n throw err;\n }\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n // Only retry connect-style failures, not aborts from long reads\n if (isAbort) {\n throw new ConnectionError(\n `Request timed out after ${timeoutMs}ms: ${String(err)}`,\n );\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n private async raiseForStatus(response: Response): Promise<never> {\n const status = response.status;\n let detail: string;\n let body: unknown;\n try {\n body = await response.json();\n detail =\n typeof body === \"object\" &&\n body !== null &&\n \"detail\" in body\n ? String((body as { detail: unknown }).detail)\n : JSON.stringify(body);\n } catch {\n detail = await response.text();\n }\n\n const normalizedDetail = detail.toLowerCase();\n if (status === 503 && (\n normalizedDetail.includes(\"inference capacity\")\n || normalizedDetail.includes(\"inference component has no capacity\")\n || normalizedDetail.includes(\"capacity is starting\")\n )) {\n throw new InferenceCapacityError(detail, status, body);\n }\n if (status === 401 || status === 403) {\n throw new AuthenticationError(detail, status, body);\n }\n if (status === 404) {\n throw new NotFoundError(detail, status, body);\n }\n if (status === 429) {\n throw new RateLimitError(detail, status, body);\n }\n if (status >= 500) {\n throw new ServerError(detail, status, body);\n }\n throw new NaijaLingoError(detail, status, body);\n }\n\n async getJson(\n path: string,\n params?: Record<string, string | undefined | null>,\n ): Promise<Record<string, unknown>> {\n let url = path;\n if (params) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v != null) qs.set(k, v);\n }\n const s = qs.toString();\n if (s) url = `${path}?${s}`;\n }\n const resp = await this.request(\"GET\", url);\n return (await resp.json()) as Record<string, unknown>;\n }\n\n async postBytes(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Uint8Array> {\n const resp = await this.request(\"POST\", path, {\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return new Uint8Array(await resp.arrayBuffer());\n }\n\n async postJson(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>> {\n const resp = await this.request(\"POST\", path, {\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return (await resp.json()) as Record<string, unknown>;\n }\n\n async *postStream(\n path: string,\n body: Record<string, unknown>,\n ): AsyncGenerator<Uint8Array, void, unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const response = await fetch(\n `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`,\n {\n method: \"POST\",\n headers: {\n ...this.defaultHeaders,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n },\n );\n clearTimeout(timer);\n\n if (!response.ok) {\n await this.raiseForStatus(response);\n }\n if (!response.body) {\n throw new ConnectionError(\"No response body for stream\");\n }\n\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value && value.length > 0) {\n yield value;\n }\n }\n } finally {\n reader.releaseLock();\n }\n return;\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof NaijaLingoError) throw err;\n lastError = err;\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n if (isAbort) {\n throw new ConnectionError(`Request timed out: ${String(err)}`);\n }\n if (attempt < 2) {\n await sleep(1500);\n continue;\n }\n }\n }\n throw new ConnectionError(\n `Unable to connect to ${this.baseUrl}: ${String(lastError)}`,\n );\n }\n\n async postMultipart(\n path: string,\n form: FormData,\n timeoutMs?: number,\n ): Promise<Response> {\n return this.request(\"POST\", path, {\n body: form,\n timeoutMs: timeoutMs ?? Math.max(this.timeout, 600_000),\n // Let fetch set multipart boundary — do not set Content-Type\n });\n }\n\n async delete(path: string): Promise<void> {\n await this.request(\"DELETE\", path);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { basename, dirname } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport { BaseClient } from \"./client.js\";\nimport {\n type HealthStatus,\n type LanguageList,\n type ResponseFormat,\n type Speaker,\n type SpeakerList,\n healthStatusFromDict,\n languageListFromDict,\n speakerFromDict,\n speakerListFromDict,\n} from \"./types.js\";\nimport {\n resolveCloneLangAndSpeaker,\n resolveTtsVoiceAndLang,\n} from \"./voice.js\";\n\nconst DEFAULT_MODEL_NAME = \"9jalingo-tts-1\";\n\nexport type GenerateOptions = {\n voice?: string;\n speaker?: string;\n lang?: string;\n language?: string;\n modelName?: string;\n speakerEmbedding?: number[];\n responseFormat?: ResponseFormat;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n enableLongForm?: boolean;\n maxChunkDuration?: number;\n silenceDuration?: number;\n};\n\nexport type StreamOptions = Omit<\n GenerateOptions,\n \"modelName\" | \"responseFormat\"\n>;\n\nexport type CloneOptions = {\n voice?: string;\n lang?: string;\n speaker?: string;\n modelName?: string;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n responseFormat?: ResponseFormat;\n};\n\nexport type ListSpeakersOptions = {\n language?: string;\n gender?: string;\n domain?: string;\n};\n\n/** Audio data returned from a generation request. */\nexport class AudioResponse {\n readonly content: Uint8Array;\n readonly mediaType: string;\n\n constructor(content: Uint8Array, mediaType = \"audio/wav\") {\n this.content = content;\n this.mediaType = mediaType;\n }\n\n /** Write the audio to a file. */\n async save(path: string): Promise<string> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, this.content);\n return path;\n }\n\n get length(): number {\n return this.content.length;\n }\n\n toString(): string {\n return `AudioResponse(${this.content.length} bytes, ${this.mediaType})`;\n }\n}\n\n/** Audio and reusable voice metadata returned from a clone request. */\nexport class CloneResponse extends AudioResponse {\n readonly voiceId: string | null;\n readonly voiceCode: string | null;\n readonly voiceName: string | null;\n readonly cloneId: string | null;\n readonly jobId: string | null;\n\n constructor(\n content: Uint8Array,\n mediaType: string,\n metadata: {\n voiceId: string | null;\n voiceCode: string | null;\n voiceName: string | null;\n cloneId: string | null;\n jobId: string | null;\n },\n ) {\n super(content, mediaType);\n this.voiceId = metadata.voiceId;\n this.voiceCode = metadata.voiceCode;\n this.voiceName = metadata.voiceName;\n this.cloneId = metadata.cloneId;\n this.jobId = metadata.jobId;\n }\n}\n\n/** Streaming audio response — yields byte chunks as they arrive. */\nexport class AudioStream implements AsyncIterable<Uint8Array> {\n private readonly chunks: AsyncIterable<Uint8Array>;\n private collected: Uint8Array | null = null;\n\n constructor(chunks: AsyncIterable<Uint8Array>) {\n this.chunks = chunks;\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n for await (const chunk of this.chunks) {\n yield chunk;\n }\n }\n\n /** Consume the entire stream and return an AudioResponse. */\n async collect(): Promise<AudioResponse> {\n if (this.collected == null) {\n const parts: Uint8Array[] = [];\n let total = 0;\n for await (const chunk of this.chunks) {\n parts.push(chunk);\n total += chunk.length;\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n this.collected = out;\n }\n return new AudioResponse(this.collected, \"audio/wav\");\n }\n}\n\nexport class TTS {\n private readonly client: BaseClient;\n\n constructor(client: BaseClient) {\n this.client = client;\n }\n\n /** Generate speech from text. */\n async generate(\n text: string,\n options: GenerateOptions = {},\n ): Promise<AudioResponse> {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const responseFormat = options.responseFormat ?? \"wav\";\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n model: options.modelName ?? DEFAULT_MODEL_NAME,\n speaker_embedding: options.speakerEmbedding,\n response_format: responseFormat,\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n const content = await this.client.postBytes(\"/v1/audio/speech\", body);\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new AudioResponse(content, mediaType);\n }\n\n /** Stream speech generation — audio arrives as it's produced. */\n stream(text: string, options: StreamOptions = {}): AudioStream {\n let [resolvedVoice, resolvedLang] = resolveTtsVoiceAndLang(options);\n if (resolvedVoice == null && options.speakerEmbedding == null) {\n resolvedVoice = \"blessing_pcm\";\n }\n const body = buildBody({\n input: text,\n voice: resolvedVoice,\n lang: resolvedLang,\n speaker_embedding: options.speakerEmbedding,\n response_format: \"wav\",\n temperature: options.temperature,\n top_p: options.topP,\n repetition_penalty: options.repetitionPenalty,\n enable_long_form: options.enableLongForm ?? true,\n max_chunk_duration: options.maxChunkDuration,\n silence_duration: options.silenceDuration,\n });\n return new AudioStream(this.client.postStream(\"/v1/audio/speech/stream\", body));\n }\n\n /**\n * Generate speech using a cloned voice from a reference audio file.\n * `audioFile` may be a filesystem path, Buffer, Uint8Array, Blob, or File.\n */\n async clone(\n text: string,\n audioFile: string | Buffer | Uint8Array | Blob,\n options: CloneOptions = {},\n ): Promise<CloneResponse> {\n const voice = options.voice ?? \"pcm\";\n const [resolvedLang, resolvedSpeaker] = resolveCloneLangAndSpeaker(\n voice,\n { lang: options.lang, speaker: options.speaker },\n );\n const responseFormat = options.responseFormat ?? \"wav\";\n\n const form = new FormData();\n form.append(\"text\", text);\n form.append(\"lang\", resolvedLang);\n form.append(\"model_name\", options.modelName ?? DEFAULT_MODEL_NAME);\n form.append(\"response_format\", responseFormat);\n if (resolvedSpeaker) {\n form.append(\"voice\", resolvedSpeaker);\n }\n if (options.temperature != null) {\n form.append(\"temperature\", String(options.temperature));\n }\n if (options.topP != null) {\n form.append(\"top_p\", String(options.topP));\n }\n if (options.repetitionPenalty != null) {\n form.append(\"repetition_penalty\", String(options.repetitionPenalty));\n }\n\n const { blob, filename } = await toAudioBlob(audioFile);\n form.append(\"audio\", blob, filename);\n\n const response = await this.client.postMultipart(\"/v1/audio/clone\", form);\n const content = new Uint8Array(await response.arrayBuffer());\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new CloneResponse(content, mediaType, {\n voiceId: response.headers.get(\"X-Voice-ID\"),\n voiceCode: response.headers.get(\"X-Voice-Code\"),\n voiceName: response.headers.get(\"X-Voice-Name\"),\n cloneId: response.headers.get(\"X-Clone-ID\"),\n jobId: response.headers.get(\"X-Job-ID\"),\n });\n }\n\n async listSpeakers(\n options: ListSpeakersOptions = {},\n ): Promise<SpeakerList> {\n const data = await this.client.getJson(\"/v1/speakers\", {\n language: options.language,\n gender: options.gender,\n domain: options.domain,\n });\n return speakerListFromDict(data);\n }\n\n async getSpeaker(speakerId: string): Promise<Speaker> {\n const data = await this.client.getJson(`/v1/speakers/${speakerId}`);\n return speakerFromDict(data);\n }\n\n /** Permanently delete a cloned voice owned by this API key. */\n async deleteVoice(voiceId: string): Promise<void> {\n const normalizedVoiceId = voiceId.trim();\n if (!normalizedVoiceId) {\n throw new Error(\"voiceId is required\");\n }\n await this.client.delete(`/v1/voices/${encodeURIComponent(normalizedVoiceId)}`);\n }\n\n async listLanguages(): Promise<LanguageList> {\n const data = await this.client.getJson(\"/v1/languages\");\n return languageListFromDict(data);\n }\n\n async health(): Promise<HealthStatus> {\n const data = await this.client.getJson(\"/v1/health\");\n return healthStatusFromDict(data);\n }\n}\n\nconst MEDIA_TYPES: Record<string, string> = {\n wav: \"audio/wav\",\n pcm: \"application/octet-stream\",\n flac: \"audio/flac\",\n aac: \"audio/aac\",\n ogg: \"audio/ogg\",\n mp3: \"audio/mpeg\",\n alac: \"audio/alac\",\n};\n\nfunction buildBody(\n kwargs: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(kwargs)) {\n if (v !== undefined && v !== null) out[k] = v;\n }\n return out;\n}\n\nasync function toAudioBlob(\n audioFile: string | Buffer | Uint8Array | Blob,\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof audioFile === \"string\") {\n const bytes = readFileSync(audioFile);\n const filename = basename(audioFile);\n const mime = guessMime(filename);\n return {\n blob: new Blob([bytes], { type: mime }),\n filename,\n };\n }\n if (audioFile instanceof Blob) {\n return {\n blob: audioFile,\n filename: \"audio.wav\",\n };\n }\n // Buffer | Uint8Array\n const bytes =\n audioFile instanceof Uint8Array\n ? audioFile\n : new Uint8Array(audioFile);\n return {\n blob: new Blob([bytes], { type: \"application/octet-stream\" }),\n filename: \"audio.wav\",\n };\n}\n\nfunction guessMime(filename: string): string {\n const lower = filename.toLowerCase();\n if (lower.endsWith(\".wav\")) return \"audio/wav\";\n if (lower.endsWith(\".mp3\")) return \"audio/mpeg\";\n if (lower.endsWith(\".flac\")) return \"audio/flac\";\n if (lower.endsWith(\".ogg\")) return \"audio/ogg\";\n if (lower.endsWith(\".aac\") || lower.endsWith(\".m4a\")) return \"audio/aac\";\n return \"application/octet-stream\";\n}\n","/** A speaker voice identity. */\nexport interface Speaker {\n id: string;\n name: string;\n language: string;\n gender?: string | null;\n domain?: string | null;\n}\n\nexport function speakerFromDict(data: Record<string, unknown>): Speaker {\n return {\n id: String(data.id ?? data.speaker_id ?? \"\"),\n name: String(data.name ?? data.id ?? \"\"),\n language: String(data.language ?? \"\"),\n gender: (data.gender as string | null | undefined) ?? null,\n domain: (data.domain as string | null | undefined) ?? null,\n };\n}\n\n/** List of speakers with metadata. */\nexport interface SpeakerList {\n speakers: Speaker[];\n total: number;\n byLanguage: Record<string, number>;\n [Symbol.iterator](): Iterator<Speaker>;\n length: number;\n}\n\nexport function speakerListFromDict(data: Record<string, unknown>): SpeakerList {\n const raw = (data.speakers as Record<string, unknown>[] | undefined) ?? [];\n const speakers = raw.map(speakerFromDict);\n const total = typeof data.total === \"number\" ? data.total : speakers.length;\n const byLanguage =\n (data.by_language as Record<string, number> | undefined) ?? {};\n return {\n speakers,\n total,\n byLanguage,\n get length() {\n return total;\n },\n [Symbol.iterator]() {\n return speakers[Symbol.iterator]();\n },\n };\n}\n\n/** A supported language. */\nexport interface Language {\n code: string;\n name: string;\n speakerCount: number;\n}\n\nexport function languageFromDict(data: Record<string, unknown>): Language {\n return {\n code: String(data.code ?? \"\"),\n name: String(data.name ?? \"\"),\n speakerCount: Number(data.speaker_count ?? 0),\n };\n}\n\n/** List of supported languages and domains. */\nexport interface LanguageList {\n languages: Language[];\n domains: string[];\n}\n\nexport function languageListFromDict(data: Record<string, unknown>): LanguageList {\n const rawLangs = data.languages;\n let languages: Language[];\n\n if (rawLangs && typeof rawLangs === \"object\" && !Array.isArray(rawLangs)) {\n languages = Object.entries(rawLangs as Record<string, unknown>).map(\n ([code, value]) => {\n if (value && typeof value === \"object\") {\n const v = value as Record<string, unknown>;\n return {\n code,\n name: String(v.name ?? code),\n speakerCount: Number(v.speaker_count ?? 0),\n };\n }\n return { code, name: String(value), speakerCount: 0 };\n },\n );\n } else {\n const arr = (rawLangs as Record<string, unknown>[] | undefined) ?? [];\n languages = arr.map(languageFromDict);\n }\n\n return {\n languages,\n domains: (data.domains as string[] | undefined) ?? [],\n };\n}\n\n/** API health check result. */\nexport interface HealthStatus {\n status: string;\n engineReady: boolean;\n codecReady: boolean;\n totalSpeakers: number;\n languages: string[];\n voiceCloningAvailable: boolean;\n speakerProjectionLoaded: boolean;\n}\n\nexport function healthStatusFromDict(data: Record<string, unknown>): HealthStatus {\n const supported = data.supported_languages;\n let languages: string[] = [];\n if (Array.isArray(data.languages)) {\n languages = data.languages as string[];\n } else if (supported && typeof supported === \"object\") {\n languages = Object.keys(supported as Record<string, unknown>);\n }\n\n return {\n status: String(data.status ?? \"unknown\"),\n engineReady: Boolean(data.engine_ready ?? data.tts_initialized ?? false),\n codecReady: Boolean(data.codec_ready ?? false),\n totalSpeakers: Number(data.total_speakers ?? data.speakers_loaded ?? 0),\n languages,\n voiceCloningAvailable: Boolean(data.voice_cloning_available ?? false),\n speakerProjectionLoaded: Boolean(data.speaker_projection_loaded ?? false),\n };\n}\n\n/** An available TTS model. */\nexport interface Model {\n id: string;\n object: string;\n ownedBy: string;\n}\n\nexport function modelFromDict(data: Record<string, unknown>): Model {\n return {\n id: String(data.id ?? \"\"),\n object: String(data.object ?? \"model\"),\n ownedBy: String(data.owned_by ?? \"9jalingo\"),\n };\n}\n\n/** List of available models. */\nexport interface ModelList {\n models: Model[];\n object: string;\n [Symbol.iterator](): Iterator<Model>;\n length: number;\n}\n\nexport function modelListFromDict(data: Record<string, unknown>): ModelList {\n const raw = (data.data as Record<string, unknown>[] | undefined) ?? [];\n const models = raw.map(modelFromDict);\n return {\n models,\n object: String(data.object ?? \"list\"),\n get length() {\n return models.length;\n },\n [Symbol.iterator]() {\n return models[Symbol.iterator]();\n },\n };\n}\n\n/** Root API information returned by GET /. */\nexport interface APIInfo {\n name: string;\n version: string;\n description: string;\n endpoints: Record<string, string>;\n}\n\nexport function apiInfoFromDict(data: Record<string, unknown>): APIInfo {\n return {\n name: String(data.name ?? \"\"),\n version: String(data.version ?? \"\"),\n description: String(data.description ?? \"\"),\n endpoints: (data.endpoints as Record<string, string> | undefined) ?? {},\n };\n}\n\n/** Service metadata returned by GET /v1. */\nexport interface ServiceInfo {\n object: string;\n name: string;\n modelsUrl: string;\n speechUrl: string;\n speechStreamUrl: string;\n}\n\nexport function serviceInfoFromDict(data: Record<string, unknown>): ServiceInfo {\n return {\n object: String(data.object ?? \"service\"),\n name: String(data.name ?? \"\"),\n modelsUrl: String(data.models_url ?? \"\"),\n speechUrl: String(data.speech_url ?? \"\"),\n speechStreamUrl: String(data.speech_stream_url ?? \"\"),\n };\n}\n\nexport type ResponseFormat =\n | \"wav\"\n | \"pcm\"\n | \"flac\"\n | \"aac\"\n | \"ogg\"\n | \"mp3\"\n | \"alac\";\n","const LANG_ALIASES: Record<string, string> = {\n yo: \"yo\",\n yor: \"yo\",\n yoruba: \"yo\",\n ig: \"ig\",\n ibo: \"ig\",\n igbo: \"ig\",\n ha: \"ha\",\n hau: \"ha\",\n hausa: \"ha\",\n pcm: \"pcm\",\n pidgin: \"pcm\",\n};\n\nexport function normalizeLang(code: string): string | null {\n return LANG_ALIASES[code.trim().toLowerCase()] ?? null;\n}\n\n/**\n * Resolve speech API fields: voice/speaker + lang/language.\n * Returns [speakerId, langCode]. Bare language codes must be passed via lang/language.\n */\nexport function resolveTtsVoiceAndLang(options: {\n voice?: string | null;\n speaker?: string | null;\n lang?: string | null;\n language?: string | null;\n}): [string | null, string | null] {\n let resolvedLang: string | null = null;\n\n if (options.lang != null) {\n resolvedLang = normalizeLang(options.lang);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.lang}'. Use ha, ig, yo, or pcm.`,\n );\n }\n } else if (options.language != null) {\n resolvedLang = normalizeLang(options.language);\n if (resolvedLang == null) {\n throw new Error(\n `Unsupported language '${options.language}'. Use ha, ig, yo, or pcm.`,\n );\n }\n }\n\n // speaker takes precedence over voice\n const candidate =\n options.speaker != null ? options.speaker : options.voice;\n if (candidate == null || !String(candidate).trim()) {\n return [null, resolvedLang];\n }\n\n const resolved = String(candidate).trim();\n const normalized = resolved.toLowerCase();\n if (normalized in LANG_ALIASES) {\n const langCode = LANG_ALIASES[normalized];\n throw new Error(\n `'${resolved}' is a language code, not a speaker ID. ` +\n `Pass speaker/voice='ada_pcm' (or another speaker ID) and ` +\n `lang='${langCode}' for the language. ` +\n `Browse speakers with: client.tts.listSpeakers({ language: '${langCode}' })`,\n );\n }\n\n if (resolvedLang == null && resolved.includes(\"_\")) {\n const suffix = resolved.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n\n return [resolved, resolvedLang];\n}\n\n/**\n * Resolve clone form fields.\n * voice may be a language code (\"pcm\") or a speaker ID with a language suffix.\n */\nexport function resolveCloneLangAndSpeaker(\n voice: string,\n options: { lang?: string | null; speaker?: string | null } = {},\n): [string, string | null] {\n let resolvedLang =\n options.lang != null ? normalizeLang(options.lang) : null;\n let resolvedSpeaker = options.speaker?.trim() || null;\n\n const voiceStr = (voice || \"\").trim();\n const voiceNorm = voiceStr.toLowerCase();\n\n if (voiceNorm in LANG_ALIASES) {\n if (resolvedLang == null) {\n resolvedLang = LANG_ALIASES[voiceNorm];\n }\n } else if (voiceStr.includes(\"_\")) {\n if (resolvedSpeaker == null) {\n resolvedSpeaker = voiceStr;\n }\n if (resolvedLang == null) {\n const suffix = voiceStr.split(\"_\").pop()!;\n resolvedLang = normalizeLang(suffix);\n }\n } else if (voiceStr && resolvedLang == null) {\n throw new Error(\n `'${voice}' is not a language code (ha/ig/yo/pcm) or a speaker ID ` +\n `with a language suffix (e.g. 'daniel_pcm'). ` +\n `Pass voice='pcm' or lang='pcm'.`,\n );\n }\n\n if (!resolvedLang) {\n throw new Error(\n \"Clone requires a language. Pass voice='pcm' (or 'ha'/'ig'/'yo'), \" +\n \"lang='pcm', or a speaker ID ending in _pcm/_ha/_ig/_yo.\",\n );\n }\n\n return [resolvedLang, resolvedSpeaker];\n}\n","import { BaseClient } from \"./client.js\";\r\n\r\nexport type TranscribeOptions = {\r\n language?: string;\r\n durationSeconds?: number;\r\n};\r\n\r\nexport type Transcription = {\r\n text: string;\r\n language?: string;\r\n duration?: number;\r\n model?: string;\r\n};\r\n\r\nexport class STT {\r\n constructor(private readonly client: BaseClient) {}\r\n\r\n async transcribe(fileUrl: string, options: TranscribeOptions = {}): Promise<Transcription> {\r\n const body: Record<string, unknown> = { file_url: fileUrl };\r\n if (options.language) body.language = options.language;\r\n if (options.durationSeconds != null) body.duration_seconds = options.durationSeconds;\r\n let response: Record<string, unknown> = await this.client.postJson(\"/v1/audio/transcriptions\", body);\r\n while (typeof response.result === \"object\" && response.result !== null && !Array.isArray(response.result)) {\r\n response = response.result as Record<string, unknown>;\r\n }\r\n return response as Transcription;\r\n }\r\n}","/**\n * 9jaLingo Node.js SDK\n *\n * Official SDK for the 9jaLingo API — Nigerian language Text-to-Speech.\n *\n * @example\n * ```ts\n * import { NaijaLingo } from \"naijalingo\";\n *\n * const client = new NaijaLingo(); // uses NAIJALINGO_API_KEY\n * const audio = await client.tts.generate(\"Bawo ni!\", {\n * voice: \"adeola_yo\",\n * lang: \"yo\",\n * });\n * await audio.save(\"greeting.wav\");\n * ```\n */\n\nimport { BaseClient, type ClientOptions } from \"./client.js\";\nimport { TTS } from \"./tts.js\";\nimport { STT } from \"./stt.js\";\nimport {\n apiInfoFromDict,\n modelListFromDict,\n serviceInfoFromDict,\n type APIInfo,\n type ModelList,\n type ServiceInfo,\n} from \"./types.js\";\n\nexport const VERSION = \"0.1.2\";\n\nexport {\n AuthenticationError,\n ConnectionError,\n InferenceCapacityError,\n NaijaLingoError,\n NotFoundError,\n RateLimitError,\n ServerError,\n} from \"./errors.js\";\n\nexport {\n AudioResponse,\n AudioStream,\n CloneResponse,\n TTS,\n type CloneOptions,\n type GenerateOptions,\n type ListSpeakersOptions,\n type StreamOptions,\n} from \"./tts.js\";\nexport { STT, type TranscribeOptions, type Transcription } from \"./stt.js\";\n\nexport type {\n APIInfo,\n HealthStatus,\n Language,\n LanguageList,\n Model,\n ModelList,\n ResponseFormat,\n ServiceInfo,\n Speaker,\n SpeakerList,\n} from \"./types.js\";\n\nexport type { ClientOptions };\n\nexport class NaijaLingo {\n private readonly _client: BaseClient;\n readonly tts: TTS;\n readonly stt: STT;\n\n constructor(options: ClientOptions = {}) {\n this._client = new BaseClient(options);\n this.tts = new TTS(this._client);\n this.stt = new STT(this._client);\n }\n\n /** List available TTS models. GET /v1/models */\n async listModels(): Promise<ModelList> {\n const data = await this._client.getJson(\"/v1/models\");\n return modelListFromDict(data);\n }\n\n /** Root API information. GET / */\n async apiInfo(): Promise<APIInfo> {\n const data = await this._client.getJson(\"/\");\n return apiInfoFromDict(data);\n }\n\n /** v1 service metadata. GET /v1 */\n async serviceInfo(): Promise<ServiceInfo> {\n const data = await this._client.getJson(\"/v1\");\n return serviceInfoFromDict(data);\n }\n}\n"],"mappings":"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","names":["bytes"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "naijalingo",
3
- "version": "0.1.2",
4
- "description": "Official Node.js SDK for 9jaLingo — AI-powered Text-to-Speech and Voice Cloning for Nigerian languages including Hausa, Igbo, Yoruba, and Nigerian Pidgin with 240+ speaker voices.",
3
+ "version": "0.2.0",
4
+ "description": "Official Node.js SDK for 9jaLingo — Text-to-Speech, Speech-to-Text, and Voice Cloning for Nigerian languages.",
5
5
  "license": "MIT",
6
6
  "author": "9jaLingo <support@9jalingo.org>",
7
7
  "homepage": "https://www.9jalingo.org",
@@ -15,6 +15,8 @@
15
15
  },
16
16
  "keywords": [
17
17
  "tts",
18
+ "stt",
19
+ "speech-to-text",
18
20
  "text-to-speech",
19
21
  "speech-synthesis",
20
22
  "voice-cloning",