sonilo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sonilo AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # sonilo
2
+
3
+ Official TypeScript/JavaScript client for the [Sonilo](https://sonilo.com) API.
4
+ Works in Node.js ≥ 18 and modern browsers. Zero runtime dependencies.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install sonilo
10
+ ```
11
+
12
+ ## Quickstart
13
+
14
+ ```ts
15
+ import { SoniloClient } from "sonilo";
16
+
17
+ const sonilo = new SoniloClient(); // reads SONILO_API_KEY
18
+
19
+ const track = await sonilo.textToMusic.generate({
20
+ prompt: "cinematic orchestral score",
21
+ duration: 60,
22
+ });
23
+ // track.audio is a Uint8Array of MP3 bytes
24
+ ```
25
+
26
+ ## Video to music
27
+
28
+ ```ts
29
+ // Node: file path, browser: File/Blob from an <input type="file">
30
+ const track = await sonilo.videoToMusic.generate({
31
+ video: "./my_video.mp4",
32
+ prompt: "upbeat, energetic",
33
+ });
34
+
35
+ // Or point at a hosted video
36
+ await sonilo.videoToMusic.generate({ videoUrl: "https://example.com/clip.mp4" });
37
+ ```
38
+
39
+ ## Streaming
40
+
41
+ ```ts
42
+ import { SoniloClient, isAudioChunkEvent } from "sonilo";
43
+
44
+ for await (const event of sonilo.textToMusic.stream({ prompt: "lofi", duration: 30 })) {
45
+ if (isAudioChunkEvent(event)) {
46
+ // event.data is a Uint8Array — feed it to your player as it arrives
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Segments
52
+
53
+ Shape the composition with start-only contiguous segments (each ends where
54
+ the next begins):
55
+
56
+ ```ts
57
+ await sonilo.textToMusic.generate({
58
+ prompt: "epic trailer",
59
+ duration: 60,
60
+ segments: [
61
+ { start: 0, prompt: "soft intro", label: "intro" },
62
+ { start: 20, prompt: "building tension", label: "verse" },
63
+ { start: 40, prompt: "full orchestra", label: "chorus" },
64
+ ],
65
+ });
66
+ ```
67
+
68
+ ## Account
69
+
70
+ ```ts
71
+ const services = await sonilo.account.services();
72
+ const usage = await sonilo.account.usage({ days: 7 });
73
+ ```
74
+
75
+ ## Errors
76
+
77
+ All errors extend `SoniloError`: `AuthenticationError` (401),
78
+ `PaymentRequiredError` (402), `RateLimitError` (429, `.retryAfter`),
79
+ `BadRequestError` (400/413/422, `.detail`), `APIError` (anything else),
80
+ and `GenerationError` for failures mid-stream.
81
+
82
+ ## License
83
+
84
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ APIError: () => APIError,
24
+ AuthenticationError: () => AuthenticationError,
25
+ BadRequestError: () => BadRequestError,
26
+ GenerationError: () => GenerationError,
27
+ PaymentRequiredError: () => PaymentRequiredError,
28
+ RateLimitError: () => RateLimitError,
29
+ SoniloClient: () => SoniloClient,
30
+ SoniloError: () => SoniloError,
31
+ VERSION: () => VERSION,
32
+ isAudioChunkEvent: () => isAudioChunkEvent,
33
+ isErrorEvent: () => isErrorEvent
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/errors.ts
38
+ var SoniloError = class extends Error {
39
+ constructor(message) {
40
+ super(message);
41
+ this.name = new.target.name;
42
+ }
43
+ };
44
+ var APIError = class extends SoniloError {
45
+ constructor(message, status, body) {
46
+ super(message);
47
+ this.status = status;
48
+ this.body = body;
49
+ }
50
+ };
51
+ var AuthenticationError = class extends APIError {
52
+ };
53
+ var PaymentRequiredError = class extends APIError {
54
+ };
55
+ var BadRequestError = class extends APIError {
56
+ get detail() {
57
+ const body = this.body;
58
+ return typeof body?.detail === "string" ? body.detail : void 0;
59
+ }
60
+ };
61
+ var RateLimitError = class extends APIError {
62
+ constructor(message, status, body, retryAfter) {
63
+ super(message, status, body);
64
+ this.retryAfter = retryAfter;
65
+ }
66
+ };
67
+ var GenerationError = class extends SoniloError {
68
+ constructor(message, code) {
69
+ super(message);
70
+ this.code = code;
71
+ }
72
+ };
73
+ async function errorFromResponse(res) {
74
+ const text = await res.text().catch(() => "");
75
+ let body = text;
76
+ try {
77
+ body = JSON.parse(text);
78
+ } catch {
79
+ }
80
+ const detail = typeof body?.detail === "string" ? body.detail : res.statusText || "request failed";
81
+ const message = `HTTP ${res.status}: ${detail}`;
82
+ switch (res.status) {
83
+ case 401:
84
+ return new AuthenticationError(message, res.status, body);
85
+ case 402:
86
+ return new PaymentRequiredError(message, res.status, body);
87
+ case 429: {
88
+ const ra = res.headers.get("retry-after");
89
+ const retryAfter = ra !== null && ra !== "" && !Number.isNaN(Number(ra)) ? Number(ra) : void 0;
90
+ return new RateLimitError(message, res.status, body, retryAfter);
91
+ }
92
+ case 400:
93
+ case 413:
94
+ case 422:
95
+ return new BadRequestError(message, res.status, body);
96
+ default:
97
+ return new APIError(message, res.status, body);
98
+ }
99
+ }
100
+
101
+ // src/resources/account.ts
102
+ var Account = class {
103
+ constructor(client) {
104
+ this.client = client;
105
+ }
106
+ async services() {
107
+ const res = await this.client.request("/v1/account/services");
108
+ return await res.json();
109
+ }
110
+ async usage(params = {}) {
111
+ const query = params.days !== void 0 ? `?days=${params.days}` : "";
112
+ const res = await this.client.request(`/v1/account/usage${query}`);
113
+ return await res.json();
114
+ }
115
+ };
116
+
117
+ // src/streaming.ts
118
+ function decodeBase64(b64) {
119
+ const bin = atob(b64);
120
+ const out = new Uint8Array(bin.length);
121
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
122
+ return out;
123
+ }
124
+ function toEvent(line) {
125
+ const raw = JSON.parse(line);
126
+ if (raw.type === "audio_chunk" && typeof raw.data === "string") {
127
+ return { ...raw, type: "audio_chunk", data: decodeBase64(raw.data) };
128
+ }
129
+ return raw;
130
+ }
131
+ async function* parseNdjson(body) {
132
+ const reader = body.getReader();
133
+ const decoder = new TextDecoder();
134
+ let buffer = "";
135
+ try {
136
+ for (; ; ) {
137
+ const { done, value } = await reader.read();
138
+ if (done) break;
139
+ buffer += decoder.decode(value, { stream: true });
140
+ let nl;
141
+ while ((nl = buffer.indexOf("\n")) !== -1) {
142
+ const line = buffer.slice(0, nl).trim();
143
+ buffer = buffer.slice(nl + 1);
144
+ if (line) yield toEvent(line);
145
+ }
146
+ }
147
+ buffer += decoder.decode();
148
+ const tail = buffer.trim();
149
+ if (tail) yield toEvent(tail);
150
+ } finally {
151
+ await reader.cancel().catch(() => {
152
+ });
153
+ }
154
+ }
155
+ async function collectTrack(events) {
156
+ const chunks = [];
157
+ let title;
158
+ let cost;
159
+ let sawComplete = false;
160
+ for await (const ev of events) {
161
+ if (ev.type === "audio_chunk" && ev.data instanceof Uint8Array) {
162
+ chunks.push(ev.data);
163
+ } else if (ev.type === "title" && typeof ev.title === "string") {
164
+ title = ev.title;
165
+ } else if (ev.type === "cost") {
166
+ const { type: _type, ...rest } = ev;
167
+ cost = rest;
168
+ } else if (ev.type === "error") {
169
+ const message = typeof ev.message === "string" ? ev.message : "generation failed";
170
+ const code = typeof ev.code === "string" ? ev.code : void 0;
171
+ throw new GenerationError(message, code);
172
+ } else if (ev.type === "complete") {
173
+ sawComplete = true;
174
+ }
175
+ }
176
+ if (!sawComplete) {
177
+ throw new GenerationError("stream ended before a 'complete' event (truncated response)");
178
+ }
179
+ const total = chunks.reduce((n, c) => n + c.length, 0);
180
+ const audio = new Uint8Array(total);
181
+ let offset = 0;
182
+ for (const c of chunks) {
183
+ audio.set(c, offset);
184
+ offset += c.length;
185
+ }
186
+ return { audio, title, cost };
187
+ }
188
+
189
+ // src/resources/textToMusic.ts
190
+ var TextToMusic = class {
191
+ constructor(client) {
192
+ this.client = client;
193
+ }
194
+ /** Stream raw generation events (audio chunks pre-decoded to bytes). */
195
+ async *stream(params) {
196
+ const form = new FormData();
197
+ form.set("prompt", params.prompt);
198
+ form.set("duration", String(params.duration));
199
+ if (params.segments !== void 0) {
200
+ form.set("segments", JSON.stringify(params.segments));
201
+ }
202
+ const res = await this.client.request("/v1/text-to-music", {
203
+ method: "POST",
204
+ body: form
205
+ });
206
+ if (!res.body) throw new SoniloError("Response has no body");
207
+ yield* parseNdjson(res.body);
208
+ }
209
+ /** Generate and buffer the whole track; throws GenerationError on stream errors. */
210
+ generate(params) {
211
+ return collectTrack(this.stream(params));
212
+ }
213
+ };
214
+
215
+ // src/upload.ts
216
+ var DEFAULT_FILENAME = "video.mp4";
217
+ async function toUploadBlob(video) {
218
+ if (typeof video === "string") {
219
+ const isNode = typeof process !== "undefined" && Boolean(process.versions?.node);
220
+ if (!isNode) {
221
+ throw new SoniloError(
222
+ "File paths are only supported in Node.js; pass a File or Blob in the browser"
223
+ );
224
+ }
225
+ const fsModule = "node:fs/promises";
226
+ const { readFile } = await import(
227
+ /* webpackIgnore: true */
228
+ /* @vite-ignore */
229
+ fsModule
230
+ );
231
+ const data = await readFile(video);
232
+ const filename = video.split(/[\\/]/).pop() || DEFAULT_FILENAME;
233
+ return { blob: new Blob([data]), filename };
234
+ }
235
+ if (typeof File !== "undefined" && video instanceof File) {
236
+ return { blob: video, filename: video.name || DEFAULT_FILENAME };
237
+ }
238
+ if (video instanceof Blob) {
239
+ return { blob: video, filename: DEFAULT_FILENAME };
240
+ }
241
+ if (video instanceof Uint8Array) {
242
+ return { blob: new Blob([video]), filename: DEFAULT_FILENAME };
243
+ }
244
+ if (video instanceof ArrayBuffer) {
245
+ return { blob: new Blob([video]), filename: DEFAULT_FILENAME };
246
+ }
247
+ if (video instanceof ReadableStream) {
248
+ return { blob: await new Response(video).blob(), filename: DEFAULT_FILENAME };
249
+ }
250
+ throw new SoniloError("Unsupported video input type");
251
+ }
252
+
253
+ // src/resources/videoToMusic.ts
254
+ var VideoToMusic = class {
255
+ constructor(client) {
256
+ this.client = client;
257
+ }
258
+ async *stream(params) {
259
+ if (params.video === void 0 === (params.videoUrl === void 0)) {
260
+ throw new SoniloError("Provide exactly one of video or videoUrl");
261
+ }
262
+ const form = new FormData();
263
+ if (params.video !== void 0) {
264
+ const { blob, filename } = await toUploadBlob(params.video);
265
+ form.set("video", blob, filename);
266
+ } else {
267
+ form.set("video_url", params.videoUrl);
268
+ }
269
+ if (params.prompt !== void 0) form.set("prompt", params.prompt);
270
+ if (params.segments !== void 0) {
271
+ form.set("segments", JSON.stringify(params.segments));
272
+ }
273
+ const res = await this.client.request("/v1/video-to-music", {
274
+ method: "POST",
275
+ body: form
276
+ });
277
+ if (!res.body) throw new SoniloError("Response has no body");
278
+ yield* parseNdjson(res.body);
279
+ }
280
+ generate(params) {
281
+ return collectTrack(this.stream(params));
282
+ }
283
+ };
284
+
285
+ // src/version.ts
286
+ var VERSION = "0.1.0";
287
+
288
+ // src/client.ts
289
+ var DEFAULT_BASE_URL = "https://api.sonilo.com";
290
+ var SoniloClient = class {
291
+ constructor(options = {}) {
292
+ const envKey = typeof process !== "undefined" ? process.env?.SONILO_API_KEY : void 0;
293
+ const apiKey = options.apiKey ?? envKey;
294
+ if (!apiKey) {
295
+ throw new SoniloError(
296
+ "Missing API key: pass { apiKey } or set the SONILO_API_KEY environment variable"
297
+ );
298
+ }
299
+ this.apiKey = apiKey;
300
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
301
+ this.fetchFn = (options.fetch ?? globalThis.fetch).bind(globalThis);
302
+ this.account = new Account(this);
303
+ this.textToMusic = new TextToMusic(this);
304
+ this.videoToMusic = new VideoToMusic(this);
305
+ }
306
+ /** Perform an authenticated request; throws a typed error on non-2xx. */
307
+ async request(path, init = {}) {
308
+ const headers = new Headers(init.headers);
309
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
310
+ headers.set("X-Sonilo-Client", "sdk-js");
311
+ headers.set("X-Sonilo-Client-Version", VERSION);
312
+ const res = await this.fetchFn(`${this.baseUrl}${path}`, { ...init, headers });
313
+ if (!res.ok) throw await errorFromResponse(res);
314
+ return res;
315
+ }
316
+ };
317
+
318
+ // src/types.ts
319
+ function isAudioChunkEvent(event) {
320
+ return event.type === "audio_chunk" && event.data instanceof Uint8Array;
321
+ }
322
+ function isErrorEvent(event) {
323
+ return event.type === "error";
324
+ }
325
+ // Annotate the CommonJS export names for ESM import in node:
326
+ 0 && (module.exports = {
327
+ APIError,
328
+ AuthenticationError,
329
+ BadRequestError,
330
+ GenerationError,
331
+ PaymentRequiredError,
332
+ RateLimitError,
333
+ SoniloClient,
334
+ SoniloError,
335
+ VERSION,
336
+ isAudioChunkEvent,
337
+ isErrorEvent
338
+ });
339
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/resources/account.ts","../src/streaming.ts","../src/resources/textToMusic.ts","../src/upload.ts","../src/resources/videoToMusic.ts","../src/version.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["export { SoniloClient, type SoniloClientOptions } from \"./client.js\";\nexport {\n APIError,\n AuthenticationError,\n BadRequestError,\n GenerationError,\n PaymentRequiredError,\n RateLimitError,\n SoniloError,\n} from \"./errors.js\";\nexport { VERSION } from \"./version.js\";\nexport type {\n AccountServices,\n AudioChunkEvent,\n CompleteEvent,\n CostEvent,\n CostInfo,\n DailyUsage,\n ErrorEvent,\n Segment,\n SegmentLabel,\n StreamEvent,\n TextToMusicParams,\n TitleEvent,\n Track,\n UnknownEvent,\n UsageResponse,\n UsageSummary,\n VideoInput,\n VideoToMusicParams,\n} from \"./types.js\";\nexport { isAudioChunkEvent, isErrorEvent } from \"./types.js\";\n","export class SoniloError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class APIError extends SoniloError {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nexport class AuthenticationError extends APIError {}\n\nexport class PaymentRequiredError extends APIError {}\n\nexport class BadRequestError extends APIError {\n get detail(): string | undefined {\n const body = this.body as { detail?: unknown } | undefined;\n return typeof body?.detail === \"string\" ? body.detail : undefined;\n }\n}\n\nexport class RateLimitError extends APIError {\n readonly retryAfter?: number;\n\n constructor(message: string, status: number, body?: unknown, retryAfter?: number) {\n super(message, status, body);\n this.retryAfter = retryAfter;\n }\n}\n\n/** Raised by generate() when an `error` event arrives mid-stream. */\nexport class GenerationError extends SoniloError {\n readonly code?: string;\n\n constructor(message: string, code?: string) {\n super(message);\n this.code = code;\n }\n}\n\nexport async function errorFromResponse(res: Response): Promise<APIError> {\n const text = await res.text().catch(() => \"\");\n let body: unknown = text;\n try {\n body = JSON.parse(text);\n } catch {\n // keep raw text\n }\n const detail =\n typeof (body as { detail?: unknown })?.detail === \"string\"\n ? (body as { detail: string }).detail\n : res.statusText || \"request failed\";\n const message = `HTTP ${res.status}: ${detail}`;\n\n switch (res.status) {\n case 401:\n return new AuthenticationError(message, res.status, body);\n case 402:\n return new PaymentRequiredError(message, res.status, body);\n case 429: {\n const ra = res.headers.get(\"retry-after\");\n const retryAfter = ra !== null && ra !== \"\" && !Number.isNaN(Number(ra)) ? Number(ra) : undefined;\n return new RateLimitError(message, res.status, body, retryAfter);\n }\n case 400:\n case 413:\n case 422:\n return new BadRequestError(message, res.status, body);\n default:\n return new APIError(message, res.status, body);\n }\n}\n","import type { SoniloClient } from \"../client.js\";\nimport type { AccountServices, UsageResponse } from \"../types.js\";\n\nexport class Account {\n constructor(private readonly client: SoniloClient) {}\n\n async services(): Promise<AccountServices> {\n const res = await this.client.request(\"/v1/account/services\");\n return (await res.json()) as AccountServices;\n }\n\n async usage(params: { days?: number } = {}): Promise<UsageResponse> {\n const query = params.days !== undefined ? `?days=${params.days}` : \"\";\n const res = await this.client.request(`/v1/account/usage${query}`);\n return (await res.json()) as UsageResponse;\n }\n}\n","import { GenerationError } from \"./errors.js\";\nimport type { CostInfo, StreamEvent, Track } from \"./types.js\";\n\nexport function decodeBase64(b64: string): Uint8Array {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nfunction toEvent(line: string): StreamEvent {\n const raw = JSON.parse(line) as { type: string; [key: string]: unknown };\n if (raw.type === \"audio_chunk\" && typeof raw.data === \"string\") {\n return { ...raw, type: \"audio_chunk\", data: decodeBase64(raw.data) };\n }\n return raw as StreamEvent;\n}\n\nexport async function* parseNdjson(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<StreamEvent, void, undefined> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let nl: number;\n while ((nl = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, nl).trim();\n buffer = buffer.slice(nl + 1);\n if (line) yield toEvent(line);\n }\n }\n buffer += decoder.decode();\n const tail = buffer.trim();\n if (tail) yield toEvent(tail);\n } finally {\n await reader.cancel().catch(() => {});\n }\n}\n\nexport async function collectTrack(events: AsyncIterable<StreamEvent>): Promise<Track> {\n const chunks: Uint8Array[] = [];\n let title: string | undefined;\n let cost: CostInfo | undefined;\n let sawComplete = false;\n\n for await (const ev of events) {\n if (ev.type === \"audio_chunk\" && ev.data instanceof Uint8Array) {\n chunks.push(ev.data);\n } else if (ev.type === \"title\" && typeof ev.title === \"string\") {\n title = ev.title;\n } else if (ev.type === \"cost\") {\n const { type: _type, ...rest } = ev;\n cost = rest as CostInfo;\n } else if (ev.type === \"error\") {\n const message = typeof ev.message === \"string\" ? ev.message : \"generation failed\";\n const code = typeof ev.code === \"string\" ? ev.code : undefined;\n throw new GenerationError(message, code);\n } else if (ev.type === \"complete\") {\n sawComplete = true;\n }\n // unknown event types: ignored\n }\n\n if (!sawComplete) {\n throw new GenerationError(\"stream ended before a 'complete' event (truncated response)\");\n }\n\n const total = chunks.reduce((n, c) => n + c.length, 0);\n const audio = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n audio.set(c, offset);\n offset += c.length;\n }\n return { audio, title, cost };\n}\n","import type { SoniloClient } from \"../client.js\";\nimport { SoniloError } from \"../errors.js\";\nimport { collectTrack, parseNdjson } from \"../streaming.js\";\nimport type { StreamEvent, TextToMusicParams, Track } from \"../types.js\";\n\nexport class TextToMusic {\n constructor(private readonly client: SoniloClient) {}\n\n /** Stream raw generation events (audio chunks pre-decoded to bytes). */\n async *stream(params: TextToMusicParams): AsyncGenerator<StreamEvent, void, undefined> {\n const form = new FormData();\n form.set(\"prompt\", params.prompt);\n form.set(\"duration\", String(params.duration));\n if (params.segments !== undefined) {\n form.set(\"segments\", JSON.stringify(params.segments));\n }\n const res = await this.client.request(\"/v1/text-to-music\", {\n method: \"POST\",\n body: form,\n });\n if (!res.body) throw new SoniloError(\"Response has no body\");\n yield* parseNdjson(res.body);\n }\n\n /** Generate and buffer the whole track; throws GenerationError on stream errors. */\n generate(params: TextToMusicParams): Promise<Track> {\n return collectTrack(this.stream(params));\n }\n}\n","import { SoniloError } from \"./errors.js\";\nimport type { VideoInput } from \"./types.js\";\n\nconst DEFAULT_FILENAME = \"video.mp4\";\n\n/**\n * Normalize every accepted video input into a FormData-ready Blob.\n * String inputs are file paths and work only in Node.js; browsers must\n * pass File/Blob/bytes/streams.\n */\nexport async function toUploadBlob(\n video: VideoInput,\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof video === \"string\") {\n const isNode =\n typeof process !== \"undefined\" && Boolean((process as { versions?: { node?: string } }).versions?.node);\n if (!isNode) {\n throw new SoniloError(\n \"File paths are only supported in Node.js; pass a File or Blob in the browser\",\n );\n }\n const fsModule = \"node:fs/promises\";\n const { readFile } = (await import(\n /* webpackIgnore: true */ /* @vite-ignore */ fsModule\n )) as typeof import(\"node:fs/promises\");\n const data = await readFile(video);\n const filename = video.split(/[\\\\/]/).pop() || DEFAULT_FILENAME;\n return { blob: new Blob([data]), filename };\n }\n if (typeof File !== \"undefined\" && video instanceof File) {\n return { blob: video, filename: video.name || DEFAULT_FILENAME };\n }\n if (video instanceof Blob) {\n return { blob: video, filename: DEFAULT_FILENAME };\n }\n if (video instanceof Uint8Array) {\n return { blob: new Blob([video as unknown as BlobPart]), filename: DEFAULT_FILENAME };\n }\n if (video instanceof ArrayBuffer) {\n return { blob: new Blob([video]), filename: DEFAULT_FILENAME };\n }\n if (video instanceof ReadableStream) {\n return { blob: await new Response(video).blob(), filename: DEFAULT_FILENAME };\n }\n throw new SoniloError(\"Unsupported video input type\");\n}\n","import type { SoniloClient } from \"../client.js\";\nimport { SoniloError } from \"../errors.js\";\nimport { collectTrack, parseNdjson } from \"../streaming.js\";\nimport { toUploadBlob } from \"../upload.js\";\nimport type { StreamEvent, Track, VideoToMusicParams } from \"../types.js\";\n\nexport class VideoToMusic {\n constructor(private readonly client: SoniloClient) {}\n\n async *stream(params: VideoToMusicParams): AsyncGenerator<StreamEvent, void, undefined> {\n if ((params.video === undefined) === (params.videoUrl === undefined)) {\n throw new SoniloError(\"Provide exactly one of video or videoUrl\");\n }\n const form = new FormData();\n if (params.video !== undefined) {\n const { blob, filename } = await toUploadBlob(params.video);\n form.set(\"video\", blob, filename);\n } else {\n form.set(\"video_url\", params.videoUrl as string);\n }\n if (params.prompt !== undefined) form.set(\"prompt\", params.prompt);\n if (params.segments !== undefined) {\n form.set(\"segments\", JSON.stringify(params.segments));\n }\n const res = await this.client.request(\"/v1/video-to-music\", {\n method: \"POST\",\n body: form,\n });\n if (!res.body) throw new SoniloError(\"Response has no body\");\n yield* parseNdjson(res.body);\n }\n\n generate(params: VideoToMusicParams): Promise<Track> {\n return collectTrack(this.stream(params));\n }\n}\n","export const VERSION = \"0.1.0\";\n","import { SoniloError, errorFromResponse } from \"./errors.js\";\nimport { Account } from \"./resources/account.js\";\nimport { TextToMusic } from \"./resources/textToMusic.js\";\nimport { VideoToMusic } from \"./resources/videoToMusic.js\";\nimport { VERSION } from \"./version.js\";\n\nexport interface SoniloClientOptions {\n /** Defaults to the SONILO_API_KEY environment variable (Node.js only). */\n apiKey?: string;\n /** Defaults to https://api.sonilo.com */\n baseUrl?: string;\n /** Injection point for tests and custom transports. */\n fetch?: typeof globalThis.fetch;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.sonilo.com\";\n\nexport class SoniloClient {\n readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly fetchFn: typeof globalThis.fetch;\n readonly account: Account;\n readonly textToMusic: TextToMusic;\n readonly videoToMusic: VideoToMusic;\n\n constructor(options: SoniloClientOptions = {}) {\n const envKey =\n typeof process !== \"undefined\" ? process.env?.SONILO_API_KEY : undefined;\n const apiKey = options.apiKey ?? envKey;\n if (!apiKey) {\n throw new SoniloError(\n \"Missing API key: pass { apiKey } or set the SONILO_API_KEY environment variable\",\n );\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.fetchFn = (options.fetch ?? globalThis.fetch).bind(globalThis);\n this.account = new Account(this);\n this.textToMusic = new TextToMusic(this);\n this.videoToMusic = new VideoToMusic(this);\n }\n\n /** Perform an authenticated request; throws a typed error on non-2xx. */\n async request(path: string, init: RequestInit = {}): Promise<Response> {\n const headers = new Headers(init.headers);\n headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n headers.set(\"X-Sonilo-Client\", \"sdk-js\");\n headers.set(\"X-Sonilo-Client-Version\", VERSION);\n const res = await this.fetchFn(`${this.baseUrl}${path}`, { ...init, headers });\n if (!res.ok) throw await errorFromResponse(res);\n return res;\n }\n}\n","export type SegmentLabel =\n | \"intro\"\n | \"verse\"\n | \"pre-chorus\"\n | \"chorus\"\n | \"bridge\"\n | \"break\"\n | \"silence\"\n | \"outro\"\n | \"none\";\n\nexport interface Segment {\n start: number;\n prompt: string;\n label?: SegmentLabel;\n}\n\n/** Monetary fields are strings, exactly as the backend serializes them. */\nexport interface CostInfo {\n billing_rate_per_sec: string;\n billing_before_discount: string;\n billing_after_discount: string;\n discount_factor: string;\n}\n\nexport interface AudioChunkEvent {\n type: \"audio_chunk\";\n /** Decoded from the wire's base64 by the SDK. */\n data: Uint8Array;\n}\n\nexport interface TitleEvent {\n type: \"title\";\n title: string;\n summary?: string;\n display_tags?: string[];\n [key: string]: unknown;\n}\n\nexport interface CompleteEvent {\n type: \"complete\";\n [key: string]: unknown;\n}\n\nexport interface ErrorEvent {\n type: \"error\";\n code?: string;\n message?: string;\n [key: string]: unknown;\n}\n\nexport interface CostEvent extends CostInfo {\n type: \"cost\";\n}\n\n/** Forward-compatibility: unrecognized event types are passed through. */\nexport interface UnknownEvent {\n type: string;\n [key: string]: unknown;\n}\n\nexport type StreamEvent =\n | AudioChunkEvent\n | TitleEvent\n | CompleteEvent\n | ErrorEvent\n | CostEvent\n | UnknownEvent;\n\nexport interface Track {\n audio: Uint8Array;\n title?: string;\n cost?: CostInfo;\n}\n\nexport interface TextToMusicParams {\n prompt: string;\n duration: number;\n segments?: Segment[];\n}\n\n/** string = file path (Node.js only). */\nexport type VideoInput =\n | File\n | Blob\n | Uint8Array\n | ArrayBuffer\n | ReadableStream<Uint8Array>\n | string;\n\nexport interface VideoToMusicParams {\n video?: VideoInput;\n videoUrl?: string;\n prompt?: string;\n segments?: Segment[];\n}\n\nexport interface AccountServices {\n available_services: string[];\n rpm_limit: number;\n concurrency_limit: number;\n discount_factor: number | string;\n max_upload_size_mb: number | null;\n}\n\nexport interface UsageSummary {\n total_requests: number;\n total_duration_seconds: number;\n total_cost: number | string;\n period_start: string;\n period_end: string;\n [key: string]: unknown;\n}\n\nexport interface DailyUsage {\n date: string;\n requests: number;\n duration_seconds: number;\n cost: number | string;\n}\n\nexport interface UsageResponse {\n summary: UsageSummary;\n daily: DailyUsage[];\n}\n\nexport function isAudioChunkEvent(event: StreamEvent): event is AudioChunkEvent {\n return event.type === \"audio_chunk\" && (event as AudioChunkEvent).data instanceof Uint8Array;\n}\n\nexport function isErrorEvent(event: StreamEvent): event is ErrorEvent {\n return event.type === \"error\";\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,163 @@
1
+ type SegmentLabel = "intro" | "verse" | "pre-chorus" | "chorus" | "bridge" | "break" | "silence" | "outro" | "none";
2
+ interface Segment {
3
+ start: number;
4
+ prompt: string;
5
+ label?: SegmentLabel;
6
+ }
7
+ /** Monetary fields are strings, exactly as the backend serializes them. */
8
+ interface CostInfo {
9
+ billing_rate_per_sec: string;
10
+ billing_before_discount: string;
11
+ billing_after_discount: string;
12
+ discount_factor: string;
13
+ }
14
+ interface AudioChunkEvent {
15
+ type: "audio_chunk";
16
+ /** Decoded from the wire's base64 by the SDK. */
17
+ data: Uint8Array;
18
+ }
19
+ interface TitleEvent {
20
+ type: "title";
21
+ title: string;
22
+ summary?: string;
23
+ display_tags?: string[];
24
+ [key: string]: unknown;
25
+ }
26
+ interface CompleteEvent {
27
+ type: "complete";
28
+ [key: string]: unknown;
29
+ }
30
+ interface ErrorEvent {
31
+ type: "error";
32
+ code?: string;
33
+ message?: string;
34
+ [key: string]: unknown;
35
+ }
36
+ interface CostEvent extends CostInfo {
37
+ type: "cost";
38
+ }
39
+ /** Forward-compatibility: unrecognized event types are passed through. */
40
+ interface UnknownEvent {
41
+ type: string;
42
+ [key: string]: unknown;
43
+ }
44
+ type StreamEvent = AudioChunkEvent | TitleEvent | CompleteEvent | ErrorEvent | CostEvent | UnknownEvent;
45
+ interface Track {
46
+ audio: Uint8Array;
47
+ title?: string;
48
+ cost?: CostInfo;
49
+ }
50
+ interface TextToMusicParams {
51
+ prompt: string;
52
+ duration: number;
53
+ segments?: Segment[];
54
+ }
55
+ /** string = file path (Node.js only). */
56
+ type VideoInput = File | Blob | Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | string;
57
+ interface VideoToMusicParams {
58
+ video?: VideoInput;
59
+ videoUrl?: string;
60
+ prompt?: string;
61
+ segments?: Segment[];
62
+ }
63
+ interface AccountServices {
64
+ available_services: string[];
65
+ rpm_limit: number;
66
+ concurrency_limit: number;
67
+ discount_factor: number | string;
68
+ max_upload_size_mb: number | null;
69
+ }
70
+ interface UsageSummary {
71
+ total_requests: number;
72
+ total_duration_seconds: number;
73
+ total_cost: number | string;
74
+ period_start: string;
75
+ period_end: string;
76
+ [key: string]: unknown;
77
+ }
78
+ interface DailyUsage {
79
+ date: string;
80
+ requests: number;
81
+ duration_seconds: number;
82
+ cost: number | string;
83
+ }
84
+ interface UsageResponse {
85
+ summary: UsageSummary;
86
+ daily: DailyUsage[];
87
+ }
88
+ declare function isAudioChunkEvent(event: StreamEvent): event is AudioChunkEvent;
89
+ declare function isErrorEvent(event: StreamEvent): event is ErrorEvent;
90
+
91
+ declare class Account {
92
+ private readonly client;
93
+ constructor(client: SoniloClient);
94
+ services(): Promise<AccountServices>;
95
+ usage(params?: {
96
+ days?: number;
97
+ }): Promise<UsageResponse>;
98
+ }
99
+
100
+ declare class TextToMusic {
101
+ private readonly client;
102
+ constructor(client: SoniloClient);
103
+ /** Stream raw generation events (audio chunks pre-decoded to bytes). */
104
+ stream(params: TextToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
105
+ /** Generate and buffer the whole track; throws GenerationError on stream errors. */
106
+ generate(params: TextToMusicParams): Promise<Track>;
107
+ }
108
+
109
+ declare class VideoToMusic {
110
+ private readonly client;
111
+ constructor(client: SoniloClient);
112
+ stream(params: VideoToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
113
+ generate(params: VideoToMusicParams): Promise<Track>;
114
+ }
115
+
116
+ interface SoniloClientOptions {
117
+ /** Defaults to the SONILO_API_KEY environment variable (Node.js only). */
118
+ apiKey?: string;
119
+ /** Defaults to https://api.sonilo.com */
120
+ baseUrl?: string;
121
+ /** Injection point for tests and custom transports. */
122
+ fetch?: typeof globalThis.fetch;
123
+ }
124
+ declare class SoniloClient {
125
+ readonly baseUrl: string;
126
+ private readonly apiKey;
127
+ private readonly fetchFn;
128
+ readonly account: Account;
129
+ readonly textToMusic: TextToMusic;
130
+ readonly videoToMusic: VideoToMusic;
131
+ constructor(options?: SoniloClientOptions);
132
+ /** Perform an authenticated request; throws a typed error on non-2xx. */
133
+ request(path: string, init?: RequestInit): Promise<Response>;
134
+ }
135
+
136
+ declare class SoniloError extends Error {
137
+ constructor(message: string);
138
+ }
139
+ declare class APIError extends SoniloError {
140
+ readonly status: number;
141
+ readonly body: unknown;
142
+ constructor(message: string, status: number, body?: unknown);
143
+ }
144
+ declare class AuthenticationError extends APIError {
145
+ }
146
+ declare class PaymentRequiredError extends APIError {
147
+ }
148
+ declare class BadRequestError extends APIError {
149
+ get detail(): string | undefined;
150
+ }
151
+ declare class RateLimitError extends APIError {
152
+ readonly retryAfter?: number;
153
+ constructor(message: string, status: number, body?: unknown, retryAfter?: number);
154
+ }
155
+ /** Raised by generate() when an `error` event arrives mid-stream. */
156
+ declare class GenerationError extends SoniloError {
157
+ readonly code?: string;
158
+ constructor(message: string, code?: string);
159
+ }
160
+
161
+ declare const VERSION = "0.1.0";
162
+
163
+ export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type CompleteEvent, type CostEvent, type CostInfo, type DailyUsage, type ErrorEvent, GenerationError, PaymentRequiredError, RateLimitError, type Segment, type SegmentLabel, SoniloClient, type SoniloClientOptions, SoniloError, type StreamEvent, type TextToMusicParams, type TitleEvent, type Track, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoToMusicParams, isAudioChunkEvent, isErrorEvent };
@@ -0,0 +1,163 @@
1
+ type SegmentLabel = "intro" | "verse" | "pre-chorus" | "chorus" | "bridge" | "break" | "silence" | "outro" | "none";
2
+ interface Segment {
3
+ start: number;
4
+ prompt: string;
5
+ label?: SegmentLabel;
6
+ }
7
+ /** Monetary fields are strings, exactly as the backend serializes them. */
8
+ interface CostInfo {
9
+ billing_rate_per_sec: string;
10
+ billing_before_discount: string;
11
+ billing_after_discount: string;
12
+ discount_factor: string;
13
+ }
14
+ interface AudioChunkEvent {
15
+ type: "audio_chunk";
16
+ /** Decoded from the wire's base64 by the SDK. */
17
+ data: Uint8Array;
18
+ }
19
+ interface TitleEvent {
20
+ type: "title";
21
+ title: string;
22
+ summary?: string;
23
+ display_tags?: string[];
24
+ [key: string]: unknown;
25
+ }
26
+ interface CompleteEvent {
27
+ type: "complete";
28
+ [key: string]: unknown;
29
+ }
30
+ interface ErrorEvent {
31
+ type: "error";
32
+ code?: string;
33
+ message?: string;
34
+ [key: string]: unknown;
35
+ }
36
+ interface CostEvent extends CostInfo {
37
+ type: "cost";
38
+ }
39
+ /** Forward-compatibility: unrecognized event types are passed through. */
40
+ interface UnknownEvent {
41
+ type: string;
42
+ [key: string]: unknown;
43
+ }
44
+ type StreamEvent = AudioChunkEvent | TitleEvent | CompleteEvent | ErrorEvent | CostEvent | UnknownEvent;
45
+ interface Track {
46
+ audio: Uint8Array;
47
+ title?: string;
48
+ cost?: CostInfo;
49
+ }
50
+ interface TextToMusicParams {
51
+ prompt: string;
52
+ duration: number;
53
+ segments?: Segment[];
54
+ }
55
+ /** string = file path (Node.js only). */
56
+ type VideoInput = File | Blob | Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | string;
57
+ interface VideoToMusicParams {
58
+ video?: VideoInput;
59
+ videoUrl?: string;
60
+ prompt?: string;
61
+ segments?: Segment[];
62
+ }
63
+ interface AccountServices {
64
+ available_services: string[];
65
+ rpm_limit: number;
66
+ concurrency_limit: number;
67
+ discount_factor: number | string;
68
+ max_upload_size_mb: number | null;
69
+ }
70
+ interface UsageSummary {
71
+ total_requests: number;
72
+ total_duration_seconds: number;
73
+ total_cost: number | string;
74
+ period_start: string;
75
+ period_end: string;
76
+ [key: string]: unknown;
77
+ }
78
+ interface DailyUsage {
79
+ date: string;
80
+ requests: number;
81
+ duration_seconds: number;
82
+ cost: number | string;
83
+ }
84
+ interface UsageResponse {
85
+ summary: UsageSummary;
86
+ daily: DailyUsage[];
87
+ }
88
+ declare function isAudioChunkEvent(event: StreamEvent): event is AudioChunkEvent;
89
+ declare function isErrorEvent(event: StreamEvent): event is ErrorEvent;
90
+
91
+ declare class Account {
92
+ private readonly client;
93
+ constructor(client: SoniloClient);
94
+ services(): Promise<AccountServices>;
95
+ usage(params?: {
96
+ days?: number;
97
+ }): Promise<UsageResponse>;
98
+ }
99
+
100
+ declare class TextToMusic {
101
+ private readonly client;
102
+ constructor(client: SoniloClient);
103
+ /** Stream raw generation events (audio chunks pre-decoded to bytes). */
104
+ stream(params: TextToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
105
+ /** Generate and buffer the whole track; throws GenerationError on stream errors. */
106
+ generate(params: TextToMusicParams): Promise<Track>;
107
+ }
108
+
109
+ declare class VideoToMusic {
110
+ private readonly client;
111
+ constructor(client: SoniloClient);
112
+ stream(params: VideoToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
113
+ generate(params: VideoToMusicParams): Promise<Track>;
114
+ }
115
+
116
+ interface SoniloClientOptions {
117
+ /** Defaults to the SONILO_API_KEY environment variable (Node.js only). */
118
+ apiKey?: string;
119
+ /** Defaults to https://api.sonilo.com */
120
+ baseUrl?: string;
121
+ /** Injection point for tests and custom transports. */
122
+ fetch?: typeof globalThis.fetch;
123
+ }
124
+ declare class SoniloClient {
125
+ readonly baseUrl: string;
126
+ private readonly apiKey;
127
+ private readonly fetchFn;
128
+ readonly account: Account;
129
+ readonly textToMusic: TextToMusic;
130
+ readonly videoToMusic: VideoToMusic;
131
+ constructor(options?: SoniloClientOptions);
132
+ /** Perform an authenticated request; throws a typed error on non-2xx. */
133
+ request(path: string, init?: RequestInit): Promise<Response>;
134
+ }
135
+
136
+ declare class SoniloError extends Error {
137
+ constructor(message: string);
138
+ }
139
+ declare class APIError extends SoniloError {
140
+ readonly status: number;
141
+ readonly body: unknown;
142
+ constructor(message: string, status: number, body?: unknown);
143
+ }
144
+ declare class AuthenticationError extends APIError {
145
+ }
146
+ declare class PaymentRequiredError extends APIError {
147
+ }
148
+ declare class BadRequestError extends APIError {
149
+ get detail(): string | undefined;
150
+ }
151
+ declare class RateLimitError extends APIError {
152
+ readonly retryAfter?: number;
153
+ constructor(message: string, status: number, body?: unknown, retryAfter?: number);
154
+ }
155
+ /** Raised by generate() when an `error` event arrives mid-stream. */
156
+ declare class GenerationError extends SoniloError {
157
+ readonly code?: string;
158
+ constructor(message: string, code?: string);
159
+ }
160
+
161
+ declare const VERSION = "0.1.0";
162
+
163
+ export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type CompleteEvent, type CostEvent, type CostInfo, type DailyUsage, type ErrorEvent, GenerationError, PaymentRequiredError, RateLimitError, type Segment, type SegmentLabel, SoniloClient, type SoniloClientOptions, SoniloError, type StreamEvent, type TextToMusicParams, type TitleEvent, type Track, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoToMusicParams, isAudioChunkEvent, isErrorEvent };
package/dist/index.js ADDED
@@ -0,0 +1,302 @@
1
+ // src/errors.ts
2
+ var SoniloError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = new.target.name;
6
+ }
7
+ };
8
+ var APIError = class extends SoniloError {
9
+ constructor(message, status, body) {
10
+ super(message);
11
+ this.status = status;
12
+ this.body = body;
13
+ }
14
+ };
15
+ var AuthenticationError = class extends APIError {
16
+ };
17
+ var PaymentRequiredError = class extends APIError {
18
+ };
19
+ var BadRequestError = class extends APIError {
20
+ get detail() {
21
+ const body = this.body;
22
+ return typeof body?.detail === "string" ? body.detail : void 0;
23
+ }
24
+ };
25
+ var RateLimitError = class extends APIError {
26
+ constructor(message, status, body, retryAfter) {
27
+ super(message, status, body);
28
+ this.retryAfter = retryAfter;
29
+ }
30
+ };
31
+ var GenerationError = class extends SoniloError {
32
+ constructor(message, code) {
33
+ super(message);
34
+ this.code = code;
35
+ }
36
+ };
37
+ async function errorFromResponse(res) {
38
+ const text = await res.text().catch(() => "");
39
+ let body = text;
40
+ try {
41
+ body = JSON.parse(text);
42
+ } catch {
43
+ }
44
+ const detail = typeof body?.detail === "string" ? body.detail : res.statusText || "request failed";
45
+ const message = `HTTP ${res.status}: ${detail}`;
46
+ switch (res.status) {
47
+ case 401:
48
+ return new AuthenticationError(message, res.status, body);
49
+ case 402:
50
+ return new PaymentRequiredError(message, res.status, body);
51
+ case 429: {
52
+ const ra = res.headers.get("retry-after");
53
+ const retryAfter = ra !== null && ra !== "" && !Number.isNaN(Number(ra)) ? Number(ra) : void 0;
54
+ return new RateLimitError(message, res.status, body, retryAfter);
55
+ }
56
+ case 400:
57
+ case 413:
58
+ case 422:
59
+ return new BadRequestError(message, res.status, body);
60
+ default:
61
+ return new APIError(message, res.status, body);
62
+ }
63
+ }
64
+
65
+ // src/resources/account.ts
66
+ var Account = class {
67
+ constructor(client) {
68
+ this.client = client;
69
+ }
70
+ async services() {
71
+ const res = await this.client.request("/v1/account/services");
72
+ return await res.json();
73
+ }
74
+ async usage(params = {}) {
75
+ const query = params.days !== void 0 ? `?days=${params.days}` : "";
76
+ const res = await this.client.request(`/v1/account/usage${query}`);
77
+ return await res.json();
78
+ }
79
+ };
80
+
81
+ // src/streaming.ts
82
+ function decodeBase64(b64) {
83
+ const bin = atob(b64);
84
+ const out = new Uint8Array(bin.length);
85
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
86
+ return out;
87
+ }
88
+ function toEvent(line) {
89
+ const raw = JSON.parse(line);
90
+ if (raw.type === "audio_chunk" && typeof raw.data === "string") {
91
+ return { ...raw, type: "audio_chunk", data: decodeBase64(raw.data) };
92
+ }
93
+ return raw;
94
+ }
95
+ async function* parseNdjson(body) {
96
+ const reader = body.getReader();
97
+ const decoder = new TextDecoder();
98
+ let buffer = "";
99
+ try {
100
+ for (; ; ) {
101
+ const { done, value } = await reader.read();
102
+ if (done) break;
103
+ buffer += decoder.decode(value, { stream: true });
104
+ let nl;
105
+ while ((nl = buffer.indexOf("\n")) !== -1) {
106
+ const line = buffer.slice(0, nl).trim();
107
+ buffer = buffer.slice(nl + 1);
108
+ if (line) yield toEvent(line);
109
+ }
110
+ }
111
+ buffer += decoder.decode();
112
+ const tail = buffer.trim();
113
+ if (tail) yield toEvent(tail);
114
+ } finally {
115
+ await reader.cancel().catch(() => {
116
+ });
117
+ }
118
+ }
119
+ async function collectTrack(events) {
120
+ const chunks = [];
121
+ let title;
122
+ let cost;
123
+ let sawComplete = false;
124
+ for await (const ev of events) {
125
+ if (ev.type === "audio_chunk" && ev.data instanceof Uint8Array) {
126
+ chunks.push(ev.data);
127
+ } else if (ev.type === "title" && typeof ev.title === "string") {
128
+ title = ev.title;
129
+ } else if (ev.type === "cost") {
130
+ const { type: _type, ...rest } = ev;
131
+ cost = rest;
132
+ } else if (ev.type === "error") {
133
+ const message = typeof ev.message === "string" ? ev.message : "generation failed";
134
+ const code = typeof ev.code === "string" ? ev.code : void 0;
135
+ throw new GenerationError(message, code);
136
+ } else if (ev.type === "complete") {
137
+ sawComplete = true;
138
+ }
139
+ }
140
+ if (!sawComplete) {
141
+ throw new GenerationError("stream ended before a 'complete' event (truncated response)");
142
+ }
143
+ const total = chunks.reduce((n, c) => n + c.length, 0);
144
+ const audio = new Uint8Array(total);
145
+ let offset = 0;
146
+ for (const c of chunks) {
147
+ audio.set(c, offset);
148
+ offset += c.length;
149
+ }
150
+ return { audio, title, cost };
151
+ }
152
+
153
+ // src/resources/textToMusic.ts
154
+ var TextToMusic = class {
155
+ constructor(client) {
156
+ this.client = client;
157
+ }
158
+ /** Stream raw generation events (audio chunks pre-decoded to bytes). */
159
+ async *stream(params) {
160
+ const form = new FormData();
161
+ form.set("prompt", params.prompt);
162
+ form.set("duration", String(params.duration));
163
+ if (params.segments !== void 0) {
164
+ form.set("segments", JSON.stringify(params.segments));
165
+ }
166
+ const res = await this.client.request("/v1/text-to-music", {
167
+ method: "POST",
168
+ body: form
169
+ });
170
+ if (!res.body) throw new SoniloError("Response has no body");
171
+ yield* parseNdjson(res.body);
172
+ }
173
+ /** Generate and buffer the whole track; throws GenerationError on stream errors. */
174
+ generate(params) {
175
+ return collectTrack(this.stream(params));
176
+ }
177
+ };
178
+
179
+ // src/upload.ts
180
+ var DEFAULT_FILENAME = "video.mp4";
181
+ async function toUploadBlob(video) {
182
+ if (typeof video === "string") {
183
+ const isNode = typeof process !== "undefined" && Boolean(process.versions?.node);
184
+ if (!isNode) {
185
+ throw new SoniloError(
186
+ "File paths are only supported in Node.js; pass a File or Blob in the browser"
187
+ );
188
+ }
189
+ const fsModule = "node:fs/promises";
190
+ const { readFile } = await import(
191
+ /* webpackIgnore: true */
192
+ /* @vite-ignore */
193
+ fsModule
194
+ );
195
+ const data = await readFile(video);
196
+ const filename = video.split(/[\\/]/).pop() || DEFAULT_FILENAME;
197
+ return { blob: new Blob([data]), filename };
198
+ }
199
+ if (typeof File !== "undefined" && video instanceof File) {
200
+ return { blob: video, filename: video.name || DEFAULT_FILENAME };
201
+ }
202
+ if (video instanceof Blob) {
203
+ return { blob: video, filename: DEFAULT_FILENAME };
204
+ }
205
+ if (video instanceof Uint8Array) {
206
+ return { blob: new Blob([video]), filename: DEFAULT_FILENAME };
207
+ }
208
+ if (video instanceof ArrayBuffer) {
209
+ return { blob: new Blob([video]), filename: DEFAULT_FILENAME };
210
+ }
211
+ if (video instanceof ReadableStream) {
212
+ return { blob: await new Response(video).blob(), filename: DEFAULT_FILENAME };
213
+ }
214
+ throw new SoniloError("Unsupported video input type");
215
+ }
216
+
217
+ // src/resources/videoToMusic.ts
218
+ var VideoToMusic = class {
219
+ constructor(client) {
220
+ this.client = client;
221
+ }
222
+ async *stream(params) {
223
+ if (params.video === void 0 === (params.videoUrl === void 0)) {
224
+ throw new SoniloError("Provide exactly one of video or videoUrl");
225
+ }
226
+ const form = new FormData();
227
+ if (params.video !== void 0) {
228
+ const { blob, filename } = await toUploadBlob(params.video);
229
+ form.set("video", blob, filename);
230
+ } else {
231
+ form.set("video_url", params.videoUrl);
232
+ }
233
+ if (params.prompt !== void 0) form.set("prompt", params.prompt);
234
+ if (params.segments !== void 0) {
235
+ form.set("segments", JSON.stringify(params.segments));
236
+ }
237
+ const res = await this.client.request("/v1/video-to-music", {
238
+ method: "POST",
239
+ body: form
240
+ });
241
+ if (!res.body) throw new SoniloError("Response has no body");
242
+ yield* parseNdjson(res.body);
243
+ }
244
+ generate(params) {
245
+ return collectTrack(this.stream(params));
246
+ }
247
+ };
248
+
249
+ // src/version.ts
250
+ var VERSION = "0.1.0";
251
+
252
+ // src/client.ts
253
+ var DEFAULT_BASE_URL = "https://api.sonilo.com";
254
+ var SoniloClient = class {
255
+ constructor(options = {}) {
256
+ const envKey = typeof process !== "undefined" ? process.env?.SONILO_API_KEY : void 0;
257
+ const apiKey = options.apiKey ?? envKey;
258
+ if (!apiKey) {
259
+ throw new SoniloError(
260
+ "Missing API key: pass { apiKey } or set the SONILO_API_KEY environment variable"
261
+ );
262
+ }
263
+ this.apiKey = apiKey;
264
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
265
+ this.fetchFn = (options.fetch ?? globalThis.fetch).bind(globalThis);
266
+ this.account = new Account(this);
267
+ this.textToMusic = new TextToMusic(this);
268
+ this.videoToMusic = new VideoToMusic(this);
269
+ }
270
+ /** Perform an authenticated request; throws a typed error on non-2xx. */
271
+ async request(path, init = {}) {
272
+ const headers = new Headers(init.headers);
273
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
274
+ headers.set("X-Sonilo-Client", "sdk-js");
275
+ headers.set("X-Sonilo-Client-Version", VERSION);
276
+ const res = await this.fetchFn(`${this.baseUrl}${path}`, { ...init, headers });
277
+ if (!res.ok) throw await errorFromResponse(res);
278
+ return res;
279
+ }
280
+ };
281
+
282
+ // src/types.ts
283
+ function isAudioChunkEvent(event) {
284
+ return event.type === "audio_chunk" && event.data instanceof Uint8Array;
285
+ }
286
+ function isErrorEvent(event) {
287
+ return event.type === "error";
288
+ }
289
+ export {
290
+ APIError,
291
+ AuthenticationError,
292
+ BadRequestError,
293
+ GenerationError,
294
+ PaymentRequiredError,
295
+ RateLimitError,
296
+ SoniloClient,
297
+ SoniloError,
298
+ VERSION,
299
+ isAudioChunkEvent,
300
+ isErrorEvent
301
+ };
302
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/resources/account.ts","../src/streaming.ts","../src/resources/textToMusic.ts","../src/upload.ts","../src/resources/videoToMusic.ts","../src/version.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["export class SoniloError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class APIError extends SoniloError {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nexport class AuthenticationError extends APIError {}\n\nexport class PaymentRequiredError extends APIError {}\n\nexport class BadRequestError extends APIError {\n get detail(): string | undefined {\n const body = this.body as { detail?: unknown } | undefined;\n return typeof body?.detail === \"string\" ? body.detail : undefined;\n }\n}\n\nexport class RateLimitError extends APIError {\n readonly retryAfter?: number;\n\n constructor(message: string, status: number, body?: unknown, retryAfter?: number) {\n super(message, status, body);\n this.retryAfter = retryAfter;\n }\n}\n\n/** Raised by generate() when an `error` event arrives mid-stream. */\nexport class GenerationError extends SoniloError {\n readonly code?: string;\n\n constructor(message: string, code?: string) {\n super(message);\n this.code = code;\n }\n}\n\nexport async function errorFromResponse(res: Response): Promise<APIError> {\n const text = await res.text().catch(() => \"\");\n let body: unknown = text;\n try {\n body = JSON.parse(text);\n } catch {\n // keep raw text\n }\n const detail =\n typeof (body as { detail?: unknown })?.detail === \"string\"\n ? (body as { detail: string }).detail\n : res.statusText || \"request failed\";\n const message = `HTTP ${res.status}: ${detail}`;\n\n switch (res.status) {\n case 401:\n return new AuthenticationError(message, res.status, body);\n case 402:\n return new PaymentRequiredError(message, res.status, body);\n case 429: {\n const ra = res.headers.get(\"retry-after\");\n const retryAfter = ra !== null && ra !== \"\" && !Number.isNaN(Number(ra)) ? Number(ra) : undefined;\n return new RateLimitError(message, res.status, body, retryAfter);\n }\n case 400:\n case 413:\n case 422:\n return new BadRequestError(message, res.status, body);\n default:\n return new APIError(message, res.status, body);\n }\n}\n","import type { SoniloClient } from \"../client.js\";\nimport type { AccountServices, UsageResponse } from \"../types.js\";\n\nexport class Account {\n constructor(private readonly client: SoniloClient) {}\n\n async services(): Promise<AccountServices> {\n const res = await this.client.request(\"/v1/account/services\");\n return (await res.json()) as AccountServices;\n }\n\n async usage(params: { days?: number } = {}): Promise<UsageResponse> {\n const query = params.days !== undefined ? `?days=${params.days}` : \"\";\n const res = await this.client.request(`/v1/account/usage${query}`);\n return (await res.json()) as UsageResponse;\n }\n}\n","import { GenerationError } from \"./errors.js\";\nimport type { CostInfo, StreamEvent, Track } from \"./types.js\";\n\nexport function decodeBase64(b64: string): Uint8Array {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nfunction toEvent(line: string): StreamEvent {\n const raw = JSON.parse(line) as { type: string; [key: string]: unknown };\n if (raw.type === \"audio_chunk\" && typeof raw.data === \"string\") {\n return { ...raw, type: \"audio_chunk\", data: decodeBase64(raw.data) };\n }\n return raw as StreamEvent;\n}\n\nexport async function* parseNdjson(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<StreamEvent, void, undefined> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let nl: number;\n while ((nl = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, nl).trim();\n buffer = buffer.slice(nl + 1);\n if (line) yield toEvent(line);\n }\n }\n buffer += decoder.decode();\n const tail = buffer.trim();\n if (tail) yield toEvent(tail);\n } finally {\n await reader.cancel().catch(() => {});\n }\n}\n\nexport async function collectTrack(events: AsyncIterable<StreamEvent>): Promise<Track> {\n const chunks: Uint8Array[] = [];\n let title: string | undefined;\n let cost: CostInfo | undefined;\n let sawComplete = false;\n\n for await (const ev of events) {\n if (ev.type === \"audio_chunk\" && ev.data instanceof Uint8Array) {\n chunks.push(ev.data);\n } else if (ev.type === \"title\" && typeof ev.title === \"string\") {\n title = ev.title;\n } else if (ev.type === \"cost\") {\n const { type: _type, ...rest } = ev;\n cost = rest as CostInfo;\n } else if (ev.type === \"error\") {\n const message = typeof ev.message === \"string\" ? ev.message : \"generation failed\";\n const code = typeof ev.code === \"string\" ? ev.code : undefined;\n throw new GenerationError(message, code);\n } else if (ev.type === \"complete\") {\n sawComplete = true;\n }\n // unknown event types: ignored\n }\n\n if (!sawComplete) {\n throw new GenerationError(\"stream ended before a 'complete' event (truncated response)\");\n }\n\n const total = chunks.reduce((n, c) => n + c.length, 0);\n const audio = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n audio.set(c, offset);\n offset += c.length;\n }\n return { audio, title, cost };\n}\n","import type { SoniloClient } from \"../client.js\";\nimport { SoniloError } from \"../errors.js\";\nimport { collectTrack, parseNdjson } from \"../streaming.js\";\nimport type { StreamEvent, TextToMusicParams, Track } from \"../types.js\";\n\nexport class TextToMusic {\n constructor(private readonly client: SoniloClient) {}\n\n /** Stream raw generation events (audio chunks pre-decoded to bytes). */\n async *stream(params: TextToMusicParams): AsyncGenerator<StreamEvent, void, undefined> {\n const form = new FormData();\n form.set(\"prompt\", params.prompt);\n form.set(\"duration\", String(params.duration));\n if (params.segments !== undefined) {\n form.set(\"segments\", JSON.stringify(params.segments));\n }\n const res = await this.client.request(\"/v1/text-to-music\", {\n method: \"POST\",\n body: form,\n });\n if (!res.body) throw new SoniloError(\"Response has no body\");\n yield* parseNdjson(res.body);\n }\n\n /** Generate and buffer the whole track; throws GenerationError on stream errors. */\n generate(params: TextToMusicParams): Promise<Track> {\n return collectTrack(this.stream(params));\n }\n}\n","import { SoniloError } from \"./errors.js\";\nimport type { VideoInput } from \"./types.js\";\n\nconst DEFAULT_FILENAME = \"video.mp4\";\n\n/**\n * Normalize every accepted video input into a FormData-ready Blob.\n * String inputs are file paths and work only in Node.js; browsers must\n * pass File/Blob/bytes/streams.\n */\nexport async function toUploadBlob(\n video: VideoInput,\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof video === \"string\") {\n const isNode =\n typeof process !== \"undefined\" && Boolean((process as { versions?: { node?: string } }).versions?.node);\n if (!isNode) {\n throw new SoniloError(\n \"File paths are only supported in Node.js; pass a File or Blob in the browser\",\n );\n }\n const fsModule = \"node:fs/promises\";\n const { readFile } = (await import(\n /* webpackIgnore: true */ /* @vite-ignore */ fsModule\n )) as typeof import(\"node:fs/promises\");\n const data = await readFile(video);\n const filename = video.split(/[\\\\/]/).pop() || DEFAULT_FILENAME;\n return { blob: new Blob([data]), filename };\n }\n if (typeof File !== \"undefined\" && video instanceof File) {\n return { blob: video, filename: video.name || DEFAULT_FILENAME };\n }\n if (video instanceof Blob) {\n return { blob: video, filename: DEFAULT_FILENAME };\n }\n if (video instanceof Uint8Array) {\n return { blob: new Blob([video as unknown as BlobPart]), filename: DEFAULT_FILENAME };\n }\n if (video instanceof ArrayBuffer) {\n return { blob: new Blob([video]), filename: DEFAULT_FILENAME };\n }\n if (video instanceof ReadableStream) {\n return { blob: await new Response(video).blob(), filename: DEFAULT_FILENAME };\n }\n throw new SoniloError(\"Unsupported video input type\");\n}\n","import type { SoniloClient } from \"../client.js\";\nimport { SoniloError } from \"../errors.js\";\nimport { collectTrack, parseNdjson } from \"../streaming.js\";\nimport { toUploadBlob } from \"../upload.js\";\nimport type { StreamEvent, Track, VideoToMusicParams } from \"../types.js\";\n\nexport class VideoToMusic {\n constructor(private readonly client: SoniloClient) {}\n\n async *stream(params: VideoToMusicParams): AsyncGenerator<StreamEvent, void, undefined> {\n if ((params.video === undefined) === (params.videoUrl === undefined)) {\n throw new SoniloError(\"Provide exactly one of video or videoUrl\");\n }\n const form = new FormData();\n if (params.video !== undefined) {\n const { blob, filename } = await toUploadBlob(params.video);\n form.set(\"video\", blob, filename);\n } else {\n form.set(\"video_url\", params.videoUrl as string);\n }\n if (params.prompt !== undefined) form.set(\"prompt\", params.prompt);\n if (params.segments !== undefined) {\n form.set(\"segments\", JSON.stringify(params.segments));\n }\n const res = await this.client.request(\"/v1/video-to-music\", {\n method: \"POST\",\n body: form,\n });\n if (!res.body) throw new SoniloError(\"Response has no body\");\n yield* parseNdjson(res.body);\n }\n\n generate(params: VideoToMusicParams): Promise<Track> {\n return collectTrack(this.stream(params));\n }\n}\n","export const VERSION = \"0.1.0\";\n","import { SoniloError, errorFromResponse } from \"./errors.js\";\nimport { Account } from \"./resources/account.js\";\nimport { TextToMusic } from \"./resources/textToMusic.js\";\nimport { VideoToMusic } from \"./resources/videoToMusic.js\";\nimport { VERSION } from \"./version.js\";\n\nexport interface SoniloClientOptions {\n /** Defaults to the SONILO_API_KEY environment variable (Node.js only). */\n apiKey?: string;\n /** Defaults to https://api.sonilo.com */\n baseUrl?: string;\n /** Injection point for tests and custom transports. */\n fetch?: typeof globalThis.fetch;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.sonilo.com\";\n\nexport class SoniloClient {\n readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly fetchFn: typeof globalThis.fetch;\n readonly account: Account;\n readonly textToMusic: TextToMusic;\n readonly videoToMusic: VideoToMusic;\n\n constructor(options: SoniloClientOptions = {}) {\n const envKey =\n typeof process !== \"undefined\" ? process.env?.SONILO_API_KEY : undefined;\n const apiKey = options.apiKey ?? envKey;\n if (!apiKey) {\n throw new SoniloError(\n \"Missing API key: pass { apiKey } or set the SONILO_API_KEY environment variable\",\n );\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.fetchFn = (options.fetch ?? globalThis.fetch).bind(globalThis);\n this.account = new Account(this);\n this.textToMusic = new TextToMusic(this);\n this.videoToMusic = new VideoToMusic(this);\n }\n\n /** Perform an authenticated request; throws a typed error on non-2xx. */\n async request(path: string, init: RequestInit = {}): Promise<Response> {\n const headers = new Headers(init.headers);\n headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n headers.set(\"X-Sonilo-Client\", \"sdk-js\");\n headers.set(\"X-Sonilo-Client-Version\", VERSION);\n const res = await this.fetchFn(`${this.baseUrl}${path}`, { ...init, headers });\n if (!res.ok) throw await errorFromResponse(res);\n return res;\n }\n}\n","export type SegmentLabel =\n | \"intro\"\n | \"verse\"\n | \"pre-chorus\"\n | \"chorus\"\n | \"bridge\"\n | \"break\"\n | \"silence\"\n | \"outro\"\n | \"none\";\n\nexport interface Segment {\n start: number;\n prompt: string;\n label?: SegmentLabel;\n}\n\n/** Monetary fields are strings, exactly as the backend serializes them. */\nexport interface CostInfo {\n billing_rate_per_sec: string;\n billing_before_discount: string;\n billing_after_discount: string;\n discount_factor: string;\n}\n\nexport interface AudioChunkEvent {\n type: \"audio_chunk\";\n /** Decoded from the wire's base64 by the SDK. */\n data: Uint8Array;\n}\n\nexport interface TitleEvent {\n type: \"title\";\n title: string;\n summary?: string;\n display_tags?: string[];\n [key: string]: unknown;\n}\n\nexport interface CompleteEvent {\n type: \"complete\";\n [key: string]: unknown;\n}\n\nexport interface ErrorEvent {\n type: \"error\";\n code?: string;\n message?: string;\n [key: string]: unknown;\n}\n\nexport interface CostEvent extends CostInfo {\n type: \"cost\";\n}\n\n/** Forward-compatibility: unrecognized event types are passed through. */\nexport interface UnknownEvent {\n type: string;\n [key: string]: unknown;\n}\n\nexport type StreamEvent =\n | AudioChunkEvent\n | TitleEvent\n | CompleteEvent\n | ErrorEvent\n | CostEvent\n | UnknownEvent;\n\nexport interface Track {\n audio: Uint8Array;\n title?: string;\n cost?: CostInfo;\n}\n\nexport interface TextToMusicParams {\n prompt: string;\n duration: number;\n segments?: Segment[];\n}\n\n/** string = file path (Node.js only). */\nexport type VideoInput =\n | File\n | Blob\n | Uint8Array\n | ArrayBuffer\n | ReadableStream<Uint8Array>\n | string;\n\nexport interface VideoToMusicParams {\n video?: VideoInput;\n videoUrl?: string;\n prompt?: string;\n segments?: Segment[];\n}\n\nexport interface AccountServices {\n available_services: string[];\n rpm_limit: number;\n concurrency_limit: number;\n discount_factor: number | string;\n max_upload_size_mb: number | null;\n}\n\nexport interface UsageSummary {\n total_requests: number;\n total_duration_seconds: number;\n total_cost: number | string;\n period_start: string;\n period_end: string;\n [key: string]: unknown;\n}\n\nexport interface DailyUsage {\n date: string;\n requests: number;\n duration_seconds: number;\n cost: number | string;\n}\n\nexport interface UsageResponse {\n summary: UsageSummary;\n daily: DailyUsage[];\n}\n\nexport function isAudioChunkEvent(event: StreamEvent): event is AudioChunkEvent {\n return event.type === \"audio_chunk\" && (event as AudioChunkEvent).data instanceof Uint8Array;\n}\n\nexport function isErrorEvent(event: StreamEvent): event is ErrorEvent {\n return event.type === \"error\";\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "sonilo",
3
+ "version": "0.1.0",
4
+ "description": "Official TypeScript/JavaScript client for the Sonilo API",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/sonilo-ai/sonilo-js.git"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
17
+ "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "sideEffects": false,
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "lint": "tsc --noEmit",
30
+ "test": "vitest run"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.19.43",
34
+ "tsup": "^8.0.2",
35
+ "typescript": "^5.4.5",
36
+ "vitest": "^1.6.0"
37
+ },
38
+ "keywords": [
39
+ "sonilo",
40
+ "music",
41
+ "generation",
42
+ "text-to-music",
43
+ "video-to-music",
44
+ "ai"
45
+ ]
46
+ }