@medalsocial/sdk 1.6.0 → 1.7.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/README.md +68 -0
- package/dist/openapi/medal-social.openapi.json +3097 -1489
- package/dist/pilot/index.d.mts +2 -2
- package/dist/pilot/index.d.ts +2 -2
- package/dist/src/index.d.mts +593 -23
- package/dist/src/index.d.ts +593 -23
- package/dist/src/index.js +472 -146
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.mjs +471 -146
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/openapi.generated.d.mts +905 -45
- package/dist/src/openapi.generated.d.ts +905 -45
- package/dist/src/openapi.generated.js.map +1 -1
- package/openapi/medal-social.openapi.yaml +944 -0
- package/package.json +18 -17
- package/skills/client/SKILL.md +2 -2
- package/skills/resources/SKILL.md +104 -3
package/dist/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BaseClient: () => BaseClient,
|
|
24
|
+
Bookings: () => Bookings,
|
|
24
25
|
CAPABILITY_IDS: () => CAPABILITY_IDS,
|
|
25
26
|
CAPABILITY_ROUTES: () => CAPABILITY_ROUTES,
|
|
26
27
|
CapabilityConfirmations: () => CapabilityConfirmations,
|
|
@@ -45,116 +46,6 @@ __export(src_exports, {
|
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(src_exports);
|
|
47
48
|
|
|
48
|
-
// src/types/capabilities.ts
|
|
49
|
-
var CAPABILITY_IDS = [
|
|
50
|
-
"channel.connect_link.create.execute",
|
|
51
|
-
"channel.connect_link.revoke.execute",
|
|
52
|
-
"channel.connection.disconnect.execute",
|
|
53
|
-
"helpdesk.conversation.reply.execute",
|
|
54
|
-
"helpdesk.conversation.update.execute",
|
|
55
|
-
"helpdesk.webhook.create.execute",
|
|
56
|
-
"helpdesk.webhook.update.execute",
|
|
57
|
-
"helpdesk.webhook.delete.execute"
|
|
58
|
-
];
|
|
59
|
-
var CAPABILITY_ROUTES = {
|
|
60
|
-
"channel.connect_link.create.execute": {
|
|
61
|
-
method: "POST",
|
|
62
|
-
path_template: "/api/v1/channels/connect-links"
|
|
63
|
-
},
|
|
64
|
-
"channel.connect_link.revoke.execute": {
|
|
65
|
-
method: "DELETE",
|
|
66
|
-
path_template: "/api/v1/channels/connect-links/{id}"
|
|
67
|
-
},
|
|
68
|
-
"channel.connection.disconnect.execute": {
|
|
69
|
-
method: "DELETE",
|
|
70
|
-
path_template: "/api/v1/channels/connections/{id}"
|
|
71
|
-
},
|
|
72
|
-
"helpdesk.conversation.reply.execute": {
|
|
73
|
-
method: "POST",
|
|
74
|
-
path_template: "/api/v1/helpdesk/replies"
|
|
75
|
-
},
|
|
76
|
-
"helpdesk.conversation.update.execute": {
|
|
77
|
-
method: "PATCH",
|
|
78
|
-
path_template: "/api/v1/helpdesk/conversations/{id}"
|
|
79
|
-
},
|
|
80
|
-
"helpdesk.webhook.create.execute": {
|
|
81
|
-
method: "POST",
|
|
82
|
-
path_template: "/api/v1/webhooks"
|
|
83
|
-
},
|
|
84
|
-
"helpdesk.webhook.update.execute": {
|
|
85
|
-
method: "PATCH",
|
|
86
|
-
path_template: "/api/v1/webhooks/{id}"
|
|
87
|
-
},
|
|
88
|
-
"helpdesk.webhook.delete.execute": {
|
|
89
|
-
method: "DELETE",
|
|
90
|
-
path_template: "/api/v1/webhooks/{id}"
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// src/capability-confirmer.ts
|
|
95
|
-
function newIdempotencyKey() {
|
|
96
|
-
const cryptoRef = globalThis.crypto;
|
|
97
|
-
if (typeof cryptoRef?.randomUUID === "function") {
|
|
98
|
-
return cryptoRef.randomUUID();
|
|
99
|
-
}
|
|
100
|
-
return `idem_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 12)}`;
|
|
101
|
-
}
|
|
102
|
-
function resolvePath(template, pathParams) {
|
|
103
|
-
return template.replace(/\{([^}/]+)\}/g, (_match, name) => {
|
|
104
|
-
const value = pathParams?.[name];
|
|
105
|
-
return value === void 0 ? `{${name}}` : encodeURIComponent(String(value));
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
var CapabilityConfirmer = class {
|
|
109
|
-
constructor(confirmations, defaults) {
|
|
110
|
-
this.confirmations = confirmations;
|
|
111
|
-
this.defaults = defaults;
|
|
112
|
-
}
|
|
113
|
-
confirmations;
|
|
114
|
-
defaults;
|
|
115
|
-
/**
|
|
116
|
-
* Return the request options to use for a confirmable write, minting the
|
|
117
|
-
* idempotency key and confirmation token first when auto-confirm is active.
|
|
118
|
-
*
|
|
119
|
-
* `body` is the pending request payload (`undefined` for `DELETE` routes).
|
|
120
|
-
* It is handed to the `previewSummary` callback by reference so the summary
|
|
121
|
-
* can describe the specific action, not just the route — it is the caller's
|
|
122
|
-
* own payload, so it is passed through unmodified and unredacted.
|
|
123
|
-
*/
|
|
124
|
-
async prepare(request, pathParams, options) {
|
|
125
|
-
const auto = options?.autoConfirm === false ? void 0 : options?.autoConfirm ?? this.defaults;
|
|
126
|
-
if (!auto) return options;
|
|
127
|
-
if (options?.idempotencyKey && options?.capabilityConfirmation) return options;
|
|
128
|
-
const route = CAPABILITY_ROUTES[request.capabilityId];
|
|
129
|
-
const idempotencyKey = options?.idempotencyKey ?? newIdempotencyKey();
|
|
130
|
-
const path = resolvePath(route.path_template, pathParams);
|
|
131
|
-
const previewSummary = auto.previewSummary({
|
|
132
|
-
...request,
|
|
133
|
-
method: route.method,
|
|
134
|
-
path,
|
|
135
|
-
...pathParams ? { pathParams } : {},
|
|
136
|
-
idempotencyKey
|
|
137
|
-
});
|
|
138
|
-
if (typeof previewSummary !== "string" || previewSummary.trim() === "") {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`autoConfirm.previewSummary must return a non-empty summary for ${request.capabilityId}. The summary is the audit record of what your user approved \u2014 refusing to assert user_approved: true without one.`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
const { data } = await this.confirmations.create({
|
|
144
|
-
capability_id: request.capabilityId,
|
|
145
|
-
...pathParams ? { path_params: pathParams } : {},
|
|
146
|
-
idempotency_key: idempotencyKey,
|
|
147
|
-
preview_summary: previewSummary,
|
|
148
|
-
user_approved: true
|
|
149
|
-
});
|
|
150
|
-
return {
|
|
151
|
-
...options,
|
|
152
|
-
idempotencyKey,
|
|
153
|
-
capabilityConfirmation: data.confirmation_token
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
49
|
// src/types/common.ts
|
|
159
50
|
var MedalApiError = class extends Error {
|
|
160
51
|
status;
|
|
@@ -170,6 +61,21 @@ var MedalApiError = class extends Error {
|
|
|
170
61
|
};
|
|
171
62
|
|
|
172
63
|
// src/client.ts
|
|
64
|
+
function randomIdempotencyKey() {
|
|
65
|
+
const webCrypto = globalThis.crypto;
|
|
66
|
+
if (typeof webCrypto.randomUUID === "function") {
|
|
67
|
+
return webCrypto.randomUUID();
|
|
68
|
+
}
|
|
69
|
+
const bytes = new Uint8Array(16);
|
|
70
|
+
webCrypto.getRandomValues(bytes);
|
|
71
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
72
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
73
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
74
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
75
|
+
}
|
|
76
|
+
function resolveIdempotencyKey(supplied) {
|
|
77
|
+
return (supplied ?? "").trim() || randomIdempotencyKey();
|
|
78
|
+
}
|
|
173
79
|
var BaseClient = class {
|
|
174
80
|
/** Resolved client configuration. */
|
|
175
81
|
config;
|
|
@@ -189,6 +95,29 @@ var BaseClient = class {
|
|
|
189
95
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
190
96
|
});
|
|
191
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Execute a POST that must never execute twice, guaranteeing an
|
|
100
|
+
* `Idempotency-Key`.
|
|
101
|
+
*
|
|
102
|
+
* {@link BaseClient.post} retries 429 and 5xx automatically, so a write
|
|
103
|
+
* whose transaction committed before the gateway failed would otherwise be
|
|
104
|
+
* submitted a second time — booking the same slot twice. A key turns that
|
|
105
|
+
* retry into a replay: the server keys on the key, the workspace, and the
|
|
106
|
+
* method+path, and answers a repeat with the stored response, or 409 while
|
|
107
|
+
* the first attempt is still in flight. Either way the write happens once.
|
|
108
|
+
*
|
|
109
|
+
* The key is minted ONCE here, outside the retry loop in `request`, so every
|
|
110
|
+
* attempt of the same logical call carries the same value — a key minted per
|
|
111
|
+
* attempt would deduplicate nothing. A caller-supplied key always wins, so
|
|
112
|
+
* callers keeping their own records stay in control. See
|
|
113
|
+
* {@link resolveIdempotencyKey} for what counts as supplied.
|
|
114
|
+
*/
|
|
115
|
+
async postOnce(path, body, options) {
|
|
116
|
+
return this.post(path, body, {
|
|
117
|
+
...options,
|
|
118
|
+
idempotencyKey: resolveIdempotencyKey(options?.idempotencyKey)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
192
121
|
/** Execute an authenticated PATCH request with a JSON body. */
|
|
193
122
|
async patch(path, body, options) {
|
|
194
123
|
return this.request(this.buildUrl(path), {
|
|
@@ -240,12 +169,22 @@ var BaseClient = class {
|
|
|
240
169
|
const controller = new AbortController();
|
|
241
170
|
const timeout = setTimeout(() => controller.abort(), this.config.timeout);
|
|
242
171
|
let res;
|
|
172
|
+
let text = "";
|
|
173
|
+
let retrying = false;
|
|
243
174
|
try {
|
|
244
175
|
res = await fetch(url, { ...init, headers, signal: controller.signal });
|
|
176
|
+
retrying = (res.status === 429 || res.status >= 500 && res.status <= 599) && attempt < maxAttempts;
|
|
177
|
+
if (retrying) {
|
|
178
|
+
const drained = res.body ? res.body.pipeTo(new WritableStream()) : res.text();
|
|
179
|
+
await drained.catch(() => {
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
text = await res.text();
|
|
183
|
+
}
|
|
245
184
|
} finally {
|
|
246
185
|
clearTimeout(timeout);
|
|
247
186
|
}
|
|
248
|
-
if (
|
|
187
|
+
if (retrying) {
|
|
249
188
|
const retryAfter = res.headers.get("retry-after");
|
|
250
189
|
let delayMs = 0;
|
|
251
190
|
if (retryAfter) {
|
|
@@ -258,7 +197,6 @@ var BaseClient = class {
|
|
|
258
197
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
259
198
|
continue;
|
|
260
199
|
}
|
|
261
|
-
const text = await res.text();
|
|
262
200
|
let parsed;
|
|
263
201
|
try {
|
|
264
202
|
parsed = text ? JSON.parse(text) : void 0;
|
|
@@ -280,6 +218,266 @@ var BaseClient = class {
|
|
|
280
218
|
}
|
|
281
219
|
};
|
|
282
220
|
|
|
221
|
+
// src/types/capabilities.ts
|
|
222
|
+
var CAPABILITY_IDS = [
|
|
223
|
+
"channel.connect_link.create.execute",
|
|
224
|
+
"channel.connect_link.revoke.execute",
|
|
225
|
+
"channel.connection.disconnect.execute",
|
|
226
|
+
"helpdesk.conversation.reply.execute",
|
|
227
|
+
"helpdesk.conversation.update.execute",
|
|
228
|
+
"helpdesk.webhook.create.execute",
|
|
229
|
+
"helpdesk.webhook.update.execute",
|
|
230
|
+
"helpdesk.webhook.delete.execute"
|
|
231
|
+
];
|
|
232
|
+
var CAPABILITY_ROUTES = {
|
|
233
|
+
"channel.connect_link.create.execute": {
|
|
234
|
+
method: "POST",
|
|
235
|
+
path_template: "/api/v1/channels/connect-links"
|
|
236
|
+
},
|
|
237
|
+
"channel.connect_link.revoke.execute": {
|
|
238
|
+
method: "DELETE",
|
|
239
|
+
path_template: "/api/v1/channels/connect-links/{id}"
|
|
240
|
+
},
|
|
241
|
+
"channel.connection.disconnect.execute": {
|
|
242
|
+
method: "DELETE",
|
|
243
|
+
path_template: "/api/v1/channels/connections/{id}"
|
|
244
|
+
},
|
|
245
|
+
"helpdesk.conversation.reply.execute": {
|
|
246
|
+
method: "POST",
|
|
247
|
+
path_template: "/api/v1/helpdesk/replies"
|
|
248
|
+
},
|
|
249
|
+
"helpdesk.conversation.update.execute": {
|
|
250
|
+
method: "PATCH",
|
|
251
|
+
path_template: "/api/v1/helpdesk/conversations/{id}"
|
|
252
|
+
},
|
|
253
|
+
"helpdesk.webhook.create.execute": {
|
|
254
|
+
method: "POST",
|
|
255
|
+
path_template: "/api/v1/webhooks"
|
|
256
|
+
},
|
|
257
|
+
"helpdesk.webhook.update.execute": {
|
|
258
|
+
method: "PATCH",
|
|
259
|
+
path_template: "/api/v1/webhooks/{id}"
|
|
260
|
+
},
|
|
261
|
+
"helpdesk.webhook.delete.execute": {
|
|
262
|
+
method: "DELETE",
|
|
263
|
+
path_template: "/api/v1/webhooks/{id}"
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// src/capability-confirmer.ts
|
|
268
|
+
function resolvePath(template, pathParams) {
|
|
269
|
+
return template.replace(/\{([^}/]+)\}/g, (_match, name) => {
|
|
270
|
+
const value = pathParams?.[name];
|
|
271
|
+
return value === void 0 ? `{${name}}` : encodeURIComponent(String(value));
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
var CapabilityConfirmer = class {
|
|
275
|
+
constructor(confirmations, defaults) {
|
|
276
|
+
this.confirmations = confirmations;
|
|
277
|
+
this.defaults = defaults;
|
|
278
|
+
}
|
|
279
|
+
confirmations;
|
|
280
|
+
defaults;
|
|
281
|
+
/**
|
|
282
|
+
* Return the request options to use for a confirmable write, minting the
|
|
283
|
+
* idempotency key and confirmation token first when auto-confirm is active.
|
|
284
|
+
*
|
|
285
|
+
* `body` is the pending request payload (`undefined` for `DELETE` routes).
|
|
286
|
+
* It is handed to the `previewSummary` callback by reference so the summary
|
|
287
|
+
* can describe the specific action, not just the route — it is the caller's
|
|
288
|
+
* own payload, so it is passed through unmodified and unredacted.
|
|
289
|
+
*/
|
|
290
|
+
async prepare(request, pathParams, options) {
|
|
291
|
+
const auto = options?.autoConfirm === false ? void 0 : options?.autoConfirm ?? this.defaults;
|
|
292
|
+
if (!auto) return options;
|
|
293
|
+
const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);
|
|
294
|
+
const callerKeyIsUsable = idempotencyKey === options?.idempotencyKey;
|
|
295
|
+
if (callerKeyIsUsable && options?.capabilityConfirmation) return options;
|
|
296
|
+
const route = CAPABILITY_ROUTES[request.capabilityId];
|
|
297
|
+
const path = resolvePath(route.path_template, pathParams);
|
|
298
|
+
const previewSummary = auto.previewSummary({
|
|
299
|
+
...request,
|
|
300
|
+
method: route.method,
|
|
301
|
+
path,
|
|
302
|
+
...pathParams ? { pathParams } : {},
|
|
303
|
+
idempotencyKey
|
|
304
|
+
});
|
|
305
|
+
if (typeof previewSummary !== "string" || previewSummary.trim() === "") {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`autoConfirm.previewSummary must return a non-empty summary for ${request.capabilityId}. The summary is the audit record of what your user approved \u2014 refusing to assert user_approved: true without one.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const { data } = await this.confirmations.create({
|
|
311
|
+
capability_id: request.capabilityId,
|
|
312
|
+
...pathParams ? { path_params: pathParams } : {},
|
|
313
|
+
idempotency_key: idempotencyKey,
|
|
314
|
+
preview_summary: previewSummary,
|
|
315
|
+
user_approved: true
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
...options,
|
|
319
|
+
idempotencyKey,
|
|
320
|
+
capabilityConfirmation: data.confirmation_token
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/resources/bookings.ts
|
|
326
|
+
var BookingsManage = class {
|
|
327
|
+
constructor(client) {
|
|
328
|
+
this.client = client;
|
|
329
|
+
}
|
|
330
|
+
client;
|
|
331
|
+
/**
|
|
332
|
+
* Read what the holder of a manage token may see and do. Honour
|
|
333
|
+
* `can_cancel` / `can_reschedule` — they already apply the policy windows.
|
|
334
|
+
*/
|
|
335
|
+
async get(token) {
|
|
336
|
+
return this.client.get(`/api/v1/bookings/manage/${encodeURIComponent(token)}`);
|
|
337
|
+
}
|
|
338
|
+
/** Cancel on the customer's behalf. Rejected outside the cancel window. */
|
|
339
|
+
async cancel(token, input, options) {
|
|
340
|
+
return this.client.postOnce(
|
|
341
|
+
`/api/v1/bookings/manage/${encodeURIComponent(token)}/cancel`,
|
|
342
|
+
input ?? {},
|
|
343
|
+
options
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Move the booking on the customer's behalf. Rejected outside the reschedule
|
|
348
|
+
* window. Returns a NEW booking id and a new manage token — the old token
|
|
349
|
+
* stops working, so relay the new one into whatever link you send next.
|
|
350
|
+
*/
|
|
351
|
+
async reschedule(token, input, options) {
|
|
352
|
+
return this.client.postOnce(
|
|
353
|
+
`/api/v1/bookings/manage/${encodeURIComponent(token)}/reschedule`,
|
|
354
|
+
input,
|
|
355
|
+
options
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
var Bookings = class {
|
|
360
|
+
constructor(client) {
|
|
361
|
+
this.client = client;
|
|
362
|
+
this.manage = new BookingsManage(client);
|
|
363
|
+
}
|
|
364
|
+
client;
|
|
365
|
+
/** Customer-side actions addressed by manage token. */
|
|
366
|
+
manage;
|
|
367
|
+
/** List the bookable service catalogue. Active-only unless asked otherwise. */
|
|
368
|
+
async listServices(options) {
|
|
369
|
+
const params = {};
|
|
370
|
+
if (options?.include_inactive !== void 0) {
|
|
371
|
+
params.include_inactive = String(options.include_inactive);
|
|
372
|
+
}
|
|
373
|
+
return this.client.get("/api/v1/bookings/services", params);
|
|
374
|
+
}
|
|
375
|
+
/** List the bookable resources — staff, rooms, and equipment. */
|
|
376
|
+
async listResources() {
|
|
377
|
+
return this.client.get("/api/v1/bookings/resources");
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* List free slots for a service over a window. Slots reflect opening hours,
|
|
381
|
+
* time off, buffers, and existing bookings at the moment of the call — they
|
|
382
|
+
* are not held, so a slot can be taken before you book it.
|
|
383
|
+
*/
|
|
384
|
+
async availability(options) {
|
|
385
|
+
const params = {
|
|
386
|
+
service_id: options.service_id,
|
|
387
|
+
from_ts: String(options.from_ts),
|
|
388
|
+
to_ts: String(options.to_ts)
|
|
389
|
+
};
|
|
390
|
+
if (options.resource_id) params.resource_id = options.resource_id;
|
|
391
|
+
return this.client.get("/api/v1/bookings/availability", params);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* The dates a service can be booked on — the half `availability` cannot
|
|
395
|
+
* answer. Availability returns free slots and nothing else, so a closed day,
|
|
396
|
+
* an evening past closing and a fully booked day are all the same empty
|
|
397
|
+
* array. A date absent from this list is closed; on a listed date, compare
|
|
398
|
+
* `last_start_ts` against the clock to tell "too late today" from "full".
|
|
399
|
+
*/
|
|
400
|
+
async schedule(options) {
|
|
401
|
+
const params = {
|
|
402
|
+
service_id: options.service_id,
|
|
403
|
+
from_ts: String(options.from_ts),
|
|
404
|
+
to_ts: String(options.to_ts)
|
|
405
|
+
};
|
|
406
|
+
if (options.resource_id) params.resource_id = options.resource_id;
|
|
407
|
+
return this.client.get("/api/v1/bookings/schedule", params);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* List bookings with cursor-based pagination and optional filters.
|
|
411
|
+
*
|
|
412
|
+
* Check `pagination.truncated`: when true the read window was clipped and
|
|
413
|
+
* matching bookings exist that no cursor reaches — narrow `from_ts`/`to_ts`.
|
|
414
|
+
*/
|
|
415
|
+
async list(options) {
|
|
416
|
+
const params = {};
|
|
417
|
+
if (options?.limit !== void 0) params.limit = String(options.limit);
|
|
418
|
+
if (options?.cursor) params.cursor = options.cursor;
|
|
419
|
+
if (options?.status) params.status = options.status;
|
|
420
|
+
if (options?.resource_id) params.resource_id = options.resource_id;
|
|
421
|
+
if (options?.from_ts !== void 0) params.from_ts = String(options.from_ts);
|
|
422
|
+
if (options?.to_ts !== void 0) params.to_ts = String(options.to_ts);
|
|
423
|
+
return this.client.get("/api/v1/bookings", params);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Book a party — every item succeeds or none do (max 50).
|
|
427
|
+
*
|
|
428
|
+
* Each created booking comes back with a `manage_token` exactly once; only
|
|
429
|
+
* its hash is stored, so persist it if you need the customer's manage link.
|
|
430
|
+
*
|
|
431
|
+
* Automatically idempotent: the SDK mints an `Idempotency-Key` so its own
|
|
432
|
+
* 5xx retries replay rather than book the slot twice. Supply
|
|
433
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too — the
|
|
434
|
+
* server keys on it for 24 hours, so re-sending the same key after a network
|
|
435
|
+
* timeout returns the original bookings instead of a second set.
|
|
436
|
+
*/
|
|
437
|
+
async create(input, options) {
|
|
438
|
+
return this.client.postOnce("/api/v1/bookings", input, options);
|
|
439
|
+
}
|
|
440
|
+
/** Get a booking by ID. */
|
|
441
|
+
async get(id) {
|
|
442
|
+
return this.client.get(`/api/v1/bookings/${encodeURIComponent(id)}`);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Annotate a booking. At least one of `notes` (customer-visible) or
|
|
446
|
+
* `internal_notes` (staff-only) is required; `""` clears a field.
|
|
447
|
+
*/
|
|
448
|
+
async update(id, input, options) {
|
|
449
|
+
return this.client.patch(`/api/v1/bookings/${encodeURIComponent(id)}`, input, options);
|
|
450
|
+
}
|
|
451
|
+
/** Cancel as the business — the cancel window is bypassed. */
|
|
452
|
+
async cancel(id, input, options) {
|
|
453
|
+
return this.client.postOnce(
|
|
454
|
+
`/api/v1/bookings/${encodeURIComponent(id)}/cancel`,
|
|
455
|
+
input ?? {},
|
|
456
|
+
options
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Move a booking as the business — the reschedule window is bypassed.
|
|
461
|
+
* Returns a NEW booking id and a new manage token; the old booking is
|
|
462
|
+
* cancelled and its token stops working.
|
|
463
|
+
*/
|
|
464
|
+
async reschedule(id, input, options) {
|
|
465
|
+
return this.client.postOnce(
|
|
466
|
+
`/api/v1/bookings/${encodeURIComponent(id)}/reschedule`,
|
|
467
|
+
input,
|
|
468
|
+
options
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
/** Mark a booking as a no-show. */
|
|
472
|
+
async markNoShow(id, options) {
|
|
473
|
+
return this.client.postOnce(
|
|
474
|
+
`/api/v1/bookings/${encodeURIComponent(id)}/no-show`,
|
|
475
|
+
void 0,
|
|
476
|
+
options
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
283
481
|
// src/resources/capability-confirmations.ts
|
|
284
482
|
var CapabilityConfirmations = class {
|
|
285
483
|
constructor(client) {
|
|
@@ -297,6 +495,13 @@ var CapabilityConfirmations = class {
|
|
|
297
495
|
* Setting `user_approved: true` asserts that a human on your side approved
|
|
298
496
|
* this specific action. `preview_summary` is what they approved, and is
|
|
299
497
|
* retained for audit — write it for a human reader, not a log parser.
|
|
498
|
+
*
|
|
499
|
+
* Deliberately unkeyed, unlike the writes it authorizes. Minting is not the
|
|
500
|
+
* state change the guarantee exists to protect: the write itself is already
|
|
501
|
+
* bound to `idempotency_key`, so a retry that mints a second token cannot
|
|
502
|
+
* produce a second write. Keying this call would instead park a credential
|
|
503
|
+
* designed to expire in 15 minutes inside a replay cache that answers for 24
|
|
504
|
+
* hours — a worse trade than the duplicate token it would avoid.
|
|
300
505
|
*/
|
|
301
506
|
async create(input) {
|
|
302
507
|
return this.client.post("/api/v1/capability-confirmations", input);
|
|
@@ -321,6 +526,13 @@ var ChannelConnectLinks = class {
|
|
|
321
526
|
*
|
|
322
527
|
* Requires the `channel.connect.manage` scope; OAuth callers additionally
|
|
323
528
|
* need the workspace `admin` role.
|
|
529
|
+
*
|
|
530
|
+
* Automatically idempotent: an unkeyed retry mints a SECOND live single-use
|
|
531
|
+
* link for the same person, and only one of the two can ever be consumed —
|
|
532
|
+
* the other stays outstanding until it is revoked or expires. The key the
|
|
533
|
+
* confirmer chose is the key that goes out — a capability confirmation is
|
|
534
|
+
* bound to its idempotency key, so minting a fresh one here would invalidate
|
|
535
|
+
* the confirmation.
|
|
324
536
|
*/
|
|
325
537
|
async create(input, options) {
|
|
326
538
|
const resolved = await this.confirmer.prepare(
|
|
@@ -328,7 +540,7 @@ var ChannelConnectLinks = class {
|
|
|
328
540
|
void 0,
|
|
329
541
|
options
|
|
330
542
|
);
|
|
331
|
-
return this.client.
|
|
543
|
+
return this.client.postOnce("/api/v1/channels/connect-links", input, resolved);
|
|
332
544
|
}
|
|
333
545
|
/**
|
|
334
546
|
* List the workspace's connect links (tokens are never returned), newest
|
|
@@ -423,9 +635,17 @@ var Contacts = class {
|
|
|
423
635
|
if (options?.search) params.search = options.search;
|
|
424
636
|
return this.client.get("/api/v1/contacts", params);
|
|
425
637
|
}
|
|
426
|
-
/**
|
|
427
|
-
|
|
428
|
-
|
|
638
|
+
/**
|
|
639
|
+
* Create a new contact. Email must be unique in the workspace.
|
|
640
|
+
*
|
|
641
|
+
* Automatically idempotent: the SDK mints an `Idempotency-Key` so its own
|
|
642
|
+
* 5xx retries replay rather than run the create a second time. Uniqueness
|
|
643
|
+
* alone would not save you here — it turns the retry of a committed create
|
|
644
|
+
* into a spurious conflict, which reads as "the contact was not created".
|
|
645
|
+
* Supply `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
646
|
+
*/
|
|
647
|
+
async create(input, options) {
|
|
648
|
+
return this.client.postOnce("/api/v1/contacts", input, options);
|
|
429
649
|
}
|
|
430
650
|
/** Get a contact by ID. */
|
|
431
651
|
async get(id) {
|
|
@@ -446,13 +666,26 @@ var Contacts = class {
|
|
|
446
666
|
if (options?.cursor) params.cursor = options.cursor;
|
|
447
667
|
return this.client.get(`/api/v1/contacts/${encodeURIComponent(id)}/activities`, params);
|
|
448
668
|
}
|
|
449
|
-
/**
|
|
450
|
-
|
|
451
|
-
|
|
669
|
+
/**
|
|
670
|
+
* Add a note to a contact's timeline.
|
|
671
|
+
*
|
|
672
|
+
* Automatically idempotent: nothing about a note is unique, so an unkeyed
|
|
673
|
+
* retry appends the same text to the timeline twice. Supply
|
|
674
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
675
|
+
*/
|
|
676
|
+
async addNote(id, input, options) {
|
|
677
|
+
return this.client.postOnce(`/api/v1/contacts/${encodeURIComponent(id)}/notes`, input, options);
|
|
452
678
|
}
|
|
453
|
-
/**
|
|
454
|
-
|
|
455
|
-
|
|
679
|
+
/**
|
|
680
|
+
* Bulk import contacts (max 500). Duplicates are skipped.
|
|
681
|
+
*
|
|
682
|
+
* Automatically idempotent: the import is processed in chunks, so a retry
|
|
683
|
+
* after a partial failure re-walks the whole batch and reports `added` /
|
|
684
|
+
* `skipped` counts for a run that was not the first. Supply
|
|
685
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
686
|
+
*/
|
|
687
|
+
async import(contacts, options) {
|
|
688
|
+
return this.client.postOnce("/api/v1/contacts/import", { contacts }, options);
|
|
456
689
|
}
|
|
457
690
|
};
|
|
458
691
|
|
|
@@ -471,9 +704,15 @@ var Deals = class {
|
|
|
471
704
|
if (options?.search) params.search = options.search;
|
|
472
705
|
return this.client.get("/api/v1/deals", params);
|
|
473
706
|
}
|
|
474
|
-
/**
|
|
475
|
-
|
|
476
|
-
|
|
707
|
+
/**
|
|
708
|
+
* Create a new deal.
|
|
709
|
+
*
|
|
710
|
+
* Automatically idempotent: nothing about a deal is unique, so an unkeyed
|
|
711
|
+
* retry puts a second identical deal in the pipeline. Supply
|
|
712
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
713
|
+
*/
|
|
714
|
+
async create(input, options) {
|
|
715
|
+
return this.client.postOnce("/api/v1/deals", input, options);
|
|
477
716
|
}
|
|
478
717
|
/** Get a deal by ID. */
|
|
479
718
|
async get(id) {
|
|
@@ -517,9 +756,18 @@ var Emails = class {
|
|
|
517
756
|
/**
|
|
518
757
|
* Send a transactional email using a template (HTTP 202). The returned `id`
|
|
519
758
|
* is an email send id — poll `emails.get(id)` with it to track delivery.
|
|
759
|
+
*
|
|
760
|
+
* Automatically idempotent: the SDK mints an `Idempotency-Key` so its own
|
|
761
|
+
* 5xx retries replay rather than queue a second copy into someone's inbox —
|
|
762
|
+
* a send that already committed cannot be un-sent. Supply
|
|
763
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
764
|
+
*
|
|
765
|
+
* `input.idempotency_key` is the older, body-level form of the same control
|
|
766
|
+
* and still takes precedence server-side, so setting it keeps working
|
|
767
|
+
* unchanged.
|
|
520
768
|
*/
|
|
521
|
-
async send(input) {
|
|
522
|
-
return this.client.
|
|
769
|
+
async send(input, options) {
|
|
770
|
+
return this.client.postOnce("/api/v1/emails", input, options);
|
|
523
771
|
}
|
|
524
772
|
/** Get the delivery status of a sent email. */
|
|
525
773
|
async get(id) {
|
|
@@ -528,9 +776,14 @@ var Emails = class {
|
|
|
528
776
|
/**
|
|
529
777
|
* Send the same template to multiple recipients (max 100, HTTP 202). Each
|
|
530
778
|
* queued recipient gets its own send id in `results` for `emails.get(id)`.
|
|
779
|
+
*
|
|
780
|
+
* Automatically idempotent — and this is the call where it matters most: an
|
|
781
|
+
* unkeyed retry of a batch that already committed sends up to 100 duplicate
|
|
782
|
+
* emails. Supply `options.idempotencyKey` to deduplicate across your OWN
|
|
783
|
+
* retries too.
|
|
531
784
|
*/
|
|
532
|
-
async batch(input) {
|
|
533
|
-
return this.client.
|
|
785
|
+
async batch(input, options) {
|
|
786
|
+
return this.client.postOnce("/api/v1/emails/batch", input, options);
|
|
534
787
|
}
|
|
535
788
|
};
|
|
536
789
|
|
|
@@ -540,9 +793,17 @@ var Gdpr = class {
|
|
|
540
793
|
this.client = client;
|
|
541
794
|
}
|
|
542
795
|
client;
|
|
543
|
-
/**
|
|
544
|
-
|
|
545
|
-
|
|
796
|
+
/**
|
|
797
|
+
* Request a workspace data export. Runs asynchronously.
|
|
798
|
+
*
|
|
799
|
+
* Automatically idempotent: the request is recorded and the export is
|
|
800
|
+
* scheduled in one step with no de-duplication of its own, so an unkeyed
|
|
801
|
+
* retry files a second subject-access request and runs a second full export
|
|
802
|
+
* of the workspace. Supply `options.idempotencyKey` to deduplicate across
|
|
803
|
+
* your OWN retries too.
|
|
804
|
+
*/
|
|
805
|
+
async requestExport(options) {
|
|
806
|
+
return this.client.postOnce("/api/v1/gdpr/export", void 0, options);
|
|
546
807
|
}
|
|
547
808
|
/** List all workspace export requests. */
|
|
548
809
|
async listExports() {
|
|
@@ -552,7 +813,13 @@ var Gdpr = class {
|
|
|
552
813
|
async getExport(id) {
|
|
553
814
|
return this.client.get(`/api/v1/gdpr/exports/${encodeURIComponent(id)}`);
|
|
554
815
|
}
|
|
555
|
-
/**
|
|
816
|
+
/**
|
|
817
|
+
* Record a GDPR consent decision for a contact by email.
|
|
818
|
+
*
|
|
819
|
+
* Deliberately unkeyed: a decision is stored once per
|
|
820
|
+
* (workspace, email, consent type) and overwritten in place, so re-sending
|
|
821
|
+
* the same body reaches the same state and returns the same record id.
|
|
822
|
+
*/
|
|
556
823
|
async recordConsent(input) {
|
|
557
824
|
return this.client.post("/api/v1/gdpr/consent", input);
|
|
558
825
|
}
|
|
@@ -560,7 +827,15 @@ var Gdpr = class {
|
|
|
560
827
|
async getConsent(email) {
|
|
561
828
|
return this.client.get(`/api/v1/gdpr/consent/${encodeURIComponent(email)}`);
|
|
562
829
|
}
|
|
563
|
-
/**
|
|
830
|
+
/**
|
|
831
|
+
* Record cookie consent from an external site (legacy endpoint).
|
|
832
|
+
*
|
|
833
|
+
* Deliberately unkeyed: this legacy route predates the versioned API and
|
|
834
|
+
* does not run the `Idempotency-Key` machinery, so a key here would be a
|
|
835
|
+
* header that changes nothing while implying a guarantee the endpoint cannot
|
|
836
|
+
* make. Treat a failed call as "unknown" and re-send only if a missing
|
|
837
|
+
* consent log matters more to you than a duplicate one.
|
|
838
|
+
*/
|
|
564
839
|
async cookieConsent(input) {
|
|
565
840
|
return this.client.post("/api/cookie-consent", input);
|
|
566
841
|
}
|
|
@@ -624,8 +899,16 @@ var HelpdeskReplies = class {
|
|
|
624
899
|
/**
|
|
625
900
|
* Send an operator reply or internal note. Returns HTTP 201.
|
|
626
901
|
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
902
|
+
* Automatically idempotent: a reply is a message to a real person, and an
|
|
903
|
+
* unkeyed retry sends it to them twice. The key the confirmer chose is the
|
|
904
|
+
* key that goes out — a capability confirmation is bound to its idempotency
|
|
905
|
+
* key, so minting a fresh one here would invalidate the confirmation.
|
|
906
|
+
*
|
|
907
|
+
* Pass `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
908
|
+
* It is REQUIRED for capability-scoped tokens, which need it paired with a
|
|
909
|
+
* `capabilityConfirmation` — a generated key satisfies the pairing's key
|
|
910
|
+
* half only; the confirmation is still yours to supply (or to let
|
|
911
|
+
* `autoConfirm` mint).
|
|
629
912
|
*/
|
|
630
913
|
async create(input, options) {
|
|
631
914
|
const resolved = await this.confirmer.prepare(
|
|
@@ -633,7 +916,7 @@ var HelpdeskReplies = class {
|
|
|
633
916
|
void 0,
|
|
634
917
|
options
|
|
635
918
|
);
|
|
636
|
-
return this.client.
|
|
919
|
+
return this.client.postOnce("/api/v1/helpdesk/replies", input, resolved);
|
|
637
920
|
}
|
|
638
921
|
};
|
|
639
922
|
var Helpdesk = class {
|
|
@@ -661,9 +944,15 @@ var Posts = class {
|
|
|
661
944
|
if (options?.type) params.type = options.type;
|
|
662
945
|
return this.client.get("/api/v1/posts", params);
|
|
663
946
|
}
|
|
664
|
-
/**
|
|
665
|
-
|
|
666
|
-
|
|
947
|
+
/**
|
|
948
|
+
* Create a new post with content and target channels.
|
|
949
|
+
*
|
|
950
|
+
* Automatically idempotent: the SDK mints an `Idempotency-Key` so its own
|
|
951
|
+
* 5xx retries replay rather than draft the post twice. Supply
|
|
952
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
953
|
+
*/
|
|
954
|
+
async create(input, options) {
|
|
955
|
+
return this.client.postOnce("/api/v1/posts", input, options);
|
|
667
956
|
}
|
|
668
957
|
/** Get a post by ID, including its per-channel variants. */
|
|
669
958
|
async get(id) {
|
|
@@ -677,11 +966,26 @@ var Posts = class {
|
|
|
677
966
|
async remove(id) {
|
|
678
967
|
return this.client.delete(`/api/v1/posts/${encodeURIComponent(id)}`);
|
|
679
968
|
}
|
|
680
|
-
/**
|
|
969
|
+
/**
|
|
970
|
+
* Schedule a post for future publication.
|
|
971
|
+
*
|
|
972
|
+
* Deliberately unkeyed: re-sending the same `scheduled_at` for an
|
|
973
|
+
* already-scheduled post returns the original `workflow_id` rather than
|
|
974
|
+
* starting a second one, so a retried schedule cannot double-publish. A
|
|
975
|
+
* *different* time is rejected — unschedule first.
|
|
976
|
+
*/
|
|
681
977
|
async schedule(id, input) {
|
|
682
978
|
return this.client.post(`/api/v1/posts/${encodeURIComponent(id)}/schedule`, input);
|
|
683
979
|
}
|
|
684
|
-
/**
|
|
980
|
+
/**
|
|
981
|
+
* Publish a post immediately to all target channels.
|
|
982
|
+
*
|
|
983
|
+
* Deliberately unkeyed: publishing moves the post out of the set of statuses
|
|
984
|
+
* that may be published, so the retry of a publish that already committed is
|
|
985
|
+
* refused rather than posting a second time. It is refused with a 400 though
|
|
986
|
+
* — treat an error here as "check the post's status", not as "nothing
|
|
987
|
+
* happened".
|
|
988
|
+
*/
|
|
685
989
|
async publish(id) {
|
|
686
990
|
return this.client.post(`/api/v1/posts/${encodeURIComponent(id)}/publish`);
|
|
687
991
|
}
|
|
@@ -702,10 +1006,15 @@ var Scan = class {
|
|
|
702
1006
|
* Queue a scan. Provide exactly one of `url`, `orgnr`, or `name`.
|
|
703
1007
|
* Runs asynchronously — poll with `get()` or use `waitForResult()`.
|
|
704
1008
|
*
|
|
1009
|
+
* Automatically idempotent: a scan job is queued the moment it is created,
|
|
1010
|
+
* so an unkeyed retry starts a second crawl of the same site and returns an
|
|
1011
|
+
* id for a job that duplicates one already running. Supply
|
|
1012
|
+
* `options.idempotencyKey` to deduplicate across your OWN retries too.
|
|
1013
|
+
*
|
|
705
1014
|
* @throws Error before any request when zero or several selectors are set —
|
|
706
1015
|
* the server would reject the body anyway; failing locally is clearer.
|
|
707
1016
|
*/
|
|
708
|
-
async create(input) {
|
|
1017
|
+
async create(input, options) {
|
|
709
1018
|
const entries = ["url", "orgnr", "name"].filter(
|
|
710
1019
|
(key2) => input[key2] !== void 0 && input[key2] !== ""
|
|
711
1020
|
);
|
|
@@ -713,7 +1022,7 @@ var Scan = class {
|
|
|
713
1022
|
throw new Error("scan.create requires exactly one of url, orgnr, or name");
|
|
714
1023
|
}
|
|
715
1024
|
const key = entries[0];
|
|
716
|
-
return this.client.
|
|
1025
|
+
return this.client.postOnce("/api/v1/scan", { [key]: input[key] }, options);
|
|
717
1026
|
}
|
|
718
1027
|
/** Get a scan job's status and, once done, its findings payload. */
|
|
719
1028
|
async get(id) {
|
|
@@ -771,6 +1080,13 @@ var Webhooks = class {
|
|
|
771
1080
|
* `secret` is typed optional because an idempotent replay (retrying with the
|
|
772
1081
|
* same `Idempotency-Key`, `X-Idempotent-Replayed: true`) returns the existing
|
|
773
1082
|
* endpoint WITHOUT the secret — handle that case (rotate if you lost it).
|
|
1083
|
+
*
|
|
1084
|
+
* Automatically idempotent: a duplicate endpoint is not a stray row, it is a
|
|
1085
|
+
* second copy of every future delivery to the same URL, forever. The key the
|
|
1086
|
+
* confirmer chose is the key that goes out — a capability confirmation is
|
|
1087
|
+
* bound to its idempotency key, so minting a fresh one here would invalidate
|
|
1088
|
+
* the confirmation. That the SDK now always sends a key is also what makes
|
|
1089
|
+
* the replay-without-secret case above reachable on a plain 5xx retry.
|
|
774
1090
|
*/
|
|
775
1091
|
async create(input, options) {
|
|
776
1092
|
const resolved = await this.confirmer.prepare(
|
|
@@ -778,7 +1094,7 @@ var Webhooks = class {
|
|
|
778
1094
|
void 0,
|
|
779
1095
|
options
|
|
780
1096
|
);
|
|
781
|
-
return this.client.
|
|
1097
|
+
return this.client.postOnce("/api/v1/webhooks", input, resolved);
|
|
782
1098
|
}
|
|
783
1099
|
/** Get a webhook endpoint by ID. */
|
|
784
1100
|
async get(id) {
|
|
@@ -813,7 +1129,14 @@ var Webhooks = class {
|
|
|
813
1129
|
if (options?.limit !== void 0) params.limit = String(options.limit);
|
|
814
1130
|
return this.client.get(`/api/v1/webhooks/${encodeURIComponent(id)}/deliveries`, params);
|
|
815
1131
|
}
|
|
816
|
-
/**
|
|
1132
|
+
/**
|
|
1133
|
+
* Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202.
|
|
1134
|
+
*
|
|
1135
|
+
* Deliberately unkeyed: a duplicate ping is the one duplicate that costs
|
|
1136
|
+
* nothing. Real deliveries are retried too, so any endpoint worth pointing at
|
|
1137
|
+
* already tolerates receiving the same event twice — that is what this call
|
|
1138
|
+
* exists to prove.
|
|
1139
|
+
*/
|
|
817
1140
|
async test(id) {
|
|
818
1141
|
return this.client.post(`/api/v1/webhooks/${encodeURIComponent(id)}/test`);
|
|
819
1142
|
}
|
|
@@ -903,6 +1226,7 @@ async function verifyWebhookSignature(input) {
|
|
|
903
1226
|
|
|
904
1227
|
// src/index.ts
|
|
905
1228
|
var Medal = class {
|
|
1229
|
+
bookings;
|
|
906
1230
|
capabilityConfirmations;
|
|
907
1231
|
channels;
|
|
908
1232
|
emails;
|
|
@@ -932,6 +1256,7 @@ var Medal = class {
|
|
|
932
1256
|
this.capabilityConfirmations,
|
|
933
1257
|
options?.autoConfirmCapabilities
|
|
934
1258
|
);
|
|
1259
|
+
this.bookings = new Bookings(client);
|
|
935
1260
|
this.channels = new Channels(client, confirmer);
|
|
936
1261
|
this.emails = new Emails(client);
|
|
937
1262
|
this.contacts = new Contacts(client);
|
|
@@ -951,6 +1276,7 @@ var src_default = Medal;
|
|
|
951
1276
|
// Annotate the CommonJS export names for ESM import in node:
|
|
952
1277
|
0 && (module.exports = {
|
|
953
1278
|
BaseClient,
|
|
1279
|
+
Bookings,
|
|
954
1280
|
CAPABILITY_IDS,
|
|
955
1281
|
CAPABILITY_ROUTES,
|
|
956
1282
|
CapabilityConfirmations,
|