@wokuapp/sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1137 @@
1
+ 'use strict';
2
+
3
+ // src/core/errors.ts
4
+ var WokuError = class extends Error {
5
+ constructor(message, options) {
6
+ super(message);
7
+ this.name = "WokuError";
8
+ this.code = options?.code;
9
+ if (options?.cause !== void 0) {
10
+ this.cause = options.cause;
11
+ }
12
+ }
13
+ };
14
+ var WokuConnectionError = class extends WokuError {
15
+ constructor(message, options) {
16
+ super(message, {
17
+ code: options?.code ?? "connection_error",
18
+ cause: options?.cause
19
+ });
20
+ this.name = "WokuConnectionError";
21
+ }
22
+ };
23
+ var WokuTimeoutError = class extends WokuConnectionError {
24
+ constructor(message = "Request timed out") {
25
+ super(message, { code: "timeout" });
26
+ this.name = "WokuTimeoutError";
27
+ }
28
+ };
29
+ var WokuAPIError = class _WokuAPIError extends WokuError {
30
+ constructor(status, body, message, requestId, code, retryAfterSeconds) {
31
+ super(message, { code: code ?? codeForStatus(status) });
32
+ this.name = "WokuAPIError";
33
+ this.status = status;
34
+ this.body = body;
35
+ this.requestId = requestId;
36
+ this.retryAfterSeconds = retryAfterSeconds ?? retryAfterFromBody(body);
37
+ }
38
+ /** Build the most specific error subclass for a status + body. */
39
+ static from(status, body, requestId, retryAfterSeconds) {
40
+ const message = messageFrom(status, body, requestId);
41
+ const ra = retryAfterSeconds;
42
+ switch (true) {
43
+ case status === 400:
44
+ return new BadRequestError(status, body, message, requestId, void 0, ra);
45
+ case status === 401:
46
+ return new AuthenticationError(status, body, message, requestId, void 0, ra);
47
+ case status === 403:
48
+ return new PermissionDeniedError(status, body, message, requestId, void 0, ra);
49
+ case status === 404:
50
+ return new NotFoundError(status, body, message, requestId, void 0, ra);
51
+ case status === 409:
52
+ return new ConflictError(status, body, message, requestId, void 0, ra);
53
+ case status === 422:
54
+ return new UnprocessableEntityError(status, body, message, requestId, void 0, ra);
55
+ case status === 429:
56
+ return new RateLimitError(status, body, message, requestId, void 0, ra);
57
+ case status >= 500:
58
+ return new InternalServerError(status, body, message, requestId, void 0, ra);
59
+ default:
60
+ return new _WokuAPIError(status, body, message, requestId, void 0, ra);
61
+ }
62
+ }
63
+ };
64
+ var BadRequestError = class extends WokuAPIError {
65
+ constructor(...args) {
66
+ super(...args);
67
+ this.name = "BadRequestError";
68
+ }
69
+ };
70
+ var AuthenticationError = class extends WokuAPIError {
71
+ constructor(...args) {
72
+ super(...args);
73
+ this.name = "AuthenticationError";
74
+ }
75
+ };
76
+ var PermissionDeniedError = class extends WokuAPIError {
77
+ constructor(...args) {
78
+ super(...args);
79
+ this.name = "PermissionDeniedError";
80
+ }
81
+ };
82
+ var NotFoundError = class extends WokuAPIError {
83
+ constructor(...args) {
84
+ super(...args);
85
+ this.name = "NotFoundError";
86
+ }
87
+ };
88
+ var ConflictError = class extends WokuAPIError {
89
+ constructor(...args) {
90
+ super(...args);
91
+ this.name = "ConflictError";
92
+ }
93
+ };
94
+ var UnprocessableEntityError = class extends WokuAPIError {
95
+ constructor(...args) {
96
+ super(...args);
97
+ this.name = "UnprocessableEntityError";
98
+ }
99
+ };
100
+ var RateLimitError = class extends WokuAPIError {
101
+ constructor(...args) {
102
+ super(...args);
103
+ this.name = "RateLimitError";
104
+ }
105
+ };
106
+ var InternalServerError = class extends WokuAPIError {
107
+ constructor(...args) {
108
+ super(...args);
109
+ this.name = "InternalServerError";
110
+ }
111
+ };
112
+ var retryAfterFromBody = (body) => body && typeof body === "object" && typeof body.retryAfter === "number" ? body.retryAfter : void 0;
113
+ var codeForStatus = (status) => {
114
+ const map = {
115
+ 400: "bad_request",
116
+ 401: "authentication_error",
117
+ 403: "permission_denied",
118
+ 404: "not_found",
119
+ 409: "conflict",
120
+ 422: "unprocessable_entity",
121
+ 429: "rate_limited"
122
+ };
123
+ return map[status] ?? (status >= 500 ? "internal_server_error" : "api_error");
124
+ };
125
+ var messageFrom = (status, body, requestId) => {
126
+ let detail = `HTTP ${status}`;
127
+ if (typeof body === "string" && body.trim()) {
128
+ detail = body.trim();
129
+ } else if (body && typeof body === "object") {
130
+ const { message } = body;
131
+ if (Array.isArray(message)) detail = message.join(", ");
132
+ else if (typeof message === "string" && message) detail = message;
133
+ else if (typeof body.error === "string") detail = body.error;
134
+ }
135
+ return requestId ? `${status} ${detail} (request_id: ${requestId})` : `${status} ${detail}`;
136
+ };
137
+
138
+ // src/core/pagination.ts
139
+ var isPageResponse = (value) => typeof value === "object" && value !== null && Array.isArray(value.data) && typeof value.total === "number";
140
+ var Page = class {
141
+ constructor(response, fetchPage, options) {
142
+ this.fetchPage = fetchPage;
143
+ this.options = options;
144
+ this.data = response.data;
145
+ this.total = response.total;
146
+ this.page = response.page;
147
+ this.limit = response.limit;
148
+ }
149
+ /** Whether another page exists after this one. */
150
+ hasNextPage() {
151
+ if (this.limit <= 0) return false;
152
+ return this.page * this.limit < this.total;
153
+ }
154
+ /** Fetch the next page (throws if there is none — guard with hasNextPage). */
155
+ getNextPage() {
156
+ if (!this.hasNextPage()) {
157
+ throw new RangeError("No next page");
158
+ }
159
+ return this.fetchPage(this.page + 1, this.options);
160
+ }
161
+ /** Yield every item across all pages, fetching lazily as needed. */
162
+ async *[Symbol.asyncIterator]() {
163
+ for await (const page of walkPages(this)) {
164
+ for (const item of page.data) yield item;
165
+ }
166
+ }
167
+ /** Yield each Page, fetching lazily as needed. */
168
+ iterPages() {
169
+ return walkPages(this);
170
+ }
171
+ };
172
+ async function* walkPages(start) {
173
+ let current = start;
174
+ for (; ; ) {
175
+ yield current;
176
+ if (!current.hasNextPage()) return;
177
+ current = await current.getNextPage();
178
+ }
179
+ }
180
+
181
+ // src/core/client.ts
182
+ var DEFAULT_BASE_URL = "https://clientapi.woku.app";
183
+ var DEFAULT_TIMEOUT = 6e4;
184
+ var DEFAULT_MAX_RETRIES = 2;
185
+ var RETRY_BASE_MS = 500;
186
+ var RETRY_CAP_MS = 8e3;
187
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
188
+ var SDK_VERSION = "0.1.0";
189
+ var randomId = () => {
190
+ const c = globalThis.crypto;
191
+ if (c?.randomUUID) return c.randomUUID();
192
+ return `idmp_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
193
+ };
194
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
195
+ var WokuClient = class {
196
+ constructor(options = {}) {
197
+ const apiKey = options.apiKey ?? globalThis.process?.env?.WOKU_API_KEY;
198
+ if (!apiKey) {
199
+ throw new WokuError(
200
+ "Missing API key: pass { apiKey } or set WOKU_API_KEY.",
201
+ { code: "config_error" }
202
+ );
203
+ }
204
+ if (!options.dangerouslyAllowBrowser && isBrowser()) {
205
+ throw new WokuError(
206
+ "The Woku SDK is server-only: the secret key must not run in a browser. Pass { dangerouslyAllowBrowser: true } only if you fully control the runtime.",
207
+ { code: "config_error" }
208
+ );
209
+ }
210
+ const fetchImpl = options.fetch ?? globalThis.fetch;
211
+ if (!fetchImpl) {
212
+ throw new WokuError(
213
+ "No fetch implementation available; pass { fetch }.",
214
+ {
215
+ code: "config_error"
216
+ }
217
+ );
218
+ }
219
+ this.apiKey = apiKey;
220
+ this.baseURL = (options.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
221
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
222
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
223
+ this.fetch = fetchImpl;
224
+ this.defaultHeaders = options.defaultHeaders ?? {};
225
+ }
226
+ /** Issue one request and return the parsed JSON body typed as `T`. */
227
+ async request(method, path, args = {}) {
228
+ const url = this.buildUrl(path, args.query);
229
+ const idempotencyKey = args.idempotencyKey ?? (args.idempotent && method.toUpperCase() === "POST" ? randomId() : void 0);
230
+ const headers = this.buildHeaders(method, { ...args, idempotencyKey });
231
+ const bodyText = args.body === void 0 ? void 0 : JSON.stringify(args.body);
232
+ const maxRetries = args.maxRetries ?? this.maxRetries;
233
+ const timeout = args.timeout ?? this.timeout;
234
+ const retryable = method.toUpperCase() === "GET" || Boolean(headers["X-Woku-Idempotency-Key"]);
235
+ let attempt = 0;
236
+ for (; ; ) {
237
+ try {
238
+ const { status, body, requestId, retryAfterSeconds } = await this.send(
239
+ url,
240
+ method,
241
+ headers,
242
+ bodyText,
243
+ timeout,
244
+ args.signal
245
+ );
246
+ if (status >= 200 && status < 300) {
247
+ return body;
248
+ }
249
+ const apiError = WokuAPIError.from(
250
+ status,
251
+ body,
252
+ requestId,
253
+ retryAfterSeconds
254
+ );
255
+ if (retryable && attempt < maxRetries && RETRYABLE_STATUS.has(status)) {
256
+ await sleep(this.backoff(attempt, apiError));
257
+ attempt += 1;
258
+ continue;
259
+ }
260
+ throw apiError;
261
+ } catch (error) {
262
+ if (error instanceof WokuAPIError) throw error;
263
+ const connError = toConnectionError(error);
264
+ if (connError.code === "aborted") throw connError;
265
+ if (retryable && attempt < maxRetries) {
266
+ await sleep(this.backoff(attempt));
267
+ attempt += 1;
268
+ continue;
269
+ }
270
+ throw connError;
271
+ }
272
+ }
273
+ }
274
+ /**
275
+ * Issue a GET that returns a paginated envelope and wrap it as a {@link Page}.
276
+ * `params` accepts any plain object (a resource's typed params interface).
277
+ */
278
+ async getPage(path, params, opts) {
279
+ const base = params ?? {};
280
+ const query = { ...base, ...opts?.query };
281
+ const response = await this.request("get", path, {
282
+ ...opts,
283
+ query
284
+ });
285
+ const envelope = isPageResponse(response) ? response : {
286
+ data: response ?? [],
287
+ total: Array.isArray(response) ? response.length : 0,
288
+ page: 1,
289
+ limit: Array.isArray(response) ? response.length : 0
290
+ };
291
+ return new Page(
292
+ envelope,
293
+ (page, pageOpts) => this.getPage(path, { ...base, page }, { ...opts, ...pageOpts }),
294
+ opts
295
+ );
296
+ }
297
+ buildUrl(path, query) {
298
+ const url = `${this.baseURL}${path.startsWith("/") ? path : `/${path}`}`;
299
+ if (!query) return url;
300
+ const search = new URLSearchParams();
301
+ for (const [key, value] of Object.entries(query)) {
302
+ if (value === void 0 || value === null) continue;
303
+ if (Array.isArray(value)) {
304
+ for (const item of value) search.append(key, String(item));
305
+ } else {
306
+ search.append(key, String(value));
307
+ }
308
+ }
309
+ const qs = search.toString();
310
+ return qs ? `${url}?${qs}` : url;
311
+ }
312
+ buildHeaders(method, args) {
313
+ const headers = {
314
+ Accept: "application/json",
315
+ "User-Agent": `woku-sdk-js/${SDK_VERSION}`,
316
+ ...this.defaultHeaders,
317
+ ...args.headers
318
+ };
319
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
320
+ if (args.body !== void 0) {
321
+ headers["Content-Type"] = "application/json";
322
+ }
323
+ if (args.idempotencyKey !== void 0 && method.toUpperCase() === "POST") {
324
+ headers["X-Woku-Idempotency-Key"] = args.idempotencyKey;
325
+ }
326
+ return headers;
327
+ }
328
+ async send(url, method, headers, body, timeout, signal) {
329
+ const controller = new AbortController();
330
+ let timedOut = false;
331
+ const timer = setTimeout(() => {
332
+ timedOut = true;
333
+ controller.abort();
334
+ }, timeout);
335
+ const onExternalAbort = () => controller.abort();
336
+ if (signal) {
337
+ if (signal.aborted) controller.abort();
338
+ else signal.addEventListener("abort", onExternalAbort);
339
+ }
340
+ try {
341
+ const response = await this.fetch(url, {
342
+ method: method.toUpperCase(),
343
+ headers,
344
+ body,
345
+ signal: controller.signal
346
+ });
347
+ const text = await response.text();
348
+ return {
349
+ status: response.status,
350
+ body: parseBody(text),
351
+ requestId: requestIdFrom(response.headers),
352
+ retryAfterSeconds: retryAfterFromHeaders(response.headers)
353
+ };
354
+ } catch (error) {
355
+ if (timedOut) throw new WokuTimeoutError();
356
+ if (signal?.aborted) {
357
+ throw new WokuConnectionError("Request aborted", { code: "aborted" });
358
+ }
359
+ throw error;
360
+ } finally {
361
+ clearTimeout(timer);
362
+ if (signal) signal.removeEventListener("abort", onExternalAbort);
363
+ }
364
+ }
365
+ backoff(attempt, apiError) {
366
+ const retryAfter = apiError && typeof apiError.retryAfterSeconds === "number" ? apiError.retryAfterSeconds * 1e3 : void 0;
367
+ if (retryAfter !== void 0) return Math.min(retryAfter, RETRY_CAP_MS);
368
+ const ceiling = Math.min(RETRY_CAP_MS, RETRY_BASE_MS * 2 ** attempt);
369
+ return Math.random() * ceiling;
370
+ }
371
+ };
372
+ var isBrowser = () => typeof globalThis.document !== "undefined";
373
+ var parseBody = (text) => {
374
+ if (!text) return void 0;
375
+ try {
376
+ return JSON.parse(text);
377
+ } catch {
378
+ return text;
379
+ }
380
+ };
381
+ var requestIdFrom = (headers) => headers.get("x-request-id") ?? headers.get("request-id") ?? headers.get("x-woku-request-id") ?? void 0;
382
+ var retryAfterFromHeaders = (headers) => {
383
+ const raw = headers.get("retry-after");
384
+ if (!raw) return void 0;
385
+ const seconds = Number(raw);
386
+ if (Number.isFinite(seconds)) return Math.max(0, seconds);
387
+ const when = Date.parse(raw);
388
+ if (!Number.isNaN(when)) return Math.max(0, (when - Date.now()) / 1e3);
389
+ return void 0;
390
+ };
391
+ var toConnectionError = (error) => {
392
+ if (error instanceof WokuConnectionError) return error;
393
+ const message = error instanceof Error ? error.message : "Network request failed";
394
+ return new WokuConnectionError(message, { cause: error });
395
+ };
396
+
397
+ // src/resources/trackers.ts
398
+ var Trackers = class {
399
+ constructor(client) {
400
+ this.client = client;
401
+ }
402
+ /** List the company tracker definitions (paginated). */
403
+ list(params, opts) {
404
+ return this.client.getPage("/v1/external-trackers", params, opts);
405
+ }
406
+ /** Create a tracker definition (idempotent). */
407
+ create(body, opts) {
408
+ return this.client.request("post", "/v1/external-trackers", {
409
+ ...opts,
410
+ body,
411
+ idempotent: true
412
+ });
413
+ }
414
+ /** Get one tracker definition. */
415
+ get(id, opts) {
416
+ return this.client.request(
417
+ "get",
418
+ `/v1/external-trackers/${id}`,
419
+ opts
420
+ );
421
+ }
422
+ /** Update a tracker definition. */
423
+ update(id, body, opts) {
424
+ return this.client.request(
425
+ "patch",
426
+ `/v1/external-trackers/${id}`,
427
+ { ...opts, body }
428
+ );
429
+ }
430
+ /** Activate a tracker definition. */
431
+ activate(id, opts) {
432
+ return this.client.request(
433
+ "patch",
434
+ `/v1/external-trackers/${id}/activate`,
435
+ opts
436
+ );
437
+ }
438
+ /** Deactivate a tracker definition. */
439
+ deactivate(id, opts) {
440
+ return this.client.request(
441
+ "patch",
442
+ `/v1/external-trackers/${id}/deactivate`,
443
+ opts
444
+ );
445
+ }
446
+ /** Search VoC entities whose trackers match every filter (AND). */
447
+ searchEntities(body, opts) {
448
+ return this.client.request(
449
+ "post",
450
+ "/v1/external-trackers/search-entities",
451
+ { ...opts, body }
452
+ );
453
+ }
454
+ /** List the tracker values assigned to a woku. */
455
+ listWokuValues(wokuId, opts) {
456
+ return this.client.request(
457
+ "get",
458
+ `/v1/external-trackers/wokus/${wokuId}`,
459
+ opts
460
+ );
461
+ }
462
+ /** Assign (upsert) a tracker value to a woku by tracker name. */
463
+ assignToWoku(wokuId, body, opts) {
464
+ return this.client.request(
465
+ "post",
466
+ `/v1/external-trackers/wokus/${wokuId}`,
467
+ { ...opts, body, idempotent: true }
468
+ );
469
+ }
470
+ /** Remove a tracker value from a woku by tracker name. */
471
+ removeFromWoku(wokuId, trackerName, opts) {
472
+ return this.client.request(
473
+ "delete",
474
+ `/v1/external-trackers/wokus/${wokuId}/${encodeURIComponent(trackerName)}`,
475
+ opts
476
+ );
477
+ }
478
+ /** Search wokus by an exact `(tracker name, value)` pair (paginated). */
479
+ searchWokus(params, opts) {
480
+ return this.client.getPage(
481
+ "/v1/external-trackers/search",
482
+ params,
483
+ opts
484
+ );
485
+ }
486
+ /** List the tracker values assigned to a VoC entity (nps/csat/ces/form/flow). */
487
+ listEntityValues(entityType, id, opts) {
488
+ return this.client.request(
489
+ "get",
490
+ `/v1/external-trackers/${entityType}/${id}`,
491
+ opts
492
+ );
493
+ }
494
+ /** Assign (upsert) a tracker value to a VoC entity by tracker name. */
495
+ assignToEntity(entityType, id, body, opts) {
496
+ return this.client.request(
497
+ "post",
498
+ `/v1/external-trackers/${entityType}/${id}`,
499
+ { ...opts, body, idempotent: true }
500
+ );
501
+ }
502
+ /** Remove a tracker value from a VoC entity by tracker name. */
503
+ removeFromEntity(entityType, id, trackerName, opts) {
504
+ return this.client.request(
505
+ "delete",
506
+ `/v1/external-trackers/${entityType}/${id}/${encodeURIComponent(trackerName)}`,
507
+ opts
508
+ );
509
+ }
510
+ };
511
+
512
+ // src/resources/voc-tools.ts
513
+ var NpsTools = class {
514
+ constructor(client) {
515
+ this.client = client;
516
+ }
517
+ list(params, opts) {
518
+ return this.client.getPage("/v1/nps-tools", params, opts);
519
+ }
520
+ create(body, opts) {
521
+ return this.client.request("post", "/v1/nps-tools", {
522
+ ...opts,
523
+ body,
524
+ idempotent: true
525
+ });
526
+ }
527
+ get(id, opts) {
528
+ return this.client.request("get", `/v1/nps-tool/${id}`, opts);
529
+ }
530
+ update(id, body, opts) {
531
+ return this.client.request("patch", `/v1/nps-tools/${id}`, {
532
+ ...opts,
533
+ body
534
+ });
535
+ }
536
+ delete(id, opts) {
537
+ return this.client.request(
538
+ "delete",
539
+ `/v1/nps-tools/${id}`,
540
+ opts
541
+ );
542
+ }
543
+ };
544
+ var CsatTools = class {
545
+ constructor(client) {
546
+ this.client = client;
547
+ }
548
+ list(params, opts) {
549
+ return this.client.getPage("/v1/csat-tools", params, opts);
550
+ }
551
+ create(body, opts) {
552
+ return this.client.request("post", "/v1/csat-tools", {
553
+ ...opts,
554
+ body,
555
+ idempotent: true
556
+ });
557
+ }
558
+ get(id, opts) {
559
+ return this.client.request("get", `/v1/csat-tool/${id}`, opts);
560
+ }
561
+ update(id, body, opts) {
562
+ return this.client.request("patch", `/v1/csat-tools/${id}`, {
563
+ ...opts,
564
+ body
565
+ });
566
+ }
567
+ delete(id, opts) {
568
+ return this.client.request(
569
+ "delete",
570
+ `/v1/csat-tools/${id}`,
571
+ opts
572
+ );
573
+ }
574
+ };
575
+ var CesTools = class {
576
+ constructor(client) {
577
+ this.client = client;
578
+ }
579
+ list(params, opts) {
580
+ return this.client.getPage("/v1/ces-tools", params, opts);
581
+ }
582
+ create(body, opts) {
583
+ return this.client.request("post", "/v1/ces-tools", {
584
+ ...opts,
585
+ body,
586
+ idempotent: true
587
+ });
588
+ }
589
+ get(id, opts) {
590
+ return this.client.request("get", `/v1/ces-tool/${id}`, opts);
591
+ }
592
+ update(id, body, opts) {
593
+ return this.client.request("patch", `/v1/ces-tools/${id}`, {
594
+ ...opts,
595
+ body
596
+ });
597
+ }
598
+ delete(id, opts) {
599
+ return this.client.request(
600
+ "delete",
601
+ `/v1/ces-tools/${id}`,
602
+ opts
603
+ );
604
+ }
605
+ };
606
+
607
+ // src/resources/surveys.ts
608
+ var Nps = class {
609
+ constructor(client) {
610
+ this.client = client;
611
+ }
612
+ /** Send the NPS survey by email or WhatsApp (idempotent). */
613
+ sendInvitations(body, opts) {
614
+ return this.client.request(
615
+ "post",
616
+ "/v1/nps/invitations",
617
+ {
618
+ ...opts,
619
+ body,
620
+ idempotent: true
621
+ }
622
+ );
623
+ }
624
+ /** List NPS responses (paginated). */
625
+ listResponses(params, opts) {
626
+ return this.client.getPage("/v1/nps", params, opts);
627
+ }
628
+ /** Get one NPS response. */
629
+ getResponse(id, opts) {
630
+ return this.client.request("get", `/v1/nps/${id}`, opts);
631
+ }
632
+ };
633
+ var Csat = class {
634
+ constructor(client) {
635
+ this.client = client;
636
+ }
637
+ sendInvitations(body, opts) {
638
+ return this.client.request(
639
+ "post",
640
+ "/v1/csat/invitations",
641
+ { ...opts, body, idempotent: true }
642
+ );
643
+ }
644
+ listResponses(params, opts) {
645
+ return this.client.getPage("/v1/csat", params, opts);
646
+ }
647
+ getResponse(id, opts) {
648
+ return this.client.request("get", `/v1/csat/${id}`, opts);
649
+ }
650
+ };
651
+ var Ces = class {
652
+ constructor(client) {
653
+ this.client = client;
654
+ }
655
+ sendInvitations(body, opts) {
656
+ return this.client.request(
657
+ "post",
658
+ "/v1/ces/invitations",
659
+ {
660
+ ...opts,
661
+ body,
662
+ idempotent: true
663
+ }
664
+ );
665
+ }
666
+ listResponses(params, opts) {
667
+ return this.client.getPage("/v1/ces", params, opts);
668
+ }
669
+ getResponse(id, opts) {
670
+ return this.client.request("get", `/v1/ces/${id}`, opts);
671
+ }
672
+ };
673
+
674
+ // src/resources/wokus.ts
675
+ var Wokus = class {
676
+ constructor(client) {
677
+ this.client = client;
678
+ }
679
+ list(params, opts) {
680
+ return this.client.getPage("/v1/wokus", params, opts);
681
+ }
682
+ create(body, opts) {
683
+ return this.client.request("post", "/v1/wokus", {
684
+ ...opts,
685
+ body,
686
+ idempotent: true
687
+ });
688
+ }
689
+ /** Get one woku with aggregated review stats. */
690
+ get(id, opts) {
691
+ return this.client.request("get", `/v1/wokus/${id}`, opts);
692
+ }
693
+ update(id, body, opts) {
694
+ return this.client.request("patch", `/v1/wokus/${id}`, {
695
+ ...opts,
696
+ body
697
+ });
698
+ }
699
+ delete(id, opts) {
700
+ return this.client.request(
701
+ "delete",
702
+ `/v1/wokus/${id}`,
703
+ opts
704
+ );
705
+ }
706
+ /** Apply the boolean settings idempotently (closed/reviewsDisabled/...). */
707
+ updateSettings(id, body, opts) {
708
+ return this.client.request("patch", `/v1/wokus/${id}/settings`, {
709
+ ...opts,
710
+ body
711
+ });
712
+ }
713
+ /** Move the woku into a folder, or to the root with `{ folderId: null }`. */
714
+ move(id, body, opts) {
715
+ return this.client.request("patch", `/v1/wokus/${id}/move`, {
716
+ ...opts,
717
+ body
718
+ });
719
+ }
720
+ /** List the reviews of a woku (paginated). */
721
+ listReviews(id, params, opts) {
722
+ return this.client.getPage(
723
+ `/v1/wokus/${id}/reviews`,
724
+ params,
725
+ opts
726
+ );
727
+ }
728
+ /** Send a woku review invitation by email or WhatsApp (idempotent). */
729
+ sendInvitations(id, body, opts) {
730
+ return this.client.request(
731
+ "post",
732
+ `/v1/wokus/${id}/invitations`,
733
+ { ...opts, body, idempotent: true }
734
+ );
735
+ }
736
+ /** Share a woku review link by email. */
737
+ share(id, body, opts) {
738
+ return this.client.request("post", `/v1/wokus/${id}/share`, {
739
+ ...opts,
740
+ body
741
+ });
742
+ }
743
+ };
744
+
745
+ // src/resources/forms.ts
746
+ var Forms = class {
747
+ constructor(client) {
748
+ this.client = client;
749
+ }
750
+ list(params, opts) {
751
+ return this.client.getPage("/v1/forms", params, opts);
752
+ }
753
+ get(id, opts) {
754
+ return this.client.request("get", `/v1/forms/${id}`, opts);
755
+ }
756
+ /** List the responses of a form (paginated). */
757
+ listResponses(id, params, opts) {
758
+ return this.client.getPage(
759
+ `/v1/forms/${id}/responses`,
760
+ params,
761
+ opts
762
+ );
763
+ }
764
+ /** Send a form by email or WhatsApp (idempotent). */
765
+ sendInvitations(id, body, opts) {
766
+ return this.client.request(
767
+ "post",
768
+ `/v1/forms/${id}/invitations`,
769
+ { ...opts, body, idempotent: true }
770
+ );
771
+ }
772
+ };
773
+
774
+ // src/resources/flows.ts
775
+ var Flows = class {
776
+ constructor(client) {
777
+ this.client = client;
778
+ }
779
+ list(params, opts) {
780
+ return this.client.getPage("/v1/flows", params, opts);
781
+ }
782
+ get(id, opts) {
783
+ return this.client.request("get", `/v1/flows/${id}`, opts);
784
+ }
785
+ };
786
+
787
+ // src/resources/action-plans.ts
788
+ var ActionPlans = class {
789
+ constructor(client) {
790
+ this.client = client;
791
+ }
792
+ list(params, opts) {
793
+ return this.client.getPage("/v1/action-plans", params, opts);
794
+ }
795
+ get(id, opts) {
796
+ return this.client.request(
797
+ "get",
798
+ `/v1/action-plans/${id}`,
799
+ opts
800
+ );
801
+ }
802
+ /** The plan timeline (events, oldest first). */
803
+ events(id, opts) {
804
+ return this.client.request(
805
+ "get",
806
+ `/v1/action-plans/${id}/events`,
807
+ opts
808
+ );
809
+ }
810
+ /** The plan AI conversation (read-only). */
811
+ getConversation(id, opts) {
812
+ return this.client.request(
813
+ "get",
814
+ `/v1/action-plans/${id}/conversation`,
815
+ opts
816
+ );
817
+ }
818
+ /**
819
+ * Reply to the plan AI agent. Each reply is a paid AI turn (`confirm:true` is
820
+ * sent automatically); the reply is composed asynchronously, so poll
821
+ * {@link getConversation} until `busy` is false.
822
+ */
823
+ reply(id, text, opts) {
824
+ return this.client.request(
825
+ "post",
826
+ `/v1/action-plans/${id}/conversation`,
827
+ { ...opts, body: { text, confirm: true } }
828
+ );
829
+ }
830
+ /** Send an approved plan to a destination (jira/monday/clickup/notion/internal). */
831
+ send(id, body, opts) {
832
+ return this.client.request(
833
+ "post",
834
+ `/v1/action-plans/${id}/send`,
835
+ { ...opts, body }
836
+ );
837
+ }
838
+ createTask(id, body, opts) {
839
+ return this.client.request(
840
+ "post",
841
+ `/v1/action-plans/${id}/tasks`,
842
+ { ...opts, body, idempotent: true }
843
+ );
844
+ }
845
+ updateTask(id, taskId, body, opts) {
846
+ return this.client.request(
847
+ "patch",
848
+ `/v1/action-plans/${id}/tasks/${taskId}`,
849
+ { ...opts, body }
850
+ );
851
+ }
852
+ reorderTasks(id, body, opts) {
853
+ return this.client.request(
854
+ "patch",
855
+ `/v1/action-plans/${id}/tasks/reorder`,
856
+ { ...opts, body }
857
+ );
858
+ }
859
+ deleteTask(id, taskId, opts) {
860
+ return this.client.request(
861
+ "delete",
862
+ `/v1/action-plans/${id}/tasks/${taskId}`,
863
+ opts
864
+ );
865
+ }
866
+ approve(id, opts) {
867
+ return this.status(id, "approve", opts);
868
+ }
869
+ reopen(id, opts) {
870
+ return this.status(id, "reopen", opts);
871
+ }
872
+ cancel(id, opts) {
873
+ return this.status(id, "cancel", opts);
874
+ }
875
+ complete(id, opts) {
876
+ return this.status(id, "complete", opts);
877
+ }
878
+ resume(id, opts) {
879
+ return this.status(id, "resume", opts);
880
+ }
881
+ status(id, action, opts) {
882
+ return this.client.request(
883
+ "post",
884
+ `/v1/action-plans/${id}/${action}`,
885
+ opts
886
+ );
887
+ }
888
+ };
889
+ var ActionPlanGroups = class {
890
+ constructor(client) {
891
+ this.client = client;
892
+ }
893
+ list(params, opts) {
894
+ return this.client.request("get", "/v1/action-plan-groups", {
895
+ ...opts,
896
+ query: { ...params, ...opts?.query }
897
+ });
898
+ }
899
+ /** Get one group with its embedded stats. */
900
+ get(id, opts) {
901
+ return this.client.request(
902
+ "get",
903
+ `/v1/action-plan-groups/${id}`,
904
+ opts
905
+ );
906
+ }
907
+ create(body, opts) {
908
+ return this.client.request("post", "/v1/action-plan-groups", {
909
+ ...opts,
910
+ body,
911
+ idempotent: true
912
+ });
913
+ }
914
+ update(id, body, opts) {
915
+ return this.client.request(
916
+ "patch",
917
+ `/v1/action-plan-groups/${id}`,
918
+ { ...opts, body }
919
+ );
920
+ }
921
+ setEnabled(id, enabled, opts) {
922
+ return this.client.request(
923
+ "patch",
924
+ `/v1/action-plan-groups/${id}/enabled`,
925
+ { ...opts, body: { enabled } }
926
+ );
927
+ }
928
+ delete(id, opts) {
929
+ return this.client.request(
930
+ "delete",
931
+ `/v1/action-plan-groups/${id}`,
932
+ opts
933
+ );
934
+ }
935
+ };
936
+
937
+ // src/resources/tickets.ts
938
+ var Tickets = class {
939
+ constructor(client) {
940
+ this.client = client;
941
+ }
942
+ list(params, opts) {
943
+ return this.client.getPage("/v1/tickets", params, opts);
944
+ }
945
+ /** Aggregate counts by tool and by SAC destination. */
946
+ stats(params, opts) {
947
+ return this.client.request("get", "/v1/tickets/stats", {
948
+ ...opts,
949
+ query: { ...params, ...opts?.query }
950
+ });
951
+ }
952
+ get(id, opts) {
953
+ return this.client.request("get", `/v1/tickets/${id}`, opts);
954
+ }
955
+ update(id, body, opts) {
956
+ return this.client.request("patch", `/v1/tickets/${id}`, {
957
+ ...opts,
958
+ body
959
+ });
960
+ }
961
+ };
962
+ var TicketDestinations = class {
963
+ constructor(client) {
964
+ this.client = client;
965
+ }
966
+ list(opts) {
967
+ return this.client.request(
968
+ "get",
969
+ "/v1/ticket-destinations",
970
+ opts
971
+ );
972
+ }
973
+ get(id, opts) {
974
+ return this.client.request(
975
+ "get",
976
+ `/v1/ticket-destinations/${id}`,
977
+ opts
978
+ );
979
+ }
980
+ create(body, opts) {
981
+ return this.client.request("post", "/v1/ticket-destinations", {
982
+ ...opts,
983
+ body,
984
+ idempotent: true
985
+ });
986
+ }
987
+ update(id, body, opts) {
988
+ return this.client.request(
989
+ "patch",
990
+ `/v1/ticket-destinations/${id}`,
991
+ { ...opts, body }
992
+ );
993
+ }
994
+ delete(id, opts) {
995
+ return this.client.request(
996
+ "delete",
997
+ `/v1/ticket-destinations/${id}`,
998
+ opts
999
+ );
1000
+ }
1001
+ /**
1002
+ * Send a real connectivity test to a saved destination. Requires
1003
+ * `confirm: true` (the test reaches the live destination).
1004
+ */
1005
+ test(id, opts) {
1006
+ return this.client.request(
1007
+ "post",
1008
+ `/v1/ticket-destinations/${id}/test`,
1009
+ { ...opts, body: { confirm: true } }
1010
+ );
1011
+ }
1012
+ };
1013
+
1014
+ // src/resources/dispatches.ts
1015
+ var Dispatches = class {
1016
+ constructor(client) {
1017
+ this.client = client;
1018
+ }
1019
+ /** List the invitation dispatches (delivery status, no recipient PII). */
1020
+ list(params, opts) {
1021
+ return this.client.getPage("/v1/dispatches", params, opts);
1022
+ }
1023
+ /** Response-rate metrics over the dispatches. */
1024
+ stats(params, opts) {
1025
+ return this.client.request("get", "/v1/dispatches/stats", {
1026
+ ...opts,
1027
+ query: { ...params, ...opts?.query }
1028
+ });
1029
+ }
1030
+ };
1031
+
1032
+ // src/resources/reports.ts
1033
+ var Reports = class {
1034
+ constructor(client) {
1035
+ this.client = client;
1036
+ }
1037
+ /** Company-level NPS report. */
1038
+ companyNps(params, opts) {
1039
+ return this.client.request("get", "/v1/reports/company-nps", {
1040
+ ...opts,
1041
+ query: { ...params, ...opts?.query }
1042
+ });
1043
+ }
1044
+ /** NPS report for one tool. */
1045
+ npsTool(npsToolId, params, opts) {
1046
+ return this.client.request(
1047
+ "get",
1048
+ `/v1/reports/nps-tool/${npsToolId}`,
1049
+ { ...opts, query: { ...params, ...opts?.query } }
1050
+ );
1051
+ }
1052
+ };
1053
+
1054
+ // src/resources/company.ts
1055
+ var Company = class {
1056
+ constructor(client) {
1057
+ this.client = client;
1058
+ }
1059
+ /** Get the caller company. */
1060
+ me(opts) {
1061
+ return this.client.request("get", "/v1/companies/me", opts);
1062
+ }
1063
+ /** Rotate the secret key. The returned key replaces the current one. */
1064
+ rotateKey(opts) {
1065
+ return this.client.request(
1066
+ "post",
1067
+ "/v1/companies/me/rotate-key",
1068
+ opts
1069
+ );
1070
+ }
1071
+ /** Revoke the secret key (all subsequent requests will be unauthorized). */
1072
+ revokeKey(opts) {
1073
+ return this.client.request(
1074
+ "post",
1075
+ "/v1/companies/me/revoke-key",
1076
+ opts
1077
+ );
1078
+ }
1079
+ };
1080
+
1081
+ // src/resources/quarantines.ts
1082
+ var Quarantines = class {
1083
+ constructor(client) {
1084
+ this.client = client;
1085
+ }
1086
+ /** Check whether a contact is quarantined. */
1087
+ check(params, opts) {
1088
+ return this.client.request("get", "/v1/quarantines/check", {
1089
+ ...opts,
1090
+ query: { ...params, ...opts?.query }
1091
+ });
1092
+ }
1093
+ };
1094
+
1095
+ // src/woku.ts
1096
+ var Woku = class {
1097
+ constructor(options) {
1098
+ const opts = typeof options === "string" ? { apiKey: options } : options ?? {};
1099
+ this.client = new WokuClient(opts);
1100
+ this.trackers = new Trackers(this.client);
1101
+ this.npsTools = new NpsTools(this.client);
1102
+ this.csatTools = new CsatTools(this.client);
1103
+ this.cesTools = new CesTools(this.client);
1104
+ this.nps = new Nps(this.client);
1105
+ this.csat = new Csat(this.client);
1106
+ this.ces = new Ces(this.client);
1107
+ this.wokus = new Wokus(this.client);
1108
+ this.forms = new Forms(this.client);
1109
+ this.flows = new Flows(this.client);
1110
+ this.actionPlans = new ActionPlans(this.client);
1111
+ this.actionPlanGroups = new ActionPlanGroups(this.client);
1112
+ this.tickets = new Tickets(this.client);
1113
+ this.ticketDestinations = new TicketDestinations(this.client);
1114
+ this.dispatches = new Dispatches(this.client);
1115
+ this.reports = new Reports(this.client);
1116
+ this.company = new Company(this.client);
1117
+ this.quarantines = new Quarantines(this.client);
1118
+ }
1119
+ };
1120
+
1121
+ exports.AuthenticationError = AuthenticationError;
1122
+ exports.BadRequestError = BadRequestError;
1123
+ exports.ConflictError = ConflictError;
1124
+ exports.InternalServerError = InternalServerError;
1125
+ exports.NotFoundError = NotFoundError;
1126
+ exports.Page = Page;
1127
+ exports.PermissionDeniedError = PermissionDeniedError;
1128
+ exports.RateLimitError = RateLimitError;
1129
+ exports.UnprocessableEntityError = UnprocessableEntityError;
1130
+ exports.Woku = Woku;
1131
+ exports.WokuAPIError = WokuAPIError;
1132
+ exports.WokuClient = WokuClient;
1133
+ exports.WokuConnectionError = WokuConnectionError;
1134
+ exports.WokuError = WokuError;
1135
+ exports.WokuTimeoutError = WokuTimeoutError;
1136
+ //# sourceMappingURL=index.cjs.map
1137
+ //# sourceMappingURL=index.cjs.map