@ddysiodev/js-sdk 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 +22 -0
- package/README.md +248 -0
- package/README.zh-CN.md +244 -0
- package/dist/index.cjs +552 -0
- package/dist/index.d.ts +465 -0
- package/dist/index.js +553 -0
- package/package.json +53 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = 'https://ddys.io/api/v1';
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 15000;
|
|
3
|
+
const DEFAULT_USER_AGENT = '@ddysiodev/js-sdk/0.1.0';
|
|
4
|
+
|
|
5
|
+
class DdysApiError extends Error {
|
|
6
|
+
constructor(message, options = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'DdysApiError';
|
|
9
|
+
this.status = options.status;
|
|
10
|
+
this.method = options.method || 'GET';
|
|
11
|
+
this.endpoint = options.endpoint || '';
|
|
12
|
+
this.response = options.response;
|
|
13
|
+
this.cause = options.cause;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class DdysTimeoutError extends DdysApiError {
|
|
18
|
+
constructor(message, options = {}) {
|
|
19
|
+
super(message, options);
|
|
20
|
+
this.name = 'DdysTimeoutError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class DdysNetworkError extends DdysApiError {
|
|
25
|
+
constructor(message, options = {}) {
|
|
26
|
+
super(message, options);
|
|
27
|
+
this.name = 'DdysNetworkError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class DdysParseError extends DdysApiError {
|
|
32
|
+
constructor(message, options = {}) {
|
|
33
|
+
super(message, options);
|
|
34
|
+
this.name = 'DdysParseError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createDdysClient(options = {}) {
|
|
39
|
+
return new DdysClient(options);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class DdysClient {
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl || DEFAULT_BASE_URL);
|
|
45
|
+
this.apiKey = options.apiKey || '';
|
|
46
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
47
|
+
this.fetchImpl = options.fetch || globalThis.fetch;
|
|
48
|
+
this.headers = { ...(options.headers || {}) };
|
|
49
|
+
this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT;
|
|
50
|
+
this.retry = options.retry ?? false;
|
|
51
|
+
|
|
52
|
+
if (typeof this.fetchImpl !== 'function') {
|
|
53
|
+
throw new DdysApiError('No fetch implementation available. Pass options.fetch or use a runtime with global fetch.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.movies = createMovieEndpoints(this);
|
|
57
|
+
this.dictionaries = createDictionaryEndpoints(this);
|
|
58
|
+
this.collections = createCollectionEndpoints(this);
|
|
59
|
+
this.shares = createShareEndpoints(this);
|
|
60
|
+
this.requests = createRequestEndpoints(this);
|
|
61
|
+
this.activities = createActivityEndpoints(this);
|
|
62
|
+
this.users = createUserEndpoints(this);
|
|
63
|
+
this.comments = createCommentEndpoints(this);
|
|
64
|
+
this.reports = createReportEndpoints(this);
|
|
65
|
+
this.follow = createFollowEndpoints(this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async request(path, options = {}) {
|
|
69
|
+
const method = (options.method || 'GET').toUpperCase();
|
|
70
|
+
const endpoint = normalizePath(path);
|
|
71
|
+
const auth = Boolean(options.auth);
|
|
72
|
+
|
|
73
|
+
if (auth && !this.apiKey) {
|
|
74
|
+
throw new DdysApiError('DDYS API key is required for this endpoint.', {
|
|
75
|
+
status: 401,
|
|
76
|
+
method,
|
|
77
|
+
endpoint
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const retryOptions = normalizeRetryOptions(options.retry ?? this.retry, method);
|
|
82
|
+
const maxAttempts = retryOptions ? retryOptions.retries + 1 : 1;
|
|
83
|
+
let attempt = 0;
|
|
84
|
+
let lastError;
|
|
85
|
+
|
|
86
|
+
while (attempt < maxAttempts) {
|
|
87
|
+
attempt++;
|
|
88
|
+
try {
|
|
89
|
+
return await this.requestOnce(endpoint, {
|
|
90
|
+
...options,
|
|
91
|
+
method,
|
|
92
|
+
auth
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
lastError = error;
|
|
96
|
+
if (!shouldRetry(error, retryOptions, method, attempt, maxAttempts)) {
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
await delay(retryOptions.delayMs * attempt);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw lastError;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async requestOnce(endpoint, options) {
|
|
107
|
+
const method = options.method;
|
|
108
|
+
const query = normalizeQuery(options.query);
|
|
109
|
+
const url = buildUrl(this.baseUrl, endpoint, query);
|
|
110
|
+
const headers = buildHeaders(this, options);
|
|
111
|
+
const timeout = createTimeoutController(options.signal, options.timeoutMs ?? this.timeoutMs);
|
|
112
|
+
|
|
113
|
+
const init = {
|
|
114
|
+
method,
|
|
115
|
+
headers,
|
|
116
|
+
signal: timeout.signal
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
if (options.body !== undefined) {
|
|
120
|
+
init.body = JSON.stringify(options.body);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let response;
|
|
124
|
+
try {
|
|
125
|
+
response = await this.fetchImpl(url, init);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (timeout.timedOut()) {
|
|
128
|
+
throw new DdysTimeoutError(`Request timed out after ${options.timeoutMs ?? this.timeoutMs}ms.`, {
|
|
129
|
+
method,
|
|
130
|
+
endpoint,
|
|
131
|
+
cause: error
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
throw new DdysNetworkError(error?.message || 'Network request failed.', {
|
|
135
|
+
method,
|
|
136
|
+
endpoint,
|
|
137
|
+
cause: error
|
|
138
|
+
});
|
|
139
|
+
} finally {
|
|
140
|
+
timeout.cleanup();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const text = await response.text();
|
|
144
|
+
let json;
|
|
145
|
+
try {
|
|
146
|
+
json = text ? JSON.parse(text) : {};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw new DdysParseError('Failed to parse DDYS API response as JSON.', {
|
|
149
|
+
status: response.status,
|
|
150
|
+
method,
|
|
151
|
+
endpoint,
|
|
152
|
+
response: text,
|
|
153
|
+
cause: error
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!response.ok || json?.success === false) {
|
|
158
|
+
throw new DdysApiError(json?.message || `HTTP ${response.status}`, {
|
|
159
|
+
status: response.status,
|
|
160
|
+
method,
|
|
161
|
+
endpoint,
|
|
162
|
+
response: json
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (json?.success !== true) {
|
|
167
|
+
throw new DdysParseError('DDYS API response is missing success=true.', {
|
|
168
|
+
status: response.status,
|
|
169
|
+
method,
|
|
170
|
+
endpoint,
|
|
171
|
+
response: json
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return json;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async get(path, query, options = {}) {
|
|
179
|
+
return this.request(path, { ...options, method: 'GET', query });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async post(path, body, options = {}) {
|
|
183
|
+
return this.request(path, { ...options, method: 'POST', body });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async delete(path, options = {}) {
|
|
187
|
+
return this.request(path, { ...options, method: 'DELETE' });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async search(params) {
|
|
191
|
+
return unwrapPaginated(await this.get('/search', params));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async suggest(q) {
|
|
195
|
+
return unwrapData(await this.get('/suggest', { q }));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async hot() {
|
|
199
|
+
return unwrapData(await this.get('/hot'));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async latest(params = {}) {
|
|
203
|
+
return unwrapData(await this.get('/latest', params));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async calendar(params = {}) {
|
|
207
|
+
return unwrapData(await this.get('/calendar', params));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async me() {
|
|
211
|
+
return unwrapData(await this.get('/me', undefined, { auth: true }));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function createMovieEndpoints(client) {
|
|
216
|
+
return {
|
|
217
|
+
list(params = {}) {
|
|
218
|
+
return client.get('/movies', normalizePagination(params)).then(unwrapPaginated);
|
|
219
|
+
},
|
|
220
|
+
detail(slug) {
|
|
221
|
+
assertNonEmpty(slug, 'slug');
|
|
222
|
+
return client.get(`/movies/${encodePathSegment(slug)}`).then(unwrapData);
|
|
223
|
+
},
|
|
224
|
+
sources(slug) {
|
|
225
|
+
assertNonEmpty(slug, 'slug');
|
|
226
|
+
return client.get(`/movies/${encodePathSegment(slug)}/sources`).then(unwrapData);
|
|
227
|
+
},
|
|
228
|
+
related(slug) {
|
|
229
|
+
assertNonEmpty(slug, 'slug');
|
|
230
|
+
return client.get(`/movies/${encodePathSegment(slug)}/related`).then(unwrapData);
|
|
231
|
+
},
|
|
232
|
+
comments(slug, params = {}) {
|
|
233
|
+
assertNonEmpty(slug, 'slug');
|
|
234
|
+
return client.get(`/movies/${encodePathSegment(slug)}/comments`, normalizePagination(params)).then(unwrapPaginated);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function createDictionaryEndpoints(client) {
|
|
240
|
+
return {
|
|
241
|
+
types() {
|
|
242
|
+
return client.get('/types').then(unwrapData);
|
|
243
|
+
},
|
|
244
|
+
genres() {
|
|
245
|
+
return client.get('/genres').then(unwrapData);
|
|
246
|
+
},
|
|
247
|
+
regions() {
|
|
248
|
+
return client.get('/regions').then(unwrapData);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function createCollectionEndpoints(client) {
|
|
254
|
+
return {
|
|
255
|
+
list(params = {}) {
|
|
256
|
+
return client.get('/collections', normalizePagination(params)).then(unwrapPaginated);
|
|
257
|
+
},
|
|
258
|
+
detail(slug, params = {}) {
|
|
259
|
+
assertNonEmpty(slug, 'slug');
|
|
260
|
+
return client.get(`/collections/${encodePathSegment(slug)}`, normalizePagination(params)).then((envelope) => ({
|
|
261
|
+
...unwrapData(envelope),
|
|
262
|
+
meta: envelope.meta
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function createShareEndpoints(client) {
|
|
269
|
+
return {
|
|
270
|
+
list(params = {}) {
|
|
271
|
+
return client.get('/shares', normalizePagination(params)).then(unwrapPaginated);
|
|
272
|
+
},
|
|
273
|
+
detail(id) {
|
|
274
|
+
assertPositiveInteger(id, 'id');
|
|
275
|
+
return client.get(`/shares/${id}`).then(unwrapData);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function createRequestEndpoints(client) {
|
|
281
|
+
return {
|
|
282
|
+
list(params = {}) {
|
|
283
|
+
return client.get('/requests', normalizePagination(params)).then(unwrapPaginated);
|
|
284
|
+
},
|
|
285
|
+
create(input) {
|
|
286
|
+
assertObject(input, 'input');
|
|
287
|
+
return client.post('/requests', input, { auth: true }).then(unwrapData);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function createActivityEndpoints(client) {
|
|
293
|
+
return {
|
|
294
|
+
list(params = {}) {
|
|
295
|
+
return client.get('/activities', normalizePagination(params)).then(unwrapPaginated);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function createUserEndpoints(client) {
|
|
301
|
+
return {
|
|
302
|
+
profile(username) {
|
|
303
|
+
assertNonEmpty(username, 'username');
|
|
304
|
+
return client.get(`/user/${encodePathSegment(username)}`).then(unwrapData);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function createCommentEndpoints(client) {
|
|
310
|
+
return {
|
|
311
|
+
create(input) {
|
|
312
|
+
assertObject(input, 'input');
|
|
313
|
+
return client.post('/comments', input, { auth: true }).then(unwrapData);
|
|
314
|
+
},
|
|
315
|
+
delete(id) {
|
|
316
|
+
assertPositiveInteger(id, 'id');
|
|
317
|
+
return client.delete(`/comments/${id}`, { auth: true }).then(unwrapData);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function createReportEndpoints(client) {
|
|
323
|
+
return {
|
|
324
|
+
invalidResource(input) {
|
|
325
|
+
assertObject(input, 'input');
|
|
326
|
+
return client.post('/report', input, { auth: true }).then(unwrapData);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function createFollowEndpoints(client) {
|
|
332
|
+
return {
|
|
333
|
+
set(input) {
|
|
334
|
+
assertObject(input, 'input');
|
|
335
|
+
return client.post('/follow', input, { auth: true }).then(unwrapData);
|
|
336
|
+
},
|
|
337
|
+
follow(username) {
|
|
338
|
+
assertNonEmpty(username, 'username');
|
|
339
|
+
return client.post('/follow', { username, action: 'follow' }, { auth: true }).then(unwrapData);
|
|
340
|
+
},
|
|
341
|
+
unfollow(username) {
|
|
342
|
+
assertNonEmpty(username, 'username');
|
|
343
|
+
return client.post('/follow', { username, action: 'unfollow' }, { auth: true }).then(unwrapData);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function normalizeBaseUrl(baseUrl) {
|
|
349
|
+
return String(baseUrl).replace(/\/+$/, '');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function normalizePath(path) {
|
|
353
|
+
const value = String(path || '');
|
|
354
|
+
return value.startsWith('/') ? value : `/${value}`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function encodePathSegment(value) {
|
|
358
|
+
return encodeURIComponent(String(value));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function buildUrl(baseUrl, path, query) {
|
|
362
|
+
const url = new URL(`${baseUrl}${normalizePath(path)}`);
|
|
363
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
364
|
+
if (value === undefined || value === null || value === '') {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (Array.isArray(value)) {
|
|
368
|
+
for (const item of value) {
|
|
369
|
+
if (item !== undefined && item !== null && item !== '') {
|
|
370
|
+
url.searchParams.append(key, String(item));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
url.searchParams.set(key, String(value));
|
|
376
|
+
}
|
|
377
|
+
return url.toString();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function normalizeQuery(query) {
|
|
381
|
+
if (!query) {
|
|
382
|
+
return {};
|
|
383
|
+
}
|
|
384
|
+
return normalizePagination(query);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function normalizePagination(params = {}) {
|
|
388
|
+
const normalized = { ...params };
|
|
389
|
+
if (normalized.perPage !== undefined && normalized.per_page === undefined) {
|
|
390
|
+
normalized.per_page = normalized.perPage;
|
|
391
|
+
}
|
|
392
|
+
delete normalized.perPage;
|
|
393
|
+
return normalized;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function buildHeaders(client, options) {
|
|
397
|
+
const headers = {
|
|
398
|
+
Accept: 'application/json',
|
|
399
|
+
...client.headers,
|
|
400
|
+
...(options.headers || {})
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
if (options.body !== undefined) {
|
|
404
|
+
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (options.auth) {
|
|
408
|
+
headers.Authorization = `Bearer ${client.apiKey}`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (client.userAgent && isNodeRuntime()) {
|
|
412
|
+
headers['User-Agent'] = client.userAgent;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return headers;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function isBrowserRuntime() {
|
|
419
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function isNodeRuntime() {
|
|
423
|
+
return typeof process !== 'undefined' && Boolean(process.versions?.node) && !isBrowserRuntime();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function createTimeoutController(externalSignal, timeoutMs) {
|
|
427
|
+
if (typeof AbortController !== 'function') {
|
|
428
|
+
return {
|
|
429
|
+
signal: externalSignal,
|
|
430
|
+
cleanup() {},
|
|
431
|
+
timedOut() {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const controller = new AbortController();
|
|
438
|
+
let didTimeout = false;
|
|
439
|
+
let timeoutId;
|
|
440
|
+
|
|
441
|
+
const abortFromExternal = () => {
|
|
442
|
+
controller.abort(externalSignal?.reason);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
if (externalSignal?.aborted) {
|
|
446
|
+
abortFromExternal();
|
|
447
|
+
} else if (externalSignal) {
|
|
448
|
+
externalSignal.addEventListener('abort', abortFromExternal, { once: true });
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (timeoutMs > 0) {
|
|
452
|
+
timeoutId = setTimeout(() => {
|
|
453
|
+
didTimeout = true;
|
|
454
|
+
controller.abort(new Error(`Request timed out after ${timeoutMs}ms.`));
|
|
455
|
+
}, timeoutMs);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
signal: controller.signal,
|
|
460
|
+
cleanup() {
|
|
461
|
+
if (timeoutId) {
|
|
462
|
+
clearTimeout(timeoutId);
|
|
463
|
+
}
|
|
464
|
+
if (externalSignal) {
|
|
465
|
+
externalSignal.removeEventListener('abort', abortFromExternal);
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
timedOut() {
|
|
469
|
+
return didTimeout;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function normalizeRetryOptions(retry, method) {
|
|
475
|
+
if (!retry || method !== 'GET') {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
if (retry === true) {
|
|
479
|
+
return {
|
|
480
|
+
retries: 1,
|
|
481
|
+
delayMs: 250,
|
|
482
|
+
statuses: [500, 502, 503, 504]
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
retries: Math.max(0, Number(retry.retries ?? 1)),
|
|
487
|
+
delayMs: Math.max(0, Number(retry.delayMs ?? 250)),
|
|
488
|
+
statuses: retry.statuses || [500, 502, 503, 504]
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function shouldRetry(error, retryOptions, method, attempt, maxAttempts) {
|
|
493
|
+
if (!retryOptions || method !== 'GET' || attempt >= maxAttempts) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
if (error instanceof DdysNetworkError || error instanceof DdysTimeoutError) {
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
return retryOptions.statuses.includes(error?.status);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function delay(ms) {
|
|
503
|
+
if (!ms) {
|
|
504
|
+
return Promise.resolve();
|
|
505
|
+
}
|
|
506
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function unwrapData(envelope) {
|
|
510
|
+
return envelope.data;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function unwrapPaginated(envelope) {
|
|
514
|
+
return {
|
|
515
|
+
data: envelope.data || [],
|
|
516
|
+
meta: envelope.meta || {
|
|
517
|
+
total: 0,
|
|
518
|
+
page: 1,
|
|
519
|
+
per_page: Array.isArray(envelope.data) ? envelope.data.length : 0,
|
|
520
|
+
total_pages: 1
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function assertNonEmpty(value, name) {
|
|
526
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
527
|
+
throw new DdysApiError(`${name} is required.`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function assertPositiveInteger(value, name) {
|
|
532
|
+
if (!Number.isInteger(Number(value)) || Number(value) <= 0) {
|
|
533
|
+
throw new DdysApiError(`${name} must be a positive integer.`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function assertObject(value, name) {
|
|
538
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
539
|
+
throw new DdysApiError(`${name} must be an object.`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export {
|
|
544
|
+
DEFAULT_BASE_URL,
|
|
545
|
+
DdysApiError,
|
|
546
|
+
DdysNetworkError,
|
|
547
|
+
DdysParseError,
|
|
548
|
+
DdysTimeoutError,
|
|
549
|
+
DdysClient,
|
|
550
|
+
createDdysClient
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
export default createDdysClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ddysiodev/js-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official JavaScript SDK for the DDYS Open API.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"README.zh-CN.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "node scripts/build.mjs",
|
|
29
|
+
"test": "node scripts/build.mjs && node --test test/*.test.mjs",
|
|
30
|
+
"prepack": "node scripts/build.mjs && node --test test/*.test.mjs",
|
|
31
|
+
"smoke:live": "node test/live-smoke.mjs"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"ddys",
|
|
35
|
+
"movie",
|
|
36
|
+
"sdk",
|
|
37
|
+
"api",
|
|
38
|
+
"typescript",
|
|
39
|
+
"fetch"
|
|
40
|
+
],
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/ddysiodev/ddys-js-sdk.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/ddysiodev/ddys-js-sdk/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/ddysiodev/ddys-js-sdk#readme",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"provenance": false
|
|
52
|
+
}
|
|
53
|
+
}
|