@hubbyesim/sdk 2.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/CHANGELOG.md +79 -0
- package/LICENSE +21 -0
- package/README.md +346 -0
- package/dist/index.cjs +1656 -0
- package/dist/index.d.cts +1877 -0
- package/dist/index.d.ts +1877 -0
- package/dist/index.js +1622 -0
- package/llms.txt +80 -0
- package/package.json +72 -0
- package/signing-vectors.json +87 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1622 @@
|
|
|
1
|
+
// lib/default-client-config.ts
|
|
2
|
+
var refuseToSend = async () => {
|
|
3
|
+
throw new Error(
|
|
4
|
+
"This @hubbyesim/sdk operation has no signing client. Call the Partner API through new Hubby({ apiKey, apiSecret })."
|
|
5
|
+
);
|
|
6
|
+
};
|
|
7
|
+
var createClientConfig = (config) => ({
|
|
8
|
+
...config,
|
|
9
|
+
fetch: refuseToSend,
|
|
10
|
+
throwOnError: true
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// src/core/bodySerializer.gen.ts
|
|
14
|
+
var jsonBodySerializer = {
|
|
15
|
+
bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/core/serverSentEvents.gen.ts
|
|
19
|
+
function createSseClient({
|
|
20
|
+
onRequest,
|
|
21
|
+
onSseError,
|
|
22
|
+
onSseEvent,
|
|
23
|
+
responseTransformer,
|
|
24
|
+
responseValidator,
|
|
25
|
+
sseDefaultRetryDelay,
|
|
26
|
+
sseMaxRetryAttempts,
|
|
27
|
+
sseMaxRetryDelay,
|
|
28
|
+
sseSleepFn,
|
|
29
|
+
url,
|
|
30
|
+
...options
|
|
31
|
+
}) {
|
|
32
|
+
let lastEventId;
|
|
33
|
+
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
34
|
+
const createStream = async function* () {
|
|
35
|
+
let retryDelay = sseDefaultRetryDelay ?? 3e3;
|
|
36
|
+
let attempt = 0;
|
|
37
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
38
|
+
while (true) {
|
|
39
|
+
if (signal.aborted) break;
|
|
40
|
+
attempt++;
|
|
41
|
+
const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
|
|
42
|
+
if (lastEventId !== void 0) {
|
|
43
|
+
headers.set("Last-Event-ID", lastEventId);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const requestInit = {
|
|
47
|
+
redirect: "follow",
|
|
48
|
+
...options,
|
|
49
|
+
body: options.serializedBody,
|
|
50
|
+
headers,
|
|
51
|
+
signal
|
|
52
|
+
};
|
|
53
|
+
let request = new Request(url, requestInit);
|
|
54
|
+
if (onRequest) {
|
|
55
|
+
request = await onRequest(url, requestInit);
|
|
56
|
+
}
|
|
57
|
+
const _fetch = options.fetch ?? globalThis.fetch;
|
|
58
|
+
const response = await _fetch(request);
|
|
59
|
+
if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
|
|
60
|
+
if (!response.body) throw new Error("No body in SSE response");
|
|
61
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
62
|
+
let buffer = "";
|
|
63
|
+
const abortHandler = () => {
|
|
64
|
+
try {
|
|
65
|
+
reader.cancel();
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
signal.addEventListener("abort", abortHandler);
|
|
70
|
+
try {
|
|
71
|
+
while (true) {
|
|
72
|
+
const { done, value } = await reader.read();
|
|
73
|
+
if (done) break;
|
|
74
|
+
buffer += value;
|
|
75
|
+
buffer = buffer.replace(/\r\n?/g, "\n");
|
|
76
|
+
const chunks = buffer.split("\n\n");
|
|
77
|
+
buffer = chunks.pop() ?? "";
|
|
78
|
+
for (const chunk of chunks) {
|
|
79
|
+
const lines = chunk.split("\n");
|
|
80
|
+
const dataLines = [];
|
|
81
|
+
let eventName;
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
if (line.startsWith("data:")) {
|
|
84
|
+
dataLines.push(line.replace(/^data:\s*/, ""));
|
|
85
|
+
} else if (line.startsWith("event:")) {
|
|
86
|
+
eventName = line.replace(/^event:\s*/, "");
|
|
87
|
+
} else if (line.startsWith("id:")) {
|
|
88
|
+
lastEventId = line.replace(/^id:\s*/, "");
|
|
89
|
+
} else if (line.startsWith("retry:")) {
|
|
90
|
+
const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
|
|
91
|
+
if (!Number.isNaN(parsed)) {
|
|
92
|
+
retryDelay = parsed;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
let data;
|
|
97
|
+
let parsedJson = false;
|
|
98
|
+
if (dataLines.length) {
|
|
99
|
+
const rawData = dataLines.join("\n");
|
|
100
|
+
try {
|
|
101
|
+
data = JSON.parse(rawData);
|
|
102
|
+
parsedJson = true;
|
|
103
|
+
} catch {
|
|
104
|
+
data = rawData;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (parsedJson) {
|
|
108
|
+
if (responseValidator) {
|
|
109
|
+
await responseValidator(data);
|
|
110
|
+
}
|
|
111
|
+
if (responseTransformer) {
|
|
112
|
+
data = await responseTransformer(data);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
onSseEvent?.({
|
|
116
|
+
data,
|
|
117
|
+
event: eventName,
|
|
118
|
+
id: lastEventId,
|
|
119
|
+
retry: retryDelay
|
|
120
|
+
});
|
|
121
|
+
if (dataLines.length) {
|
|
122
|
+
yield data;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} finally {
|
|
127
|
+
signal.removeEventListener("abort", abortHandler);
|
|
128
|
+
reader.releaseLock();
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
onSseError?.(error);
|
|
133
|
+
if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
|
|
137
|
+
await sleep(backoff);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const stream = createStream();
|
|
142
|
+
return { stream };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/core/pathSerializer.gen.ts
|
|
146
|
+
var separatorArrayExplode = (style) => {
|
|
147
|
+
switch (style) {
|
|
148
|
+
case "label":
|
|
149
|
+
return ".";
|
|
150
|
+
case "matrix":
|
|
151
|
+
return ";";
|
|
152
|
+
case "simple":
|
|
153
|
+
return ",";
|
|
154
|
+
default:
|
|
155
|
+
return "&";
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var separatorArrayNoExplode = (style) => {
|
|
159
|
+
switch (style) {
|
|
160
|
+
case "form":
|
|
161
|
+
return ",";
|
|
162
|
+
case "pipeDelimited":
|
|
163
|
+
return "|";
|
|
164
|
+
case "spaceDelimited":
|
|
165
|
+
return "%20";
|
|
166
|
+
default:
|
|
167
|
+
return ",";
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var separatorObjectExplode = (style) => {
|
|
171
|
+
switch (style) {
|
|
172
|
+
case "label":
|
|
173
|
+
return ".";
|
|
174
|
+
case "matrix":
|
|
175
|
+
return ";";
|
|
176
|
+
case "simple":
|
|
177
|
+
return ",";
|
|
178
|
+
default:
|
|
179
|
+
return "&";
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var serializeArrayParam = ({
|
|
183
|
+
allowReserved,
|
|
184
|
+
explode,
|
|
185
|
+
name,
|
|
186
|
+
style,
|
|
187
|
+
value
|
|
188
|
+
}) => {
|
|
189
|
+
if (!explode) {
|
|
190
|
+
const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
|
|
191
|
+
switch (style) {
|
|
192
|
+
case "label":
|
|
193
|
+
return `.${joinedValues2}`;
|
|
194
|
+
case "matrix":
|
|
195
|
+
return `;${name}=${joinedValues2}`;
|
|
196
|
+
case "simple":
|
|
197
|
+
return joinedValues2;
|
|
198
|
+
default:
|
|
199
|
+
return `${name}=${joinedValues2}`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const separator = separatorArrayExplode(style);
|
|
203
|
+
const joinedValues = value.map((v) => {
|
|
204
|
+
if (style === "label" || style === "simple") {
|
|
205
|
+
return allowReserved ? v : encodeURIComponent(v);
|
|
206
|
+
}
|
|
207
|
+
return serializePrimitiveParam({
|
|
208
|
+
allowReserved,
|
|
209
|
+
name,
|
|
210
|
+
value: v
|
|
211
|
+
});
|
|
212
|
+
}).join(separator);
|
|
213
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
214
|
+
};
|
|
215
|
+
var serializePrimitiveParam = ({
|
|
216
|
+
allowReserved,
|
|
217
|
+
name,
|
|
218
|
+
value
|
|
219
|
+
}) => {
|
|
220
|
+
if (value === void 0 || value === null) {
|
|
221
|
+
return "";
|
|
222
|
+
}
|
|
223
|
+
if (typeof value === "object") {
|
|
224
|
+
throw new Error(
|
|
225
|
+
"Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
|
|
229
|
+
};
|
|
230
|
+
var serializeObjectParam = ({
|
|
231
|
+
allowReserved,
|
|
232
|
+
explode,
|
|
233
|
+
name,
|
|
234
|
+
style,
|
|
235
|
+
value,
|
|
236
|
+
valueOnly
|
|
237
|
+
}) => {
|
|
238
|
+
if (value instanceof Date) {
|
|
239
|
+
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
|
240
|
+
}
|
|
241
|
+
if (style !== "deepObject" && !explode) {
|
|
242
|
+
let values = [];
|
|
243
|
+
Object.entries(value).forEach(([key, v]) => {
|
|
244
|
+
values = [...values, key, allowReserved ? v : encodeURIComponent(v)];
|
|
245
|
+
});
|
|
246
|
+
const joinedValues2 = values.join(",");
|
|
247
|
+
switch (style) {
|
|
248
|
+
case "form":
|
|
249
|
+
return `${name}=${joinedValues2}`;
|
|
250
|
+
case "label":
|
|
251
|
+
return `.${joinedValues2}`;
|
|
252
|
+
case "matrix":
|
|
253
|
+
return `;${name}=${joinedValues2}`;
|
|
254
|
+
default:
|
|
255
|
+
return joinedValues2;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const separator = separatorObjectExplode(style);
|
|
259
|
+
const joinedValues = Object.entries(value).map(
|
|
260
|
+
([key, v]) => serializePrimitiveParam({
|
|
261
|
+
allowReserved,
|
|
262
|
+
name: style === "deepObject" ? `${name}[${key}]` : key,
|
|
263
|
+
value: v
|
|
264
|
+
})
|
|
265
|
+
).join(separator);
|
|
266
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// src/core/utils.gen.ts
|
|
270
|
+
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
271
|
+
var defaultPathSerializer = ({ path, url: _url }) => {
|
|
272
|
+
let url = _url;
|
|
273
|
+
const matches = _url.match(PATH_PARAM_RE);
|
|
274
|
+
if (matches) {
|
|
275
|
+
for (const match of matches) {
|
|
276
|
+
let explode = false;
|
|
277
|
+
let name = match.substring(1, match.length - 1);
|
|
278
|
+
let style = "simple";
|
|
279
|
+
if (name.endsWith("*")) {
|
|
280
|
+
explode = true;
|
|
281
|
+
name = name.substring(0, name.length - 1);
|
|
282
|
+
}
|
|
283
|
+
if (name.startsWith(".")) {
|
|
284
|
+
name = name.substring(1);
|
|
285
|
+
style = "label";
|
|
286
|
+
} else if (name.startsWith(";")) {
|
|
287
|
+
name = name.substring(1);
|
|
288
|
+
style = "matrix";
|
|
289
|
+
}
|
|
290
|
+
const value = path[name];
|
|
291
|
+
if (value === void 0 || value === null) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (Array.isArray(value)) {
|
|
295
|
+
url = url.replace(match, serializeArrayParam({ explode, name, style, value }));
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (typeof value === "object") {
|
|
299
|
+
url = url.replace(
|
|
300
|
+
match,
|
|
301
|
+
serializeObjectParam({
|
|
302
|
+
explode,
|
|
303
|
+
name,
|
|
304
|
+
style,
|
|
305
|
+
value,
|
|
306
|
+
valueOnly: true
|
|
307
|
+
})
|
|
308
|
+
);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (style === "matrix") {
|
|
312
|
+
url = url.replace(
|
|
313
|
+
match,
|
|
314
|
+
`;${serializePrimitiveParam({
|
|
315
|
+
name,
|
|
316
|
+
value
|
|
317
|
+
})}`
|
|
318
|
+
);
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const replaceValue = encodeURIComponent(
|
|
322
|
+
style === "label" ? `.${value}` : value
|
|
323
|
+
);
|
|
324
|
+
url = url.replace(match, replaceValue);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return url;
|
|
328
|
+
};
|
|
329
|
+
var getUrl = ({
|
|
330
|
+
baseUrl,
|
|
331
|
+
path,
|
|
332
|
+
query,
|
|
333
|
+
querySerializer,
|
|
334
|
+
url: _url
|
|
335
|
+
}) => {
|
|
336
|
+
const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
|
|
337
|
+
let url = (baseUrl ?? "") + pathUrl;
|
|
338
|
+
if (path) {
|
|
339
|
+
url = defaultPathSerializer({ path, url });
|
|
340
|
+
}
|
|
341
|
+
let search = query ? querySerializer(query) : "";
|
|
342
|
+
if (search.startsWith("?")) {
|
|
343
|
+
search = search.substring(1);
|
|
344
|
+
}
|
|
345
|
+
if (search) {
|
|
346
|
+
url += `?${search}`;
|
|
347
|
+
}
|
|
348
|
+
return url;
|
|
349
|
+
};
|
|
350
|
+
function getValidRequestBody(options) {
|
|
351
|
+
const hasBody = options.body !== void 0;
|
|
352
|
+
const isSerializedBody = hasBody && options.bodySerializer;
|
|
353
|
+
if (isSerializedBody) {
|
|
354
|
+
if ("serializedBody" in options) {
|
|
355
|
+
const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
|
|
356
|
+
return hasSerializedBody ? options.serializedBody : null;
|
|
357
|
+
}
|
|
358
|
+
return options.body !== "" ? options.body : null;
|
|
359
|
+
}
|
|
360
|
+
if (hasBody) {
|
|
361
|
+
return options.body;
|
|
362
|
+
}
|
|
363
|
+
return void 0;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/core/auth.gen.ts
|
|
367
|
+
var getAuthToken = async (auth, callback) => {
|
|
368
|
+
const token = typeof callback === "function" ? await callback(auth) : callback;
|
|
369
|
+
if (!token) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (auth.scheme === "bearer") {
|
|
373
|
+
return `Bearer ${token}`;
|
|
374
|
+
}
|
|
375
|
+
if (auth.scheme === "basic") {
|
|
376
|
+
return `Basic ${btoa(token)}`;
|
|
377
|
+
}
|
|
378
|
+
return token;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/client/utils.gen.ts
|
|
382
|
+
var createQuerySerializer = ({
|
|
383
|
+
parameters = {},
|
|
384
|
+
...args
|
|
385
|
+
} = {}) => {
|
|
386
|
+
const querySerializer = (queryParams) => {
|
|
387
|
+
const search = [];
|
|
388
|
+
if (queryParams && typeof queryParams === "object") {
|
|
389
|
+
for (const name in queryParams) {
|
|
390
|
+
const value = queryParams[name];
|
|
391
|
+
if (value === void 0 || value === null) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const options = parameters[name] || args;
|
|
395
|
+
if (Array.isArray(value)) {
|
|
396
|
+
const serializedArray = serializeArrayParam({
|
|
397
|
+
allowReserved: options.allowReserved,
|
|
398
|
+
explode: true,
|
|
399
|
+
name,
|
|
400
|
+
style: "form",
|
|
401
|
+
value,
|
|
402
|
+
...options.array
|
|
403
|
+
});
|
|
404
|
+
if (serializedArray) search.push(serializedArray);
|
|
405
|
+
} else if (typeof value === "object") {
|
|
406
|
+
const serializedObject = serializeObjectParam({
|
|
407
|
+
allowReserved: options.allowReserved,
|
|
408
|
+
explode: true,
|
|
409
|
+
name,
|
|
410
|
+
style: "deepObject",
|
|
411
|
+
value,
|
|
412
|
+
...options.object
|
|
413
|
+
});
|
|
414
|
+
if (serializedObject) search.push(serializedObject);
|
|
415
|
+
} else {
|
|
416
|
+
const serializedPrimitive = serializePrimitiveParam({
|
|
417
|
+
allowReserved: options.allowReserved,
|
|
418
|
+
name,
|
|
419
|
+
value
|
|
420
|
+
});
|
|
421
|
+
if (serializedPrimitive) search.push(serializedPrimitive);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return search.join("&");
|
|
426
|
+
};
|
|
427
|
+
return querySerializer;
|
|
428
|
+
};
|
|
429
|
+
var getParseAs = (contentType) => {
|
|
430
|
+
if (!contentType) {
|
|
431
|
+
return "stream";
|
|
432
|
+
}
|
|
433
|
+
const cleanContent = contentType.split(";")[0]?.trim();
|
|
434
|
+
if (!cleanContent) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
|
|
438
|
+
return "json";
|
|
439
|
+
}
|
|
440
|
+
if (cleanContent === "multipart/form-data") {
|
|
441
|
+
return "formData";
|
|
442
|
+
}
|
|
443
|
+
if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
|
|
444
|
+
return "blob";
|
|
445
|
+
}
|
|
446
|
+
if (cleanContent.startsWith("text/")) {
|
|
447
|
+
return "text";
|
|
448
|
+
}
|
|
449
|
+
return;
|
|
450
|
+
};
|
|
451
|
+
var checkForExistence = (options, name) => {
|
|
452
|
+
if (!name) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
};
|
|
460
|
+
async function setAuthParams(options) {
|
|
461
|
+
for (const auth of options.security ?? []) {
|
|
462
|
+
if (checkForExistence(options, auth.name)) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const token = await getAuthToken(auth, options.auth);
|
|
466
|
+
if (!token) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const name = auth.name ?? "Authorization";
|
|
470
|
+
switch (auth.in) {
|
|
471
|
+
case "query":
|
|
472
|
+
if (!options.query) {
|
|
473
|
+
options.query = {};
|
|
474
|
+
}
|
|
475
|
+
options.query[name] = token;
|
|
476
|
+
break;
|
|
477
|
+
case "cookie":
|
|
478
|
+
options.headers.append("Cookie", `${name}=${token}`);
|
|
479
|
+
break;
|
|
480
|
+
case "header":
|
|
481
|
+
default:
|
|
482
|
+
options.headers.set(name, token);
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
var buildUrl = (options) => getUrl({
|
|
488
|
+
baseUrl: options.baseUrl,
|
|
489
|
+
path: options.path,
|
|
490
|
+
query: options.query,
|
|
491
|
+
querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
|
|
492
|
+
url: options.url
|
|
493
|
+
});
|
|
494
|
+
var mergeConfigs = (a, b) => {
|
|
495
|
+
const config = { ...a, ...b };
|
|
496
|
+
if (config.baseUrl?.endsWith("/")) {
|
|
497
|
+
config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
|
|
498
|
+
}
|
|
499
|
+
config.headers = mergeHeaders(a.headers, b.headers);
|
|
500
|
+
return config;
|
|
501
|
+
};
|
|
502
|
+
var headersEntries = (headers) => {
|
|
503
|
+
const entries = [];
|
|
504
|
+
headers.forEach((value, key) => {
|
|
505
|
+
entries.push([key, value]);
|
|
506
|
+
});
|
|
507
|
+
return entries;
|
|
508
|
+
};
|
|
509
|
+
var mergeHeaders = (...headers) => {
|
|
510
|
+
const mergedHeaders = new Headers();
|
|
511
|
+
for (const header of headers) {
|
|
512
|
+
if (!header) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
|
|
516
|
+
for (const [key, value] of iterator) {
|
|
517
|
+
if (value === null) {
|
|
518
|
+
mergedHeaders.delete(key);
|
|
519
|
+
} else if (Array.isArray(value)) {
|
|
520
|
+
for (const v of value) {
|
|
521
|
+
mergedHeaders.append(key, v);
|
|
522
|
+
}
|
|
523
|
+
} else if (value !== void 0) {
|
|
524
|
+
mergedHeaders.set(
|
|
525
|
+
key,
|
|
526
|
+
typeof value === "object" ? JSON.stringify(value) : value
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return mergedHeaders;
|
|
532
|
+
};
|
|
533
|
+
var Interceptors = class {
|
|
534
|
+
fns = [];
|
|
535
|
+
clear() {
|
|
536
|
+
this.fns = [];
|
|
537
|
+
}
|
|
538
|
+
eject(id) {
|
|
539
|
+
const index = this.getInterceptorIndex(id);
|
|
540
|
+
if (this.fns[index]) {
|
|
541
|
+
this.fns[index] = null;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
exists(id) {
|
|
545
|
+
const index = this.getInterceptorIndex(id);
|
|
546
|
+
return Boolean(this.fns[index]);
|
|
547
|
+
}
|
|
548
|
+
getInterceptorIndex(id) {
|
|
549
|
+
if (typeof id === "number") {
|
|
550
|
+
return this.fns[id] ? id : -1;
|
|
551
|
+
}
|
|
552
|
+
return this.fns.indexOf(id);
|
|
553
|
+
}
|
|
554
|
+
update(id, fn) {
|
|
555
|
+
const index = this.getInterceptorIndex(id);
|
|
556
|
+
if (this.fns[index]) {
|
|
557
|
+
this.fns[index] = fn;
|
|
558
|
+
return id;
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
use(fn) {
|
|
563
|
+
this.fns.push(fn);
|
|
564
|
+
return this.fns.length - 1;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
var createInterceptors = () => ({
|
|
568
|
+
error: new Interceptors(),
|
|
569
|
+
request: new Interceptors(),
|
|
570
|
+
response: new Interceptors()
|
|
571
|
+
});
|
|
572
|
+
var defaultQuerySerializer = createQuerySerializer({
|
|
573
|
+
allowReserved: false,
|
|
574
|
+
array: {
|
|
575
|
+
explode: true,
|
|
576
|
+
style: "form"
|
|
577
|
+
},
|
|
578
|
+
object: {
|
|
579
|
+
explode: true,
|
|
580
|
+
style: "deepObject"
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
var defaultHeaders = {
|
|
584
|
+
"Content-Type": "application/json"
|
|
585
|
+
};
|
|
586
|
+
var createConfig = (override = {}) => ({
|
|
587
|
+
...jsonBodySerializer,
|
|
588
|
+
headers: defaultHeaders,
|
|
589
|
+
parseAs: "auto",
|
|
590
|
+
querySerializer: defaultQuerySerializer,
|
|
591
|
+
...override
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// src/client/client.gen.ts
|
|
595
|
+
var createClient = (config = {}) => {
|
|
596
|
+
let _config = mergeConfigs(createConfig(), config);
|
|
597
|
+
const getConfig = () => ({ ..._config });
|
|
598
|
+
const setConfig = (config2) => {
|
|
599
|
+
_config = mergeConfigs(_config, config2);
|
|
600
|
+
return getConfig();
|
|
601
|
+
};
|
|
602
|
+
const interceptors = createInterceptors();
|
|
603
|
+
const beforeRequest = async (options) => {
|
|
604
|
+
const opts = {
|
|
605
|
+
..._config,
|
|
606
|
+
...options,
|
|
607
|
+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
|
|
608
|
+
headers: mergeHeaders(_config.headers, options.headers),
|
|
609
|
+
serializedBody: void 0
|
|
610
|
+
};
|
|
611
|
+
if (opts.security) {
|
|
612
|
+
await setAuthParams(opts);
|
|
613
|
+
}
|
|
614
|
+
if (opts.requestValidator) {
|
|
615
|
+
await opts.requestValidator(opts);
|
|
616
|
+
}
|
|
617
|
+
if (opts.body !== void 0 && opts.bodySerializer) {
|
|
618
|
+
opts.serializedBody = opts.bodySerializer(opts.body);
|
|
619
|
+
}
|
|
620
|
+
if (opts.body === void 0 || opts.serializedBody === "") {
|
|
621
|
+
opts.headers.delete("Content-Type");
|
|
622
|
+
}
|
|
623
|
+
const resolvedOpts = opts;
|
|
624
|
+
const url = buildUrl(resolvedOpts);
|
|
625
|
+
return { opts: resolvedOpts, url };
|
|
626
|
+
};
|
|
627
|
+
const request = async (options) => {
|
|
628
|
+
const throwOnError = options.throwOnError ?? _config.throwOnError;
|
|
629
|
+
const responseStyle = options.responseStyle ?? _config.responseStyle;
|
|
630
|
+
let request2;
|
|
631
|
+
let response;
|
|
632
|
+
try {
|
|
633
|
+
const { opts, url } = await beforeRequest(options);
|
|
634
|
+
const requestInit = {
|
|
635
|
+
redirect: "follow",
|
|
636
|
+
...opts,
|
|
637
|
+
body: getValidRequestBody(opts)
|
|
638
|
+
};
|
|
639
|
+
request2 = new Request(url, requestInit);
|
|
640
|
+
for (const fn of interceptors.request.fns) {
|
|
641
|
+
if (fn) {
|
|
642
|
+
request2 = await fn(request2, opts);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const _fetch = opts.fetch;
|
|
646
|
+
response = await _fetch(request2);
|
|
647
|
+
for (const fn of interceptors.response.fns) {
|
|
648
|
+
if (fn) {
|
|
649
|
+
response = await fn(response, request2, opts);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const result = {
|
|
653
|
+
request: request2,
|
|
654
|
+
response
|
|
655
|
+
};
|
|
656
|
+
if (response.ok) {
|
|
657
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
658
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
659
|
+
let emptyData;
|
|
660
|
+
switch (parseAs) {
|
|
661
|
+
case "arrayBuffer":
|
|
662
|
+
case "blob":
|
|
663
|
+
case "text":
|
|
664
|
+
emptyData = await response[parseAs]();
|
|
665
|
+
break;
|
|
666
|
+
case "formData":
|
|
667
|
+
emptyData = new FormData();
|
|
668
|
+
break;
|
|
669
|
+
case "stream":
|
|
670
|
+
emptyData = response.body;
|
|
671
|
+
break;
|
|
672
|
+
case "json":
|
|
673
|
+
default:
|
|
674
|
+
emptyData = {};
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
return opts.responseStyle === "data" ? emptyData : {
|
|
678
|
+
data: emptyData,
|
|
679
|
+
...result
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
let data;
|
|
683
|
+
switch (parseAs) {
|
|
684
|
+
case "arrayBuffer":
|
|
685
|
+
case "blob":
|
|
686
|
+
case "formData":
|
|
687
|
+
case "text":
|
|
688
|
+
data = await response[parseAs]();
|
|
689
|
+
break;
|
|
690
|
+
case "json": {
|
|
691
|
+
const text = await response.text();
|
|
692
|
+
data = text ? JSON.parse(text) : {};
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case "stream":
|
|
696
|
+
return opts.responseStyle === "data" ? response.body : {
|
|
697
|
+
data: response.body,
|
|
698
|
+
...result
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
if (parseAs === "json") {
|
|
702
|
+
if (opts.responseValidator) {
|
|
703
|
+
await opts.responseValidator(data);
|
|
704
|
+
}
|
|
705
|
+
if (opts.responseTransformer) {
|
|
706
|
+
data = await opts.responseTransformer(data);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return opts.responseStyle === "data" ? data : {
|
|
710
|
+
data,
|
|
711
|
+
...result
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
const textError = await response.text();
|
|
715
|
+
let jsonError;
|
|
716
|
+
try {
|
|
717
|
+
jsonError = JSON.parse(textError);
|
|
718
|
+
} catch {
|
|
719
|
+
}
|
|
720
|
+
throw jsonError ?? textError;
|
|
721
|
+
} catch (error) {
|
|
722
|
+
let finalError = error;
|
|
723
|
+
for (const fn of interceptors.error.fns) {
|
|
724
|
+
if (fn) {
|
|
725
|
+
finalError = await fn(finalError, response, request2, options);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
finalError = finalError || {};
|
|
729
|
+
if (throwOnError) {
|
|
730
|
+
throw finalError;
|
|
731
|
+
}
|
|
732
|
+
return responseStyle === "data" ? void 0 : {
|
|
733
|
+
error: finalError,
|
|
734
|
+
request: request2,
|
|
735
|
+
response
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
740
|
+
const makeSseFn = (method) => async (options) => {
|
|
741
|
+
const { opts, url } = await beforeRequest(options);
|
|
742
|
+
return createSseClient({
|
|
743
|
+
...opts,
|
|
744
|
+
body: opts.body,
|
|
745
|
+
method,
|
|
746
|
+
onRequest: async (url2, init) => {
|
|
747
|
+
let request2 = new Request(url2, init);
|
|
748
|
+
for (const fn of interceptors.request.fns) {
|
|
749
|
+
if (fn) {
|
|
750
|
+
request2 = await fn(request2, opts);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return request2;
|
|
754
|
+
},
|
|
755
|
+
serializedBody: getValidRequestBody(opts),
|
|
756
|
+
url
|
|
757
|
+
});
|
|
758
|
+
};
|
|
759
|
+
const _buildUrl = (options) => buildUrl({ ..._config, ...options });
|
|
760
|
+
return {
|
|
761
|
+
buildUrl: _buildUrl,
|
|
762
|
+
connect: makeMethodFn("CONNECT"),
|
|
763
|
+
delete: makeMethodFn("DELETE"),
|
|
764
|
+
get: makeMethodFn("GET"),
|
|
765
|
+
getConfig,
|
|
766
|
+
head: makeMethodFn("HEAD"),
|
|
767
|
+
interceptors,
|
|
768
|
+
options: makeMethodFn("OPTIONS"),
|
|
769
|
+
patch: makeMethodFn("PATCH"),
|
|
770
|
+
post: makeMethodFn("POST"),
|
|
771
|
+
put: makeMethodFn("PUT"),
|
|
772
|
+
request,
|
|
773
|
+
setConfig,
|
|
774
|
+
sse: {
|
|
775
|
+
connect: makeSseFn("CONNECT"),
|
|
776
|
+
delete: makeSseFn("DELETE"),
|
|
777
|
+
get: makeSseFn("GET"),
|
|
778
|
+
head: makeSseFn("HEAD"),
|
|
779
|
+
options: makeSseFn("OPTIONS"),
|
|
780
|
+
patch: makeSseFn("PATCH"),
|
|
781
|
+
post: makeSseFn("POST"),
|
|
782
|
+
put: makeSseFn("PUT"),
|
|
783
|
+
trace: makeSseFn("TRACE")
|
|
784
|
+
},
|
|
785
|
+
trace: makeMethodFn("TRACE")
|
|
786
|
+
};
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
// src/client.gen.ts
|
|
790
|
+
var client = createClient(createClientConfig(createConfig({ baseUrl: "http://localhost/api" })));
|
|
791
|
+
|
|
792
|
+
// src/sdk.gen.ts
|
|
793
|
+
var partnerApiV1AnalyticsSummary = (options) => (options?.client ?? client).get({
|
|
794
|
+
security: [
|
|
795
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
796
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
797
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
798
|
+
],
|
|
799
|
+
url: "/v1/analytics/summary",
|
|
800
|
+
...options
|
|
801
|
+
});
|
|
802
|
+
var partnerApiV1AnalyticsFunnel = (options) => (options?.client ?? client).get({
|
|
803
|
+
security: [
|
|
804
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
805
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
806
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
807
|
+
],
|
|
808
|
+
url: "/v1/analytics/funnel",
|
|
809
|
+
...options
|
|
810
|
+
});
|
|
811
|
+
var partnerApiV1AnalyticsTimeseries = (options) => (options?.client ?? client).get({
|
|
812
|
+
security: [
|
|
813
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
814
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
815
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
816
|
+
],
|
|
817
|
+
url: "/v1/analytics/timeseries",
|
|
818
|
+
...options
|
|
819
|
+
});
|
|
820
|
+
var partnerApiV1AnalyticsBreakdowns = (options) => (options?.client ?? client).get({
|
|
821
|
+
security: [
|
|
822
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
823
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
824
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
825
|
+
],
|
|
826
|
+
url: "/v1/analytics/breakdowns",
|
|
827
|
+
...options
|
|
828
|
+
});
|
|
829
|
+
var partnerApiV1BookingsIndex = (options) => (options?.client ?? client).get({
|
|
830
|
+
security: [
|
|
831
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
832
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
833
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
834
|
+
],
|
|
835
|
+
url: "/v1/bookings",
|
|
836
|
+
...options
|
|
837
|
+
});
|
|
838
|
+
var partnerApiV1BookingsStore = (options) => (options.client ?? client).post({
|
|
839
|
+
security: [
|
|
840
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
841
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
842
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
843
|
+
],
|
|
844
|
+
url: "/v1/bookings",
|
|
845
|
+
...options,
|
|
846
|
+
headers: {
|
|
847
|
+
"Content-Type": "application/json",
|
|
848
|
+
...options.headers
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
var partnerApiV1BookingsImportsPresignUpload = (options) => (options.client ?? client).post({
|
|
852
|
+
security: [
|
|
853
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
854
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
855
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
856
|
+
],
|
|
857
|
+
url: "/v1/bookings/imports/presign-upload",
|
|
858
|
+
...options,
|
|
859
|
+
headers: {
|
|
860
|
+
"Content-Type": "application/json",
|
|
861
|
+
...options.headers
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
var partnerApiV1BookingsUpdatePackageSpecifications = (options) => (options.client ?? client).patch({
|
|
865
|
+
security: [
|
|
866
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
867
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
868
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
869
|
+
],
|
|
870
|
+
url: "/v1/bookings/{bookingId}/package-specifications/{uuid}",
|
|
871
|
+
...options,
|
|
872
|
+
headers: {
|
|
873
|
+
"Content-Type": "application/json",
|
|
874
|
+
...options.headers
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
var partnerApiV1BookingsDestroy = (options) => (options.client ?? client).delete({
|
|
878
|
+
security: [
|
|
879
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
880
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
881
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
882
|
+
],
|
|
883
|
+
url: "/v1/bookings/{id}",
|
|
884
|
+
...options
|
|
885
|
+
});
|
|
886
|
+
var partnerApiV1BookingsShow = (options) => (options.client ?? client).get({
|
|
887
|
+
security: [
|
|
888
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
889
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
890
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
891
|
+
],
|
|
892
|
+
url: "/v1/bookings/{id}",
|
|
893
|
+
...options
|
|
894
|
+
});
|
|
895
|
+
var partnerApiV1BookingsUpdate = (options) => (options.client ?? client).patch({
|
|
896
|
+
security: [
|
|
897
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
898
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
899
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
900
|
+
],
|
|
901
|
+
url: "/v1/bookings/{id}",
|
|
902
|
+
...options,
|
|
903
|
+
headers: {
|
|
904
|
+
"Content-Type": "application/json",
|
|
905
|
+
...options.headers
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
var partnerApiV1BookingsItemsPackageDestroy = (options) => (options.client ?? client).delete({
|
|
909
|
+
security: [
|
|
910
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
911
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
912
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
913
|
+
],
|
|
914
|
+
url: "/v1/bookings/{booking}/items/{item}/package",
|
|
915
|
+
...options
|
|
916
|
+
});
|
|
917
|
+
var partnerApiV1BookingsItemsPackageShow = (options) => (options.client ?? client).get({
|
|
918
|
+
security: [
|
|
919
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
920
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
921
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
922
|
+
],
|
|
923
|
+
url: "/v1/bookings/{booking}/items/{item}/package",
|
|
924
|
+
...options
|
|
925
|
+
});
|
|
926
|
+
var partnerApiV1BookingsItemsPackageStore = (options) => (options.client ?? client).post({
|
|
927
|
+
security: [
|
|
928
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
929
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
930
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
931
|
+
],
|
|
932
|
+
url: "/v1/bookings/{booking}/items/{item}/package",
|
|
933
|
+
...options,
|
|
934
|
+
headers: {
|
|
935
|
+
"Content-Type": "application/json",
|
|
936
|
+
...options.headers
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
var partnerApiV1DestinationsBundlesIndex = (options) => (options.client ?? client).get({
|
|
940
|
+
security: [
|
|
941
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
942
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
943
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
944
|
+
],
|
|
945
|
+
url: "/v1/destinations/{destination}/bundles",
|
|
946
|
+
...options
|
|
947
|
+
});
|
|
948
|
+
var partnerApiV1BundlesIndex = (options) => (options?.client ?? client).get({
|
|
949
|
+
security: [
|
|
950
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
951
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
952
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
953
|
+
],
|
|
954
|
+
url: "/v1/bundles",
|
|
955
|
+
...options
|
|
956
|
+
});
|
|
957
|
+
var partnerApiV1Me = (options) => (options?.client ?? client).get({
|
|
958
|
+
security: [
|
|
959
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
960
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
961
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
962
|
+
],
|
|
963
|
+
url: "/v1/me",
|
|
964
|
+
...options
|
|
965
|
+
});
|
|
966
|
+
var partnerApiV1Destinations = (options) => (options?.client ?? client).get({
|
|
967
|
+
security: [
|
|
968
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
969
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
970
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
971
|
+
],
|
|
972
|
+
url: "/v1/destinations",
|
|
973
|
+
...options
|
|
974
|
+
});
|
|
975
|
+
var partnerApiV1DestinationsShow = (options) => (options.client ?? client).get({
|
|
976
|
+
security: [
|
|
977
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
978
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
979
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
980
|
+
],
|
|
981
|
+
url: "/v1/destinations/{destination}",
|
|
982
|
+
...options
|
|
983
|
+
});
|
|
984
|
+
var partnerApiV1DeviceCompatibilityIndex = (options) => (options?.client ?? client).get({
|
|
985
|
+
security: [
|
|
986
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
987
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
988
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
989
|
+
],
|
|
990
|
+
url: "/v1/device-compatibility",
|
|
991
|
+
...options
|
|
992
|
+
});
|
|
993
|
+
var partnerApiV1DeviceCompatibilityCheck = (options) => (options.client ?? client).post({
|
|
994
|
+
security: [
|
|
995
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
996
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
997
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
998
|
+
],
|
|
999
|
+
url: "/v1/device-compatibility",
|
|
1000
|
+
...options,
|
|
1001
|
+
headers: {
|
|
1002
|
+
"Content-Type": "application/json",
|
|
1003
|
+
...options.headers
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
var partnerApiV1EsimsShow = (options) => (options.client ?? client).get({
|
|
1007
|
+
security: [
|
|
1008
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
1009
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
1010
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
1011
|
+
],
|
|
1012
|
+
url: "/v1/esims/{iccid}",
|
|
1013
|
+
...options
|
|
1014
|
+
});
|
|
1015
|
+
var partnerApiV1EsimsInstructions = (options) => (options.client ?? client).get({
|
|
1016
|
+
security: [
|
|
1017
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
1018
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
1019
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
1020
|
+
],
|
|
1021
|
+
url: "/v1/esims/{iccid}/instructions",
|
|
1022
|
+
...options
|
|
1023
|
+
});
|
|
1024
|
+
var partnerApiV1RedirectTokensCreate = (options) => (options.client ?? client).post({
|
|
1025
|
+
security: [
|
|
1026
|
+
{ name: "X-Api-Key", type: "apiKey" },
|
|
1027
|
+
{ name: "X-Signature", type: "apiKey" },
|
|
1028
|
+
{ name: "X-Timestamp", type: "apiKey" }
|
|
1029
|
+
],
|
|
1030
|
+
url: "/v1/redirect-tokens",
|
|
1031
|
+
...options,
|
|
1032
|
+
headers: {
|
|
1033
|
+
"Content-Type": "application/json",
|
|
1034
|
+
...options.headers
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
// lib/api-error.ts
|
|
1039
|
+
var HubbyApiError = class extends Error {
|
|
1040
|
+
status;
|
|
1041
|
+
/** The refusal code, when the body has one, for example `idempotency_key_in_use`. */
|
|
1042
|
+
code;
|
|
1043
|
+
/** The field errors of a `422`, keyed by field; absent when the body's `errors` has another shape. */
|
|
1044
|
+
errors;
|
|
1045
|
+
/** The parsed error body: JSON when it parses, else the text. */
|
|
1046
|
+
body;
|
|
1047
|
+
response;
|
|
1048
|
+
constructor(response, body) {
|
|
1049
|
+
const answer = isRecord(body) ? body : {};
|
|
1050
|
+
super(
|
|
1051
|
+
typeof answer.message === "string" && answer.message !== "" ? answer.message : `Partner API answered ${response.status}`
|
|
1052
|
+
);
|
|
1053
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
1054
|
+
this.name = "HubbyApiError";
|
|
1055
|
+
this.status = response.status;
|
|
1056
|
+
this.code = typeof answer.code === "string" ? answer.code : void 0;
|
|
1057
|
+
this.errors = isFieldErrors(answer.errors) ? answer.errors : void 0;
|
|
1058
|
+
this.body = body;
|
|
1059
|
+
this.response = response;
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
function isRecord(value) {
|
|
1063
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1064
|
+
}
|
|
1065
|
+
function isFieldErrors(value) {
|
|
1066
|
+
return isRecord(value) && Object.values(value).every(
|
|
1067
|
+
(messages) => Array.isArray(messages) && messages.every((message) => typeof message === "string")
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// lib/crypto.ts
|
|
1072
|
+
var encoder = new TextEncoder();
|
|
1073
|
+
function utf8(text) {
|
|
1074
|
+
return encoder.encode(text);
|
|
1075
|
+
}
|
|
1076
|
+
function toHex(bytes) {
|
|
1077
|
+
const view = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
1078
|
+
let hex = "";
|
|
1079
|
+
for (const byte of view) {
|
|
1080
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
1081
|
+
}
|
|
1082
|
+
return hex;
|
|
1083
|
+
}
|
|
1084
|
+
async function sha256Hex(bytes) {
|
|
1085
|
+
return toHex(await crypto.subtle.digest("SHA-256", bytes));
|
|
1086
|
+
}
|
|
1087
|
+
async function hmacSha256(secret, message) {
|
|
1088
|
+
const key = await crypto.subtle.importKey(
|
|
1089
|
+
"raw",
|
|
1090
|
+
utf8(secret),
|
|
1091
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1092
|
+
false,
|
|
1093
|
+
["sign"]
|
|
1094
|
+
);
|
|
1095
|
+
return new Uint8Array(await crypto.subtle.sign("HMAC", key, message));
|
|
1096
|
+
}
|
|
1097
|
+
function constantTimeEqual(left, right) {
|
|
1098
|
+
if (left.length !== right.length) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
let difference = 0;
|
|
1102
|
+
for (let index = 0; index < left.length; index++) {
|
|
1103
|
+
difference |= left[index] ^ right[index];
|
|
1104
|
+
}
|
|
1105
|
+
return difference === 0;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// lib/signing.ts
|
|
1109
|
+
var SIGNATURE_VERSION = "HUBBY-HMAC-SHA256-v1";
|
|
1110
|
+
async function signRequest(secret, request) {
|
|
1111
|
+
const url = new URL(request.url);
|
|
1112
|
+
const canonical = [
|
|
1113
|
+
SIGNATURE_VERSION,
|
|
1114
|
+
request.method.toUpperCase(),
|
|
1115
|
+
url.host,
|
|
1116
|
+
url.pathname + url.search,
|
|
1117
|
+
String(request.timestamp),
|
|
1118
|
+
await sha256Hex(request.body)
|
|
1119
|
+
].join("\n");
|
|
1120
|
+
return toHex(await hmacSha256(secret, utf8(canonical)));
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// lib/version.ts
|
|
1124
|
+
var SUPPORTED_API_VERSION = "5.13.0";
|
|
1125
|
+
var SDK_VERSION = "2.1.0";
|
|
1126
|
+
|
|
1127
|
+
// lib/transport.ts
|
|
1128
|
+
var DEFAULT_RETRY = {
|
|
1129
|
+
maxAttempts: 3,
|
|
1130
|
+
maxDelayMs: 3e4,
|
|
1131
|
+
baseDelayMs: 500
|
|
1132
|
+
};
|
|
1133
|
+
var IDEMPOTENCY_HEADER = "Idempotency-Key";
|
|
1134
|
+
var USER_AGENT = `hubbyesim-sdk/${SDK_VERSION}`;
|
|
1135
|
+
var REPEATABLE_AFTER_NETWORK_ERROR = /* @__PURE__ */ new Set(["GET", "HEAD", "POST"]);
|
|
1136
|
+
function createPartnerFetch(options) {
|
|
1137
|
+
return async (request) => {
|
|
1138
|
+
const body = new Uint8Array(await request.arrayBuffer());
|
|
1139
|
+
const headers = new Headers(request.headers);
|
|
1140
|
+
if (request.method === "POST" && !headers.has(IDEMPOTENCY_HEADER)) {
|
|
1141
|
+
headers.set(IDEMPOTENCY_HEADER, options.idempotencyKey());
|
|
1142
|
+
}
|
|
1143
|
+
if (!headers.has("User-Agent")) {
|
|
1144
|
+
headers.set("User-Agent", USER_AGENT);
|
|
1145
|
+
}
|
|
1146
|
+
const send = async () => {
|
|
1147
|
+
const timestamp = Math.floor(options.now() / 1e3);
|
|
1148
|
+
const signed = new Headers(headers);
|
|
1149
|
+
signed.set("X-Api-Key", options.apiKey);
|
|
1150
|
+
signed.set("X-Timestamp", String(timestamp));
|
|
1151
|
+
signed.set(
|
|
1152
|
+
"X-Signature",
|
|
1153
|
+
await signRequest(options.apiSecret, {
|
|
1154
|
+
method: request.method,
|
|
1155
|
+
url: request.url,
|
|
1156
|
+
timestamp,
|
|
1157
|
+
body
|
|
1158
|
+
})
|
|
1159
|
+
);
|
|
1160
|
+
return options.fetch(
|
|
1161
|
+
new Request(request.url, {
|
|
1162
|
+
method: request.method,
|
|
1163
|
+
headers: signed,
|
|
1164
|
+
body: body.length > 0 ? body : void 0,
|
|
1165
|
+
signal: request.signal,
|
|
1166
|
+
redirect: request.redirect
|
|
1167
|
+
})
|
|
1168
|
+
);
|
|
1169
|
+
};
|
|
1170
|
+
for (let attempt = 1; ; attempt++) {
|
|
1171
|
+
const isLastAttempt = attempt >= options.retry.maxAttempts;
|
|
1172
|
+
const backoff = options.retry.baseDelayMs * 2 ** (attempt - 1);
|
|
1173
|
+
let response;
|
|
1174
|
+
try {
|
|
1175
|
+
response = await send();
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
if (isLastAttempt || request.signal.aborted || !REPEATABLE_AFTER_NETWORK_ERROR.has(request.method)) {
|
|
1178
|
+
throw error;
|
|
1179
|
+
}
|
|
1180
|
+
await waitUnlessAborted(
|
|
1181
|
+
options.sleep,
|
|
1182
|
+
Math.min(backoff, options.retry.maxDelayMs),
|
|
1183
|
+
request.signal
|
|
1184
|
+
);
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
if (isLastAttempt || request.signal.aborted || !await isRetryable(response)) {
|
|
1188
|
+
return response;
|
|
1189
|
+
}
|
|
1190
|
+
const delay = retryAfterMilliseconds(response.headers.get("Retry-After"), options.now()) ?? backoff;
|
|
1191
|
+
if (delay > options.retry.maxDelayMs) {
|
|
1192
|
+
return response;
|
|
1193
|
+
}
|
|
1194
|
+
await response.body?.cancel();
|
|
1195
|
+
await waitUnlessAborted(options.sleep, delay, request.signal);
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
function waitUnlessAborted(sleep, milliseconds, signal) {
|
|
1200
|
+
if (signal.aborted) {
|
|
1201
|
+
return Promise.reject(abortReason(signal));
|
|
1202
|
+
}
|
|
1203
|
+
return new Promise((resolve, reject) => {
|
|
1204
|
+
const onAbort = () => reject(abortReason(signal));
|
|
1205
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1206
|
+
sleep(milliseconds).then(
|
|
1207
|
+
() => {
|
|
1208
|
+
signal.removeEventListener("abort", onAbort);
|
|
1209
|
+
resolve();
|
|
1210
|
+
},
|
|
1211
|
+
(error) => {
|
|
1212
|
+
signal.removeEventListener("abort", onAbort);
|
|
1213
|
+
reject(error);
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
function abortReason(signal) {
|
|
1219
|
+
return signal.reason ?? new DOMException("This operation was aborted", "AbortError");
|
|
1220
|
+
}
|
|
1221
|
+
async function isRetryable(response) {
|
|
1222
|
+
if (response.status === 429) {
|
|
1223
|
+
return true;
|
|
1224
|
+
}
|
|
1225
|
+
if (response.status !== 409) {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
try {
|
|
1229
|
+
const answer = await response.clone().json();
|
|
1230
|
+
return answer.code === "idempotency_key_in_use";
|
|
1231
|
+
} catch {
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
function retryAfterMilliseconds(header, now) {
|
|
1236
|
+
if (header === null || header.trim() === "") {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
if (/^\d+$/.test(header.trim())) {
|
|
1240
|
+
return Number(header.trim()) * 1e3;
|
|
1241
|
+
}
|
|
1242
|
+
const date = Date.parse(header);
|
|
1243
|
+
return Number.isNaN(date) ? null : Math.max(0, date - now);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// lib/client.ts
|
|
1247
|
+
var defaultSleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1248
|
+
function createSigningClient(options) {
|
|
1249
|
+
assertValidOptions(options);
|
|
1250
|
+
const partnerFetch = createPartnerFetch({
|
|
1251
|
+
apiKey: options.apiKey,
|
|
1252
|
+
apiSecret: options.apiSecret,
|
|
1253
|
+
fetch: options.fetch ?? ((request) => globalThis.fetch(request)),
|
|
1254
|
+
now: options.now ?? Date.now,
|
|
1255
|
+
sleep: options.sleep ?? defaultSleep,
|
|
1256
|
+
idempotencyKey: () => crypto.randomUUID(),
|
|
1257
|
+
retry: options.retry === false ? { ...DEFAULT_RETRY, maxAttempts: 1 } : { ...DEFAULT_RETRY, ...options.retry }
|
|
1258
|
+
});
|
|
1259
|
+
const client2 = createClient(
|
|
1260
|
+
createConfig({
|
|
1261
|
+
baseUrl: options.baseUrl.replace(/\/+$/, ""),
|
|
1262
|
+
// The option is typed as the global `fetch`, but the generated client always calls it with one built `Request`.
|
|
1263
|
+
fetch: (input) => partnerFetch(input)
|
|
1264
|
+
})
|
|
1265
|
+
);
|
|
1266
|
+
client2.interceptors.error.use(rejectWhenNothingWasAnswered);
|
|
1267
|
+
return client2;
|
|
1268
|
+
}
|
|
1269
|
+
function assertValidOptions(options) {
|
|
1270
|
+
if (typeof options.apiKey !== "string" || options.apiKey === "") {
|
|
1271
|
+
throw new Error(`Hubby: \`apiKey\` must be a non-empty string.`);
|
|
1272
|
+
}
|
|
1273
|
+
if (typeof options.apiSecret !== "string" || options.apiSecret === "") {
|
|
1274
|
+
throw new Error(`Hubby: \`apiSecret\` must be a non-empty string.`);
|
|
1275
|
+
}
|
|
1276
|
+
if (typeof options.baseUrl !== "string" || !URL.canParse(options.baseUrl)) {
|
|
1277
|
+
throw new Error(
|
|
1278
|
+
`Hubby: \`baseUrl\` must be an absolute URL, for example \`https://core.hubbyesim.com/api\`.`
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
function rejectWhenNothingWasAnswered(error, response) {
|
|
1283
|
+
if (response === void 0) {
|
|
1284
|
+
throw error;
|
|
1285
|
+
}
|
|
1286
|
+
return error;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// lib/hubby.ts
|
|
1290
|
+
var PRODUCTION_BASE_URL = "https://core.hubbyesim.com/api";
|
|
1291
|
+
var Hubby = class {
|
|
1292
|
+
#client;
|
|
1293
|
+
constructor(options) {
|
|
1294
|
+
this.#client = createSigningClient({
|
|
1295
|
+
...options,
|
|
1296
|
+
baseUrl: options.baseUrl ?? PRODUCTION_BASE_URL
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
/** `GET /v1/me`: the Partner the API key belongs to, and its settings. */
|
|
1300
|
+
async getPartner(options = {}) {
|
|
1301
|
+
return unwrap(await partnerApiV1Me({ client: this.#client, ...options }));
|
|
1302
|
+
}
|
|
1303
|
+
/** `GET /v1/bundles`: the Bundles you sell. */
|
|
1304
|
+
async getBundles(options = {}) {
|
|
1305
|
+
return unwrap(await partnerApiV1BundlesIndex({ client: this.#client, ...options }));
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* `POST /v1/bookings`: creates a booking. The SDK gives the call an
|
|
1309
|
+
* `Idempotency-Key` and keeps it across its retries.
|
|
1310
|
+
*/
|
|
1311
|
+
async createBooking(input, options = {}) {
|
|
1312
|
+
return unwrap(
|
|
1313
|
+
await partnerApiV1BookingsStore({ client: this.#client, ...options, body: input })
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* `POST /v1/bookings/{booking}/items/{item}/package`: issues the item's
|
|
1318
|
+
* package. Create `idempotencyKey` once per issue attempt and store it with
|
|
1319
|
+
* your own purchase record before the call, so a retry after a crash
|
|
1320
|
+
* replays rather than issues again. After a terminate, the next issue of
|
|
1321
|
+
* the item needs a new key.
|
|
1322
|
+
*/
|
|
1323
|
+
async issuePackage(booking, item, input, options = {}) {
|
|
1324
|
+
const { idempotencyKey, ...body } = input;
|
|
1325
|
+
return unwrap(
|
|
1326
|
+
await partnerApiV1BookingsItemsPackageStore({
|
|
1327
|
+
client: this.#client,
|
|
1328
|
+
...options,
|
|
1329
|
+
path: { booking, item },
|
|
1330
|
+
headers: { ...options.headers, "Idempotency-Key": idempotencyKey },
|
|
1331
|
+
body
|
|
1332
|
+
})
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
/** `GET /v1/bookings/{booking}/items/{item}/package`: the package issued for the item. */
|
|
1336
|
+
async getPackage(booking, item, options = {}) {
|
|
1337
|
+
return unwrap(
|
|
1338
|
+
await partnerApiV1BookingsItemsPackageShow({
|
|
1339
|
+
client: this.#client,
|
|
1340
|
+
...options,
|
|
1341
|
+
path: { booking, item }
|
|
1342
|
+
})
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* `DELETE /v1/bookings/{booking}/items/{item}/package`: terminates the
|
|
1347
|
+
* item's package. Resolves once the Partner API answers `204`. Not retried
|
|
1348
|
+
* after a network error: read the package to learn whether it ended.
|
|
1349
|
+
*/
|
|
1350
|
+
async terminatePackage(booking, item, input = {}, options = {}) {
|
|
1351
|
+
unwrap(
|
|
1352
|
+
await partnerApiV1BookingsItemsPackageDestroy({
|
|
1353
|
+
client: this.#client,
|
|
1354
|
+
...options,
|
|
1355
|
+
path: { booking, item },
|
|
1356
|
+
query: input.reason === void 0 ? void 0 : { reason: input.reason }
|
|
1357
|
+
})
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* `GET /v1/esims/{iccid}`: the eSIM and its packages. `refresh: true` reads
|
|
1362
|
+
* it from the network now instead of answering the last readout; it is
|
|
1363
|
+
* rate-limited per Partner.
|
|
1364
|
+
*/
|
|
1365
|
+
async getEsim(iccid, input = {}, options = {}) {
|
|
1366
|
+
return unwrap(
|
|
1367
|
+
await partnerApiV1EsimsShow({
|
|
1368
|
+
client: this.#client,
|
|
1369
|
+
...options,
|
|
1370
|
+
path: { iccid },
|
|
1371
|
+
query: input.refresh === void 0 ? void 0 : { refresh: `${input.refresh}` }
|
|
1372
|
+
})
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
/** `GET /v1/destinations`: the destinations, filtered by `iso3` when given. */
|
|
1376
|
+
async getDestinations(filters = {}, options = {}) {
|
|
1377
|
+
return unwrap(
|
|
1378
|
+
await partnerApiV1Destinations({ client: this.#client, ...options, query: filters })
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* `GET /v1/destinations/{destination}`: one destination.
|
|
1383
|
+
*
|
|
1384
|
+
* @param destination a ULID, an ISO alpha-2 or alpha-3 code, or a name
|
|
1385
|
+
* (English, exact, any letter case).
|
|
1386
|
+
*/
|
|
1387
|
+
async getDestination(destination, options = {}) {
|
|
1388
|
+
return unwrap(
|
|
1389
|
+
await partnerApiV1DestinationsShow({
|
|
1390
|
+
client: this.#client,
|
|
1391
|
+
...options,
|
|
1392
|
+
path: { destination }
|
|
1393
|
+
})
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* `GET /v1/destinations/{destination}/bundles`: the Bundles you sell for one destination.
|
|
1398
|
+
*
|
|
1399
|
+
* @param destination a ULID, an ISO alpha-2 or alpha-3 code, or a name
|
|
1400
|
+
* (English, exact, any letter case).
|
|
1401
|
+
*/
|
|
1402
|
+
async getDestinationBundles(destination, options = {}) {
|
|
1403
|
+
return unwrap(
|
|
1404
|
+
await partnerApiV1DestinationsBundlesIndex({
|
|
1405
|
+
client: this.#client,
|
|
1406
|
+
...options,
|
|
1407
|
+
path: { destination }
|
|
1408
|
+
})
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
/** `GET /v1/bookings`: a page of your bookings, filtered and sorted. Pass `pagination.nextCursor` back as `cursor` for the next page. */
|
|
1412
|
+
async getBookings(filters = {}, options = {}) {
|
|
1413
|
+
return unwrap(
|
|
1414
|
+
await partnerApiV1BookingsIndex({ client: this.#client, ...options, query: filters })
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
/** `GET /v1/bookings/{id}`: one booking, by any identifier the guide lists. */
|
|
1418
|
+
async getBooking(id, options = {}) {
|
|
1419
|
+
return unwrap(
|
|
1420
|
+
await partnerApiV1BookingsShow({ client: this.#client, ...options, path: { id } })
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
/** `PATCH /v1/bookings/{id}`: changes a booking. Not retried after a network error. */
|
|
1424
|
+
async updateBooking(id, input, options = {}) {
|
|
1425
|
+
return unwrap(
|
|
1426
|
+
await partnerApiV1BookingsUpdate({
|
|
1427
|
+
client: this.#client,
|
|
1428
|
+
...options,
|
|
1429
|
+
path: { id },
|
|
1430
|
+
body: input
|
|
1431
|
+
})
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* `DELETE /v1/bookings/{id}`: cancels the booking (a Booking cancellation).
|
|
1436
|
+
* Resolves once the Partner API accepts it. Not retried after a network
|
|
1437
|
+
* error: read the booking to learn whether it was cancelled.
|
|
1438
|
+
*/
|
|
1439
|
+
async cancelBooking(id, options = {}) {
|
|
1440
|
+
unwrap(
|
|
1441
|
+
await partnerApiV1BookingsDestroy({ client: this.#client, ...options, path: { id } })
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* `PATCH /v1/bookings/{booking_id}/package-specifications/{uuid}`: changes
|
|
1446
|
+
* the package specification of one item, by its package queue `uuid`.
|
|
1447
|
+
*/
|
|
1448
|
+
async updatePackageSpecification(bookingId, uuid, input, options = {}) {
|
|
1449
|
+
return unwrap(
|
|
1450
|
+
await partnerApiV1BookingsUpdatePackageSpecifications({
|
|
1451
|
+
client: this.#client,
|
|
1452
|
+
...options,
|
|
1453
|
+
path: { bookingId, uuid },
|
|
1454
|
+
body: input
|
|
1455
|
+
})
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* `PATCH /v1/bookings/{booking_id}/package-specifications`: changes the
|
|
1460
|
+
* package specification of every item of the booking not yet issued.
|
|
1461
|
+
*/
|
|
1462
|
+
async updatePackageSpecifications(bookingId, input, options = {}) {
|
|
1463
|
+
const withoutItem = {
|
|
1464
|
+
client: this.#client,
|
|
1465
|
+
...options,
|
|
1466
|
+
url: "/v1/bookings/{bookingId}/package-specifications",
|
|
1467
|
+
path: { bookingId, uuid: null },
|
|
1468
|
+
body: input
|
|
1469
|
+
};
|
|
1470
|
+
return unwrap(await partnerApiV1BookingsUpdatePackageSpecifications(withoutItem));
|
|
1471
|
+
}
|
|
1472
|
+
/** `POST /v1/bookings/imports/presign-upload`: a URL to upload a booking import file to. */
|
|
1473
|
+
async presignBookingImport(input, options = {}) {
|
|
1474
|
+
return unwrap(
|
|
1475
|
+
await partnerApiV1BookingsImportsPresignUpload({
|
|
1476
|
+
client: this.#client,
|
|
1477
|
+
...options,
|
|
1478
|
+
body: input
|
|
1479
|
+
})
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* `GET /v1/esims/{iccid}/instructions`: how to install the eSIM, in `locale`
|
|
1484
|
+
* when given, else in the locale of the booking that issued it.
|
|
1485
|
+
*/
|
|
1486
|
+
async getEsimInstructions(iccid, input = {}, options = {}) {
|
|
1487
|
+
return unwrap(
|
|
1488
|
+
await partnerApiV1EsimsInstructions({
|
|
1489
|
+
client: this.#client,
|
|
1490
|
+
...options,
|
|
1491
|
+
path: { iccid },
|
|
1492
|
+
query: input.locale === void 0 ? void 0 : { locale: input.locale }
|
|
1493
|
+
})
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1496
|
+
/** `GET /v1/device-compatibility`: the devices that take an eSIM. */
|
|
1497
|
+
async getCompatibleDevices(options = {}) {
|
|
1498
|
+
return unwrap(
|
|
1499
|
+
await partnerApiV1DeviceCompatibilityIndex({ client: this.#client, ...options })
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
/** `POST /v1/device-compatibility`: whether one device takes an eSIM. */
|
|
1503
|
+
async checkDeviceCompatibility(input, options = {}) {
|
|
1504
|
+
return unwrap(
|
|
1505
|
+
await partnerApiV1DeviceCompatibilityCheck({
|
|
1506
|
+
client: this.#client,
|
|
1507
|
+
...options,
|
|
1508
|
+
body: input
|
|
1509
|
+
})
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
/** `POST /v1/redirect-tokens`: a redirect token that signs one of your travellers in, creating their Customer on first use. */
|
|
1513
|
+
async createRedirectToken(input, options = {}) {
|
|
1514
|
+
return unwrap(
|
|
1515
|
+
await partnerApiV1RedirectTokensCreate({
|
|
1516
|
+
client: this.#client,
|
|
1517
|
+
...options,
|
|
1518
|
+
body: input
|
|
1519
|
+
})
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
/** `GET /v1/analytics/summary`: the totals for a date range, with the preceding period when `compare` is set. */
|
|
1523
|
+
async getAnalyticsSummary(query = {}, options = {}) {
|
|
1524
|
+
return unwrap(
|
|
1525
|
+
await partnerApiV1AnalyticsSummary({ client: this.#client, ...options, query })
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
/** `GET /v1/analytics/funnel`: booking codes issued through to first data used, split by `dimension` when given. */
|
|
1529
|
+
async getAnalyticsFunnel(query = {}, options = {}) {
|
|
1530
|
+
return unwrap(
|
|
1531
|
+
await partnerApiV1AnalyticsFunnel({ client: this.#client, ...options, query })
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
/** `GET /v1/analytics/timeseries`: the totals per `bucket` (day, week or month). */
|
|
1535
|
+
async getAnalyticsTimeseries(query = {}, options = {}) {
|
|
1536
|
+
return unwrap(
|
|
1537
|
+
await partnerApiV1AnalyticsTimeseries({ client: this.#client, ...options, query })
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
/** `GET /v1/analytics/breakdowns`: the totals split by each dimension. */
|
|
1541
|
+
async getAnalyticsBreakdowns(query = {}, options = {}) {
|
|
1542
|
+
return unwrap(
|
|
1543
|
+
await partnerApiV1AnalyticsBreakdowns({ client: this.#client, ...options, query })
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
function unwrap(result) {
|
|
1548
|
+
if (result.error !== void 0 || result.response?.ok !== true) {
|
|
1549
|
+
throw new HubbyApiError(result.response, result.error);
|
|
1550
|
+
}
|
|
1551
|
+
return result.data;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// lib/webhooks.ts
|
|
1555
|
+
var WebhookVerificationError = class extends Error {
|
|
1556
|
+
constructor(reason, message) {
|
|
1557
|
+
super(message);
|
|
1558
|
+
this.reason = reason;
|
|
1559
|
+
this.name = "WebhookVerificationError";
|
|
1560
|
+
}
|
|
1561
|
+
reason;
|
|
1562
|
+
};
|
|
1563
|
+
var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
1564
|
+
async function verifyWebhook(options) {
|
|
1565
|
+
const signatureHeader = readHeader(options.headers, "x-hubby-signature");
|
|
1566
|
+
const timestampHeader = readHeader(options.headers, "x-hubby-timestamp");
|
|
1567
|
+
if (!signatureHeader) {
|
|
1568
|
+
throw new WebhookVerificationError(
|
|
1569
|
+
"missing_signature",
|
|
1570
|
+
"The delivery carries no x-hubby-signature header."
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
if (!timestampHeader || !/^\d+$/.test(timestampHeader)) {
|
|
1574
|
+
throw new WebhookVerificationError(
|
|
1575
|
+
"missing_timestamp",
|
|
1576
|
+
"The delivery carries no x-hubby-timestamp header in unix seconds."
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
const tolerance = options.toleranceSeconds ?? DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
1580
|
+
const nowSeconds = Math.floor((options.now ?? Date.now)() / 1e3);
|
|
1581
|
+
if (Math.abs(nowSeconds - Number(timestampHeader)) > tolerance) {
|
|
1582
|
+
throw new WebhookVerificationError(
|
|
1583
|
+
"stale_timestamp",
|
|
1584
|
+
`x-hubby-timestamp is more than ${tolerance} seconds from now.`
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
const body = typeof options.body === "string" ? utf8(options.body) : options.body;
|
|
1588
|
+
const signedPayload = new Uint8Array([...utf8(`${timestampHeader}.`), ...body]);
|
|
1589
|
+
const expected = utf8(`sha256=${toHex(await hmacSha256(options.secret, signedPayload))}`);
|
|
1590
|
+
let matched = false;
|
|
1591
|
+
for (const candidate of signatureHeader.split(",")) {
|
|
1592
|
+
matched = constantTimeEqual(expected, utf8(candidate.trim())) || matched;
|
|
1593
|
+
}
|
|
1594
|
+
if (!matched) {
|
|
1595
|
+
throw new WebhookVerificationError(
|
|
1596
|
+
"invalid_signature",
|
|
1597
|
+
"No x-hubby-signature entry matches the body."
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
try {
|
|
1601
|
+
return JSON.parse(new TextDecoder().decode(body));
|
|
1602
|
+
} catch {
|
|
1603
|
+
throw new WebhookVerificationError("invalid_body", "The signed body is not JSON.");
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function readHeader(headers, name) {
|
|
1607
|
+
if (headers instanceof Headers) {
|
|
1608
|
+
return headers.get(name);
|
|
1609
|
+
}
|
|
1610
|
+
const match = Object.entries(headers).find(([key]) => key.toLowerCase() === name)?.[1];
|
|
1611
|
+
return Array.isArray(match) ? match[0] ?? null : match ?? null;
|
|
1612
|
+
}
|
|
1613
|
+
export {
|
|
1614
|
+
DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
|
|
1615
|
+
Hubby,
|
|
1616
|
+
HubbyApiError,
|
|
1617
|
+
SIGNATURE_VERSION,
|
|
1618
|
+
SUPPORTED_API_VERSION,
|
|
1619
|
+
WebhookVerificationError,
|
|
1620
|
+
signRequest,
|
|
1621
|
+
verifyWebhook
|
|
1622
|
+
};
|