naijalingo 0.1.1 → 0.1.2

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
@@ -145,6 +145,14 @@ const audio = await client.tts.clone(
145
145
  );
146
146
  await audio.save("cloned.mp3");
147
147
 
148
+ // The backend persists the clone and returns a reusable voice ID.
149
+ const greeting = await client.tts.generate("Nnoo, kedu ka i mere?", {
150
+ voice: audio.voiceId,
151
+ });
152
+ await greeting.save("cloned_voice_greeting.wav");
153
+
154
+ // `speaker: audio.voiceId` is equivalent.
155
+
148
156
  // From a Buffer
149
157
  import { readFileSync } from "node:fs";
150
158
  const buf = readFileSync("reference.wav");
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  AudioResponse: () => AudioResponse,
24
24
  AudioStream: () => AudioStream,
25
25
  AuthenticationError: () => AuthenticationError,
26
+ CloneResponse: () => CloneResponse,
26
27
  ConnectionError: () => ConnectionError,
27
28
  InferenceCapacityError: () => InferenceCapacityError,
28
29
  NaijaLingo: () => NaijaLingo,
@@ -97,7 +98,7 @@ var BaseClient = class {
97
98
  this.baseUrl = base.replace(/\/+$/, "");
98
99
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
99
100
  this.defaultHeaders = {
100
- "User-Agent": "naijalingo-js/0.1.1"
101
+ "User-Agent": "naijalingo-js/0.1.2"
101
102
  };
102
103
  if (this.apiKey) {
103
104
  this.defaultHeaders["X-API-Key"] = this.apiKey;
@@ -253,12 +254,11 @@ var BaseClient = class {
253
254
  );
254
255
  }
255
256
  async postMultipart(path, form, timeoutMs) {
256
- const resp = await this.request("POST", path, {
257
+ return this.request("POST", path, {
257
258
  body: form,
258
259
  timeoutMs: timeoutMs ?? Math.max(this.timeout, 6e5)
259
260
  // Let fetch set multipart boundary — do not set Content-Type
260
261
  });
261
- return new Uint8Array(await resp.arrayBuffer());
262
262
  }
263
263
  };
264
264
  function sleep(ms) {
@@ -491,6 +491,21 @@ var AudioResponse = class {
491
491
  return `AudioResponse(${this.content.length} bytes, ${this.mediaType})`;
492
492
  }
493
493
  };
494
+ var CloneResponse = class extends AudioResponse {
495
+ voiceId;
496
+ voiceCode;
497
+ voiceName;
498
+ cloneId;
499
+ jobId;
500
+ constructor(content, mediaType, metadata) {
501
+ super(content, mediaType);
502
+ this.voiceId = metadata.voiceId;
503
+ this.voiceCode = metadata.voiceCode;
504
+ this.voiceName = metadata.voiceName;
505
+ this.cloneId = metadata.cloneId;
506
+ this.jobId = metadata.jobId;
507
+ }
508
+ };
494
509
  var AudioStream = class {
495
510
  chunks;
496
511
  collected = null;
@@ -603,9 +618,16 @@ var TTS = class {
603
618
  }
604
619
  const { blob, filename } = await toAudioBlob(audioFile);
605
620
  form.append("audio", blob, filename);
606
- const content = await this.client.postMultipart("/v1/audio/clone", form);
621
+ const response = await this.client.postMultipart("/v1/audio/clone", form);
622
+ const content = new Uint8Array(await response.arrayBuffer());
607
623
  const mediaType = MEDIA_TYPES[responseFormat] ?? "application/octet-stream";
608
- return new AudioResponse(content, mediaType);
624
+ return new CloneResponse(content, mediaType, {
625
+ voiceId: response.headers.get("X-Voice-ID"),
626
+ voiceCode: response.headers.get("X-Voice-Code"),
627
+ voiceName: response.headers.get("X-Voice-Name"),
628
+ cloneId: response.headers.get("X-Clone-ID"),
629
+ jobId: response.headers.get("X-Job-ID")
630
+ });
609
631
  }
610
632
  async listSpeakers(options = {}) {
611
633
  const data = await this.client.getJson("/v1/speakers", {
@@ -677,7 +699,7 @@ function guessMime(filename) {
677
699
  }
678
700
 
679
701
  // src/index.ts
680
- var VERSION = "0.1.1";
702
+ var VERSION = "0.1.2";
681
703
  var NaijaLingo = class {
682
704
  _client;
683
705
  tts;
@@ -706,6 +728,7 @@ var NaijaLingo = class {
706
728
  AudioResponse,
707
729
  AudioStream,
708
730
  AuthenticationError,
731
+ CloneResponse,
709
732
  ConnectionError,
710
733
  InferenceCapacityError,
711
734
  NaijaLingo,
@@ -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.1\";\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 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.1\",\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<Uint8Array> {\n const resp = await 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 return new Uint8Array(await resp.arrayBuffer());\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/** 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<AudioResponse> {\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 content = await this.client.postMultipart(\"/v1/audio/clone\", form);\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new AudioResponse(content, mediaType);\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"],"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"]}
package/dist/index.d.cts CHANGED
@@ -15,7 +15,7 @@ declare class BaseClient {
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
17
  postStream(path: string, body: Record<string, unknown>): AsyncGenerator<Uint8Array, void, unknown>;
18
- postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<Uint8Array>;
18
+ postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<Response>;
19
19
  }
20
20
 
21
21
  /** A speaker voice identity. */
@@ -126,6 +126,21 @@ declare class AudioResponse {
126
126
  get length(): number;
127
127
  toString(): string;
128
128
  }
129
+ /** Audio and reusable voice metadata returned from a clone request. */
130
+ declare class CloneResponse extends AudioResponse {
131
+ readonly voiceId: string | null;
132
+ readonly voiceCode: string | null;
133
+ readonly voiceName: string | null;
134
+ readonly cloneId: string | null;
135
+ readonly jobId: string | null;
136
+ constructor(content: Uint8Array, mediaType: string, metadata: {
137
+ voiceId: string | null;
138
+ voiceCode: string | null;
139
+ voiceName: string | null;
140
+ cloneId: string | null;
141
+ jobId: string | null;
142
+ });
143
+ }
129
144
  /** Streaming audio response — yields byte chunks as they arrive. */
130
145
  declare class AudioStream implements AsyncIterable<Uint8Array> {
131
146
  private readonly chunks;
@@ -146,7 +161,7 @@ declare class TTS {
146
161
  * Generate speech using a cloned voice from a reference audio file.
147
162
  * `audioFile` may be a filesystem path, Buffer, Uint8Array, Blob, or File.
148
163
  */
149
- clone(text: string, audioFile: string | Buffer | Uint8Array | Blob, options?: CloneOptions): Promise<AudioResponse>;
164
+ clone(text: string, audioFile: string | Buffer | Uint8Array | Blob, options?: CloneOptions): Promise<CloneResponse>;
150
165
  listSpeakers(options?: ListSpeakersOptions): Promise<SpeakerList>;
151
166
  getSpeaker(speakerId: string): Promise<Speaker>;
152
167
  listLanguages(): Promise<LanguageList>;
@@ -202,7 +217,7 @@ declare class ConnectionError extends NaijaLingoError {
202
217
  * ```
203
218
  */
204
219
 
205
- declare const VERSION = "0.1.1";
220
+ declare const VERSION = "0.1.2";
206
221
 
207
222
  declare class NaijaLingo {
208
223
  private readonly _client;
@@ -216,4 +231,4 @@ declare class NaijaLingo {
216
231
  serviceInfo(): Promise<ServiceInfo>;
217
232
  }
218
233
 
219
- export { type APIInfo, AudioResponse, AudioStream, AuthenticationError, type ClientOptions, type CloneOptions, 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 };
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 };
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ declare class BaseClient {
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
17
  postStream(path: string, body: Record<string, unknown>): AsyncGenerator<Uint8Array, void, unknown>;
18
- postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<Uint8Array>;
18
+ postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<Response>;
19
19
  }
20
20
 
21
21
  /** A speaker voice identity. */
@@ -126,6 +126,21 @@ declare class AudioResponse {
126
126
  get length(): number;
127
127
  toString(): string;
128
128
  }
129
+ /** Audio and reusable voice metadata returned from a clone request. */
130
+ declare class CloneResponse extends AudioResponse {
131
+ readonly voiceId: string | null;
132
+ readonly voiceCode: string | null;
133
+ readonly voiceName: string | null;
134
+ readonly cloneId: string | null;
135
+ readonly jobId: string | null;
136
+ constructor(content: Uint8Array, mediaType: string, metadata: {
137
+ voiceId: string | null;
138
+ voiceCode: string | null;
139
+ voiceName: string | null;
140
+ cloneId: string | null;
141
+ jobId: string | null;
142
+ });
143
+ }
129
144
  /** Streaming audio response — yields byte chunks as they arrive. */
130
145
  declare class AudioStream implements AsyncIterable<Uint8Array> {
131
146
  private readonly chunks;
@@ -146,7 +161,7 @@ declare class TTS {
146
161
  * Generate speech using a cloned voice from a reference audio file.
147
162
  * `audioFile` may be a filesystem path, Buffer, Uint8Array, Blob, or File.
148
163
  */
149
- clone(text: string, audioFile: string | Buffer | Uint8Array | Blob, options?: CloneOptions): Promise<AudioResponse>;
164
+ clone(text: string, audioFile: string | Buffer | Uint8Array | Blob, options?: CloneOptions): Promise<CloneResponse>;
150
165
  listSpeakers(options?: ListSpeakersOptions): Promise<SpeakerList>;
151
166
  getSpeaker(speakerId: string): Promise<Speaker>;
152
167
  listLanguages(): Promise<LanguageList>;
@@ -202,7 +217,7 @@ declare class ConnectionError extends NaijaLingoError {
202
217
  * ```
203
218
  */
204
219
 
205
- declare const VERSION = "0.1.1";
220
+ declare const VERSION = "0.1.2";
206
221
 
207
222
  declare class NaijaLingo {
208
223
  private readonly _client;
@@ -216,4 +231,4 @@ declare class NaijaLingo {
216
231
  serviceInfo(): Promise<ServiceInfo>;
217
232
  }
218
233
 
219
- export { type APIInfo, AudioResponse, AudioStream, AuthenticationError, type ClientOptions, type CloneOptions, 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 };
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 };
package/dist/index.js CHANGED
@@ -60,7 +60,7 @@ var BaseClient = class {
60
60
  this.baseUrl = base.replace(/\/+$/, "");
61
61
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
62
62
  this.defaultHeaders = {
63
- "User-Agent": "naijalingo-js/0.1.1"
63
+ "User-Agent": "naijalingo-js/0.1.2"
64
64
  };
65
65
  if (this.apiKey) {
66
66
  this.defaultHeaders["X-API-Key"] = this.apiKey;
@@ -216,12 +216,11 @@ var BaseClient = class {
216
216
  );
217
217
  }
218
218
  async postMultipart(path, form, timeoutMs) {
219
- const resp = await this.request("POST", path, {
219
+ return this.request("POST", path, {
220
220
  body: form,
221
221
  timeoutMs: timeoutMs ?? Math.max(this.timeout, 6e5)
222
222
  // Let fetch set multipart boundary — do not set Content-Type
223
223
  });
224
- return new Uint8Array(await resp.arrayBuffer());
225
224
  }
226
225
  };
227
226
  function sleep(ms) {
@@ -454,6 +453,21 @@ var AudioResponse = class {
454
453
  return `AudioResponse(${this.content.length} bytes, ${this.mediaType})`;
455
454
  }
456
455
  };
456
+ var CloneResponse = class extends AudioResponse {
457
+ voiceId;
458
+ voiceCode;
459
+ voiceName;
460
+ cloneId;
461
+ jobId;
462
+ constructor(content, mediaType, metadata) {
463
+ super(content, mediaType);
464
+ this.voiceId = metadata.voiceId;
465
+ this.voiceCode = metadata.voiceCode;
466
+ this.voiceName = metadata.voiceName;
467
+ this.cloneId = metadata.cloneId;
468
+ this.jobId = metadata.jobId;
469
+ }
470
+ };
457
471
  var AudioStream = class {
458
472
  chunks;
459
473
  collected = null;
@@ -566,9 +580,16 @@ var TTS = class {
566
580
  }
567
581
  const { blob, filename } = await toAudioBlob(audioFile);
568
582
  form.append("audio", blob, filename);
569
- const content = await this.client.postMultipart("/v1/audio/clone", form);
583
+ const response = await this.client.postMultipart("/v1/audio/clone", form);
584
+ const content = new Uint8Array(await response.arrayBuffer());
570
585
  const mediaType = MEDIA_TYPES[responseFormat] ?? "application/octet-stream";
571
- return new AudioResponse(content, mediaType);
586
+ return new CloneResponse(content, mediaType, {
587
+ voiceId: response.headers.get("X-Voice-ID"),
588
+ voiceCode: response.headers.get("X-Voice-Code"),
589
+ voiceName: response.headers.get("X-Voice-Name"),
590
+ cloneId: response.headers.get("X-Clone-ID"),
591
+ jobId: response.headers.get("X-Job-ID")
592
+ });
572
593
  }
573
594
  async listSpeakers(options = {}) {
574
595
  const data = await this.client.getJson("/v1/speakers", {
@@ -640,7 +661,7 @@ function guessMime(filename) {
640
661
  }
641
662
 
642
663
  // src/index.ts
643
- var VERSION = "0.1.1";
664
+ var VERSION = "0.1.2";
644
665
  var NaijaLingo = class {
645
666
  _client;
646
667
  tts;
@@ -668,6 +689,7 @@ export {
668
689
  AudioResponse,
669
690
  AudioStream,
670
691
  AuthenticationError,
692
+ CloneResponse,
671
693
  ConnectionError,
672
694
  InferenceCapacityError,
673
695
  NaijaLingo,
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.1\",\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<Uint8Array> {\n const resp = await 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 return new Uint8Array(await resp.arrayBuffer());\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/** 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<AudioResponse> {\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 content = await this.client.postMultipart(\"/v1/audio/clone\", form);\n const mediaType = MEDIA_TYPES[responseFormat] ?? \"application/octet-stream\";\n return new AudioResponse(content, mediaType);\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.1\";\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 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/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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naijalingo",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
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.",
5
5
  "license": "MIT",
6
6
  "author": "9jaLingo <support@9jalingo.org>",
@@ -52,6 +52,7 @@
52
52
  "scripts": {
53
53
  "build": "tsup",
54
54
  "typecheck": "tsc --noEmit",
55
+ "test": "npm run build && node --test test/**/*.test.mjs",
55
56
  "prepublishOnly": "npm run build"
56
57
  },
57
58
  "devDependencies": {