@wokuapp/sdk 0.2.2 → 0.4.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.js CHANGED
@@ -1,3 +1,43 @@
1
+ // src/resources/media.ts
2
+ var Media = class {
3
+ constructor(client) {
4
+ this.client = client;
5
+ }
6
+ upload(input, opts) {
7
+ const file = new Blob(
8
+ [input.file instanceof Blob ? input.file : new Uint8Array(input.file)],
9
+ {
10
+ type: input.contentType ?? (input.file instanceof Blob ? input.file.type : "")
11
+ }
12
+ );
13
+ const body = new FormData();
14
+ body.append("file", file, input.filename);
15
+ return this.client.request("post", "/v1/woku-media", {
16
+ ...opts,
17
+ body,
18
+ maxRetries: 0
19
+ });
20
+ }
21
+ };
22
+
23
+ // src/resources/follow-ups.ts
24
+ var FollowUps = class {
25
+ constructor(client) {
26
+ this.client = client;
27
+ }
28
+ async get(id, opts) {
29
+ const result = await this.client.request(
30
+ "get",
31
+ `/v1/follow-ups/${encodeURIComponent(id)}`,
32
+ opts
33
+ );
34
+ return result.data;
35
+ }
36
+ };
37
+
38
+ // package.json
39
+ var version = "0.4.0";
40
+
1
41
  // src/core/errors.ts
2
42
  var WokuError = class extends Error {
3
43
  constructor(message, options) {
@@ -39,23 +79,95 @@ var WokuAPIError = class _WokuAPIError extends WokuError {
39
79
  const ra = retryAfterSeconds;
40
80
  switch (true) {
41
81
  case status === 400:
42
- return new BadRequestError(status, body, message, requestId, void 0, ra);
82
+ return new BadRequestError(
83
+ status,
84
+ body,
85
+ message,
86
+ requestId,
87
+ void 0,
88
+ ra
89
+ );
43
90
  case status === 401:
44
- return new AuthenticationError(status, body, message, requestId, void 0, ra);
91
+ return new AuthenticationError(
92
+ status,
93
+ body,
94
+ message,
95
+ requestId,
96
+ void 0,
97
+ ra
98
+ );
45
99
  case status === 403:
46
- return new PermissionDeniedError(status, body, message, requestId, void 0, ra);
100
+ return new PermissionDeniedError(
101
+ status,
102
+ body,
103
+ message,
104
+ requestId,
105
+ void 0,
106
+ ra
107
+ );
47
108
  case status === 404:
48
- return new NotFoundError(status, body, message, requestId, void 0, ra);
109
+ return new NotFoundError(
110
+ status,
111
+ body,
112
+ message,
113
+ requestId,
114
+ void 0,
115
+ ra
116
+ );
49
117
  case status === 409:
50
- return new ConflictError(status, body, message, requestId, void 0, ra);
118
+ return new ConflictError(
119
+ status,
120
+ body,
121
+ message,
122
+ requestId,
123
+ void 0,
124
+ ra
125
+ );
126
+ case status === 413:
127
+ return new PayloadTooLargeError(
128
+ status,
129
+ body,
130
+ message,
131
+ requestId,
132
+ void 0,
133
+ ra
134
+ );
51
135
  case status === 422:
52
- return new UnprocessableEntityError(status, body, message, requestId, void 0, ra);
136
+ return new UnprocessableEntityError(
137
+ status,
138
+ body,
139
+ message,
140
+ requestId,
141
+ void 0,
142
+ ra
143
+ );
53
144
  case status === 429:
54
- return new RateLimitError(status, body, message, requestId, void 0, ra);
145
+ return new RateLimitError(
146
+ status,
147
+ body,
148
+ message,
149
+ requestId,
150
+ void 0,
151
+ ra
152
+ );
55
153
  case status >= 500:
56
- return new InternalServerError(status, body, message, requestId, void 0, ra);
154
+ return new InternalServerError(
155
+ status,
156
+ body,
157
+ message,
158
+ requestId,
159
+ void 0,
160
+ ra
161
+ );
57
162
  default:
58
- return new _WokuAPIError(status, body, message, requestId, void 0, ra);
163
+ return new _WokuAPIError(
164
+ status,
165
+ body,
166
+ message,
167
+ requestId,
168
+ void 0,
169
+ ra
170
+ );
59
171
  }
60
172
  }
61
173
  };
@@ -89,6 +201,12 @@ var ConflictError = class extends WokuAPIError {
89
201
  this.name = "ConflictError";
90
202
  }
91
203
  };
