kugelaudio 0.3.0 → 0.5.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/CHANGELOG.md +36 -0
- package/README.md +49 -16
- package/dist/index.d.mts +185 -8
- package/dist/index.d.ts +185 -8
- package/dist/index.js +212 -9
- package/dist/index.mjs +209 -9
- package/package.json +2 -2
- package/src/client.test.ts +10 -10
- package/src/client.ts +14 -10
- package/src/dictionaries.test.ts +212 -0
- package/src/dictionaries.ts +278 -0
- package/src/errors.ts +24 -1
- package/src/index.ts +11 -0
- package/src/types.ts +94 -7
package/dist/index.js
CHANGED
|
@@ -22,10 +22,13 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AuthenticationError: () => AuthenticationError,
|
|
24
24
|
ConnectionError: () => ConnectionError,
|
|
25
|
+
DictionariesResource: () => DictionariesResource,
|
|
26
|
+
DictionaryEntriesResource: () => DictionaryEntriesResource,
|
|
25
27
|
ErrorCodes: () => ErrorCodes,
|
|
26
28
|
InsufficientCreditsError: () => InsufficientCreditsError,
|
|
27
29
|
KugelAudio: () => KugelAudio,
|
|
28
30
|
KugelAudioError: () => KugelAudioError,
|
|
31
|
+
NotFoundError: () => NotFoundError,
|
|
29
32
|
RateLimitError: () => RateLimitError,
|
|
30
33
|
ValidationError: () => ValidationError,
|
|
31
34
|
WsCloseCodes: () => WsCloseCodes,
|
|
@@ -40,6 +43,187 @@ __export(index_exports, {
|
|
|
40
43
|
});
|
|
41
44
|
module.exports = __toCommonJS(index_exports);
|
|
42
45
|
|
|
46
|
+
// src/dictionaries.ts
|
|
47
|
+
function mapDictionary(raw) {
|
|
48
|
+
return {
|
|
49
|
+
id: raw.id,
|
|
50
|
+
projectId: raw.project_id,
|
|
51
|
+
name: raw.name,
|
|
52
|
+
description: raw.description ?? void 0,
|
|
53
|
+
language: raw.language ?? void 0,
|
|
54
|
+
isActive: raw.is_active ?? true,
|
|
55
|
+
createdAt: raw.created_at,
|
|
56
|
+
updatedAt: raw.updated_at
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function mapEntry(raw) {
|
|
60
|
+
return {
|
|
61
|
+
id: raw.id,
|
|
62
|
+
dictionaryId: raw.dictionary_id,
|
|
63
|
+
word: raw.word,
|
|
64
|
+
replacement: raw.replacement,
|
|
65
|
+
ipa: raw.ipa ?? void 0,
|
|
66
|
+
caseSensitive: raw.case_sensitive ?? false,
|
|
67
|
+
createdAt: raw.created_at,
|
|
68
|
+
updatedAt: raw.updated_at
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function entryPayload(input) {
|
|
72
|
+
const payload = {
|
|
73
|
+
word: input.word,
|
|
74
|
+
replacement: input.replacement
|
|
75
|
+
};
|
|
76
|
+
if (input.ipa !== void 0) payload.ipa = input.ipa;
|
|
77
|
+
if (input.caseSensitive !== void 0) {
|
|
78
|
+
payload.case_sensitive = input.caseSensitive;
|
|
79
|
+
}
|
|
80
|
+
return payload;
|
|
81
|
+
}
|
|
82
|
+
function buildPath(base, params) {
|
|
83
|
+
const search = new URLSearchParams();
|
|
84
|
+
for (const [key, val] of Object.entries(params)) {
|
|
85
|
+
if (val === void 0 || val === null) continue;
|
|
86
|
+
search.set(key, String(val));
|
|
87
|
+
}
|
|
88
|
+
const query = search.toString();
|
|
89
|
+
return query ? `${base}?${query}` : base;
|
|
90
|
+
}
|
|
91
|
+
var DictionaryEntriesResource = class {
|
|
92
|
+
constructor(client) {
|
|
93
|
+
this.client = client;
|
|
94
|
+
}
|
|
95
|
+
/** List entries with optional search + pagination. */
|
|
96
|
+
async list(dictionaryId, options) {
|
|
97
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries`, {
|
|
98
|
+
search: options?.search,
|
|
99
|
+
limit: options?.limit,
|
|
100
|
+
offset: options?.offset,
|
|
101
|
+
project_id: options?.projectId
|
|
102
|
+
});
|
|
103
|
+
const raw = await this.client.request("GET", path);
|
|
104
|
+
return {
|
|
105
|
+
entries: raw.entries.map(mapEntry),
|
|
106
|
+
total: raw.total,
|
|
107
|
+
limit: raw.limit,
|
|
108
|
+
offset: raw.offset
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Add a single entry to a dictionary. */
|
|
112
|
+
async add(dictionaryId, entry, options) {
|
|
113
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries`, {
|
|
114
|
+
project_id: options?.projectId
|
|
115
|
+
});
|
|
116
|
+
const raw = await this.client.request(
|
|
117
|
+
"POST",
|
|
118
|
+
path,
|
|
119
|
+
entryPayload(entry)
|
|
120
|
+
);
|
|
121
|
+
return mapEntry(raw);
|
|
122
|
+
}
|
|
123
|
+
/** Update an existing entry. */
|
|
124
|
+
async update(dictionaryId, entryId, updates, options) {
|
|
125
|
+
const payload = {};
|
|
126
|
+
if (updates.word !== void 0) payload.word = updates.word;
|
|
127
|
+
if (updates.replacement !== void 0) payload.replacement = updates.replacement;
|
|
128
|
+
if (updates.ipa !== void 0) payload.ipa = updates.ipa;
|
|
129
|
+
if (updates.caseSensitive !== void 0) {
|
|
130
|
+
payload.case_sensitive = updates.caseSensitive;
|
|
131
|
+
}
|
|
132
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries/${entryId}`, {
|
|
133
|
+
project_id: options?.projectId
|
|
134
|
+
});
|
|
135
|
+
const raw = await this.client.request(
|
|
136
|
+
"PATCH",
|
|
137
|
+
path,
|
|
138
|
+
payload
|
|
139
|
+
);
|
|
140
|
+
return mapEntry(raw);
|
|
141
|
+
}
|
|
142
|
+
/** Delete a single entry. */
|
|
143
|
+
async delete(dictionaryId, entryId, options) {
|
|
144
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries/${entryId}`, {
|
|
145
|
+
project_id: options?.projectId
|
|
146
|
+
});
|
|
147
|
+
await this.client.request("DELETE", path);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Replace every entry in the dictionary atomically.
|
|
151
|
+
*
|
|
152
|
+
* Each item must have ``word`` and ``replacement``; existing entries
|
|
153
|
+
* whose ``word`` is not in the supplied list are deleted. Idempotent.
|
|
154
|
+
*/
|
|
155
|
+
async replaceAll(dictionaryId, entries, options) {
|
|
156
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries`, {
|
|
157
|
+
project_id: options?.projectId
|
|
158
|
+
});
|
|
159
|
+
const raw = await this.client.request("PUT", path, {
|
|
160
|
+
entries: entries.map(entryPayload)
|
|
161
|
+
});
|
|
162
|
+
return raw;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var DictionariesResource = class {
|
|
166
|
+
constructor(client) {
|
|
167
|
+
this.client = client;
|
|
168
|
+
this.entries = new DictionaryEntriesResource(client);
|
|
169
|
+
}
|
|
170
|
+
/** List every dictionary in the caller's project. */
|
|
171
|
+
async list(options) {
|
|
172
|
+
const path = buildPath("/v1/dictionaries", {
|
|
173
|
+
project_id: options?.projectId
|
|
174
|
+
});
|
|
175
|
+
const raw = await this.client.request("GET", path);
|
|
176
|
+
return raw.dictionaries.map(mapDictionary);
|
|
177
|
+
}
|
|
178
|
+
/** Create a new dictionary scoped to the caller's project. */
|
|
179
|
+
async create(body, options) {
|
|
180
|
+
const path = buildPath("/v1/dictionaries", {
|
|
181
|
+
project_id: options?.projectId
|
|
182
|
+
});
|
|
183
|
+
const payload = { name: body.name };
|
|
184
|
+
if (body.description !== void 0) payload.description = body.description;
|
|
185
|
+
if (body.language !== void 0) payload.language = body.language;
|
|
186
|
+
const raw = await this.client.request(
|
|
187
|
+
"POST",
|
|
188
|
+
path,
|
|
189
|
+
payload
|
|
190
|
+
);
|
|
191
|
+
return mapDictionary(raw);
|
|
192
|
+
}
|
|
193
|
+
/** Fetch a single dictionary. */
|
|
194
|
+
async get(dictionaryId, options) {
|
|
195
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}`, {
|
|
196
|
+
project_id: options?.projectId
|
|
197
|
+
});
|
|
198
|
+
const raw = await this.client.request("GET", path);
|
|
199
|
+
return mapDictionary(raw);
|
|
200
|
+
}
|
|
201
|
+
/** Update name / description / language / isActive. */
|
|
202
|
+
async update(dictionaryId, updates, options) {
|
|
203
|
+
const payload = {};
|
|
204
|
+
if (updates.name !== void 0) payload.name = updates.name;
|
|
205
|
+
if (updates.description !== void 0) payload.description = updates.description;
|
|
206
|
+
if (updates.language !== void 0) payload.language = updates.language;
|
|
207
|
+
if (updates.isActive !== void 0) payload.is_active = updates.isActive;
|
|
208
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}`, {
|
|
209
|
+
project_id: options?.projectId
|
|
210
|
+
});
|
|
211
|
+
const raw = await this.client.request(
|
|
212
|
+
"PATCH",
|
|
213
|
+
path,
|
|
214
|
+
payload
|
|
215
|
+
);
|
|
216
|
+
return mapDictionary(raw);
|
|
217
|
+
}
|
|
218
|
+
/** Delete a dictionary (cascades to its entries). */
|
|
219
|
+
async delete(dictionaryId, options) {
|
|
220
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}`, {
|
|
221
|
+
project_id: options?.projectId
|
|
222
|
+
});
|
|
223
|
+
await this.client.request("DELETE", path);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
43
227
|
// src/errors.ts
|
|
44
228
|
var ErrorCodes = {
|
|
45
229
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
@@ -112,6 +296,17 @@ var ConnectionError = class _ConnectionError extends KugelAudioError {
|
|
|
112
296
|
Object.setPrototypeOf(this, _ConnectionError.prototype);
|
|
113
297
|
}
|
|
114
298
|
};
|
|
299
|
+
var NotFoundError = class _NotFoundError extends KugelAudioError {
|
|
300
|
+
constructor(message, options = {}) {
|
|
301
|
+
super(message ?? "Not found.", {
|
|
302
|
+
statusCode: 404,
|
|
303
|
+
errorCode: ErrorCodes.NOT_FOUND,
|
|
304
|
+
...options
|
|
305
|
+
});
|
|
306
|
+
this.name = "NotFoundError";
|
|
307
|
+
Object.setPrototypeOf(this, _NotFoundError.prototype);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
115
310
|
function build(status, errorCode, message, opts = {}) {
|
|
116
311
|
const common = { ...opts };
|
|
117
312
|
if (status !== void 0) common.statusCode = status;
|
|
@@ -135,6 +330,9 @@ function build(status, errorCode, message, opts = {}) {
|
|
|
135
330
|
common
|
|
136
331
|
);
|
|
137
332
|
}
|
|
333
|
+
if (errorCode === ErrorCodes.NOT_FOUND || status === 404) {
|
|
334
|
+
return new NotFoundError(message || void 0, common);
|
|
335
|
+
}
|
|
138
336
|
return new KugelAudioError(message || `HTTP ${status}`, common);
|
|
139
337
|
}
|
|
140
338
|
function readHeader(headers, name) {
|
|
@@ -312,11 +510,9 @@ function getWebSocket() {
|
|
|
312
510
|
}
|
|
313
511
|
|
|
314
512
|
// src/client.ts
|
|
315
|
-
var
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
global: "https://global-api.kugelaudio.com"
|
|
319
|
-
};
|
|
513
|
+
var DEFAULT_API_URL = "https://api.kugelaudio.com";
|
|
514
|
+
var EU_API_URL = "https://api.eu.kugelaudio.com";
|
|
515
|
+
var SUPPORTED_REGIONS = ["eu", "us", "global"];
|
|
320
516
|
var REGION_PREFIXES = ["eu-", "us-", "global-"];
|
|
321
517
|
function parseApiKey(apiKey) {
|
|
322
518
|
for (const prefix of REGION_PREFIXES) {
|
|
@@ -1539,19 +1735,23 @@ var KugelAudio = class _KugelAudio {
|
|
|
1539
1735
|
if (options.apiUrl) {
|
|
1540
1736
|
this._apiUrl = options.apiUrl.replace(/\/$/, "");
|
|
1541
1737
|
} else {
|
|
1542
|
-
const effectiveRegion = options.region || detectedRegion
|
|
1543
|
-
if (!
|
|
1738
|
+
const effectiveRegion = options.region || detectedRegion;
|
|
1739
|
+
if (!effectiveRegion) {
|
|
1740
|
+
this._apiUrl = DEFAULT_API_URL;
|
|
1741
|
+
} else if (!SUPPORTED_REGIONS.includes(effectiveRegion)) {
|
|
1544
1742
|
throw new ValidationError(
|
|
1545
|
-
`Invalid region '${effectiveRegion}'. Must be one of: ${
|
|
1743
|
+
`Invalid region '${effectiveRegion}'. Must be one of: ${SUPPORTED_REGIONS.join(", ")}.`
|
|
1546
1744
|
);
|
|
1745
|
+
} else {
|
|
1746
|
+
this._apiUrl = effectiveRegion === "eu" ? EU_API_URL : DEFAULT_API_URL;
|
|
1547
1747
|
}
|
|
1548
|
-
this._apiUrl = REGION_URLS[effectiveRegion];
|
|
1549
1748
|
}
|
|
1550
1749
|
this._ttsUrl = (options.ttsUrl || this._apiUrl).replace(/\/$/, "");
|
|
1551
1750
|
this._timeout = options.timeout || 6e4;
|
|
1552
1751
|
this._keepalivePingInterval = options.keepalivePingInterval !== void 0 ? options.keepalivePingInterval : 2e4;
|
|
1553
1752
|
this.models = new ModelsResource(this);
|
|
1554
1753
|
this.voices = new VoicesResource(this);
|
|
1754
|
+
this.dictionaries = new DictionariesResource(this);
|
|
1555
1755
|
this.tts = new TTSResource(this);
|
|
1556
1756
|
}
|
|
1557
1757
|
/**
|
|
@@ -1718,10 +1918,13 @@ var KugelAudio = class _KugelAudio {
|
|
|
1718
1918
|
0 && (module.exports = {
|
|
1719
1919
|
AuthenticationError,
|
|
1720
1920
|
ConnectionError,
|
|
1921
|
+
DictionariesResource,
|
|
1922
|
+
DictionaryEntriesResource,
|
|
1721
1923
|
ErrorCodes,
|
|
1722
1924
|
InsufficientCreditsError,
|
|
1723
1925
|
KugelAudio,
|
|
1724
1926
|
KugelAudioError,
|
|
1927
|
+
NotFoundError,
|
|
1725
1928
|
RateLimitError,
|
|
1726
1929
|
ValidationError,
|
|
1727
1930
|
WsCloseCodes,
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,187 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
5
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
+
// src/dictionaries.ts
|
|
9
|
+
function mapDictionary(raw) {
|
|
10
|
+
return {
|
|
11
|
+
id: raw.id,
|
|
12
|
+
projectId: raw.project_id,
|
|
13
|
+
name: raw.name,
|
|
14
|
+
description: raw.description ?? void 0,
|
|
15
|
+
language: raw.language ?? void 0,
|
|
16
|
+
isActive: raw.is_active ?? true,
|
|
17
|
+
createdAt: raw.created_at,
|
|
18
|
+
updatedAt: raw.updated_at
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function mapEntry(raw) {
|
|
22
|
+
return {
|
|
23
|
+
id: raw.id,
|
|
24
|
+
dictionaryId: raw.dictionary_id,
|
|
25
|
+
word: raw.word,
|
|
26
|
+
replacement: raw.replacement,
|
|
27
|
+
ipa: raw.ipa ?? void 0,
|
|
28
|
+
caseSensitive: raw.case_sensitive ?? false,
|
|
29
|
+
createdAt: raw.created_at,
|
|
30
|
+
updatedAt: raw.updated_at
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function entryPayload(input) {
|
|
34
|
+
const payload = {
|
|
35
|
+
word: input.word,
|
|
36
|
+
replacement: input.replacement
|
|
37
|
+
};
|
|
38
|
+
if (input.ipa !== void 0) payload.ipa = input.ipa;
|
|
39
|
+
if (input.caseSensitive !== void 0) {
|
|
40
|
+
payload.case_sensitive = input.caseSensitive;
|
|
41
|
+
}
|
|
42
|
+
return payload;
|
|
43
|
+
}
|
|
44
|
+
function buildPath(base, params) {
|
|
45
|
+
const search = new URLSearchParams();
|
|
46
|
+
for (const [key, val] of Object.entries(params)) {
|
|
47
|
+
if (val === void 0 || val === null) continue;
|
|
48
|
+
search.set(key, String(val));
|
|
49
|
+
}
|
|
50
|
+
const query = search.toString();
|
|
51
|
+
return query ? `${base}?${query}` : base;
|
|
52
|
+
}
|
|
53
|
+
var DictionaryEntriesResource = class {
|
|
54
|
+
constructor(client) {
|
|
55
|
+
this.client = client;
|
|
56
|
+
}
|
|
57
|
+
/** List entries with optional search + pagination. */
|
|
58
|
+
async list(dictionaryId, options) {
|
|
59
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries`, {
|
|
60
|
+
search: options?.search,
|
|
61
|
+
limit: options?.limit,
|
|
62
|
+
offset: options?.offset,
|
|
63
|
+
project_id: options?.projectId
|
|
64
|
+
});
|
|
65
|
+
const raw = await this.client.request("GET", path);
|
|
66
|
+
return {
|
|
67
|
+
entries: raw.entries.map(mapEntry),
|
|
68
|
+
total: raw.total,
|
|
69
|
+
limit: raw.limit,
|
|
70
|
+
offset: raw.offset
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Add a single entry to a dictionary. */
|
|
74
|
+
async add(dictionaryId, entry, options) {
|
|
75
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries`, {
|
|
76
|
+
project_id: options?.projectId
|
|
77
|
+
});
|
|
78
|
+
const raw = await this.client.request(
|
|
79
|
+
"POST",
|
|
80
|
+
path,
|
|
81
|
+
entryPayload(entry)
|
|
82
|
+
);
|
|
83
|
+
return mapEntry(raw);
|
|
84
|
+
}
|
|
85
|
+
/** Update an existing entry. */
|
|
86
|
+
async update(dictionaryId, entryId, updates, options) {
|
|
87
|
+
const payload = {};
|
|
88
|
+
if (updates.word !== void 0) payload.word = updates.word;
|
|
89
|
+
if (updates.replacement !== void 0) payload.replacement = updates.replacement;
|
|
90
|
+
if (updates.ipa !== void 0) payload.ipa = updates.ipa;
|
|
91
|
+
if (updates.caseSensitive !== void 0) {
|
|
92
|
+
payload.case_sensitive = updates.caseSensitive;
|
|
93
|
+
}
|
|
94
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries/${entryId}`, {
|
|
95
|
+
project_id: options?.projectId
|
|
96
|
+
});
|
|
97
|
+
const raw = await this.client.request(
|
|
98
|
+
"PATCH",
|
|
99
|
+
path,
|
|
100
|
+
payload
|
|
101
|
+
);
|
|
102
|
+
return mapEntry(raw);
|
|
103
|
+
}
|
|
104
|
+
/** Delete a single entry. */
|
|
105
|
+
async delete(dictionaryId, entryId, options) {
|
|
106
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries/${entryId}`, {
|
|
107
|
+
project_id: options?.projectId
|
|
108
|
+
});
|
|
109
|
+
await this.client.request("DELETE", path);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Replace every entry in the dictionary atomically.
|
|
113
|
+
*
|
|
114
|
+
* Each item must have ``word`` and ``replacement``; existing entries
|
|
115
|
+
* whose ``word`` is not in the supplied list are deleted. Idempotent.
|
|
116
|
+
*/
|
|
117
|
+
async replaceAll(dictionaryId, entries, options) {
|
|
118
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}/entries`, {
|
|
119
|
+
project_id: options?.projectId
|
|
120
|
+
});
|
|
121
|
+
const raw = await this.client.request("PUT", path, {
|
|
122
|
+
entries: entries.map(entryPayload)
|
|
123
|
+
});
|
|
124
|
+
return raw;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var DictionariesResource = class {
|
|
128
|
+
constructor(client) {
|
|
129
|
+
this.client = client;
|
|
130
|
+
this.entries = new DictionaryEntriesResource(client);
|
|
131
|
+
}
|
|
132
|
+
/** List every dictionary in the caller's project. */
|
|
133
|
+
async list(options) {
|
|
134
|
+
const path = buildPath("/v1/dictionaries", {
|
|
135
|
+
project_id: options?.projectId
|
|
136
|
+
});
|
|
137
|
+
const raw = await this.client.request("GET", path);
|
|
138
|
+
return raw.dictionaries.map(mapDictionary);
|
|
139
|
+
}
|
|
140
|
+
/** Create a new dictionary scoped to the caller's project. */
|
|
141
|
+
async create(body, options) {
|
|
142
|
+
const path = buildPath("/v1/dictionaries", {
|
|
143
|
+
project_id: options?.projectId
|
|
144
|
+
});
|
|
145
|
+
const payload = { name: body.name };
|
|
146
|
+
if (body.description !== void 0) payload.description = body.description;
|
|
147
|
+
if (body.language !== void 0) payload.language = body.language;
|
|
148
|
+
const raw = await this.client.request(
|
|
149
|
+
"POST",
|
|
150
|
+
path,
|
|
151
|
+
payload
|
|
152
|
+
);
|
|
153
|
+
return mapDictionary(raw);
|
|
154
|
+
}
|
|
155
|
+
/** Fetch a single dictionary. */
|
|
156
|
+
async get(dictionaryId, options) {
|
|
157
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}`, {
|
|
158
|
+
project_id: options?.projectId
|
|
159
|
+
});
|
|
160
|
+
const raw = await this.client.request("GET", path);
|
|
161
|
+
return mapDictionary(raw);
|
|
162
|
+
}
|
|
163
|
+
/** Update name / description / language / isActive. */
|
|
164
|
+
async update(dictionaryId, updates, options) {
|
|
165
|
+
const payload = {};
|
|
166
|
+
if (updates.name !== void 0) payload.name = updates.name;
|
|
167
|
+
if (updates.description !== void 0) payload.description = updates.description;
|
|
168
|
+
if (updates.language !== void 0) payload.language = updates.language;
|
|
169
|
+
if (updates.isActive !== void 0) payload.is_active = updates.isActive;
|
|
170
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}`, {
|
|
171
|
+
project_id: options?.projectId
|
|
172
|
+
});
|
|
173
|
+
const raw = await this.client.request(
|
|
174
|
+
"PATCH",
|
|
175
|
+
path,
|
|
176
|
+
payload
|
|
177
|
+
);
|
|
178
|
+
return mapDictionary(raw);
|
|
179
|
+
}
|
|
180
|
+
/** Delete a dictionary (cascades to its entries). */
|
|
181
|
+
async delete(dictionaryId, options) {
|
|
182
|
+
const path = buildPath(`/v1/dictionaries/${dictionaryId}`, {
|
|
183
|
+
project_id: options?.projectId
|
|
184
|
+
});
|
|
185
|
+
await this.client.request("DELETE", path);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
8
189
|
// src/errors.ts
|
|
9
190
|
var ErrorCodes = {
|
|
10
191
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
@@ -77,6 +258,17 @@ var ConnectionError = class _ConnectionError extends KugelAudioError {
|
|
|
77
258
|
Object.setPrototypeOf(this, _ConnectionError.prototype);
|
|
78
259
|
}
|
|
79
260
|
};
|
|
261
|
+
var NotFoundError = class _NotFoundError extends KugelAudioError {
|
|
262
|
+
constructor(message, options = {}) {
|
|
263
|
+
super(message ?? "Not found.", {
|
|
264
|
+
statusCode: 404,
|
|
265
|
+
errorCode: ErrorCodes.NOT_FOUND,
|
|
266
|
+
...options
|
|
267
|
+
});
|
|
268
|
+
this.name = "NotFoundError";
|
|
269
|
+
Object.setPrototypeOf(this, _NotFoundError.prototype);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
80
272
|
function build(status, errorCode, message, opts = {}) {
|
|
81
273
|
const common = { ...opts };
|
|
82
274
|
if (status !== void 0) common.statusCode = status;
|
|
@@ -100,6 +292,9 @@ function build(status, errorCode, message, opts = {}) {
|
|
|
100
292
|
common
|
|
101
293
|
);
|
|
102
294
|
}
|
|
295
|
+
if (errorCode === ErrorCodes.NOT_FOUND || status === 404) {
|
|
296
|
+
return new NotFoundError(message || void 0, common);
|
|
297
|
+
}
|
|
103
298
|
return new KugelAudioError(message || `HTTP ${status}`, common);
|
|
104
299
|
}
|
|
105
300
|
function readHeader(headers, name) {
|
|
@@ -277,11 +472,9 @@ function getWebSocket() {
|
|
|
277
472
|
}
|
|
278
473
|
|
|
279
474
|
// src/client.ts
|
|
280
|
-
var
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
global: "https://global-api.kugelaudio.com"
|
|
284
|
-
};
|
|
475
|
+
var DEFAULT_API_URL = "https://api.kugelaudio.com";
|
|
476
|
+
var EU_API_URL = "https://api.eu.kugelaudio.com";
|
|
477
|
+
var SUPPORTED_REGIONS = ["eu", "us", "global"];
|
|
285
478
|
var REGION_PREFIXES = ["eu-", "us-", "global-"];
|
|
286
479
|
function parseApiKey(apiKey) {
|
|
287
480
|
for (const prefix of REGION_PREFIXES) {
|
|
@@ -1504,19 +1697,23 @@ var KugelAudio = class _KugelAudio {
|
|
|
1504
1697
|
if (options.apiUrl) {
|
|
1505
1698
|
this._apiUrl = options.apiUrl.replace(/\/$/, "");
|
|
1506
1699
|
} else {
|
|
1507
|
-
const effectiveRegion = options.region || detectedRegion
|
|
1508
|
-
if (!
|
|
1700
|
+
const effectiveRegion = options.region || detectedRegion;
|
|
1701
|
+
if (!effectiveRegion) {
|
|
1702
|
+
this._apiUrl = DEFAULT_API_URL;
|
|
1703
|
+
} else if (!SUPPORTED_REGIONS.includes(effectiveRegion)) {
|
|
1509
1704
|
throw new ValidationError(
|
|
1510
|
-
`Invalid region '${effectiveRegion}'. Must be one of: ${
|
|
1705
|
+
`Invalid region '${effectiveRegion}'. Must be one of: ${SUPPORTED_REGIONS.join(", ")}.`
|
|
1511
1706
|
);
|
|
1707
|
+
} else {
|
|
1708
|
+
this._apiUrl = effectiveRegion === "eu" ? EU_API_URL : DEFAULT_API_URL;
|
|
1512
1709
|
}
|
|
1513
|
-
this._apiUrl = REGION_URLS[effectiveRegion];
|
|
1514
1710
|
}
|
|
1515
1711
|
this._ttsUrl = (options.ttsUrl || this._apiUrl).replace(/\/$/, "");
|
|
1516
1712
|
this._timeout = options.timeout || 6e4;
|
|
1517
1713
|
this._keepalivePingInterval = options.keepalivePingInterval !== void 0 ? options.keepalivePingInterval : 2e4;
|
|
1518
1714
|
this.models = new ModelsResource(this);
|
|
1519
1715
|
this.voices = new VoicesResource(this);
|
|
1716
|
+
this.dictionaries = new DictionariesResource(this);
|
|
1520
1717
|
this.tts = new TTSResource(this);
|
|
1521
1718
|
}
|
|
1522
1719
|
/**
|
|
@@ -1682,10 +1879,13 @@ var KugelAudio = class _KugelAudio {
|
|
|
1682
1879
|
export {
|
|
1683
1880
|
AuthenticationError,
|
|
1684
1881
|
ConnectionError,
|
|
1882
|
+
DictionariesResource,
|
|
1883
|
+
DictionaryEntriesResource,
|
|
1685
1884
|
ErrorCodes,
|
|
1686
1885
|
InsufficientCreditsError,
|
|
1687
1886
|
KugelAudio,
|
|
1688
1887
|
KugelAudioError,
|
|
1888
|
+
NotFoundError,
|
|
1689
1889
|
RateLimitError,
|
|
1690
1890
|
ValidationError,
|
|
1691
1891
|
WsCloseCodes,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kugelaudio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -58,4 +58,4 @@
|
|
|
58
58
|
"tsx": "^4.21.0",
|
|
59
59
|
"ws": "^8.18.0"
|
|
60
60
|
}
|
|
61
|
-
}
|
|
61
|
+
}
|
package/src/client.test.ts
CHANGED
|
@@ -180,42 +180,42 @@ describe('TTSResource.toReadable()', () => {
|
|
|
180
180
|
// ---------------------------------------------------------------------------
|
|
181
181
|
|
|
182
182
|
describe('KugelAudio multi-region', () => {
|
|
183
|
-
it('
|
|
183
|
+
it('uses canonical API when no region or prefix is given', () => {
|
|
184
184
|
const client = new KugelAudio({ apiKey: 'ka_test123' });
|
|
185
185
|
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
it('selects
|
|
188
|
+
it('selects canonical endpoint with explicit region', () => {
|
|
189
189
|
const client = new KugelAudio({ apiKey: 'ka_test123', region: 'us' });
|
|
190
|
-
expect((client as any)._apiUrl).toBe('https://
|
|
190
|
+
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
it('selects global endpoint with explicit region', () => {
|
|
194
194
|
const client = new KugelAudio({ apiKey: 'ka_test123', region: 'global' });
|
|
195
|
-
expect((client as any)._apiUrl).toBe('https://
|
|
195
|
+
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
-
it('detects
|
|
198
|
+
it('detects canonical route from key prefix and strips it', () => {
|
|
199
199
|
const client = new KugelAudio({ apiKey: 'us-ka_test123' });
|
|
200
|
-
expect((client as any)._apiUrl).toBe('https://
|
|
200
|
+
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
201
201
|
expect((client as any)._apiKey).toBe('ka_test123');
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
it('detects global region from key prefix and strips it', () => {
|
|
205
205
|
const client = new KugelAudio({ apiKey: 'global-ka_test123' });
|
|
206
|
-
expect((client as any)._apiUrl).toBe('https://
|
|
206
|
+
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
207
207
|
expect((client as any)._apiKey).toBe('ka_test123');
|
|
208
208
|
});
|
|
209
209
|
|
|
210
210
|
it('detects EU region from key prefix and strips it', () => {
|
|
211
211
|
const client = new KugelAudio({ apiKey: 'eu-ka_test123' });
|
|
212
|
-
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
212
|
+
expect((client as any)._apiUrl).toBe('https://api.eu.kugelaudio.com');
|
|
213
213
|
expect((client as any)._apiKey).toBe('ka_test123');
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
it('explicit region overrides key prefix', () => {
|
|
217
217
|
const client = new KugelAudio({ apiKey: 'us-ka_test123', region: 'global' });
|
|
218
|
-
expect((client as any)._apiUrl).toBe('https://
|
|
218
|
+
expect((client as any)._apiUrl).toBe('https://api.kugelaudio.com');
|
|
219
219
|
expect((client as any)._apiKey).toBe('ka_test123');
|
|
220
220
|
});
|
|
221
221
|
|
|
@@ -237,7 +237,7 @@ describe('KugelAudio multi-region', () => {
|
|
|
237
237
|
|
|
238
238
|
it('ttsUrl defaults to the resolved region URL', () => {
|
|
239
239
|
const client = new KugelAudio({ apiKey: 'us-ka_test123' });
|
|
240
|
-
expect((client as any)._ttsUrl).toBe('https://
|
|
240
|
+
expect((client as any)._ttsUrl).toBe('https://api.kugelaudio.com');
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
it('unprefixed key is not modified', () => {
|
package/src/client.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* KugelAudio API Client.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { DictionariesResource } from './dictionaries';
|
|
5
6
|
import {
|
|
6
7
|
ConnectionError,
|
|
7
8
|
KugelAudioError,
|
|
@@ -33,11 +34,9 @@ import { getWebSocket } from './websocket';
|
|
|
33
34
|
|
|
34
35
|
import type { Region } from './types';
|
|
35
36
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
global: 'https://global-api.kugelaudio.com',
|
|
40
|
-
};
|
|
37
|
+
const DEFAULT_API_URL = 'https://api.kugelaudio.com';
|
|
38
|
+
const EU_API_URL = 'https://api.eu.kugelaudio.com';
|
|
39
|
+
const SUPPORTED_REGIONS = ['eu', 'us', 'global'] as const;
|
|
41
40
|
|
|
42
41
|
const REGION_PREFIXES = ['eu-', 'us-', 'global-'] as const;
|
|
43
42
|
|
|
@@ -1616,6 +1615,8 @@ export class KugelAudio {
|
|
|
1616
1615
|
public readonly models: ModelsResource;
|
|
1617
1616
|
/** Voices resource */
|
|
1618
1617
|
public readonly voices: VoicesResource;
|
|
1618
|
+
/** Custom dictionaries resource */
|
|
1619
|
+
public readonly dictionaries: DictionariesResource;
|
|
1619
1620
|
/** TTS resource */
|
|
1620
1621
|
public readonly tts: TTSResource;
|
|
1621
1622
|
|
|
@@ -1637,13 +1638,16 @@ export class KugelAudio {
|
|
|
1637
1638
|
if (options.apiUrl) {
|
|
1638
1639
|
this._apiUrl = options.apiUrl.replace(/\/$/, '');
|
|
1639
1640
|
} else {
|
|
1640
|
-
const effectiveRegion = options.region || detectedRegion
|
|
1641
|
-
if (!
|
|
1641
|
+
const effectiveRegion = options.region || detectedRegion;
|
|
1642
|
+
if (!effectiveRegion) {
|
|
1643
|
+
this._apiUrl = DEFAULT_API_URL;
|
|
1644
|
+
} else if (!SUPPORTED_REGIONS.includes(effectiveRegion as Region)) {
|
|
1642
1645
|
throw new ValidationError(
|
|
1643
|
-
`Invalid region '${effectiveRegion}'. Must be one of: ${
|
|
1646
|
+
`Invalid region '${effectiveRegion}'. Must be one of: ${SUPPORTED_REGIONS.join(', ')}.`,
|
|
1644
1647
|
);
|
|
1648
|
+
} else {
|
|
1649
|
+
this._apiUrl = effectiveRegion === 'eu' ? EU_API_URL : DEFAULT_API_URL;
|
|
1645
1650
|
}
|
|
1646
|
-
this._apiUrl = REGION_URLS[effectiveRegion as Region];
|
|
1647
1651
|
}
|
|
1648
1652
|
|
|
1649
1653
|
// If ttsUrl not specified, use apiUrl (backend proxies to TTS server)
|
|
@@ -1655,6 +1659,7 @@ export class KugelAudio {
|
|
|
1655
1659
|
|
|
1656
1660
|
this.models = new ModelsResource(this);
|
|
1657
1661
|
this.voices = new VoicesResource(this);
|
|
1662
|
+
this.dictionaries = new DictionariesResource(this);
|
|
1658
1663
|
this.tts = new TTSResource(this);
|
|
1659
1664
|
}
|
|
1660
1665
|
|
|
@@ -1843,4 +1848,3 @@ export class KugelAudio {
|
|
|
1843
1848
|
}
|
|
1844
1849
|
}
|
|
1845
1850
|
}
|
|
1846
|
-
|