@dyrected/sdk 0.0.1 → 1.0.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.md +50 -0
- package/README.md +46 -1
- package/dist/index.cjs +187 -465
- package/dist/index.d.cts +118 -13
- package/dist/index.d.ts +118 -13
- package/dist/index.js +186 -465
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,448 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import qs from "qs";
|
|
3
3
|
|
|
4
|
-
// ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/request/constants.js
|
|
5
|
-
var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
|
|
6
|
-
|
|
7
|
-
// ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/utils/body.js
|
|
8
|
-
var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
9
|
-
const { all = false, dot = false } = options;
|
|
10
|
-
const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
|
|
11
|
-
const contentType = headers.get("Content-Type");
|
|
12
|
-
if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
|
|
13
|
-
return parseFormData(request, { all, dot });
|
|
14
|
-
}
|
|
15
|
-
return {};
|
|
16
|
-
};
|
|
17
|
-
async function parseFormData(request, options) {
|
|
18
|
-
const formData = await request.formData();
|
|
19
|
-
if (formData) {
|
|
20
|
-
return convertFormDataToBodyData(formData, options);
|
|
21
|
-
}
|
|
22
|
-
return {};
|
|
23
|
-
}
|
|
24
|
-
function convertFormDataToBodyData(formData, options) {
|
|
25
|
-
const form = /* @__PURE__ */ Object.create(null);
|
|
26
|
-
formData.forEach((value, key) => {
|
|
27
|
-
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
28
|
-
if (!shouldParseAllValues) {
|
|
29
|
-
form[key] = value;
|
|
30
|
-
} else {
|
|
31
|
-
handleParsingAllValues(form, key, value);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
if (options.dot) {
|
|
35
|
-
Object.entries(form).forEach(([key, value]) => {
|
|
36
|
-
const shouldParseDotValues = key.includes(".");
|
|
37
|
-
if (shouldParseDotValues) {
|
|
38
|
-
handleParsingNestedValues(form, key, value);
|
|
39
|
-
delete form[key];
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
return form;
|
|
44
|
-
}
|
|
45
|
-
var handleParsingAllValues = (form, key, value) => {
|
|
46
|
-
if (form[key] !== void 0) {
|
|
47
|
-
if (Array.isArray(form[key])) {
|
|
48
|
-
;
|
|
49
|
-
form[key].push(value);
|
|
50
|
-
} else {
|
|
51
|
-
form[key] = [form[key], value];
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
if (!key.endsWith("[]")) {
|
|
55
|
-
form[key] = value;
|
|
56
|
-
} else {
|
|
57
|
-
form[key] = [value];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
var handleParsingNestedValues = (form, key, value) => {
|
|
62
|
-
if (/(?:^|\.)__proto__\./.test(key)) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
let nestedForm = form;
|
|
66
|
-
const keys = key.split(".");
|
|
67
|
-
keys.forEach((key2, index) => {
|
|
68
|
-
if (index === keys.length - 1) {
|
|
69
|
-
nestedForm[key2] = value;
|
|
70
|
-
} else {
|
|
71
|
-
if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {
|
|
72
|
-
nestedForm[key2] = /* @__PURE__ */ Object.create(null);
|
|
73
|
-
}
|
|
74
|
-
nestedForm = nestedForm[key2];
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/utils/url.js
|
|
80
|
-
var tryDecode = (str, decoder) => {
|
|
81
|
-
try {
|
|
82
|
-
return decoder(str);
|
|
83
|
-
} catch {
|
|
84
|
-
return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match2) => {
|
|
85
|
-
try {
|
|
86
|
-
return decoder(match2);
|
|
87
|
-
} catch {
|
|
88
|
-
return match2;
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
var _decodeURI = (value) => {
|
|
94
|
-
if (!/[%+]/.test(value)) {
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
if (value.indexOf("+") !== -1) {
|
|
98
|
-
value = value.replace(/\+/g, " ");
|
|
99
|
-
}
|
|
100
|
-
return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
|
|
101
|
-
};
|
|
102
|
-
var _getQueryParam = (url, key, multiple) => {
|
|
103
|
-
let encoded;
|
|
104
|
-
if (!multiple && key && !/[%+]/.test(key)) {
|
|
105
|
-
let keyIndex2 = url.indexOf("?", 8);
|
|
106
|
-
if (keyIndex2 === -1) {
|
|
107
|
-
return void 0;
|
|
108
|
-
}
|
|
109
|
-
if (!url.startsWith(key, keyIndex2 + 1)) {
|
|
110
|
-
keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
|
|
111
|
-
}
|
|
112
|
-
while (keyIndex2 !== -1) {
|
|
113
|
-
const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1);
|
|
114
|
-
if (trailingKeyCode === 61) {
|
|
115
|
-
const valueIndex = keyIndex2 + key.length + 2;
|
|
116
|
-
const endIndex = url.indexOf("&", valueIndex);
|
|
117
|
-
return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex));
|
|
118
|
-
} else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) {
|
|
119
|
-
return "";
|
|
120
|
-
}
|
|
121
|
-
keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
|
|
122
|
-
}
|
|
123
|
-
encoded = /[%+]/.test(url);
|
|
124
|
-
if (!encoded) {
|
|
125
|
-
return void 0;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const results = {};
|
|
129
|
-
encoded ??= /[%+]/.test(url);
|
|
130
|
-
let keyIndex = url.indexOf("?", 8);
|
|
131
|
-
while (keyIndex !== -1) {
|
|
132
|
-
const nextKeyIndex = url.indexOf("&", keyIndex + 1);
|
|
133
|
-
let valueIndex = url.indexOf("=", keyIndex);
|
|
134
|
-
if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) {
|
|
135
|
-
valueIndex = -1;
|
|
136
|
-
}
|
|
137
|
-
let name = url.slice(
|
|
138
|
-
keyIndex + 1,
|
|
139
|
-
valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex
|
|
140
|
-
);
|
|
141
|
-
if (encoded) {
|
|
142
|
-
name = _decodeURI(name);
|
|
143
|
-
}
|
|
144
|
-
keyIndex = nextKeyIndex;
|
|
145
|
-
if (name === "") {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
let value;
|
|
149
|
-
if (valueIndex === -1) {
|
|
150
|
-
value = "";
|
|
151
|
-
} else {
|
|
152
|
-
value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex);
|
|
153
|
-
if (encoded) {
|
|
154
|
-
value = _decodeURI(value);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (multiple) {
|
|
158
|
-
if (!(results[name] && Array.isArray(results[name]))) {
|
|
159
|
-
results[name] = [];
|
|
160
|
-
}
|
|
161
|
-
;
|
|
162
|
-
results[name].push(value);
|
|
163
|
-
} else {
|
|
164
|
-
results[name] ??= value;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return key ? results[key] : results;
|
|
168
|
-
};
|
|
169
|
-
var getQueryParam = _getQueryParam;
|
|
170
|
-
var getQueryParams = (url, key) => {
|
|
171
|
-
return _getQueryParam(url, key, true);
|
|
172
|
-
};
|
|
173
|
-
var decodeURIComponent_ = decodeURIComponent;
|
|
174
|
-
|
|
175
|
-
// ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/request.js
|
|
176
|
-
var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
|
|
177
|
-
var HonoRequest = class {
|
|
178
|
-
/**
|
|
179
|
-
* `.raw` can get the raw Request object.
|
|
180
|
-
*
|
|
181
|
-
* @see {@link https://hono.dev/docs/api/request#raw}
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```ts
|
|
185
|
-
* // For Cloudflare Workers
|
|
186
|
-
* app.post('/', async (c) => {
|
|
187
|
-
* const metadata = c.req.raw.cf?.hostMetadata?
|
|
188
|
-
* ...
|
|
189
|
-
* })
|
|
190
|
-
* ```
|
|
191
|
-
*/
|
|
192
|
-
raw;
|
|
193
|
-
#validatedData;
|
|
194
|
-
// Short name of validatedData
|
|
195
|
-
#matchResult;
|
|
196
|
-
routeIndex = 0;
|
|
197
|
-
/**
|
|
198
|
-
* `.path` can get the pathname of the request.
|
|
199
|
-
*
|
|
200
|
-
* @see {@link https://hono.dev/docs/api/request#path}
|
|
201
|
-
*
|
|
202
|
-
* @example
|
|
203
|
-
* ```ts
|
|
204
|
-
* app.get('/about/me', (c) => {
|
|
205
|
-
* const pathname = c.req.path // `/about/me`
|
|
206
|
-
* })
|
|
207
|
-
* ```
|
|
208
|
-
*/
|
|
209
|
-
path;
|
|
210
|
-
bodyCache = {};
|
|
211
|
-
constructor(request, path = "/", matchResult = [[]]) {
|
|
212
|
-
this.raw = request;
|
|
213
|
-
this.path = path;
|
|
214
|
-
this.#matchResult = matchResult;
|
|
215
|
-
this.#validatedData = {};
|
|
216
|
-
}
|
|
217
|
-
param(key) {
|
|
218
|
-
return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
|
|
219
|
-
}
|
|
220
|
-
#getDecodedParam(key) {
|
|
221
|
-
const paramKey = this.#matchResult[0][this.routeIndex][1][key];
|
|
222
|
-
const param = this.#getParamValue(paramKey);
|
|
223
|
-
return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
|
|
224
|
-
}
|
|
225
|
-
#getAllDecodedParams() {
|
|
226
|
-
const decoded = {};
|
|
227
|
-
const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
|
|
228
|
-
for (const key of keys) {
|
|
229
|
-
const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
|
|
230
|
-
if (value !== void 0) {
|
|
231
|
-
decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return decoded;
|
|
235
|
-
}
|
|
236
|
-
#getParamValue(paramKey) {
|
|
237
|
-
return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
|
|
238
|
-
}
|
|
239
|
-
query(key) {
|
|
240
|
-
return getQueryParam(this.url, key);
|
|
241
|
-
}
|
|
242
|
-
queries(key) {
|
|
243
|
-
return getQueryParams(this.url, key);
|
|
244
|
-
}
|
|
245
|
-
header(name) {
|
|
246
|
-
if (name) {
|
|
247
|
-
return this.raw.headers.get(name) ?? void 0;
|
|
248
|
-
}
|
|
249
|
-
const headerData = {};
|
|
250
|
-
this.raw.headers.forEach((value, key) => {
|
|
251
|
-
headerData[key] = value;
|
|
252
|
-
});
|
|
253
|
-
return headerData;
|
|
254
|
-
}
|
|
255
|
-
async parseBody(options) {
|
|
256
|
-
return parseBody(this, options);
|
|
257
|
-
}
|
|
258
|
-
#cachedBody = (key) => {
|
|
259
|
-
const { bodyCache, raw } = this;
|
|
260
|
-
const cachedBody = bodyCache[key];
|
|
261
|
-
if (cachedBody) {
|
|
262
|
-
return cachedBody;
|
|
263
|
-
}
|
|
264
|
-
const anyCachedKey = Object.keys(bodyCache)[0];
|
|
265
|
-
if (anyCachedKey) {
|
|
266
|
-
return bodyCache[anyCachedKey].then((body) => {
|
|
267
|
-
if (anyCachedKey === "json") {
|
|
268
|
-
body = JSON.stringify(body);
|
|
269
|
-
}
|
|
270
|
-
return new Response(body)[key]();
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
return bodyCache[key] = raw[key]();
|
|
274
|
-
};
|
|
275
|
-
/**
|
|
276
|
-
* `.json()` can parse Request body of type `application/json`
|
|
277
|
-
*
|
|
278
|
-
* @see {@link https://hono.dev/docs/api/request#json}
|
|
279
|
-
*
|
|
280
|
-
* @example
|
|
281
|
-
* ```ts
|
|
282
|
-
* app.post('/entry', async (c) => {
|
|
283
|
-
* const body = await c.req.json()
|
|
284
|
-
* })
|
|
285
|
-
* ```
|
|
286
|
-
*/
|
|
287
|
-
json() {
|
|
288
|
-
return this.#cachedBody("text").then((text) => JSON.parse(text));
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* `.text()` can parse Request body of type `text/plain`
|
|
292
|
-
*
|
|
293
|
-
* @see {@link https://hono.dev/docs/api/request#text}
|
|
294
|
-
*
|
|
295
|
-
* @example
|
|
296
|
-
* ```ts
|
|
297
|
-
* app.post('/entry', async (c) => {
|
|
298
|
-
* const body = await c.req.text()
|
|
299
|
-
* })
|
|
300
|
-
* ```
|
|
301
|
-
*/
|
|
302
|
-
text() {
|
|
303
|
-
return this.#cachedBody("text");
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* `.arrayBuffer()` parse Request body as an `ArrayBuffer`
|
|
307
|
-
*
|
|
308
|
-
* @see {@link https://hono.dev/docs/api/request#arraybuffer}
|
|
309
|
-
*
|
|
310
|
-
* @example
|
|
311
|
-
* ```ts
|
|
312
|
-
* app.post('/entry', async (c) => {
|
|
313
|
-
* const body = await c.req.arrayBuffer()
|
|
314
|
-
* })
|
|
315
|
-
* ```
|
|
316
|
-
*/
|
|
317
|
-
arrayBuffer() {
|
|
318
|
-
return this.#cachedBody("arrayBuffer");
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Parses the request body as a `Blob`.
|
|
322
|
-
* @example
|
|
323
|
-
* ```ts
|
|
324
|
-
* app.post('/entry', async (c) => {
|
|
325
|
-
* const body = await c.req.blob();
|
|
326
|
-
* });
|
|
327
|
-
* ```
|
|
328
|
-
* @see https://hono.dev/docs/api/request#blob
|
|
329
|
-
*/
|
|
330
|
-
blob() {
|
|
331
|
-
return this.#cachedBody("blob");
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Parses the request body as `FormData`.
|
|
335
|
-
* @example
|
|
336
|
-
* ```ts
|
|
337
|
-
* app.post('/entry', async (c) => {
|
|
338
|
-
* const body = await c.req.formData();
|
|
339
|
-
* });
|
|
340
|
-
* ```
|
|
341
|
-
* @see https://hono.dev/docs/api/request#formdata
|
|
342
|
-
*/
|
|
343
|
-
formData() {
|
|
344
|
-
return this.#cachedBody("formData");
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Adds validated data to the request.
|
|
348
|
-
*
|
|
349
|
-
* @param target - The target of the validation.
|
|
350
|
-
* @param data - The validated data to add.
|
|
351
|
-
*/
|
|
352
|
-
addValidatedData(target, data) {
|
|
353
|
-
this.#validatedData[target] = data;
|
|
354
|
-
}
|
|
355
|
-
valid(target) {
|
|
356
|
-
return this.#validatedData[target];
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* `.url()` can get the request url strings.
|
|
360
|
-
*
|
|
361
|
-
* @see {@link https://hono.dev/docs/api/request#url}
|
|
362
|
-
*
|
|
363
|
-
* @example
|
|
364
|
-
* ```ts
|
|
365
|
-
* app.get('/about/me', (c) => {
|
|
366
|
-
* const url = c.req.url // `http://localhost:8787/about/me`
|
|
367
|
-
* ...
|
|
368
|
-
* })
|
|
369
|
-
* ```
|
|
370
|
-
*/
|
|
371
|
-
get url() {
|
|
372
|
-
return this.raw.url;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* `.method()` can get the method name of the request.
|
|
376
|
-
*
|
|
377
|
-
* @see {@link https://hono.dev/docs/api/request#method}
|
|
378
|
-
*
|
|
379
|
-
* @example
|
|
380
|
-
* ```ts
|
|
381
|
-
* app.get('/about/me', (c) => {
|
|
382
|
-
* const method = c.req.method // `GET`
|
|
383
|
-
* })
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
|
-
get method() {
|
|
387
|
-
return this.raw.method;
|
|
388
|
-
}
|
|
389
|
-
get [GET_MATCH_RESULT]() {
|
|
390
|
-
return this.#matchResult;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* `.matchedRoutes()` can return a matched route in the handler
|
|
394
|
-
*
|
|
395
|
-
* @deprecated
|
|
396
|
-
*
|
|
397
|
-
* Use matchedRoutes helper defined in "hono/route" instead.
|
|
398
|
-
*
|
|
399
|
-
* @see {@link https://hono.dev/docs/api/request#matchedroutes}
|
|
400
|
-
*
|
|
401
|
-
* @example
|
|
402
|
-
* ```ts
|
|
403
|
-
* app.use('*', async function logger(c, next) {
|
|
404
|
-
* await next()
|
|
405
|
-
* c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
|
|
406
|
-
* const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
|
|
407
|
-
* console.log(
|
|
408
|
-
* method,
|
|
409
|
-
* ' ',
|
|
410
|
-
* path,
|
|
411
|
-
* ' '.repeat(Math.max(10 - path.length, 0)),
|
|
412
|
-
* name,
|
|
413
|
-
* i === c.req.routeIndex ? '<- respond from here' : ''
|
|
414
|
-
* )
|
|
415
|
-
* })
|
|
416
|
-
* })
|
|
417
|
-
* ```
|
|
418
|
-
*/
|
|
419
|
-
get matchedRoutes() {
|
|
420
|
-
return this.#matchResult[0].map(([[, route]]) => route);
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* `routePath()` can retrieve the path registered within the handler
|
|
424
|
-
*
|
|
425
|
-
* @deprecated
|
|
426
|
-
*
|
|
427
|
-
* Use routePath helper defined in "hono/route" instead.
|
|
428
|
-
*
|
|
429
|
-
* @see {@link https://hono.dev/docs/api/request#routepath}
|
|
430
|
-
*
|
|
431
|
-
* @example
|
|
432
|
-
* ```ts
|
|
433
|
-
* app.get('/posts/:id', (c) => {
|
|
434
|
-
* return c.json({ path: c.req.routePath })
|
|
435
|
-
* })
|
|
436
|
-
* ```
|
|
437
|
-
*/
|
|
438
|
-
get routePath() {
|
|
439
|
-
return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
// ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
444
|
-
var regExpMetaChars = new Set(".\\+*[^]$()");
|
|
445
|
-
|
|
446
4
|
// src/query-builder.ts
|
|
447
5
|
var QueryBuilder = class {
|
|
448
6
|
constructor(collection, executor) {
|
|
@@ -472,6 +30,10 @@ var QueryBuilder = class {
|
|
|
472
30
|
this.args.depth = depth;
|
|
473
31
|
return this;
|
|
474
32
|
}
|
|
33
|
+
seed(data) {
|
|
34
|
+
this.args.initialData = data;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
475
37
|
async exec() {
|
|
476
38
|
return this.executor(this.collection, this.args);
|
|
477
39
|
}
|
|
@@ -482,28 +44,82 @@ var QueryBuilder = class {
|
|
|
482
44
|
};
|
|
483
45
|
|
|
484
46
|
// src/index.ts
|
|
47
|
+
var DyrectedError = class extends Error {
|
|
48
|
+
statusCode;
|
|
49
|
+
errors;
|
|
50
|
+
constructor(message, statusCode, errors = []) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "DyrectedError";
|
|
53
|
+
this.statusCode = statusCode;
|
|
54
|
+
this.errors = errors;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
485
57
|
var DyrectedClient = class {
|
|
486
58
|
baseUrl;
|
|
487
59
|
headers;
|
|
488
60
|
fetch;
|
|
61
|
+
defaultDepth;
|
|
489
62
|
constructor(config) {
|
|
490
63
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
491
64
|
this.fetch = config.fetch || fetch;
|
|
65
|
+
this.defaultDepth = config.defaultDepth ?? 1;
|
|
492
66
|
this.headers = {
|
|
493
67
|
"Content-Type": "application/json",
|
|
494
|
-
...config.apiKey ? { "
|
|
68
|
+
...config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
69
|
+
...config.siteId ? { "x-site-id": config.siteId } : {},
|
|
495
70
|
...config.headers
|
|
496
71
|
};
|
|
497
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Update the Authorization header with a Bearer token.
|
|
75
|
+
* Call this after a successful login.
|
|
76
|
+
*/
|
|
77
|
+
setToken(token) {
|
|
78
|
+
this.headers["Authorization"] = `Bearer ${token}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Remove the Authorization header.
|
|
82
|
+
* Call this after logout.
|
|
83
|
+
*/
|
|
84
|
+
clearToken() {
|
|
85
|
+
delete this.headers["Authorization"];
|
|
86
|
+
}
|
|
498
87
|
getBaseUrl() {
|
|
499
88
|
return this.baseUrl;
|
|
500
89
|
}
|
|
501
90
|
async getSchemas() {
|
|
502
91
|
return this.request("/api/schemas");
|
|
503
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Fetch draft data for a specific preview token.
|
|
95
|
+
* Used in "token" preview mode.
|
|
96
|
+
*/
|
|
97
|
+
async getPreviewData(token) {
|
|
98
|
+
return this.request(`/api/preview-data?token=${token}`);
|
|
99
|
+
}
|
|
504
100
|
async find(collection, args = {}) {
|
|
505
|
-
const
|
|
101
|
+
const { initialData, ...queryArgs } = args;
|
|
102
|
+
const normalizedArgs = { ...queryArgs };
|
|
103
|
+
if (normalizedArgs.where && typeof normalizedArgs.where === "object") {
|
|
104
|
+
normalizedArgs.where = JSON.stringify(normalizedArgs.where);
|
|
105
|
+
}
|
|
106
|
+
const query = qs.stringify(normalizedArgs, { addQueryPrefix: true });
|
|
506
107
|
const res = await this.request(`/api/collections/${collection}${query}`);
|
|
108
|
+
if (res.docs.length === 0 && initialData && initialData.length > 0) {
|
|
109
|
+
this.request(`/api/collections/${collection}/seed`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
body: JSON.stringify({ data: initialData })
|
|
112
|
+
}).catch((err) => console.error(`[dyrected/sdk] Failed to auto-seed collection "${collection}":`, err));
|
|
113
|
+
return {
|
|
114
|
+
docs: initialData,
|
|
115
|
+
total: initialData.length,
|
|
116
|
+
limit: initialData.length,
|
|
117
|
+
page: 1,
|
|
118
|
+
totalPages: 1,
|
|
119
|
+
hasNextPage: false,
|
|
120
|
+
hasPrevPage: false
|
|
121
|
+
};
|
|
122
|
+
}
|
|
507
123
|
return res;
|
|
508
124
|
}
|
|
509
125
|
/**
|
|
@@ -519,18 +135,80 @@ var DyrectedClient = class {
|
|
|
519
135
|
if (args.limit) qb.limit(args.limit);
|
|
520
136
|
if (args.page) qb.page(args.page);
|
|
521
137
|
if (args.depth) qb.depth(args.depth);
|
|
138
|
+
if (args.initialData) qb.seed(args.initialData);
|
|
522
139
|
}
|
|
523
140
|
return qb;
|
|
524
141
|
},
|
|
525
142
|
findOne: (id, args = {}) => this.findOne(slug, id, args),
|
|
526
143
|
create: (data) => this.create(slug, data),
|
|
527
144
|
update: (id, data) => this.update(slug, id, data),
|
|
528
|
-
delete: (id) => this.delete(slug, id)
|
|
145
|
+
delete: (id) => this.delete(slug, id),
|
|
146
|
+
/**
|
|
147
|
+
* Upload a file to this collection. Sends as multipart/form-data.
|
|
148
|
+
* @param file - A File or Blob (browser) or Buffer with filename/mimeType (Node.js)
|
|
149
|
+
* @param data - Additional metadata fields to save alongside the file (e.g. alt, caption)
|
|
150
|
+
*/
|
|
151
|
+
upload: (file, data) => this._upload(slug, file, data),
|
|
152
|
+
// ---- Auth methods (only meaningful when the collection has auth: true) ----
|
|
153
|
+
/**
|
|
154
|
+
* Log in with email + password. Returns a JWT token and the user document.
|
|
155
|
+
* Call `client.setToken(token)` afterwards to authenticate subsequent requests.
|
|
156
|
+
*/
|
|
157
|
+
login: (email, password) => this.request(`/api/collections/${slug}/login`, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
body: JSON.stringify({ email, password })
|
|
160
|
+
}),
|
|
161
|
+
/** Log out. Stateless — token must be discarded client-side; call client.clearToken() too. */
|
|
162
|
+
logout: () => this.request(`/api/collections/${slug}/logout`, { method: "POST" }),
|
|
163
|
+
/** Return the currently authenticated user (requires a token via setToken). */
|
|
164
|
+
me: () => this.request(`/api/collections/${slug}/me`),
|
|
165
|
+
/** Issue a fresh token for the currently authenticated user. */
|
|
166
|
+
refreshToken: () => this.request(`/api/collections/${slug}/refresh-token`, { method: "POST" }),
|
|
167
|
+
/** Check if this auth collection has any users (initialized). */
|
|
168
|
+
isInitialized: () => this.request(`/api/collections/${slug}/init`),
|
|
169
|
+
/** Register the very first user in an empty auth collection. */
|
|
170
|
+
registerFirstUser: (data) => this.request(`/api/collections/${slug}/first-user`, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
body: JSON.stringify(data)
|
|
173
|
+
}),
|
|
174
|
+
/** Send an invitation email to a new user. Requires authentication. */
|
|
175
|
+
invite: (email) => this.request(`/api/collections/${slug}/invite`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
body: JSON.stringify({ email })
|
|
178
|
+
}),
|
|
179
|
+
/** Accept an invitation and create an account. Returns token + user. */
|
|
180
|
+
acceptInvite: (token, password, extraFields) => this.request(`/api/collections/${slug}/accept-invite`, {
|
|
181
|
+
method: "POST",
|
|
182
|
+
body: JSON.stringify({ token, password, ...extraFields })
|
|
183
|
+
})
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Access a global by its slug with a fluent builder.
|
|
188
|
+
* @example client.global('site-settings').get()
|
|
189
|
+
* @example client.global('site-settings').update({ siteName: 'My Site' })
|
|
190
|
+
*/
|
|
191
|
+
global(slug) {
|
|
192
|
+
return {
|
|
193
|
+
get: (args = {}) => this.getGlobal(slug, args),
|
|
194
|
+
update: (data) => this.updateGlobal(slug, data)
|
|
529
195
|
};
|
|
530
196
|
}
|
|
531
197
|
async findOne(collection, id, args = {}) {
|
|
532
|
-
const
|
|
533
|
-
|
|
198
|
+
const { initialData, ...queryArgs } = args;
|
|
199
|
+
const query = qs.stringify(queryArgs, { addQueryPrefix: true });
|
|
200
|
+
try {
|
|
201
|
+
return await this.request(`/api/collections/${collection}/${id}${query}`);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
if (err instanceof DyrectedError && err.statusCode === 404 && initialData) {
|
|
204
|
+
this.request(`/api/collections/${collection}/seed`, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
body: JSON.stringify({ data: [{ id, ...initialData }] })
|
|
207
|
+
}).catch((err2) => console.error(`[dyrected/sdk] Failed to auto-seed document "${id}" in collection "${collection}":`, err2));
|
|
208
|
+
return initialData;
|
|
209
|
+
}
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
534
212
|
}
|
|
535
213
|
async create(collection, data) {
|
|
536
214
|
return this.request(`/api/collections/${collection}`, {
|
|
@@ -550,8 +228,28 @@ var DyrectedClient = class {
|
|
|
550
228
|
});
|
|
551
229
|
}
|
|
552
230
|
async getGlobal(slug, args = {}) {
|
|
553
|
-
const
|
|
554
|
-
|
|
231
|
+
const { initialData, ...queryArgs } = args;
|
|
232
|
+
const query = qs.stringify(queryArgs, { addQueryPrefix: true });
|
|
233
|
+
try {
|
|
234
|
+
const res = await this.request(`/api/globals/${slug}${query}`);
|
|
235
|
+
if ((!res || Object.keys(res).length === 0) && initialData) {
|
|
236
|
+
this.request(`/api/globals/${slug}/seed`, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
body: JSON.stringify({ data: initialData })
|
|
239
|
+
}).catch((err) => console.error(`[dyrected/sdk] Failed to auto-seed global "${slug}":`, err));
|
|
240
|
+
return initialData;
|
|
241
|
+
}
|
|
242
|
+
return res;
|
|
243
|
+
} catch (err) {
|
|
244
|
+
if (err instanceof DyrectedError && err.statusCode === 404 && initialData) {
|
|
245
|
+
this.request(`/api/globals/${slug}/seed`, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
body: JSON.stringify({ data: initialData })
|
|
248
|
+
}).catch((err2) => console.error(`[dyrected/sdk] Failed to auto-seed global "${slug}":`, err2));
|
|
249
|
+
return initialData;
|
|
250
|
+
}
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
555
253
|
}
|
|
556
254
|
async updateGlobal(slug, data) {
|
|
557
255
|
return this.request(`/api/globals/${slug}`, {
|
|
@@ -559,37 +257,59 @@ var DyrectedClient = class {
|
|
|
559
257
|
body: JSON.stringify(data)
|
|
560
258
|
});
|
|
561
259
|
}
|
|
562
|
-
async listMedia(args = {}) {
|
|
563
|
-
|
|
564
|
-
|
|
260
|
+
async listMedia(args = {}, collection = "media") {
|
|
261
|
+
return this.find(collection, args);
|
|
262
|
+
}
|
|
263
|
+
/** @deprecated Use client.collection('media').upload(file, data) instead */
|
|
264
|
+
async uploadMedia(file, collection = "media") {
|
|
265
|
+
return this._upload(collection, file);
|
|
565
266
|
}
|
|
566
|
-
|
|
267
|
+
/**
|
|
268
|
+
* Internal upload implementation shared by collection().upload() and uploadMedia().
|
|
269
|
+
*/
|
|
270
|
+
async _upload(collection, file, data) {
|
|
567
271
|
const formData = new FormData();
|
|
568
272
|
formData.append("file", file);
|
|
273
|
+
if (data) {
|
|
274
|
+
for (const [key, value] of Object.entries(data)) {
|
|
275
|
+
formData.append(key, value);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
569
278
|
const { "Content-Type": _, ...headers } = this.headers;
|
|
570
|
-
return this.request(
|
|
279
|
+
return this.request(`/api/collections/${collection}`, {
|
|
571
280
|
method: "POST",
|
|
572
|
-
headers
|
|
281
|
+
headers: {
|
|
282
|
+
...headers,
|
|
283
|
+
"Content-Type": void 0
|
|
284
|
+
},
|
|
573
285
|
body: formData
|
|
574
286
|
});
|
|
575
287
|
}
|
|
576
|
-
async deleteMedia(id) {
|
|
577
|
-
return this.
|
|
578
|
-
method: "DELETE"
|
|
579
|
-
});
|
|
288
|
+
async deleteMedia(id, collection = "media") {
|
|
289
|
+
return this.delete(collection, id);
|
|
580
290
|
}
|
|
581
291
|
async request(path, init) {
|
|
582
292
|
const url = `${this.baseUrl}${path}`;
|
|
293
|
+
const allHeaders = {
|
|
294
|
+
...this.headers,
|
|
295
|
+
...init?.headers
|
|
296
|
+
};
|
|
297
|
+
Object.keys(allHeaders).forEach((key) => {
|
|
298
|
+
if (allHeaders[key] === void 0) {
|
|
299
|
+
delete allHeaders[key];
|
|
300
|
+
}
|
|
301
|
+
});
|
|
583
302
|
const res = await this.fetch(url, {
|
|
584
303
|
...init,
|
|
585
|
-
headers:
|
|
586
|
-
...this.headers,
|
|
587
|
-
...init?.headers
|
|
588
|
-
}
|
|
304
|
+
headers: allHeaders
|
|
589
305
|
});
|
|
590
306
|
if (!res.ok) {
|
|
591
|
-
const
|
|
592
|
-
throw new
|
|
307
|
+
const body = await res.json().catch(() => ({ message: "Unknown error" }));
|
|
308
|
+
throw new DyrectedError(
|
|
309
|
+
body.message || `Request failed with status ${res.status}`,
|
|
310
|
+
res.status,
|
|
311
|
+
body.errors || []
|
|
312
|
+
);
|
|
593
313
|
}
|
|
594
314
|
return res.json();
|
|
595
315
|
}
|
|
@@ -599,5 +319,6 @@ function createClient(config) {
|
|
|
599
319
|
}
|
|
600
320
|
export {
|
|
601
321
|
DyrectedClient,
|
|
322
|
+
DyrectedError,
|
|
602
323
|
createClient
|
|
603
324
|
};
|