204
+ var PayloadTooLargeError = class extends WokuAPIError {
205
+ constructor(...args) {
206
+ super(...args);
207
+ this.name = "PayloadTooLargeError";
208
+ }
209
+ };
92
210
  var UnprocessableEntityError = class extends WokuAPIError {
93
211
  constructor(...args) {
94
212
  super(...args);
@@ -107,7 +225,7 @@ var InternalServerError = class extends WokuAPIError {
107
225
  this.name = "InternalServerError";
108
226
  }
109
227
  };
110
- var retryAfterFromBody = (body) => body && typeof body === "object" && typeof body.retryAfter === "number" ? body.retryAfter : void 0;
228
+ var retryAfterFromBody = (body) => body && typeof body === "object" && typeof body.retryAfter === "number" && Number.isFinite(body.retryAfter) && body.retryAfter >= 0 ? body.retryAfter : void 0;
111
229
  var codeForStatus = (status) => {
112
230
  const map = {
113
231
  400: "bad_request",
@@ -115,6 +233,7 @@ var codeForStatus = (status) => {
115
233
  403: "permission_denied",
116
234
  404: "not_found",
117
235
  409: "conflict",
236
+ 413: "payload_too_large",
118
237
  422: "unprocessable_entity",
119
238
  429: "rate_limited"
120
239
  };
@@ -125,7 +244,7 @@ var messageFrom = (status, body, requestId) => {
125
244
  if (typeof body === "string" && body.trim()) {
126
245
  detail = body.trim();
127
246
  } else if (body && typeof body === "object") {
128
- const { message } = body;
247
+ const message = body.message && typeof body.message === "object" && !Array.isArray(body.message) ? body.message.message : body.message;
129
248
  if (Array.isArray(message)) detail = message.join(", ");
130
249
  else if (typeof message === "string" && message) detail = message;
131
250
  else if (typeof body.error === "string") detail = body.error;
@@ -154,7 +273,13 @@ var Page = class {
154
273
  if (!this.hasNextPage()) {
155
274
  throw new RangeError("No next page");
156
275
  }
157
- return this.fetchPage(this.page + 1, this.options);
276
+ return this.fetchPage(this.page + 1, this.options).then((next) => {
277
+ if (next.page <= this.page)
278
+ throw new WokuError("Pagination did not advance.", {
279
+ code: "pagination_error"
280
+ });
281
+ return next;
282
+ });
158
283
  }
159
284
  /** Yield every item across all pages, fetching lazily as needed. */
160
285
  async *[Symbol.asyncIterator]() {
@@ -183,13 +308,32 @@ var DEFAULT_MAX_RETRIES = 2;
183
308
  var RETRY_BASE_MS = 500;
184
309
  var RETRY_CAP_MS = 8e3;
185
310
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
186
- var SDK_VERSION = "0.1.0";
187
311
  var randomId = () => {
188
312
  const c = globalThis.crypto;
189
313
  if (c?.randomUUID) return c.randomUUID();
190
314
  return `idmp_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
191
315
  };
192
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
316
+ var sleep = (ms, signal) => ms > 2147483647 ? sleep(2147483647, signal).then(() => sleep(ms - 2147483647, signal)) : new Promise((resolve, reject) => {
317
+ if (signal?.aborted) {
318
+ reject(
319
+ new WokuConnectionError("Request aborted", { code: "aborted" })
320
+ );
321
+ return;
322
+ }
323
+ const cleanup = () => signal?.removeEventListener("abort", abort);
324
+ const timer = setTimeout(() => {
325
+ cleanup();
326
+ resolve();
327
+ }, ms);
328
+ const abort = () => {
329
+ clearTimeout(timer);
330
+ cleanup();
331
+ reject(
332
+ new WokuConnectionError("Request aborted", { code: "aborted" })
333
+ );
334
+ };
335
+ signal?.addEventListener("abort", abort, { once: true });
336
+ });
193
337
  var WokuClient = class {
194
338
  constructor(options = {}) {
195
339
  const apiKey = options.apiKey ?? globalThis.process?.env?.WOKU_API_KEY;
@@ -226,10 +370,10 @@ var WokuClient = class {
226
370
  const url = this.buildUrl(path, args.query);
227
371
  const idempotencyKey = args.idempotencyKey ?? (args.idempotent && method.toUpperCase() === "POST" ? randomId() : void 0);
228
372
  const headers = this.buildHeaders(method, { ...args, idempotencyKey });
229
- const bodyText = args.body === void 0 ? void 0 : JSON.stringify(args.body);
373
+ const bodyText = args.body === void 0 ? void 0 : args.body instanceof FormData ? args.body : JSON.stringify(args.body);
230
374
  const maxRetries = args.maxRetries ?? this.maxRetries;
231
375
  const timeout = args.timeout ?? this.timeout;
232
- const retryable = method.toUpperCase() === "GET" || Boolean(headers["X-Woku-Idempotency-Key"]);
376
+ const retryable = method.toUpperCase() === "GET" || method.toUpperCase() === "POST" && args.idempotent === true && Boolean(headers["X-Woku-Idempotency-Key"]);
233
377
  let attempt = 0;
234
378
  for (; ; ) {
235
379
  try {
@@ -250,8 +394,9 @@ var WokuClient = class {
250
394
  requestId,
251
395
  retryAfterSeconds
252
396
  );
397
+ apiError.idempotencyKey = idempotencyKey;
253
398
  if (retryable && attempt < maxRetries && RETRYABLE_STATUS.has(status)) {
254
- await sleep(this.backoff(attempt, apiError));
399
+ await sleep(this.backoff(attempt, apiError), args.signal);
255
400
  attempt += 1;
256
401
  continue;
257
402
  }
@@ -259,9 +404,16 @@ var WokuClient = class {
259
404
  } catch (error) {
260
405
  if (error instanceof WokuAPIError) throw error;
261
406
  const connError = toConnectionError(error);
407
+ connError.idempotencyKey = idempotencyKey;
262
408
  if (connError.code === "aborted") throw connError;
263
409
  if (retryable && attempt < maxRetries) {
264
- await sleep(this.backoff(attempt));
410
+ try {
411
+ await sleep(this.backoff(attempt), args.signal);
412
+ } catch (waitingError) {
413
+ if (waitingError instanceof WokuError)
414
+ waitingError.idempotencyKey = idempotencyKey;
415
+ throw waitingError;
416
+ }
265
417
  attempt += 1;
266
418
  continue;
267
419
  }
@@ -288,11 +440,25 @@ var WokuClient = class {
288
440
  };
289
441
  return new Page(
290
442
  envelope,
291
- (page, pageOpts) => this.getPage(path, { ...base, page }, { ...opts, ...pageOpts }),
443
+ (page, pageOpts) => this.getPage(
444
+ path,
445
+ { ...query, page },
446
+ {
447
+ ...opts,
448
+ ...pageOpts,
449
+ query: { ...opts?.query, ...pageOpts?.query, page }
450
+ }
451
+ ),
292
452
  opts
293
453
  );
294
454
  }
295
455
  buildUrl(path, query) {
456
+ if (/^\s*[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith("//")) {
457
+ throw new WokuError(
458
+ "Use an API path relative to baseURL, not an external URL.",
459
+ { code: "config_error" }
460
+ );
461
+ }
296
462
  const url = `${this.baseURL}${path.startsWith("/") ? path : `/${path}`}`;
297
463
  if (!query) return url;
298
464
  const search = new URLSearchParams();
@@ -308,22 +474,35 @@ var WokuClient = class {
308
474
  return qs ? `${url}?${qs}` : url;
309
475
  }
310
476
  buildHeaders(method, args) {
311
- const headers = {
477
+ const defaults = {
312
478
  Accept: "application/json",
313
- "User-Agent": `woku-sdk-js/${SDK_VERSION}`,
314
- ...this.defaultHeaders,
315
- ...args.headers
479
+ "User-Agent": `woku-sdk-js/${version}`
316
480
  };
481
+ const merged = /* @__PURE__ */ new Map();
482
+ for (const source of [defaults, this.defaultHeaders, args.headers ?? {}]) {
483
+ for (const [name, value] of Object.entries(source))
484
+ merged.set(name.toLowerCase(), [name, value]);
485
+ }
486
+ merged.set("authorization", ["Authorization", `Bearer ${this.apiKey}`]);
487
+ const headers = Object.fromEntries(merged.values());
317
488
  headers["Authorization"] = `Bearer ${this.apiKey}`;
318
489
  if (args.body !== void 0) {
319
- headers["Content-Type"] = "application/json";
490
+ for (const name of Object.keys(headers))
491
+ if (name.toLowerCase() === "content-type") delete headers[name];
492
+ if (!(args.body instanceof FormData))
493
+ headers["Content-Type"] = "application/json";
320
494
  }
321
495
  if (args.idempotencyKey !== void 0 && method.toUpperCase() === "POST") {
496
+ for (const name of Object.keys(headers))
497
+ if (name.toLowerCase() === "x-woku-idempotency-key")
498
+ delete headers[name];
322
499
  headers["X-Woku-Idempotency-Key"] = args.idempotencyKey;
323
500
  }
324
501
  return headers;
325
502
  }
326
503
  async send(url, method, headers, body, timeout, signal) {
504
+ if (signal?.aborted)
505
+ throw new WokuConnectionError("Request aborted", { code: "aborted" });
327
506
  const controller = new AbortController();
328
507
  let timedOut = false;
329
508
  const timer = setTimeout(() => {
@@ -362,7 +541,8 @@ var WokuClient = class {
362
541
  }
363
542
  backoff(attempt, apiError) {
364
543
  const retryAfter = apiError && typeof apiError.retryAfterSeconds === "number" ? apiError.retryAfterSeconds * 1e3 : void 0;
365
- if (retryAfter !== void 0) return Math.min(retryAfter, RETRY_CAP_MS);
544
+ if (retryAfter !== void 0 && Number.isFinite(retryAfter) && retryAfter >= 0)
545
+ return retryAfter;
366
546
  const ceiling = Math.min(RETRY_CAP_MS, RETRY_BASE_MS * 2 ** attempt);
367
547
  return Math.random() * ceiling;
368
548
  }
@@ -413,7 +593,7 @@ var Trackers = class {
413
593
  get(id, opts) {
414
594
  return this.client.request(
415
595
  "get",
416
- `/v1/external-trackers/${id}`,
596
+ `/v1/external-trackers/${encodeURIComponent(id)}`,
417
597
  opts
418
598
  );
419
599
  }
@@ -421,7 +601,7 @@ var Trackers = class {
421
601
  update(id, body, opts) {
422
602
  return this.client.request(
423
603
  "patch",
424
- `/v1/external-trackers/${id}`,
604
+ `/v1/external-trackers/${encodeURIComponent(id)}`,
425
605
  { ...opts, body }
426
606
  );
427
607
  }
@@ -429,7 +609,7 @@ var Trackers = class {
429
609
  activate(id, opts) {
430
610
  return this.client.request(
431
611
  "patch",
432
- `/v1/external-trackers/${id}/activate`,
612
+ `/v1/external-trackers/${encodeURIComponent(id)}/activate`,
433
613
  opts
434
614
  );
435
615
  }
@@ -437,7 +617,7 @@ var Trackers = class {
437
617
  deactivate(id, opts) {
438
618
  return this.client.request(
439
619
  "patch",
440
- `/v1/external-trackers/${id}/deactivate`,
620
+ `/v1/external-trackers/${encodeURIComponent(id)}/deactivate`,
441
621
  opts
442
622
  );
443
623
  }
@@ -453,7 +633,7 @@ var Trackers = class {
453
633
  listWokuValues(wokuId, opts) {
454
634
  return this.client.request(
455
635
  "get",
456
- `/v1/external-trackers/wokus/${wokuId}`,
636
+ `/v1/external-trackers/wokus/${encodeURIComponent(wokuId)}`,
457
637
  opts
458
638
  );
459
639
  }
@@ -461,15 +641,15 @@ var Trackers = class {
461
641
  assignToWoku(wokuId, body, opts) {
462
642
  return this.client.request(
463
643
  "post",
464
- `/v1/external-trackers/wokus/${wokuId}`,
465
- { ...opts, body, idempotent: true }
644
+ `/v1/external-trackers/wokus/${encodeURIComponent(wokuId)}`,
645
+ { ...opts, body, maxRetries: 0 }
466
646
  );
467
647
  }
468
648
  /** Remove a tracker value from a woku by tracker name. */
469
649
  removeFromWoku(wokuId, trackerName, opts) {
470
650
  return this.client.request(
471
651
  "delete",
472
- `/v1/external-trackers/wokus/${wokuId}/${encodeURIComponent(trackerName)}`,
652
+ `/v1/external-trackers/wokus/${encodeURIComponent(wokuId)}/${encodeURIComponent(trackerName)}`,
473
653
  opts
474
654
  );
475
655
  }
@@ -485,7 +665,7 @@ var Trackers = class {
485
665
  listEntityValues(entityType, id, opts) {
486
666
  return this.client.request(
487
667
  "get",
488
- `/v1/external-trackers/${entityType}/${id}`,
668
+ `/v1/external-trackers/${encodeURIComponent(entityType)}/${encodeURIComponent(id)}`,
489
669
  opts
490
670
  );
491
671
  }
@@ -493,15 +673,15 @@ var Trackers = class {
493
673
  assignToEntity(entityType, id, body, opts) {
494
674
  return this.client.request(
495
675
  "post",
496
- `/v1/external-trackers/${entityType}/${id}`,
497
- { ...opts, body, idempotent: true }
676
+ `/v1/external-trackers/${encodeURIComponent(entityType)}/${encodeURIComponent(id)}`,
677
+ { ...opts, body, maxRetries: 0 }
498
678
  );
499
679
  }
500
680
  /** Remove a tracker value from a VoC entity by tracker name. */
501
681
  removeFromEntity(entityType, id, trackerName, opts) {
502
682
  return this.client.request(
503
683
  "delete",
504
- `/v1/external-trackers/${entityType}/${id}/${encodeURIComponent(trackerName)}`,
684
+ `/v1/external-trackers/${encodeURIComponent(entityType)}/${encodeURIComponent(id)}/${encodeURIComponent(trackerName)}`,
505
685
  opts
506
686
  );
507
687
  }
@@ -523,18 +703,26 @@ var NpsTools = class {
523
703
  });
524
704
  }
525
705
  get(id, opts) {
526
- return this.client.request("get", `/v1/nps-tool/${id}`, opts);
706
+ return this.client.request(
707
+ "get",
708
+ `/v1/nps-tool/${encodeURIComponent(id)}`,
709
+ opts
710
+ );
527
711
  }
528
712
  update(id, body, opts) {
529
- return this.client.request("patch", `/v1/nps-tools/${id}`, {
530
- ...opts,
531
- body
532
- });
713
+ return this.client.request(
714
+ "patch",
715
+ `/v1/nps-tools/${encodeURIComponent(id)}`,
716
+ {
717
+ ...opts,
718
+ body
719
+ }
720
+ );
533
721
  }
534
722
  delete(id, opts) {
535
723
  return this.client.request(
536
724
  "delete",
537
- `/v1/nps-tools/${id}`,
725
+ `/v1/nps-tools/${encodeURIComponent(id)}`,
538
726
  opts
539
727
  );
540
728
  }
@@ -554,18 +742,26 @@ var CsatTools = class {
554
742
  });
555
743
  }
556
744
  get(id, opts) {
557
- return this.client.request("get", `/v1/csat-tool/${id}`, opts);
745
+ return this.client.request(
746
+ "get",
747
+ `/v1/csat-tool/${encodeURIComponent(id)}`,
748
+ opts
749
+ );
558
750
  }
559
751
  update(id, body, opts) {
560
- return this.client.request("patch", `/v1/csat-tools/${id}`, {
561
- ...opts,
562
- body
563
- });
752
+ return this.client.request(
753
+ "patch",
754
+ `/v1/csat-tools/${encodeURIComponent(id)}`,
755
+ {
756
+ ...opts,
757
+ body
758
+ }
759
+ );
564
760
  }
565
761
  delete(id, opts) {
566
762
  return this.client.request(
567
763
  "delete",
568
- `/v1/csat-tools/${id}`,
764
+ `/v1/csat-tools/${encodeURIComponent(id)}`,
569
765
  opts
570
766
  );
571
767
  }
@@ -585,18 +781,26 @@ var CesTools = class {
585
781
  });
586
782
  }
587
783
  get(id, opts) {
588
- return this.client.request("get", `/v1/ces-tool/${id}`, opts);
784
+ return this.client.request(
785
+ "get",
786
+ `/v1/ces-tool/${encodeURIComponent(id)}`,
787
+ opts
788
+ );
589
789
  }
590
790
  update(id, body, opts) {
591
- return this.client.request("patch", `/v1/ces-tools/${id}`, {
592
- ...opts,
593
- body
594
- });
791
+ return this.client.request(
792
+ "patch",
793
+ `/v1/ces-tools/${encodeURIComponent(id)}`,
794
+ {
795
+ ...opts,
796
+ body
797
+ }
798
+ );
595
799
  }
596
800
  delete(id, opts) {
597
801
  return this.client.request(
598
802
  "delete",
599
- `/v1/ces-tools/${id}`,
803
+ `/v1/ces-tools/${encodeURIComponent(id)}`,
600
804
  opts
601
805
  );
602
806
  }
@@ -625,7 +829,11 @@ var Nps = class {
625
829
  }
626
830
  /** Get one NPS response. */
627
831
  getResponse(id, opts) {
628
- return this.client.request("get", `/v1/nps/${id}`, opts);
832
+ return this.client.request(
833
+ "get",
834
+ `/v1/nps/${encodeURIComponent(id)}`,
835
+ opts
836
+ );
629
837
  }
630
838
  };
631
839
  var Csat = class {
@@ -643,7 +851,11 @@ var Csat = class {
643
851
  return this.client.getPage("/v1/csat", params, opts);
644
852
  }
645
853
  getResponse(id, opts) {
646
- return this.client.request("get", `/v1/csat/${id}`, opts);
854
+ return this.client.request(
855
+ "get",
856
+ `/v1/csat/${encodeURIComponent(id)}`,
857
+ opts
858
+ );
647
859
  }
648
860
  };
649
861
  var Ces = class {
@@ -665,7 +877,11 @@ var Ces = class {
665
877
  return this.client.getPage("/v1/ces", params, opts);
666
878
  }
667
879
  getResponse(id, opts) {
668
- return this.client.request("get", `/v1/ces/${id}`, opts);
880
+ return this.client.request(
881
+ "get",
882
+ `/v1/ces/${encodeURIComponent(id)}`,
883
+ opts
884
+ );
669
885
  }
670
886
  };
671
887
 
@@ -681,44 +897,60 @@ var Wokus = class {
681
897
  return this.client.request("post", "/v1/wokus", {
682
898
  ...opts,
683
899
  body,
684
- idempotent: true
900
+ maxRetries: 0
685
901
  });
686
902
  }
687
903
  /** Get one woku with aggregated review stats. */
688
904
  get(id, opts) {
689
- return this.client.request("get", `/v1/wokus/${id}`, opts);
905
+ return this.client.request(
906
+ "get",
907
+ `/v1/wokus/${encodeURIComponent(id)}`,
908
+ opts
909
+ );
690
910
  }
691
911
  update(id, body, opts) {
692
- return this.client.request("patch", `/v1/wokus/${id}`, {
693
- ...opts,
694
- body
695
- });
912
+ return this.client.request(
913
+ "patch",
914
+ `/v1/wokus/${encodeURIComponent(id)}`,
915
+ {
916
+ ...opts,
917
+ body
918
+ }
919
+ );
696
920
  }
697
921
  delete(id, opts) {
698
922
  return this.client.request(
699
923
  "delete",
700
- `/v1/wokus/${id}`,
924
+ `/v1/wokus/${encodeURIComponent(id)}`,
701
925
  opts
702
926
  );
703
927
  }
704
928
  /** Apply the boolean settings idempotently (closed/reviewsDisabled/...). */
705
929
  updateSettings(id, body, opts) {
706
- return this.client.request("patch", `/v1/wokus/${id}/settings`, {
707
- ...opts,
708
- body
709
- });
930
+ return this.client.request(
931
+ "patch",
932
+ `/v1/wokus/${encodeURIComponent(id)}/settings`,
933
+ {
934
+ ...opts,
935
+ body
936
+ }
937
+ );
710
938
  }
711
939
  /** Move the woku into a folder, or to the root with `{ folderId: null }`. */
712
940
  move(id, body, opts) {
713
- return this.client.request("patch", `/v1/wokus/${id}/move`, {
714
- ...opts,
715
- body
716
- });
941
+ return this.client.request(
942
+ "patch",
943
+ `/v1/wokus/${encodeURIComponent(id)}/move`,
944
+ {
945
+ ...opts,
946
+ body
947
+ }
948
+ );
717
949
  }
718
950
  /** List the reviews of a woku (paginated). */
719
951
  listReviews(id, params, opts) {
720
952
  return this.client.getPage(
721
- `/v1/wokus/${id}/reviews`,
953
+ `/v1/wokus/${encodeURIComponent(id)}/reviews`,
722
954
  params,
723
955
  opts
724
956
  );
@@ -727,16 +959,20 @@ var Wokus = class {
727
959
  sendInvitations(id, body, opts) {
728
960
  return this.client.request(
729
961
  "post",
730
- `/v1/wokus/${id}/invitations`,
962
+ `/v1/wokus/${encodeURIComponent(id)}/invitations`,
731
963
  { ...opts, body, idempotent: true }
732
964
  );
733
965
  }
734
966
  /** Share a woku review link by email. */
735
967
  share(id, body, opts) {
736
- return this.client.request("post", `/v1/wokus/${id}/share`, {
737
- ...opts,
738
- body
739
- });
968
+ return this.client.request(
969
+ "post",
970
+ `/v1/wokus/${encodeURIComponent(id)}/share`,
971
+ {
972
+ ...opts,
973
+ body
974
+ }
975
+ );
740
976
  }
741
977
  };
742
978
 
@@ -749,12 +985,16 @@ var Forms = class {
749
985
  return this.client.getPage("/v1/forms", params, opts);
750
986
  }
751
987
  get(id, opts) {
752
- return this.client.request("get", `/v1/forms/${id}`, opts);
988
+ return this.client.request(
989
+ "get",
990
+ `/v1/forms/${encodeURIComponent(id)}`,
991
+ opts
992
+ );
753
993
  }
754
994
  /** List the responses of a form (paginated). */
755
995
  listResponses(id, params, opts) {
756
996
  return this.client.getPage(
757
- `/v1/forms/${id}/responses`,
997
+ `/v1/forms/${encodeURIComponent(id)}/responses`,
758
998
  params,
759
999
  opts
760
1000
  );
@@ -763,7 +1003,7 @@ var Forms = class {
763
1003
  sendInvitations(id, body, opts) {
764
1004
  return this.client.request(
765
1005
  "post",
766
- `/v1/forms/${id}/invitations`,
1006
+ `/v1/forms/${encodeURIComponent(id)}/invitations`,
767
1007
  { ...opts, body, idempotent: true }
768
1008
  );
769
1009
  }
@@ -778,7 +1018,11 @@ var Flows = class {
778
1018
  return this.client.getPage("/v1/flows", params, opts);
779
1019
  }
780
1020
  get(id, opts) {
781
- return this.client.request("get", `/v1/flows/${id}`, opts);
1021
+ return this.client.request(
1022
+ "get",
1023
+ `/v1/flows/${encodeURIComponent(id)}`,
1024
+ opts
1025
+ );
782
1026
  }
783
1027
  };
784
1028
 
@@ -793,7 +1037,7 @@ var ActionPlans = class {
793
1037
  get(id, opts) {
794
1038
  return this.client.request(
795
1039
  "get",
796
- `/v1/action-plans/${id}`,
1040
+ `/v1/action-plans/${encodeURIComponent(id)}`,
797
1041
  opts
798
1042
  );
799
1043
  }
@@ -801,7 +1045,7 @@ var ActionPlans = class {
801
1045
  events(id, opts) {
802
1046
  return this.client.request(
803
1047
  "get",
804
- `/v1/action-plans/${id}/events`,
1048
+ `/v1/action-plans/${encodeURIComponent(id)}/events`,
805
1049
  opts
806
1050
  );
807
1051
  }
@@ -809,7 +1053,7 @@ var ActionPlans = class {
809
1053
  getConversation(id, opts) {
810
1054
  return this.client.request(
811
1055
  "get",
812
- `/v1/action-plans/${id}/conversation`,
1056
+ `/v1/action-plans/${encodeURIComponent(id)}/conversation`,
813
1057
  opts
814
1058
  );
815
1059
  }
@@ -821,35 +1065,35 @@ var ActionPlans = class {
821
1065
  reply(id, text, opts) {
822
1066
  return this.client.request(
823
1067
  "post",
824
- `/v1/action-plans/${id}/conversation`,
1068
+ `/v1/action-plans/${encodeURIComponent(id)}/conversation`,
825
1069
  { ...opts, body: { text, confirm: true } }
826
1070
  );
827
1071
  }
828
1072
  createTask(id, body, opts) {
829
1073
  return this.client.request(
830
1074
  "post",
831
- `/v1/action-plans/${id}/tasks`,
832
- { ...opts, body, idempotent: true }
1075
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks`,
1076
+ { ...opts, body, maxRetries: 0 }
833
1077
  );
834
1078
  }
835
1079
  updateTask(id, taskId, body, opts) {
836
1080
  return this.client.request(
837
1081
  "patch",
838
- `/v1/action-plans/${id}/tasks/${taskId}`,
1082
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks/${encodeURIComponent(taskId)}`,
839
1083
  { ...opts, body }
840
1084
  );
841
1085
  }
842
1086
  reorderTasks(id, body, opts) {
843
1087
  return this.client.request(
844
1088
  "patch",
845
- `/v1/action-plans/${id}/tasks/reorder`,
1089
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks/reorder`,
846
1090
  { ...opts, body }
847
1091
  );
848
1092
  }
849
1093
  deleteTask(id, taskId, opts) {
850
1094
  return this.client.request(
851
1095
  "delete",
852
- `/v1/action-plans/${id}/tasks/${taskId}`,
1096
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks/${encodeURIComponent(taskId)}`,
853
1097
  opts
854
1098
  );
855
1099
  }
@@ -871,7 +1115,7 @@ var ActionPlans = class {
871
1115
  status(id, action, opts) {
872
1116
  return this.client.request(
873
1117
  "post",
874
- `/v1/action-plans/${id}/${action}`,
1118
+ `/v1/action-plans/${encodeURIComponent(id)}/${action}`,
875
1119
  opts
876
1120
  );
877
1121
  }
@@ -890,7 +1134,7 @@ var ActionPlanGroups = class {
890
1134
  get(id, opts) {
891
1135
  return this.client.request(
892
1136
  "get",
893
- `/v1/action-plan-groups/${id}`,
1137
+ `/v1/action-plan-groups/${encodeURIComponent(id)}`,
894
1138
  opts
895
1139
  );
896
1140
  }
@@ -898,27 +1142,27 @@ var ActionPlanGroups = class {
898
1142
  return this.client.request("post", "/v1/action-plan-groups", {
899
1143
  ...opts,
900
1144
  body,
901
- idempotent: true
1145
+ maxRetries: 0
902
1146
  });
903
1147
  }
904
1148
  update(id, body, opts) {
905
1149
  return this.client.request(
906
1150
  "patch",
907
- `/v1/action-plan-groups/${id}`,
1151
+ `/v1/action-plan-groups/${encodeURIComponent(id)}`,
908
1152
  { ...opts, body }
909
1153
  );
910
1154
  }
911
1155
  setEnabled(id, enabled, opts) {
912
1156
  return this.client.request(
913
1157
  "patch",
914
- `/v1/action-plan-groups/${id}/enabled`,
1158
+ `/v1/action-plan-groups/${encodeURIComponent(id)}/enabled`,
915
1159
  { ...opts, body: { enabled } }
916
1160
  );
917
1161
  }
918
1162
  delete(id, opts) {
919
1163
  return this.client.request(
920
1164
  "delete",
921
- `/v1/action-plan-groups/${id}`,
1165
+ `/v1/action-plan-groups/${encodeURIComponent(id)}`,
922
1166
  opts
923
1167
  );
924
1168
  }
@@ -940,13 +1184,21 @@ var Tickets = class {
940
1184
  });
941
1185
  }
942
1186
  get(id, opts) {
943
- return this.client.request("get", `/v1/tickets/${id}`, opts);
1187
+ return this.client.request(
1188
+ "get",
1189
+ `/v1/tickets/${encodeURIComponent(id)}`,
1190
+ opts
1191
+ );
944
1192
  }
945
1193
  update(id, body, opts) {
946
- return this.client.request("patch", `/v1/tickets/${id}`, {
947
- ...opts,
948
- body
949
- });
1194
+ return this.client.request(
1195
+ "patch",
1196
+ `/v1/tickets/${encodeURIComponent(id)}`,
1197
+ {
1198
+ ...opts,
1199
+ body
1200
+ }
1201
+ );
950
1202
  }
951
1203
  };
952
1204
  var TicketDestinations = class {
@@ -963,7 +1215,7 @@ var TicketDestinations = class {
963
1215
  get(id, opts) {
964
1216
  return this.client.request(
965
1217
  "get",
966
- `/v1/ticket-destinations/${id}`,
1218
+ `/v1/ticket-destinations/${encodeURIComponent(id)}`,
967
1219
  opts
968
1220
  );
969
1221
  }
@@ -971,20 +1223,20 @@ var TicketDestinations = class {
971
1223
  return this.client.request("post", "/v1/ticket-destinations", {
972
1224
  ...opts,
973
1225
  body,
974
- idempotent: true
1226
+ maxRetries: 0
975
1227
  });
976
1228
  }
977
1229
  update(id, body, opts) {
978
1230
  return this.client.request(
979
1231
  "patch",
980
- `/v1/ticket-destinations/${id}`,
1232
+ `/v1/ticket-destinations/${encodeURIComponent(id)}`,
981
1233
  { ...opts, body }
982
1234
  );
983
1235
  }
984
1236
  delete(id, opts) {
985
1237
  return this.client.request(
986
1238
  "delete",
987
- `/v1/ticket-destinations/${id}`,
1239
+ `/v1/ticket-destinations/${encodeURIComponent(id)}`,
988
1240
  opts
989
1241
  );
990
1242
  }
@@ -995,7 +1247,7 @@ var TicketDestinations = class {
995
1247
  test(id, opts) {
996
1248
  return this.client.request(
997
1249
  "post",
998
- `/v1/ticket-destinations/${id}/test`,
1250
+ `/v1/ticket-destinations/${encodeURIComponent(id)}/test`,
999
1251
  { ...opts, body: { confirm: true } }
1000
1252
  );
1001
1253
  }
@@ -1035,7 +1287,7 @@ var Reports = class {
1035
1287
  npsTool(npsToolId, params, opts) {
1036
1288
  return this.client.request(
1037
1289
  "get",
1038
- `/v1/reports/nps-tool/${npsToolId}`,
1290
+ `/v1/reports/nps-tool/${encodeURIComponent(npsToolId)}`,
1039
1291
  { ...opts, query: { ...params, ...opts?.query } }
1040
1292
  );
1041
1293
  }
@@ -1068,6 +1320,193 @@ var Company = class {
1068
1320
  }
1069
1321
  };
1070
1322
 
1323
+ // src/resources/journeys.ts
1324
+ var Journeys = class {
1325
+ constructor(client) {
1326
+ this.client = client;
1327
+ }
1328
+ /** Read customer entry metadata without starting or preparing an evaluation. */
1329
+ entryInfo(journeyId, opts) {
1330
+ return this.client.request(
1331
+ "get",
1332
+ `/v1/journey-entries/${encodeURIComponent(journeyId)}`,
1333
+ opts
1334
+ );
1335
+ }
1336
+ /** Prepare a first tool. Only its saved response with dispatchToken confirms entry. */
1337
+ prepareEntry(journeyId, body, opts) {
1338
+ return this.client.request(
1339
+ "post",
1340
+ `/v1/journey-entries/${encodeURIComponent(journeyId)}`,
1341
+ { ...opts, body, maxRetries: 0 }
1342
+ );
1343
+ }
1344
+ /** Every journey of your company. */
1345
+ list(opts) {
1346
+ return this.client.request("get", "/v1/journeys", opts);
1347
+ }
1348
+ /** One journey with its moments. */
1349
+ get(journeyId, opts) {
1350
+ return this.client.request(
1351
+ "get",
1352
+ `/v1/journeys/${encodeURIComponent(journeyId)}`,
1353
+ opts
1354
+ );
1355
+ }
1356
+ /**
1357
+ * Create a journey. The response carries `webhookSecret` once and only here:
1358
+ * it signs legacy woku_signature calls. V2 URL tokens and sender secrets
1359
+ * are configured separately per moment. Store it securely.
1360
+ */
1361
+ create(body, opts) {
1362
+ return this.client.request("post", "/v1/journeys", {
1363
+ ...opts,
1364
+ body,
1365
+ idempotent: true
1366
+ });
1367
+ }
1368
+ /**
1369
+ * Rename a journey, switch it on or off, or replace its moments. Replacing
1370
+ * the moments mints a new version; the enrollments already running keep
1371
+ * executing the version they started with.
1372
+ */
1373
+ update(journeyId, body, opts) {
1374
+ return this.client.request(
1375
+ "patch",
1376
+ `/v1/journeys/${encodeURIComponent(journeyId)}`,
1377
+ {
1378
+ ...opts,
1379
+ body
1380
+ }
1381
+ );
1382
+ }
1383
+ /** Cursor page of exact customer cases; pass nextCursor to retrieve another page. */
1384
+ listEnrollments(journeyId, params, opts) {
1385
+ return this.client.request(
1386
+ "get",
1387
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments`,
1388
+ { ...opts, query: { ...params, ...opts?.query } }
1389
+ );
1390
+ }
1391
+ /** Iterate exact cases lazily, preserving options while advancing the server cursor. */
1392
+ async *iterEnrollments(journeyId, params, opts) {
1393
+ let query = { ...params, ...opts?.query };
1394
+ const seen = /* @__PURE__ */ new Set();
1395
+ for (; ; ) {
1396
+ const page = await this.listEnrollments(journeyId, void 0, {
1397
+ ...opts,
1398
+ query
1399
+ });
1400
+ for (const item of page.items) yield item;
1401
+ const next = page.nextCursor;
1402
+ if (!next) return;
1403
+ if (seen.has(next) || next === query.cursor)
1404
+ throw new WokuError("Enrollment pagination did not advance.", {
1405
+ code: "pagination_error"
1406
+ });
1407
+ seen.add(next);
1408
+ query = { ...query, cursor: next };
1409
+ }
1410
+ }
1411
+ /** History and next step for one exact participation. */
1412
+ getEnrollment(journeyId, enrollmentId, opts) {
1413
+ return this.client.request(
1414
+ "get",
1415
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments/${encodeURIComponent(enrollmentId)}`,
1416
+ opts
1417
+ );
1418
+ }
1419
+ /** Cancel future work for this case. Accepted provider sends cannot be recalled. */
1420
+ stopEnrollment(journeyId, enrollmentId, body = {}, opts) {
1421
+ return this.client.request(
1422
+ "post",
1423
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments/${encodeURIComponent(enrollmentId)}/stop`,
1424
+ { ...opts, body, idempotent: true }
1425
+ );
1426
+ }
1427
+ /** Credential readiness is not evidence that an actual webhook has arrived. */
1428
+ connections(journeyId, opts) {
1429
+ return this.client.request(
1430
+ "get",
1431
+ `/v1/journeys/${encodeURIComponent(journeyId)}/connections`,
1432
+ opts
1433
+ );
1434
+ }
1435
+ /** Replaces this moment's URL credential. Store the returned URL in the sender. */
1436
+ async mintMomentUrl(journeyId, stageKey, opts) {
1437
+ const result = await this.client.request(
1438
+ "post",
1439
+ `/v1/journeys/${encodeURIComponent(journeyId)}/moments/${encodeURIComponent(stageKey)}/url-token`,
1440
+ { ...opts, body: {}, idempotent: true }
1441
+ );
1442
+ return {
1443
+ ...result,
1444
+ url: new URL(result.url, this.client.baseURL).toString()
1445
+ };
1446
+ }
1447
+ /** Store the external sender's signing secret encrypted; it is never returned. */
1448
+ setSenderSecret(journeyId, stageKey, senderSecret, opts) {
1449
+ return this.client.request(
1450
+ "post",
1451
+ `/v1/journeys/${encodeURIComponent(journeyId)}/moments/${encodeURIComponent(stageKey)}/sender-secret`,
1452
+ { ...opts, body: { senderSecret }, maxRetries: 0 }
1453
+ );
1454
+ }
1455
+ /** Test saved payload mapping without creating a participation or sending a tool. */
1456
+ previewMoment(journeyId, stageKey, payload, opts) {
1457
+ return this.client.request(
1458
+ "post",
1459
+ `/v1/journeys/${encodeURIComponent(journeyId)}/moments/${encodeURIComponent(stageKey)}/preview`,
1460
+ { ...opts, body: { payload }, maxRetries: 0 }
1461
+ );
1462
+ }
1463
+ /** Delete a journey. What it already started keeps its own history. */
1464
+ delete(journeyId, opts) {
1465
+ return this.client.request(
1466
+ "delete",
1467
+ `/v1/journeys/${encodeURIComponent(journeyId)}`,
1468
+ opts
1469
+ );
1470
+ }
1471
+ /**
1472
+ * Mint a new signing secret. The previous one stays valid until the next
1473
+ * rotation, so senders can be updated without a gap.
1474
+ */
1475
+ rotateWebhookSecret(journeyId, opts) {
1476
+ return this.client.request(
1477
+ "post",
1478
+ `/v1/journeys/${encodeURIComponent(journeyId)}/webhook-secret`,
1479
+ { ...opts, body: {}, maxRetries: 0 }
1480
+ );
1481
+ }
1482
+ /**
1483
+ * Start the journey for one subject. Enrolling the same person for a later
1484
+ * cycle reuses the subject key after the previous cycle completes or stops.
1485
+ */
1486
+ enroll(journeyId, body, opts) {
1487
+ return this.client.request(
1488
+ "post",
1489
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments`,
1490
+ { ...opts, body, idempotent: true }
1491
+ );
1492
+ }
1493
+ /**
1494
+ * Emit one of your own events. Every journey whose moments listen for that
1495
+ * name reacts.
1496
+ */
1497
+ emitEvent(body, opts) {
1498
+ return this.client.request(
1499
+ "post",
1500
+ "/v1/journey-events",
1501
+ {
1502
+ ...opts,
1503
+ body,
1504
+ idempotent: true
1505
+ }
1506
+ );
1507
+ }
1508
+ };
1509
+
1071
1510
  // src/resources/quarantines.ts
1072
1511
  var Quarantines = class {
1073
1512
  constructor(client) {
@@ -1087,6 +1526,7 @@ var Woku = class {
1087
1526
  constructor(options) {
1088
1527
  const opts = typeof options === "string" ? { apiKey: options } : options ?? {};
1089
1528
  this.client = new WokuClient(opts);
1529
+ this.media = new Media(this.client);
1090
1530
  this.trackers = new Trackers(this.client);
1091
1531
  this.npsTools = new NpsTools(this.client);
1092
1532
  this.csatTools = new CsatTools(this.client);
@@ -1100,14 +1540,16 @@ var Woku = class {
1100
1540
  this.actionPlans = new ActionPlans(this.client);
1101
1541
  this.actionPlanGroups = new ActionPlanGroups(this.client);
1102
1542
  this.tickets = new Tickets(this.client);
1543
+ this.followUps = new FollowUps(this.client);
1103
1544
  this.ticketDestinations = new TicketDestinations(this.client);
1104
1545
  this.dispatches = new Dispatches(this.client);
1105
1546
  this.reports = new Reports(this.client);
1106
1547
  this.company = new Company(this.client);
1548
+ this.journeys = new Journeys(this.client);
1107
1549
  this.quarantines = new Quarantines(this.client);
1108
1550
  }
1109
1551
  };
1110
1552
 
1111
- export { AuthenticationError, BadRequestError, ConflictError, InternalServerError, NotFoundError, Page, PermissionDeniedError, RateLimitError, UnprocessableEntityError, Woku, WokuAPIError, WokuClient, WokuConnectionError, WokuError, WokuTimeoutError };
1553
+ export { AuthenticationError, BadRequestError, ConflictError, FollowUps, InternalServerError, Journeys, Media, NotFoundError, Page, PayloadTooLargeError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, Woku, WokuAPIError, WokuClient, WokuConnectionError, WokuError, WokuTimeoutError };
1112
1554
  //# sourceMappingURL=index.js.map
1113
1555
  //# sourceMappingURL=index.js.map