bannerify-js 0.0.28 → 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/dist/index.cjs +199 -162
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +714 -440
- package/dist/index.d.ts +714 -440
- package/dist/index.js +196 -159
- package/dist/index.js.map +1 -1
- package/package.json +17 -18
package/dist/index.cjs
CHANGED
|
@@ -18,39 +18,24 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
23
|
Bannerify: () => Bannerify,
|
|
24
24
|
createClient: () => createClient
|
|
25
25
|
});
|
|
26
|
-
module.exports = __toCommonJS(
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
|
|
28
|
-
// node_modules
|
|
28
|
+
// ../../../node_modules/ky/distribution/errors/HTTPError.js
|
|
29
29
|
var HTTPError = class extends Error {
|
|
30
|
+
response;
|
|
31
|
+
request;
|
|
32
|
+
options;
|
|
30
33
|
constructor(response, request, options) {
|
|
31
34
|
const code = response.status || response.status === 0 ? response.status : "";
|
|
32
35
|
const title = response.statusText || "";
|
|
33
36
|
const status = `${code} ${title}`.trim();
|
|
34
37
|
const reason = status ? `status code ${status}` : "an unknown error";
|
|
35
|
-
super(`Request failed with ${reason}`);
|
|
36
|
-
Object.defineProperty(this, "response", {
|
|
37
|
-
enumerable: true,
|
|
38
|
-
configurable: true,
|
|
39
|
-
writable: true,
|
|
40
|
-
value: void 0
|
|
41
|
-
});
|
|
42
|
-
Object.defineProperty(this, "request", {
|
|
43
|
-
enumerable: true,
|
|
44
|
-
configurable: true,
|
|
45
|
-
writable: true,
|
|
46
|
-
value: void 0
|
|
47
|
-
});
|
|
48
|
-
Object.defineProperty(this, "options", {
|
|
49
|
-
enumerable: true,
|
|
50
|
-
configurable: true,
|
|
51
|
-
writable: true,
|
|
52
|
-
value: void 0
|
|
53
|
-
});
|
|
38
|
+
super(`Request failed with ${reason}: ${request.method} ${request.url}`);
|
|
54
39
|
this.name = "HTTPError";
|
|
55
40
|
this.response = response;
|
|
56
41
|
this.request = request;
|
|
@@ -58,25 +43,20 @@ var HTTPError = class extends Error {
|
|
|
58
43
|
}
|
|
59
44
|
};
|
|
60
45
|
|
|
61
|
-
// node_modules
|
|
46
|
+
// ../../../node_modules/ky/distribution/errors/TimeoutError.js
|
|
62
47
|
var TimeoutError = class extends Error {
|
|
48
|
+
request;
|
|
63
49
|
constructor(request) {
|
|
64
|
-
super(
|
|
65
|
-
Object.defineProperty(this, "request", {
|
|
66
|
-
enumerable: true,
|
|
67
|
-
configurable: true,
|
|
68
|
-
writable: true,
|
|
69
|
-
value: void 0
|
|
70
|
-
});
|
|
50
|
+
super(`Request timed out: ${request.method} ${request.url}`);
|
|
71
51
|
this.name = "TimeoutError";
|
|
72
52
|
this.request = request;
|
|
73
53
|
}
|
|
74
54
|
};
|
|
75
55
|
|
|
76
|
-
// node_modules
|
|
56
|
+
// ../../../node_modules/ky/distribution/utils/is.js
|
|
77
57
|
var isObject = (value) => value !== null && typeof value === "object";
|
|
78
58
|
|
|
79
|
-
// node_modules
|
|
59
|
+
// ../../../node_modules/ky/distribution/utils/merge.js
|
|
80
60
|
var validateAndMerge = (...sources) => {
|
|
81
61
|
for (const source of sources) {
|
|
82
62
|
if ((!isObject(source) || Array.isArray(source)) && source !== void 0) {
|
|
@@ -98,9 +78,19 @@ var mergeHeaders = (source1 = {}, source2 = {}) => {
|
|
|
98
78
|
}
|
|
99
79
|
return result;
|
|
100
80
|
};
|
|
81
|
+
function newHookValue(original, incoming, property) {
|
|
82
|
+
return Object.hasOwn(incoming, property) && incoming[property] === void 0 ? [] : deepMerge(original[property] ?? [], incoming[property] ?? []);
|
|
83
|
+
}
|
|
84
|
+
var mergeHooks = (original = {}, incoming = {}) => ({
|
|
85
|
+
beforeRequest: newHookValue(original, incoming, "beforeRequest"),
|
|
86
|
+
beforeRetry: newHookValue(original, incoming, "beforeRetry"),
|
|
87
|
+
afterResponse: newHookValue(original, incoming, "afterResponse"),
|
|
88
|
+
beforeError: newHookValue(original, incoming, "beforeError")
|
|
89
|
+
});
|
|
101
90
|
var deepMerge = (...sources) => {
|
|
102
91
|
let returnValue = {};
|
|
103
92
|
let headers = {};
|
|
93
|
+
let hooks = {};
|
|
104
94
|
for (const source of sources) {
|
|
105
95
|
if (Array.isArray(source)) {
|
|
106
96
|
if (!Array.isArray(returnValue)) {
|
|
@@ -114,6 +104,10 @@ var deepMerge = (...sources) => {
|
|
|
114
104
|
}
|
|
115
105
|
returnValue = { ...returnValue, [key]: value };
|
|
116
106
|
}
|
|
107
|
+
if (isObject(source.hooks)) {
|
|
108
|
+
hooks = mergeHooks(hooks, source.hooks);
|
|
109
|
+
returnValue.hooks = hooks;
|
|
110
|
+
}
|
|
117
111
|
if (isObject(source.headers)) {
|
|
118
112
|
headers = mergeHeaders(headers, source.headers);
|
|
119
113
|
returnValue.headers = headers;
|
|
@@ -123,22 +117,29 @@ var deepMerge = (...sources) => {
|
|
|
123
117
|
return returnValue;
|
|
124
118
|
};
|
|
125
119
|
|
|
126
|
-
// node_modules
|
|
120
|
+
// ../../../node_modules/ky/distribution/core/constants.js
|
|
127
121
|
var supportsRequestStreams = (() => {
|
|
128
122
|
let duplexAccessed = false;
|
|
129
123
|
let hasContentType = false;
|
|
130
124
|
const supportsReadableStream = typeof globalThis.ReadableStream === "function";
|
|
131
125
|
const supportsRequest = typeof globalThis.Request === "function";
|
|
132
126
|
if (supportsReadableStream && supportsRequest) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
127
|
+
try {
|
|
128
|
+
hasContentType = new globalThis.Request("https://empty.invalid", {
|
|
129
|
+
body: new globalThis.ReadableStream(),
|
|
130
|
+
method: "POST",
|
|
131
|
+
// @ts-expect-error - Types are outdated.
|
|
132
|
+
get duplex() {
|
|
133
|
+
duplexAccessed = true;
|
|
134
|
+
return "half";
|
|
135
|
+
}
|
|
136
|
+
}).headers.has("Content-Type");
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (error instanceof Error && error.message === "unsupported BodyInit type") {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
142
143
|
}
|
|
143
144
|
return duplexAccessed && !hasContentType;
|
|
144
145
|
})();
|
|
@@ -160,6 +161,7 @@ var stop = Symbol("stop");
|
|
|
160
161
|
var kyOptionKeys = {
|
|
161
162
|
json: true,
|
|
162
163
|
parseJson: true,
|
|
164
|
+
stringifyJson: true,
|
|
163
165
|
searchParams: true,
|
|
164
166
|
prefixUrl: true,
|
|
165
167
|
retry: true,
|
|
@@ -184,10 +186,11 @@ var requestOptionsRegistry = {
|
|
|
184
186
|
signal: true,
|
|
185
187
|
window: true,
|
|
186
188
|
dispatcher: true,
|
|
187
|
-
duplex: true
|
|
189
|
+
duplex: true,
|
|
190
|
+
priority: true
|
|
188
191
|
};
|
|
189
192
|
|
|
190
|
-
// node_modules
|
|
193
|
+
// ../../../node_modules/ky/distribution/utils/normalize.js
|
|
191
194
|
var normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input;
|
|
192
195
|
var retryMethods = ["get", "put", "head", "delete", "options", "trace"];
|
|
193
196
|
var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
|
|
@@ -216,12 +219,11 @@ var normalizeRetryOptions = (retry = {}) => {
|
|
|
216
219
|
}
|
|
217
220
|
return {
|
|
218
221
|
...defaultRetryOptions,
|
|
219
|
-
...retry
|
|
220
|
-
afterStatusCodes: retryAfterStatusCodes
|
|
222
|
+
...retry
|
|
221
223
|
};
|
|
222
224
|
};
|
|
223
225
|
|
|
224
|
-
// node_modules
|
|
226
|
+
// ../../../node_modules/ky/distribution/utils/timeout.js
|
|
225
227
|
async function timeout(request, init, abortController, options) {
|
|
226
228
|
return new Promise((resolve, reject) => {
|
|
227
229
|
const timeoutId = setTimeout(() => {
|
|
@@ -236,7 +238,7 @@ async function timeout(request, init, abortController, options) {
|
|
|
236
238
|
});
|
|
237
239
|
}
|
|
238
240
|
|
|
239
|
-
// node_modules
|
|
241
|
+
// ../../../node_modules/ky/distribution/utils/delay.js
|
|
240
242
|
async function delay(ms, { signal }) {
|
|
241
243
|
return new Promise((resolve, reject) => {
|
|
242
244
|
if (signal) {
|
|
@@ -254,7 +256,7 @@ async function delay(ms, { signal }) {
|
|
|
254
256
|
});
|
|
255
257
|
}
|
|
256
258
|
|
|
257
|
-
// node_modules
|
|
259
|
+
// ../../../node_modules/ky/distribution/utils/options.js
|
|
258
260
|
var findUnknownOptions = (request, options) => {
|
|
259
261
|
const unknownOptions = {};
|
|
260
262
|
for (const key in options) {
|
|
@@ -265,11 +267,11 @@ var findUnknownOptions = (request, options) => {
|
|
|
265
267
|
return unknownOptions;
|
|
266
268
|
};
|
|
267
269
|
|
|
268
|
-
// node_modules
|
|
270
|
+
// ../../../node_modules/ky/distribution/core/Ky.js
|
|
269
271
|
var Ky = class _Ky {
|
|
270
272
|
static create(input, options) {
|
|
271
273
|
const ky2 = new _Ky(input, options);
|
|
272
|
-
const
|
|
274
|
+
const function_ = async () => {
|
|
273
275
|
if (typeof ky2._options.timeout === "number" && ky2._options.timeout > maxSafeTimeout) {
|
|
274
276
|
throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`);
|
|
275
277
|
}
|
|
@@ -301,12 +303,11 @@ var Ky = class _Ky {
|
|
|
301
303
|
return response;
|
|
302
304
|
};
|
|
303
305
|
const isRetriableMethod = ky2._options.retry.methods.includes(ky2.request.method.toLowerCase());
|
|
304
|
-
const result = isRetriableMethod ? ky2._retry(
|
|
306
|
+
const result = isRetriableMethod ? ky2._retry(function_) : function_();
|
|
305
307
|
for (const [type, mimeType] of Object.entries(responseTypes)) {
|
|
306
308
|
result[type] = async () => {
|
|
307
309
|
ky2.request.headers.set("accept", ky2.request.headers.get("accept") || mimeType);
|
|
308
|
-
const
|
|
309
|
-
const response = awaitedResult.clone();
|
|
310
|
+
const response = await result;
|
|
310
311
|
if (type === "json") {
|
|
311
312
|
if (response.status === 204) {
|
|
312
313
|
return "";
|
|
@@ -325,51 +326,24 @@ var Ky = class _Ky {
|
|
|
325
326
|
}
|
|
326
327
|
return result;
|
|
327
328
|
}
|
|
329
|
+
request;
|
|
330
|
+
abortController;
|
|
331
|
+
_retryCount = 0;
|
|
332
|
+
_input;
|
|
333
|
+
_options;
|
|
328
334
|
// eslint-disable-next-line complexity
|
|
329
335
|
constructor(input, options = {}) {
|
|
330
|
-
Object.defineProperty(this, "request", {
|
|
331
|
-
enumerable: true,
|
|
332
|
-
configurable: true,
|
|
333
|
-
writable: true,
|
|
334
|
-
value: void 0
|
|
335
|
-
});
|
|
336
|
-
Object.defineProperty(this, "abortController", {
|
|
337
|
-
enumerable: true,
|
|
338
|
-
configurable: true,
|
|
339
|
-
writable: true,
|
|
340
|
-
value: void 0
|
|
341
|
-
});
|
|
342
|
-
Object.defineProperty(this, "_retryCount", {
|
|
343
|
-
enumerable: true,
|
|
344
|
-
configurable: true,
|
|
345
|
-
writable: true,
|
|
346
|
-
value: 0
|
|
347
|
-
});
|
|
348
|
-
Object.defineProperty(this, "_input", {
|
|
349
|
-
enumerable: true,
|
|
350
|
-
configurable: true,
|
|
351
|
-
writable: true,
|
|
352
|
-
value: void 0
|
|
353
|
-
});
|
|
354
|
-
Object.defineProperty(this, "_options", {
|
|
355
|
-
enumerable: true,
|
|
356
|
-
configurable: true,
|
|
357
|
-
writable: true,
|
|
358
|
-
value: void 0
|
|
359
|
-
});
|
|
360
336
|
this._input = input;
|
|
361
337
|
this._options = {
|
|
362
|
-
// TODO: credentials can be removed when the spec change is implemented in all browsers. Context: https://www.chromestatus.com/feature/4539473312350208
|
|
363
|
-
credentials: this._input.credentials || "same-origin",
|
|
364
338
|
...options,
|
|
365
339
|
headers: mergeHeaders(this._input.headers, options.headers),
|
|
366
|
-
hooks:
|
|
340
|
+
hooks: mergeHooks({
|
|
367
341
|
beforeRequest: [],
|
|
368
342
|
beforeRetry: [],
|
|
369
343
|
beforeError: [],
|
|
370
344
|
afterResponse: []
|
|
371
345
|
}, options.hooks),
|
|
372
|
-
method: normalizeRequestMethod(options.method ?? this._input.method),
|
|
346
|
+
method: normalizeRequestMethod(options.method ?? this._input.method ?? "GET"),
|
|
373
347
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
374
348
|
prefixUrl: String(options.prefixUrl || ""),
|
|
375
349
|
retry: normalizeRetryOptions(options.retry),
|
|
@@ -391,17 +365,22 @@ var Ky = class _Ky {
|
|
|
391
365
|
}
|
|
392
366
|
if (supportsAbortController) {
|
|
393
367
|
this.abortController = new globalThis.AbortController();
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
this.
|
|
397
|
-
this.abortController.abort(originalSignal.reason);
|
|
398
|
-
});
|
|
368
|
+
const originalSignal = this._options.signal ?? this._input.signal;
|
|
369
|
+
if (originalSignal?.aborted) {
|
|
370
|
+
this.abortController.abort(originalSignal?.reason);
|
|
399
371
|
}
|
|
372
|
+
originalSignal?.addEventListener("abort", () => {
|
|
373
|
+
this.abortController.abort(originalSignal.reason);
|
|
374
|
+
});
|
|
400
375
|
this._options.signal = this.abortController.signal;
|
|
401
376
|
}
|
|
402
377
|
if (supportsRequestStreams) {
|
|
403
378
|
this._options.duplex = "half";
|
|
404
379
|
}
|
|
380
|
+
if (this._options.json !== void 0) {
|
|
381
|
+
this._options.body = this._options.stringifyJson?.(this._options.json) ?? JSON.stringify(this._options.json);
|
|
382
|
+
this._options.headers.set("content-type", this._options.headers.get("content-type") ?? "application/json");
|
|
383
|
+
}
|
|
405
384
|
this.request = new globalThis.Request(this._input, this._options);
|
|
406
385
|
if (this._options.searchParams) {
|
|
407
386
|
const textSearchParams = typeof this._options.searchParams === "string" ? this._options.searchParams.replace(/^\?/, "") : new URLSearchParams(this._options.searchParams).toString();
|
|
@@ -412,40 +391,33 @@ var Ky = class _Ky {
|
|
|
412
391
|
}
|
|
413
392
|
this.request = new globalThis.Request(new globalThis.Request(url, { ...this.request }), this._options);
|
|
414
393
|
}
|
|
415
|
-
if (this._options.json !== void 0) {
|
|
416
|
-
this._options.body = JSON.stringify(this._options.json);
|
|
417
|
-
this.request.headers.set("content-type", this._options.headers.get("content-type") ?? "application/json");
|
|
418
|
-
this.request = new globalThis.Request(this.request, { body: this._options.body });
|
|
419
|
-
}
|
|
420
394
|
}
|
|
421
395
|
_calculateRetryDelay(error) {
|
|
422
396
|
this._retryCount++;
|
|
423
|
-
if (this._retryCount
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
return 0;
|
|
438
|
-
}
|
|
439
|
-
return after;
|
|
440
|
-
}
|
|
441
|
-
if (error.response.status === 413) {
|
|
442
|
-
return 0;
|
|
397
|
+
if (this._retryCount > this._options.retry.limit || error instanceof TimeoutError) {
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
if (error instanceof HTTPError) {
|
|
401
|
+
if (!this._options.retry.statusCodes.includes(error.response.status)) {
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
const retryAfter = error.response.headers.get("Retry-After") ?? error.response.headers.get("RateLimit-Reset") ?? error.response.headers.get("X-RateLimit-Reset") ?? error.response.headers.get("X-Rate-Limit-Reset");
|
|
405
|
+
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) {
|
|
406
|
+
let after = Number(retryAfter) * 1e3;
|
|
407
|
+
if (Number.isNaN(after)) {
|
|
408
|
+
after = Date.parse(retryAfter) - Date.now();
|
|
409
|
+
} else if (after >= Date.parse("2024-01-01")) {
|
|
410
|
+
after -= Date.now();
|
|
443
411
|
}
|
|
412
|
+
const max = this._options.retry.maxRetryAfter ?? after;
|
|
413
|
+
return after < max ? after : max;
|
|
414
|
+
}
|
|
415
|
+
if (error.response.status === 413) {
|
|
416
|
+
throw error;
|
|
444
417
|
}
|
|
445
|
-
const retryDelay = this._options.retry.delay(this._retryCount);
|
|
446
|
-
return Math.min(this._options.retry.backoffLimit, retryDelay);
|
|
447
418
|
}
|
|
448
|
-
|
|
419
|
+
const retryDelay = this._options.retry.delay(this._retryCount);
|
|
420
|
+
return Math.min(this._options.retry.backoffLimit, retryDelay);
|
|
449
421
|
}
|
|
450
422
|
_decorateResponse(response) {
|
|
451
423
|
if (this._options.parseJson) {
|
|
@@ -453,27 +425,27 @@ var Ky = class _Ky {
|
|
|
453
425
|
}
|
|
454
426
|
return response;
|
|
455
427
|
}
|
|
456
|
-
async _retry(
|
|
428
|
+
async _retry(function_) {
|
|
457
429
|
try {
|
|
458
|
-
return await
|
|
430
|
+
return await function_();
|
|
459
431
|
} catch (error) {
|
|
460
432
|
const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout);
|
|
461
|
-
if (
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
433
|
+
if (this._retryCount < 1) {
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
await delay(ms, { signal: this._options.signal });
|
|
437
|
+
for (const hook of this._options.hooks.beforeRetry) {
|
|
438
|
+
const hookResult = await hook({
|
|
439
|
+
request: this.request,
|
|
440
|
+
options: this._options,
|
|
441
|
+
error,
|
|
442
|
+
retryCount: this._retryCount
|
|
443
|
+
});
|
|
444
|
+
if (hookResult === stop) {
|
|
445
|
+
return;
|
|
473
446
|
}
|
|
474
|
-
return this._retry(fn);
|
|
475
447
|
}
|
|
476
|
-
|
|
448
|
+
return this._retry(function_);
|
|
477
449
|
}
|
|
478
450
|
}
|
|
479
451
|
async _fetch() {
|
|
@@ -488,10 +460,12 @@ var Ky = class _Ky {
|
|
|
488
460
|
}
|
|
489
461
|
}
|
|
490
462
|
const nonRequestOptions = findUnknownOptions(this.request, this._options);
|
|
463
|
+
const mainRequest = this.request;
|
|
464
|
+
this.request = mainRequest.clone();
|
|
491
465
|
if (this._options.timeout === false) {
|
|
492
|
-
return this._options.fetch(
|
|
466
|
+
return this._options.fetch(mainRequest, nonRequestOptions);
|
|
493
467
|
}
|
|
494
|
-
return timeout(
|
|
468
|
+
return timeout(mainRequest, nonRequestOptions, this.abortController, this._options);
|
|
495
469
|
}
|
|
496
470
|
/* istanbul ignore next */
|
|
497
471
|
_stream(response, onDownloadProgress) {
|
|
@@ -537,14 +511,19 @@ var Ky = class _Ky {
|
|
|
537
511
|
}
|
|
538
512
|
};
|
|
539
513
|
|
|
540
|
-
// node_modules
|
|
514
|
+
// ../../../node_modules/ky/distribution/index.js
|
|
541
515
|
var createInstance = (defaults) => {
|
|
542
516
|
const ky2 = (input, options) => Ky.create(input, validateAndMerge(defaults, options));
|
|
543
517
|
for (const method of requestMethods) {
|
|
544
518
|
ky2[method] = (input, options) => Ky.create(input, validateAndMerge(defaults, options, { method }));
|
|
545
519
|
}
|
|
546
520
|
ky2.create = (newDefaults) => createInstance(validateAndMerge(newDefaults));
|
|
547
|
-
ky2.extend = (newDefaults) =>
|
|
521
|
+
ky2.extend = (newDefaults) => {
|
|
522
|
+
if (typeof newDefaults === "function") {
|
|
523
|
+
newDefaults = newDefaults(defaults ?? {});
|
|
524
|
+
}
|
|
525
|
+
return createInstance(validateAndMerge(defaults, newDefaults));
|
|
526
|
+
};
|
|
548
527
|
ky2.stop = stop;
|
|
549
528
|
return ky2;
|
|
550
529
|
};
|
|
@@ -578,6 +557,24 @@ function getTelemetry() {
|
|
|
578
557
|
return { platform, runtime, sdkVersions };
|
|
579
558
|
}
|
|
580
559
|
|
|
560
|
+
// src/format.ts
|
|
561
|
+
var SUPPORTED_FORMATS = ["png", "jpeg", "webp"];
|
|
562
|
+
var isSupportedFormat = (value) => SUPPORTED_FORMATS.includes(value);
|
|
563
|
+
var validateImageFormat = (value) => {
|
|
564
|
+
if (!value) {
|
|
565
|
+
return { ok: true, format: "png" };
|
|
566
|
+
}
|
|
567
|
+
if (isSupportedFormat(value)) {
|
|
568
|
+
return { ok: true, format: value };
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
ok: false,
|
|
572
|
+
error: {
|
|
573
|
+
message: `Unsupported format "${value}". Valid formats are png, jpeg, or webp.`
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
|
|
581
578
|
// src/types.ts
|
|
582
579
|
var timeoutError = {
|
|
583
580
|
error: {
|
|
@@ -590,6 +587,15 @@ var timeoutError = {
|
|
|
590
587
|
};
|
|
591
588
|
|
|
592
589
|
// src/client.ts
|
|
590
|
+
var invalidFormatResult = (value) => ({
|
|
591
|
+
error: {
|
|
592
|
+
// @ts-ignore – local validation error
|
|
593
|
+
code: "INVALID_FORMAT",
|
|
594
|
+
docs: "https://bannerify.co/docs",
|
|
595
|
+
message: `Unsupported format "${value ?? "undefined"}". Valid formats are png, jpeg, or webp.`,
|
|
596
|
+
requestId: "local"
|
|
597
|
+
}
|
|
598
|
+
});
|
|
593
599
|
function createClient(apiKey, opts) {
|
|
594
600
|
return new Bannerify(apiKey, opts);
|
|
595
601
|
}
|
|
@@ -608,32 +614,35 @@ var Bannerify = class {
|
|
|
608
614
|
"X-Platform": telemetry?.platform,
|
|
609
615
|
"X-Runtime": telemetry?.runtime
|
|
610
616
|
},
|
|
611
|
-
timeout: opts?.timeout ??
|
|
617
|
+
timeout: opts?.timeout ?? 6e4
|
|
612
618
|
});
|
|
613
619
|
}
|
|
614
620
|
client;
|
|
615
621
|
baseUrl;
|
|
616
622
|
async createImage(templateId, options) {
|
|
617
623
|
try {
|
|
624
|
+
const normalizedFormat = validateImageFormat(options?.format);
|
|
625
|
+
if (!normalizedFormat.ok) {
|
|
626
|
+
return invalidFormatResult(options?.format);
|
|
627
|
+
}
|
|
618
628
|
const res = await this.client.post("templates/createImage", {
|
|
619
629
|
json: {
|
|
620
630
|
modifications: options?.modifications ?? [],
|
|
621
631
|
// template: JSON.stringify(options?.template ?? {}),
|
|
622
632
|
templateId,
|
|
623
633
|
apiKey: this.apiKey,
|
|
624
|
-
format:
|
|
634
|
+
format: normalizedFormat.format,
|
|
635
|
+
thumbnail: options?.thumbnail ?? false
|
|
625
636
|
}
|
|
626
637
|
});
|
|
627
|
-
if (options?.format === "svg") {
|
|
628
|
-
return { result: await res.text() };
|
|
629
|
-
}
|
|
630
638
|
return { result: await res.arrayBuffer() };
|
|
631
639
|
} catch (e) {
|
|
632
640
|
if (e instanceof HTTPError) {
|
|
633
|
-
|
|
641
|
+
const json = await e.response.json();
|
|
642
|
+
return { error: json.error };
|
|
634
643
|
}
|
|
635
644
|
if (e instanceof TimeoutError) {
|
|
636
|
-
return timeoutError;
|
|
645
|
+
return { error: timeoutError.error };
|
|
637
646
|
}
|
|
638
647
|
throw e;
|
|
639
648
|
}
|
|
@@ -645,40 +654,47 @@ var Bannerify = class {
|
|
|
645
654
|
modifications: options?.modifications ?? [],
|
|
646
655
|
// template: JSON.stringify(options?.template ?? {}),
|
|
647
656
|
templateId,
|
|
648
|
-
apiKey: this.apiKey
|
|
649
|
-
format: options?.format
|
|
657
|
+
apiKey: this.apiKey
|
|
650
658
|
}
|
|
651
659
|
});
|
|
652
660
|
return { result: await res.arrayBuffer() };
|
|
653
661
|
} catch (e) {
|
|
654
662
|
if (e instanceof HTTPError) {
|
|
655
|
-
|
|
663
|
+
const json = await e.response.json();
|
|
664
|
+
return { error: json.error };
|
|
656
665
|
}
|
|
657
666
|
if (e instanceof TimeoutError) {
|
|
658
|
-
return timeoutError;
|
|
667
|
+
return { error: timeoutError.error };
|
|
659
668
|
}
|
|
660
669
|
throw e;
|
|
661
670
|
}
|
|
662
671
|
}
|
|
663
672
|
async createStoredImage(templateId, options) {
|
|
664
673
|
try {
|
|
674
|
+
const normalizedFormat = validateImageFormat(options?.format);
|
|
675
|
+
if (!normalizedFormat.ok) {
|
|
676
|
+
return invalidFormatResult(options?.format);
|
|
677
|
+
}
|
|
665
678
|
const res = await this.client.post("templates/createStoredImage", {
|
|
666
679
|
json: {
|
|
667
680
|
modifications: options?.modifications ?? [],
|
|
668
681
|
// template: JSON.stringify(options?.template ?? {}),
|
|
669
682
|
templateId,
|
|
670
683
|
apiKey: this.apiKey,
|
|
671
|
-
format:
|
|
684
|
+
format: normalizedFormat.format,
|
|
685
|
+
thumbnail: options?.thumbnail ?? false,
|
|
686
|
+
s3Config: options?.s3Config
|
|
672
687
|
}
|
|
673
688
|
});
|
|
674
689
|
const json = await res.json();
|
|
675
690
|
return { result: json.url };
|
|
676
691
|
} catch (e) {
|
|
677
692
|
if (e instanceof HTTPError) {
|
|
678
|
-
|
|
693
|
+
const json = await e.response.json();
|
|
694
|
+
return { error: json.error };
|
|
679
695
|
}
|
|
680
696
|
if (e instanceof TimeoutError) {
|
|
681
|
-
return timeoutError;
|
|
697
|
+
return { error: timeoutError.error };
|
|
682
698
|
}
|
|
683
699
|
throw e;
|
|
684
700
|
}
|
|
@@ -690,12 +706,33 @@ var Bannerify = class {
|
|
|
690
706
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
691
707
|
return hashHex;
|
|
692
708
|
};
|
|
709
|
+
// The signature is an HMAC keyed with the API key itself, so a signed URL can
|
|
710
|
+
// be shared without exposing anything that can sign a different one. The
|
|
711
|
+
// apiKeyHashed in the URL only tells the server which key to verify with.
|
|
712
|
+
#signText = async (text) => {
|
|
713
|
+
const key = await crypto.subtle.importKey(
|
|
714
|
+
"raw",
|
|
715
|
+
new TextEncoder().encode(this.apiKey),
|
|
716
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
717
|
+
false,
|
|
718
|
+
["sign"]
|
|
719
|
+
);
|
|
720
|
+
const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(text));
|
|
721
|
+
return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
722
|
+
};
|
|
693
723
|
async generateImageSignedUrl(templateId, options) {
|
|
694
724
|
const apiKeyHashed = await this.#hashText(this.apiKey);
|
|
695
725
|
const searchParams = new URLSearchParams();
|
|
696
726
|
searchParams.set("apiKeyHashed", apiKeyHashed);
|
|
697
|
-
if (options?.format
|
|
698
|
-
|
|
727
|
+
if (options?.format) {
|
|
728
|
+
const normalizedFormat = validateImageFormat(options.format);
|
|
729
|
+
if (!normalizedFormat.ok) {
|
|
730
|
+
throw new Error(normalizedFormat.error.message);
|
|
731
|
+
}
|
|
732
|
+
searchParams.set("format", normalizedFormat.format);
|
|
733
|
+
}
|
|
734
|
+
if (options?.quality !== void 0) {
|
|
735
|
+
searchParams.set("quality", String(options.quality));
|
|
699
736
|
}
|
|
700
737
|
if (options?.modifications) {
|
|
701
738
|
searchParams.set("modifications", JSON.stringify(options?.modifications));
|
|
@@ -703,12 +740,12 @@ var Bannerify = class {
|
|
|
703
740
|
if (options?.nocache) {
|
|
704
741
|
searchParams.set("nocache", "true");
|
|
705
742
|
}
|
|
743
|
+
if (options?.thumbnail) {
|
|
744
|
+
searchParams.set("thumbnail", "true");
|
|
745
|
+
}
|
|
706
746
|
searchParams.set("templateId", templateId);
|
|
707
747
|
searchParams.sort();
|
|
708
|
-
searchParams.set(
|
|
709
|
-
"sign",
|
|
710
|
-
await this.#hashText(searchParams.toString() + searchParams.get("apiKeyHashed"))
|
|
711
|
-
);
|
|
748
|
+
searchParams.set("sign", await this.#signText(searchParams.toString()));
|
|
712
749
|
return `${this.baseUrl}/templates/signedurl?${searchParams.toString()}`;
|
|
713
750
|
}
|
|
714
751
|
};
|