@wokuapp/sdk 0.2.1 → 0.3.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,28 @@
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
+ // package.json
24
+ var version = "0.3.0";
25
+
1
26
  // src/core/errors.ts
2
27
  var WokuError = class extends Error {
3
28
  constructor(message, options) {
@@ -39,23 +64,95 @@ var WokuAPIError = class _WokuAPIError extends WokuError {
39
64
  const ra = retryAfterSeconds;
40
65
  switch (true) {
41
66
  case status === 400:
42
- return new BadRequestError(status, body, message, requestId, void 0, ra);
67
+ return new BadRequestError(
68
+ status,
69
+ body,
70
+ message,
71
+ requestId,
72
+ void 0,
73
+ ra
74
+ );
43
75
  case status === 401:
44
- return new AuthenticationError(status, body, message, requestId, void 0, ra);
76
+ return new AuthenticationError(
77
+ status,
78
+ body,
79
+ message,
80
+ requestId,
81
+ void 0,
82
+ ra
83
+ );
45
84
  case status === 403:
46
- return new PermissionDeniedError(status, body, message, requestId, void 0, ra);
85
+ return new PermissionDeniedError(
86
+ status,
87
+ body,
88
+ message,
89
+ requestId,
90
+ void 0,
91
+ ra
92
+ );
47
93
  case status === 404:
48
- return new NotFoundError(status, body, message, requestId, void 0, ra);
94
+ return new NotFoundError(
95
+ status,
96
+ body,
97
+ message,
98
+ requestId,
99
+ void 0,
100
+ ra
101
+ );
49
102
  case status === 409:
50
- return new ConflictError(status, body, message, requestId, void 0, ra);
103
+ return new ConflictError(
104
+ status,
105
+ body,
106
+ message,
107
+ requestId,
108
+ void 0,
109
+ ra
110
+ );
111
+ case status === 413:
112
+ return new PayloadTooLargeError(
113
+ status,
114
+ body,
115
+ message,
116
+ requestId,
117
+ void 0,
118
+ ra
119
+ );
51
120
  case status === 422:
52
- return new UnprocessableEntityError(status, body, message, requestId, void 0, ra);
121
+ return new UnprocessableEntityError(
122
+ status,
123
+ body,
124
+ message,
125
+ requestId,
126
+ void 0,
127
+ ra
128
+ );
53
129
  case status === 429:
54
- return new RateLimitError(status, body, message, requestId, void 0, ra);
130
+ return new RateLimitError(
131
+ status,
132
+ body,
133
+ message,
134
+ requestId,
135
+ void 0,
136
+ ra
137
+ );
55
138
  case status >= 500:
56
- return new InternalServerError(status, body, message, requestId, void 0, ra);
139
+ return new InternalServerError(
140
+ status,
141
+ body,
142
+ message,
143
+ requestId,
144
+ void 0,
145
+ ra
146
+ );
57
147
  default:
58
- return new _WokuAPIError(status, body, message, requestId, void 0, ra);
148
+ return new _WokuAPIError(
149
+ status,
150
+ body,
151
+ message,
152
+ requestId,
153
+ void 0,
154
+ ra
155
+ );
59
156
  }
60
157
  }
61
158
  };
@@ -89,6 +186,12 @@ var ConflictError = class extends WokuAPIError {
89
186
  this.name = "ConflictError";
90
187
  }
91
188
  };
189
+ var PayloadTooLargeError = class extends WokuAPIError {
190
+ constructor(...args) {
191
+ super(...args);
192
+ this.name = "PayloadTooLargeError";
193
+ }
194
+ };
92
195
  var UnprocessableEntityError = class extends WokuAPIError {
93
196
  constructor(...args) {
94
197
  super(...args);
@@ -107,7 +210,7 @@ var InternalServerError = class extends WokuAPIError {
107
210
  this.name = "InternalServerError";
108
211
  }
109
212
  };
110
- var retryAfterFromBody = (body) => body && typeof body === "object" && typeof body.retryAfter === "number" ? body.retryAfter : void 0;
213
+ var retryAfterFromBody = (body) => body && typeof body === "object" && typeof body.retryAfter === "number" && Number.isFinite(body.retryAfter) && body.retryAfter >= 0 ? body.retryAfter : void 0;
111
214
  var codeForStatus = (status) => {
112
215
  const map = {
113
216
  400: "bad_request",
@@ -115,6 +218,7 @@ var codeForStatus = (status) => {
115
218
  403: "permission_denied",
116
219
  404: "not_found",
117
220
  409: "conflict",
221
+ 413: "payload_too_large",
118
222
  422: "unprocessable_entity",
119
223
  429: "rate_limited"
120
224
  };
@@ -125,7 +229,7 @@ var messageFrom = (status, body, requestId) => {
125
229
  if (typeof body === "string" && body.trim()) {
126
230
  detail = body.trim();
127
231
  } else if (body && typeof body === "object") {
128
- const { message } = body;
232
+ const message = body.message && typeof body.message === "object" && !Array.isArray(body.message) ? body.message.message : body.message;
129
233
  if (Array.isArray(message)) detail = message.join(", ");
130
234
  else if (typeof message === "string" && message) detail = message;
131
235
  else if (typeof body.error === "string") detail = body.error;
@@ -154,7 +258,13 @@ var Page = class {
154
258
  if (!this.hasNextPage()) {
155
259
  throw new RangeError("No next page");
156
260
  }
157
- return this.fetchPage(this.page + 1, this.options);
261
+ return this.fetchPage(this.page + 1, this.options).then((next) => {
262
+ if (next.page <= this.page)
263
+ throw new WokuError("Pagination did not advance.", {
264
+ code: "pagination_error"
265
+ });
266
+ return next;
267
+ });
158
268
  }
159
269
  /** Yield every item across all pages, fetching lazily as needed. */
160
270
  async *[Symbol.asyncIterator]() {
@@ -183,13 +293,32 @@ var DEFAULT_MAX_RETRIES = 2;
183
293
  var RETRY_BASE_MS = 500;
184
294
  var RETRY_CAP_MS = 8e3;
185
295
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
186
- var SDK_VERSION = "0.1.0";
187
296
  var randomId = () => {
188
297
  const c = globalThis.crypto;
189
298
  if (c?.randomUUID) return c.randomUUID();
190
299
  return `idmp_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
191
300
  };
192
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
301
+ var sleep = (ms, signal) => ms > 2147483647 ? sleep(2147483647, signal).then(() => sleep(ms - 2147483647, signal)) : new Promise((resolve, reject) => {
302
+ if (signal?.aborted) {
303
+ reject(
304
+ new WokuConnectionError("Request aborted", { code: "aborted" })
305
+ );
306
+ return;
307
+ }
308
+ const cleanup = () => signal?.removeEventListener("abort", abort);
309
+ const timer = setTimeout(() => {
310
+ cleanup();
311
+ resolve();
312
+ }, ms);
313
+ const abort = () => {
314
+ clearTimeout(timer);
315
+ cleanup();
316
+ reject(
317
+ new WokuConnectionError("Request aborted", { code: "aborted" })
318
+ );
319
+ };
320
+ signal?.addEventListener("abort", abort, { once: true });
321
+ });
193
322
  var WokuClient = class {
194
323
  constructor(options = {}) {
195
324
  const apiKey = options.apiKey ?? globalThis.process?.env?.WOKU_API_KEY;
@@ -226,10 +355,10 @@ var WokuClient = class {
226
355
  const url = this.buildUrl(path, args.query);
227
356
  const idempotencyKey = args.idempotencyKey ?? (args.idempotent && method.toUpperCase() === "POST" ? randomId() : void 0);
228
357
  const headers = this.buildHeaders(method, { ...args, idempotencyKey });
229
- const bodyText = args.body === void 0 ? void 0 : JSON.stringify(args.body);
358
+ const bodyText = args.body === void 0 ? void 0 : args.body instanceof FormData ? args.body : JSON.stringify(args.body);
230
359
  const maxRetries = args.maxRetries ?? this.maxRetries;
231
360
  const timeout = args.timeout ?? this.timeout;
232
- const retryable = method.toUpperCase() === "GET" || Boolean(headers["X-Woku-Idempotency-Key"]);
361
+ const retryable = method.toUpperCase() === "GET" || method.toUpperCase() === "POST" && args.idempotent === true && Boolean(headers["X-Woku-Idempotency-Key"]);
233
362
  let attempt = 0;
234
363
  for (; ; ) {
235
364
  try {
@@ -250,8 +379,9 @@ var WokuClient = class {
250
379
  requestId,
251
380
  retryAfterSeconds
252
381
  );
382
+ apiError.idempotencyKey = idempotencyKey;
253
383
  if (retryable && attempt < maxRetries && RETRYABLE_STATUS.has(status)) {
254
- await sleep(this.backoff(attempt, apiError));
384
+ await sleep(this.backoff(attempt, apiError), args.signal);
255
385
  attempt += 1;
256
386
  continue;
257
387
  }
@@ -259,9 +389,16 @@ var WokuClient = class {
259
389
  } catch (error) {
260
390
  if (error instanceof WokuAPIError) throw error;
261
391
  const connError = toConnectionError(error);
392
+ connError.idempotencyKey = idempotencyKey;
262
393
  if (connError.code === "aborted") throw connError;
263
394
  if (retryable && attempt < maxRetries) {
264
- await sleep(this.backoff(attempt));
395
+ try {
396
+ await sleep(this.backoff(attempt), args.signal);
397
+ } catch (waitingError) {
398
+ if (waitingError instanceof WokuError)
399
+ waitingError.idempotencyKey = idempotencyKey;
400
+ throw waitingError;
401
+ }
265
402
  attempt += 1;
266
403
  continue;
267
404
  }
@@ -288,11 +425,25 @@ var WokuClient = class {
288
425
  };
289
426
  return new Page(
290
427
  envelope,
291
- (page, pageOpts) => this.getPage(path, { ...base, page }, { ...opts, ...pageOpts }),
428
+ (page, pageOpts) => this.getPage(
429
+ path,
430
+ { ...query, page },
431
+ {
432
+ ...opts,
433
+ ...pageOpts,
434
+ query: { ...opts?.query, ...pageOpts?.query, page }
435
+ }
436
+ ),
292
437
  opts
293
438
  );
294
439
  }
295
440
  buildUrl(path, query) {
441
+ if (/^\s*[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith("//")) {
442
+ throw new WokuError(
443
+ "Use an API path relative to baseURL, not an external URL.",
444
+ { code: "config_error" }
445
+ );
446
+ }
296
447
  const url = `${this.baseURL}${path.startsWith("/") ? path : `/${path}`}`;
297
448
  if (!query) return url;
298
449
  const search = new URLSearchParams();
@@ -308,22 +459,35 @@ var WokuClient = class {
308
459
  return qs ? `${url}?${qs}` : url;
309
460
  }
310
461
  buildHeaders(method, args) {
311
- const headers = {
462
+ const defaults = {
312
463
  Accept: "application/json",
313
- "User-Agent": `woku-sdk-js/${SDK_VERSION}`,
314
- ...this.defaultHeaders,
315
- ...args.headers
464
+ "User-Agent": `woku-sdk-js/${version}`
316
465
  };
466
+ const merged = /* @__PURE__ */ new Map();
467
+ for (const source of [defaults, this.defaultHeaders, args.headers ?? {}]) {
468
+ for (const [name, value] of Object.entries(source))
469
+ merged.set(name.toLowerCase(), [name, value]);
470
+ }
471
+ merged.set("authorization", ["Authorization", `Bearer ${this.apiKey}`]);
472
+ const headers = Object.fromEntries(merged.values());
317
473
  headers["Authorization"] = `Bearer ${this.apiKey}`;
318
474
  if (args.body !== void 0) {
319
- headers["Content-Type"] = "application/json";
475
+ for (const name of Object.keys(headers))
476
+ if (name.toLowerCase() === "content-type") delete headers[name];
477
+ if (!(args.body instanceof FormData))
478
+ headers["Content-Type"] = "application/json";
320
479
  }
321
480
  if (args.idempotencyKey !== void 0 && method.toUpperCase() === "POST") {
481
+ for (const name of Object.keys(headers))
482
+ if (name.toLowerCase() === "x-woku-idempotency-key")
483
+ delete headers[name];
322
484
  headers["X-Woku-Idempotency-Key"] = args.idempotencyKey;
323
485
  }
324
486
  return headers;
325
487
  }
326
488
  async send(url, method, headers, body, timeout, signal) {
489
+ if (signal?.aborted)
490
+ throw new WokuConnectionError("Request aborted", { code: "aborted" });
327
491
  const controller = new AbortController();
328
492
  let timedOut = false;
329
493
  const timer = setTimeout(() => {
@@ -362,7 +526,8 @@ var WokuClient = class {
362
526
  }
363
527
  backoff(attempt, apiError) {
364
528
  const retryAfter = apiError && typeof apiError.retryAfterSeconds === "number" ? apiError.retryAfterSeconds * 1e3 : void 0;
365
- if (retryAfter !== void 0) return Math.min(retryAfter, RETRY_CAP_MS);
529
+ if (retryAfter !== void 0 && Number.isFinite(retryAfter) && retryAfter >= 0)
530
+ return retryAfter;
366
531
  const ceiling = Math.min(RETRY_CAP_MS, RETRY_BASE_MS * 2 ** attempt);
367
532
  return Math.random() * ceiling;
368
533
  }
@@ -413,7 +578,7 @@ var Trackers = class {
413
578
  get(id, opts) {
414
579
  return this.client.request(
415
580
  "get",
416
- `/v1/external-trackers/${id}`,
581
+ `/v1/external-trackers/${encodeURIComponent(id)}`,
417
582
  opts
418
583
  );
419
584
  }
@@ -421,7 +586,7 @@ var Trackers = class {
421
586
  update(id, body, opts) {
422
587
  return this.client.request(
423
588
  "patch",
424
- `/v1/external-trackers/${id}`,
589
+ `/v1/external-trackers/${encodeURIComponent(id)}`,
425
590
  { ...opts, body }
426
591
  );
427
592
  }
@@ -429,7 +594,7 @@ var Trackers = class {
429
594
  activate(id, opts) {
430
595
  return this.client.request(
431
596
  "patch",
432
- `/v1/external-trackers/${id}/activate`,
597
+ `/v1/external-trackers/${encodeURIComponent(id)}/activate`,
433
598
  opts
434
599
  );
435
600
  }
@@ -437,7 +602,7 @@ var Trackers = class {
437
602
  deactivate(id, opts) {
438
603
  return this.client.request(
439
604
  "patch",
440
- `/v1/external-trackers/${id}/deactivate`,
605
+ `/v1/external-trackers/${encodeURIComponent(id)}/deactivate`,
441
606
  opts
442
607
  );
443
608
  }
@@ -453,7 +618,7 @@ var Trackers = class {
453
618
  listWokuValues(wokuId, opts) {
454
619
  return this.client.request(
455
620
  "get",
456
- `/v1/external-trackers/wokus/${wokuId}`,
621
+ `/v1/external-trackers/wokus/${encodeURIComponent(wokuId)}`,
457
622
  opts
458
623
  );
459
624
  }
@@ -461,15 +626,15 @@ var Trackers = class {
461
626
  assignToWoku(wokuId, body, opts) {
462
627
  return this.client.request(
463
628
  "post",
464
- `/v1/external-trackers/wokus/${wokuId}`,
465
- { ...opts, body, idempotent: true }
629
+ `/v1/external-trackers/wokus/${encodeURIComponent(wokuId)}`,
630
+ { ...opts, body, maxRetries: 0 }
466
631
  );
467
632
  }
468
633
  /** Remove a tracker value from a woku by tracker name. */
469
634
  removeFromWoku(wokuId, trackerName, opts) {
470
635
  return this.client.request(
471
636
  "delete",
472
- `/v1/external-trackers/wokus/${wokuId}/${encodeURIComponent(trackerName)}`,
637
+ `/v1/external-trackers/wokus/${encodeURIComponent(wokuId)}/${encodeURIComponent(trackerName)}`,
473
638
  opts
474
639
  );
475
640
  }
@@ -485,7 +650,7 @@ var Trackers = class {
485
650
  listEntityValues(entityType, id, opts) {
486
651
  return this.client.request(
487
652
  "get",
488
- `/v1/external-trackers/${entityType}/${id}`,
653
+ `/v1/external-trackers/${encodeURIComponent(entityType)}/${encodeURIComponent(id)}`,
489
654
  opts
490
655
  );
491
656
  }
@@ -493,15 +658,15 @@ var Trackers = class {
493
658
  assignToEntity(entityType, id, body, opts) {
494
659
  return this.client.request(
495
660
  "post",
496
- `/v1/external-trackers/${entityType}/${id}`,
497
- { ...opts, body, idempotent: true }
661
+ `/v1/external-trackers/${encodeURIComponent(entityType)}/${encodeURIComponent(id)}`,
662
+ { ...opts, body, maxRetries: 0 }
498
663
  );
499
664
  }
500
665
  /** Remove a tracker value from a VoC entity by tracker name. */
501
666
  removeFromEntity(entityType, id, trackerName, opts) {
502
667
  return this.client.request(
503
668
  "delete",
504
- `/v1/external-trackers/${entityType}/${id}/${encodeURIComponent(trackerName)}`,
669
+ `/v1/external-trackers/${encodeURIComponent(entityType)}/${encodeURIComponent(id)}/${encodeURIComponent(trackerName)}`,
505
670
  opts
506
671
  );
507
672
  }
@@ -523,18 +688,26 @@ var NpsTools = class {
523
688
  });
524
689
  }
525
690
  get(id, opts) {
526
- return this.client.request("get", `/v1/nps-tool/${id}`, opts);
691
+ return this.client.request(
692
+ "get",
693
+ `/v1/nps-tool/${encodeURIComponent(id)}`,
694
+ opts
695
+ );
527
696
  }
528
697
  update(id, body, opts) {
529
- return this.client.request("patch", `/v1/nps-tools/${id}`, {
530
- ...opts,
531
- body
532
- });
698
+ return this.client.request(
699
+ "patch",
700
+ `/v1/nps-tools/${encodeURIComponent(id)}`,
701
+ {
702
+ ...opts,
703
+ body
704
+ }
705
+ );
533
706
  }
534
707
  delete(id, opts) {
535
708
  return this.client.request(
536
709
  "delete",
537
- `/v1/nps-tools/${id}`,
710
+ `/v1/nps-tools/${encodeURIComponent(id)}`,
538
711
  opts
539
712
  );
540
713
  }
@@ -554,18 +727,26 @@ var CsatTools = class {
554
727
  });
555
728
  }
556
729
  get(id, opts) {
557
- return this.client.request("get", `/v1/csat-tool/${id}`, opts);
730
+ return this.client.request(
731
+ "get",
732
+ `/v1/csat-tool/${encodeURIComponent(id)}`,
733
+ opts
734
+ );
558
735
  }
559
736
  update(id, body, opts) {
560
- return this.client.request("patch", `/v1/csat-tools/${id}`, {
561
- ...opts,
562
- body
563
- });
737
+ return this.client.request(
738
+ "patch",
739
+ `/v1/csat-tools/${encodeURIComponent(id)}`,
740
+ {
741
+ ...opts,
742
+ body
743
+ }
744
+ );
564
745
  }
565
746
  delete(id, opts) {
566
747
  return this.client.request(
567
748
  "delete",
568
- `/v1/csat-tools/${id}`,
749
+ `/v1/csat-tools/${encodeURIComponent(id)}`,
569
750
  opts
570
751
  );
571
752
  }
@@ -585,18 +766,26 @@ var CesTools = class {
585
766
  });
586
767
  }
587
768
  get(id, opts) {
588
- return this.client.request("get", `/v1/ces-tool/${id}`, opts);
769
+ return this.client.request(
770
+ "get",
771
+ `/v1/ces-tool/${encodeURIComponent(id)}`,
772
+ opts
773
+ );
589
774
  }
590
775
  update(id, body, opts) {
591
- return this.client.request("patch", `/v1/ces-tools/${id}`, {
592
- ...opts,
593
- body
594
- });
776
+ return this.client.request(
777
+ "patch",
778
+ `/v1/ces-tools/${encodeURIComponent(id)}`,
779
+ {
780
+ ...opts,
781
+ body
782
+ }
783
+ );
595
784
  }
596
785
  delete(id, opts) {
597
786
  return this.client.request(
598
787
  "delete",
599
- `/v1/ces-tools/${id}`,
788
+ `/v1/ces-tools/${encodeURIComponent(id)}`,
600
789
  opts
601
790
  );
602
791
  }
@@ -625,7 +814,11 @@ var Nps = class {
625
814
  }
626
815
  /** Get one NPS response. */
627
816
  getResponse(id, opts) {
628
- return this.client.request("get", `/v1/nps/${id}`, opts);
817
+ return this.client.request(
818
+ "get",
819
+ `/v1/nps/${encodeURIComponent(id)}`,
820
+ opts
821
+ );
629
822
  }
630
823
  };
631
824
  var Csat = class {
@@ -643,7 +836,11 @@ var Csat = class {
643
836
  return this.client.getPage("/v1/csat", params, opts);
644
837
  }
645
838
  getResponse(id, opts) {
646
- return this.client.request("get", `/v1/csat/${id}`, opts);
839
+ return this.client.request(
840
+ "get",
841
+ `/v1/csat/${encodeURIComponent(id)}`,
842
+ opts
843
+ );
647
844
  }
648
845
  };
649
846
  var Ces = class {
@@ -665,7 +862,11 @@ var Ces = class {
665
862
  return this.client.getPage("/v1/ces", params, opts);
666
863
  }
667
864
  getResponse(id, opts) {
668
- return this.client.request("get", `/v1/ces/${id}`, opts);
865
+ return this.client.request(
866
+ "get",
867
+ `/v1/ces/${encodeURIComponent(id)}`,
868
+ opts
869
+ );
669
870
  }
670
871
  };
671
872
 
@@ -681,44 +882,60 @@ var Wokus = class {
681
882
  return this.client.request("post", "/v1/wokus", {
682
883
  ...opts,
683
884
  body,
684
- idempotent: true
885
+ maxRetries: 0
685
886
  });
686
887
  }
687
888
  /** Get one woku with aggregated review stats. */
688
889
  get(id, opts) {
689
- return this.client.request("get", `/v1/wokus/${id}`, opts);
890
+ return this.client.request(
891
+ "get",
892
+ `/v1/wokus/${encodeURIComponent(id)}`,
893
+ opts
894
+ );
690
895
  }
691
896
  update(id, body, opts) {
692
- return this.client.request("patch", `/v1/wokus/${id}`, {
693
- ...opts,
694
- body
695
- });
897
+ return this.client.request(
898
+ "patch",
899
+ `/v1/wokus/${encodeURIComponent(id)}`,
900
+ {
901
+ ...opts,
902
+ body
903
+ }
904
+ );
696
905
  }
697
906
  delete(id, opts) {
698
907
  return this.client.request(
699
908
  "delete",
700
- `/v1/wokus/${id}`,
909
+ `/v1/wokus/${encodeURIComponent(id)}`,
701
910
  opts
702
911
  );
703
912
  }
704
913
  /** Apply the boolean settings idempotently (closed/reviewsDisabled/...). */
705
914
  updateSettings(id, body, opts) {
706
- return this.client.request("patch", `/v1/wokus/${id}/settings`, {
707
- ...opts,
708
- body
709
- });
915
+ return this.client.request(
916
+ "patch",
917
+ `/v1/wokus/${encodeURIComponent(id)}/settings`,
918
+ {
919
+ ...opts,
920
+ body
921
+ }
922
+ );
710
923
  }
711
924
  /** Move the woku into a folder, or to the root with `{ folderId: null }`. */
712
925
  move(id, body, opts) {
713
- return this.client.request("patch", `/v1/wokus/${id}/move`, {
714
- ...opts,
715
- body
716
- });
926
+ return this.client.request(
927
+ "patch",
928
+ `/v1/wokus/${encodeURIComponent(id)}/move`,
929
+ {
930
+ ...opts,
931
+ body
932
+ }
933
+ );
717
934
  }
718
935
  /** List the reviews of a woku (paginated). */
719
936
  listReviews(id, params, opts) {
720
937
  return this.client.getPage(
721
- `/v1/wokus/${id}/reviews`,
938
+ `/v1/wokus/${encodeURIComponent(id)}/reviews`,
722
939
  params,
723
940
  opts
724
941
  );
@@ -727,16 +944,20 @@ var Wokus = class {
727
944
  sendInvitations(id, body, opts) {
728
945
  return this.client.request(
729
946
  "post",
730
- `/v1/wokus/${id}/invitations`,
947
+ `/v1/wokus/${encodeURIComponent(id)}/invitations`,
731
948
  { ...opts, body, idempotent: true }
732
949
  );
733
950
  }
734
951
  /** Share a woku review link by email. */
735
952
  share(id, body, opts) {
736
- return this.client.request("post", `/v1/wokus/${id}/share`, {
737
- ...opts,
738
- body
739
- });
953
+ return this.client.request(
954
+ "post",
955
+ `/v1/wokus/${encodeURIComponent(id)}/share`,
956
+ {
957
+ ...opts,
958
+ body
959
+ }
960
+ );
740
961
  }
741
962
  };
742
963
 
@@ -749,12 +970,16 @@ var Forms = class {
749
970
  return this.client.getPage("/v1/forms", params, opts);
750
971
  }
751
972
  get(id, opts) {
752
- return this.client.request("get", `/v1/forms/${id}`, opts);
973
+ return this.client.request(
974
+ "get",
975
+ `/v1/forms/${encodeURIComponent(id)}`,
976
+ opts
977
+ );
753
978
  }
754
979
  /** List the responses of a form (paginated). */
755
980
  listResponses(id, params, opts) {
756
981
  return this.client.getPage(
757
- `/v1/forms/${id}/responses`,
982
+ `/v1/forms/${encodeURIComponent(id)}/responses`,
758
983
  params,
759
984
  opts
760
985
  );
@@ -763,7 +988,7 @@ var Forms = class {
763
988
  sendInvitations(id, body, opts) {
764
989
  return this.client.request(
765
990
  "post",
766
- `/v1/forms/${id}/invitations`,
991
+ `/v1/forms/${encodeURIComponent(id)}/invitations`,
767
992
  { ...opts, body, idempotent: true }
768
993
  );
769
994
  }
@@ -778,7 +1003,11 @@ var Flows = class {
778
1003
  return this.client.getPage("/v1/flows", params, opts);
779
1004
  }
780
1005
  get(id, opts) {
781
- return this.client.request("get", `/v1/flows/${id}`, opts);
1006
+ return this.client.request(
1007
+ "get",
1008
+ `/v1/flows/${encodeURIComponent(id)}`,
1009
+ opts
1010
+ );
782
1011
  }
783
1012
  };
784
1013
 
@@ -793,7 +1022,7 @@ var ActionPlans = class {
793
1022
  get(id, opts) {
794
1023
  return this.client.request(
795
1024
  "get",
796
- `/v1/action-plans/${id}`,
1025
+ `/v1/action-plans/${encodeURIComponent(id)}`,
797
1026
  opts
798
1027
  );
799
1028
  }
@@ -801,7 +1030,7 @@ var ActionPlans = class {
801
1030
  events(id, opts) {
802
1031
  return this.client.request(
803
1032
  "get",
804
- `/v1/action-plans/${id}/events`,
1033
+ `/v1/action-plans/${encodeURIComponent(id)}/events`,
805
1034
  opts
806
1035
  );
807
1036
  }
@@ -809,7 +1038,7 @@ var ActionPlans = class {
809
1038
  getConversation(id, opts) {
810
1039
  return this.client.request(
811
1040
  "get",
812
- `/v1/action-plans/${id}/conversation`,
1041
+ `/v1/action-plans/${encodeURIComponent(id)}/conversation`,
813
1042
  opts
814
1043
  );
815
1044
  }
@@ -821,35 +1050,35 @@ var ActionPlans = class {
821
1050
  reply(id, text, opts) {
822
1051
  return this.client.request(
823
1052
  "post",
824
- `/v1/action-plans/${id}/conversation`,
1053
+ `/v1/action-plans/${encodeURIComponent(id)}/conversation`,
825
1054
  { ...opts, body: { text, confirm: true } }
826
1055
  );
827
1056
  }
828
1057
  createTask(id, body, opts) {
829
1058
  return this.client.request(
830
1059
  "post",
831
- `/v1/action-plans/${id}/tasks`,
832
- { ...opts, body, idempotent: true }
1060
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks`,
1061
+ { ...opts, body, maxRetries: 0 }
833
1062
  );
834
1063
  }
835
1064
  updateTask(id, taskId, body, opts) {
836
1065
  return this.client.request(
837
1066
  "patch",
838
- `/v1/action-plans/${id}/tasks/${taskId}`,
1067
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks/${encodeURIComponent(taskId)}`,
839
1068
  { ...opts, body }
840
1069
  );
841
1070
  }
842
1071
  reorderTasks(id, body, opts) {
843
1072
  return this.client.request(
844
1073
  "patch",
845
- `/v1/action-plans/${id}/tasks/reorder`,
1074
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks/reorder`,
846
1075
  { ...opts, body }
847
1076
  );
848
1077
  }
849
1078
  deleteTask(id, taskId, opts) {
850
1079
  return this.client.request(
851
1080
  "delete",
852
- `/v1/action-plans/${id}/tasks/${taskId}`,
1081
+ `/v1/action-plans/${encodeURIComponent(id)}/tasks/${encodeURIComponent(taskId)}`,
853
1082
  opts
854
1083
  );
855
1084
  }
@@ -871,7 +1100,7 @@ var ActionPlans = class {
871
1100
  status(id, action, opts) {
872
1101
  return this.client.request(
873
1102
  "post",
874
- `/v1/action-plans/${id}/${action}`,
1103
+ `/v1/action-plans/${encodeURIComponent(id)}/${action}`,
875
1104
  opts
876
1105
  );
877
1106
  }
@@ -890,7 +1119,7 @@ var ActionPlanGroups = class {
890
1119
  get(id, opts) {
891
1120
  return this.client.request(
892
1121
  "get",
893
- `/v1/action-plan-groups/${id}`,
1122
+ `/v1/action-plan-groups/${encodeURIComponent(id)}`,
894
1123
  opts
895
1124
  );
896
1125
  }
@@ -898,27 +1127,27 @@ var ActionPlanGroups = class {
898
1127
  return this.client.request("post", "/v1/action-plan-groups", {
899
1128
  ...opts,
900
1129
  body,
901
- idempotent: true
1130
+ maxRetries: 0
902
1131
  });
903
1132
  }
904
1133
  update(id, body, opts) {
905
1134
  return this.client.request(
906
1135
  "patch",
907
- `/v1/action-plan-groups/${id}`,
1136
+ `/v1/action-plan-groups/${encodeURIComponent(id)}`,
908
1137
  { ...opts, body }
909
1138
  );
910
1139
  }
911
1140
  setEnabled(id, enabled, opts) {
912
1141
  return this.client.request(
913
1142
  "patch",
914
- `/v1/action-plan-groups/${id}/enabled`,
1143
+ `/v1/action-plan-groups/${encodeURIComponent(id)}/enabled`,
915
1144
  { ...opts, body: { enabled } }
916
1145
  );
917
1146
  }
918
1147
  delete(id, opts) {
919
1148
  return this.client.request(
920
1149
  "delete",
921
- `/v1/action-plan-groups/${id}`,
1150
+ `/v1/action-plan-groups/${encodeURIComponent(id)}`,
922
1151
  opts
923
1152
  );
924
1153
  }
@@ -940,13 +1169,21 @@ var Tickets = class {
940
1169
  });
941
1170
  }
942
1171
  get(id, opts) {
943
- return this.client.request("get", `/v1/tickets/${id}`, opts);
1172
+ return this.client.request(
1173
+ "get",
1174
+ `/v1/tickets/${encodeURIComponent(id)}`,
1175
+ opts
1176
+ );
944
1177
  }
945
1178
  update(id, body, opts) {
946
- return this.client.request("patch", `/v1/tickets/${id}`, {
947
- ...opts,
948
- body
949
- });
1179
+ return this.client.request(
1180
+ "patch",
1181
+ `/v1/tickets/${encodeURIComponent(id)}`,
1182
+ {
1183
+ ...opts,
1184
+ body
1185
+ }
1186
+ );
950
1187
  }
951
1188
  };
952
1189
  var TicketDestinations = class {
@@ -963,7 +1200,7 @@ var TicketDestinations = class {
963
1200
  get(id, opts) {
964
1201
  return this.client.request(
965
1202
  "get",
966
- `/v1/ticket-destinations/${id}`,
1203
+ `/v1/ticket-destinations/${encodeURIComponent(id)}`,
967
1204
  opts
968
1205
  );
969
1206
  }
@@ -971,20 +1208,20 @@ var TicketDestinations = class {
971
1208
  return this.client.request("post", "/v1/ticket-destinations", {
972
1209
  ...opts,
973
1210
  body,
974
- idempotent: true
1211
+ maxRetries: 0
975
1212
  });
976
1213
  }
977
1214
  update(id, body, opts) {
978
1215
  return this.client.request(
979
1216
  "patch",
980
- `/v1/ticket-destinations/${id}`,
1217
+ `/v1/ticket-destinations/${encodeURIComponent(id)}`,
981
1218
  { ...opts, body }
982
1219
  );
983
1220
  }
984
1221
  delete(id, opts) {
985
1222
  return this.client.request(
986
1223
  "delete",
987
- `/v1/ticket-destinations/${id}`,
1224
+ `/v1/ticket-destinations/${encodeURIComponent(id)}`,
988
1225
  opts
989
1226
  );
990
1227
  }
@@ -995,7 +1232,7 @@ var TicketDestinations = class {
995
1232
  test(id, opts) {
996
1233
  return this.client.request(
997
1234
  "post",
998
- `/v1/ticket-destinations/${id}/test`,
1235
+ `/v1/ticket-destinations/${encodeURIComponent(id)}/test`,
999
1236
  { ...opts, body: { confirm: true } }
1000
1237
  );
1001
1238
  }
@@ -1035,7 +1272,7 @@ var Reports = class {
1035
1272
  npsTool(npsToolId, params, opts) {
1036
1273
  return this.client.request(
1037
1274
  "get",
1038
- `/v1/reports/nps-tool/${npsToolId}`,
1275
+ `/v1/reports/nps-tool/${encodeURIComponent(npsToolId)}`,
1039
1276
  { ...opts, query: { ...params, ...opts?.query } }
1040
1277
  );
1041
1278
  }
@@ -1068,6 +1305,193 @@ var Company = class {
1068
1305
  }
1069
1306
  };
1070
1307
 
1308
+ // src/resources/journeys.ts
1309
+ var Journeys = class {
1310
+ constructor(client) {
1311
+ this.client = client;
1312
+ }
1313
+ /** Read customer entry metadata without starting or preparing an evaluation. */
1314
+ entryInfo(journeyId, opts) {
1315
+ return this.client.request(
1316
+ "get",
1317
+ `/v1/journey-entries/${encodeURIComponent(journeyId)}`,
1318
+ opts
1319
+ );
1320
+ }
1321
+ /** Prepare a first tool. Only its saved response with dispatchToken confirms entry. */
1322
+ prepareEntry(journeyId, body, opts) {
1323
+ return this.client.request(
1324
+ "post",
1325
+ `/v1/journey-entries/${encodeURIComponent(journeyId)}`,
1326
+ { ...opts, body, maxRetries: 0 }
1327
+ );
1328
+ }
1329
+ /** Every journey of your company. */
1330
+ list(opts) {
1331
+ return this.client.request("get", "/v1/journeys", opts);
1332
+ }
1333
+ /** One journey with its moments. */
1334
+ get(journeyId, opts) {
1335
+ return this.client.request(
1336
+ "get",
1337
+ `/v1/journeys/${encodeURIComponent(journeyId)}`,
1338
+ opts
1339
+ );
1340
+ }
1341
+ /**
1342
+ * Create a journey. The response carries `webhookSecret` once and only here:
1343
+ * it signs legacy woku_signature calls. V2 URL tokens and sender secrets
1344
+ * are configured separately per moment. Store it securely.
1345
+ */
1346
+ create(body, opts) {
1347
+ return this.client.request("post", "/v1/journeys", {
1348
+ ...opts,
1349
+ body,
1350
+ idempotent: true
1351
+ });
1352
+ }
1353
+ /**
1354
+ * Rename a journey, switch it on or off, or replace its moments. Replacing
1355
+ * the moments mints a new version; the enrollments already running keep
1356
+ * executing the version they started with.
1357
+ */
1358
+ update(journeyId, body, opts) {
1359
+ return this.client.request(
1360
+ "patch",
1361
+ `/v1/journeys/${encodeURIComponent(journeyId)}`,
1362
+ {
1363
+ ...opts,
1364
+ body
1365
+ }
1366
+ );
1367
+ }
1368
+ /** Cursor page of exact customer cases; pass nextCursor to retrieve another page. */
1369
+ listEnrollments(journeyId, params, opts) {
1370
+ return this.client.request(
1371
+ "get",
1372
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments`,
1373
+ { ...opts, query: { ...params, ...opts?.query } }
1374
+ );
1375
+ }
1376
+ /** Iterate exact cases lazily, preserving options while advancing the server cursor. */
1377
+ async *iterEnrollments(journeyId, params, opts) {
1378
+ let query = { ...params, ...opts?.query };
1379
+ const seen = /* @__PURE__ */ new Set();
1380
+ for (; ; ) {
1381
+ const page = await this.listEnrollments(journeyId, void 0, {
1382
+ ...opts,
1383
+ query
1384
+ });
1385
+ for (const item of page.items) yield item;
1386
+ const next = page.nextCursor;
1387
+ if (!next) return;
1388
+ if (seen.has(next) || next === query.cursor)
1389
+ throw new WokuError("Enrollment pagination did not advance.", {
1390
+ code: "pagination_error"
1391
+ });
1392
+ seen.add(next);
1393
+ query = { ...query, cursor: next };
1394
+ }
1395
+ }
1396
+ /** History and next step for one exact participation. */
1397
+ getEnrollment(journeyId, enrollmentId, opts) {
1398
+ return this.client.request(
1399
+ "get",
1400
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments/${encodeURIComponent(enrollmentId)}`,
1401
+ opts
1402
+ );
1403
+ }
1404
+ /** Cancel future work for this case. Accepted provider sends cannot be recalled. */
1405
+ stopEnrollment(journeyId, enrollmentId, body = {}, opts) {
1406
+ return this.client.request(
1407
+ "post",
1408
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments/${encodeURIComponent(enrollmentId)}/stop`,
1409
+ { ...opts, body, idempotent: true }
1410
+ );
1411
+ }
1412
+ /** Credential readiness is not evidence that an actual webhook has arrived. */
1413
+ connections(journeyId, opts) {
1414
+ return this.client.request(
1415
+ "get",
1416
+ `/v1/journeys/${encodeURIComponent(journeyId)}/connections`,
1417
+ opts
1418
+ );
1419
+ }
1420
+ /** Replaces this moment's URL credential. Store the returned URL in the sender. */
1421
+ async mintMomentUrl(journeyId, stageKey, opts) {
1422
+ const result = await this.client.request(
1423
+ "post",
1424
+ `/v1/journeys/${encodeURIComponent(journeyId)}/moments/${encodeURIComponent(stageKey)}/url-token`,
1425
+ { ...opts, body: {}, idempotent: true }
1426
+ );
1427
+ return {
1428
+ ...result,
1429
+ url: new URL(result.url, this.client.baseURL).toString()
1430
+ };
1431
+ }
1432
+ /** Store the external sender's signing secret encrypted; it is never returned. */
1433
+ setSenderSecret(journeyId, stageKey, senderSecret, opts) {
1434
+ return this.client.request(
1435
+ "post",
1436
+ `/v1/journeys/${encodeURIComponent(journeyId)}/moments/${encodeURIComponent(stageKey)}/sender-secret`,
1437
+ { ...opts, body: { senderSecret }, maxRetries: 0 }
1438
+ );
1439
+ }
1440
+ /** Test saved payload mapping without creating a participation or sending a tool. */
1441
+ previewMoment(journeyId, stageKey, payload, opts) {
1442
+ return this.client.request(
1443
+ "post",
1444
+ `/v1/journeys/${encodeURIComponent(journeyId)}/moments/${encodeURIComponent(stageKey)}/preview`,
1445
+ { ...opts, body: { payload }, maxRetries: 0 }
1446
+ );
1447
+ }
1448
+ /** Delete a journey. What it already started keeps its own history. */
1449
+ delete(journeyId, opts) {
1450
+ return this.client.request(
1451
+ "delete",
1452
+ `/v1/journeys/${encodeURIComponent(journeyId)}`,
1453
+ opts
1454
+ );
1455
+ }
1456
+ /**
1457
+ * Mint a new signing secret. The previous one stays valid until the next
1458
+ * rotation, so senders can be updated without a gap.
1459
+ */
1460
+ rotateWebhookSecret(journeyId, opts) {
1461
+ return this.client.request(
1462
+ "post",
1463
+ `/v1/journeys/${encodeURIComponent(journeyId)}/webhook-secret`,
1464
+ { ...opts, body: {}, maxRetries: 0 }
1465
+ );
1466
+ }
1467
+ /**
1468
+ * Start the journey for one subject. Enrolling the same person for a later
1469
+ * cycle reuses the subject key after the previous cycle completes or stops.
1470
+ */
1471
+ enroll(journeyId, body, opts) {
1472
+ return this.client.request(
1473
+ "post",
1474
+ `/v1/journeys/${encodeURIComponent(journeyId)}/enrollments`,
1475
+ { ...opts, body, idempotent: true }
1476
+ );
1477
+ }
1478
+ /**
1479
+ * Emit one of your own events. Every journey whose moments listen for that
1480
+ * name reacts.
1481
+ */
1482
+ emitEvent(body, opts) {
1483
+ return this.client.request(
1484
+ "post",
1485
+ "/v1/journey-events",
1486
+ {
1487
+ ...opts,
1488
+ body,
1489
+ idempotent: true
1490
+ }
1491
+ );
1492
+ }
1493
+ };
1494
+
1071
1495
  // src/resources/quarantines.ts
1072
1496
  var Quarantines = class {
1073
1497
  constructor(client) {
@@ -1087,6 +1511,7 @@ var Woku = class {
1087
1511
  constructor(options) {
1088
1512
  const opts = typeof options === "string" ? { apiKey: options } : options ?? {};
1089
1513
  this.client = new WokuClient(opts);
1514
+ this.media = new Media(this.client);
1090
1515
  this.trackers = new Trackers(this.client);
1091
1516
  this.npsTools = new NpsTools(this.client);
1092
1517
  this.csatTools = new CsatTools(this.client);
@@ -1104,10 +1529,11 @@ var Woku = class {
1104
1529
  this.dispatches = new Dispatches(this.client);
1105
1530
  this.reports = new Reports(this.client);
1106
1531
  this.company = new Company(this.client);
1532
+ this.journeys = new Journeys(this.client);
1107
1533
  this.quarantines = new Quarantines(this.client);
1108
1534
  }
1109
1535
  };
1110
1536
 
1111
- export { AuthenticationError, BadRequestError, ConflictError, InternalServerError, NotFoundError, Page, PermissionDeniedError, RateLimitError, UnprocessableEntityError, Woku, WokuAPIError, WokuClient, WokuConnectionError, WokuError, WokuTimeoutError };
1537
+ export { AuthenticationError, BadRequestError, ConflictError, InternalServerError, Journeys, Media, NotFoundError, Page, PayloadTooLargeError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, Woku, WokuAPIError, WokuClient, WokuConnectionError, WokuError, WokuTimeoutError };
1112
1538
  //# sourceMappingURL=index.js.map
1113
1539
  //# sourceMappingURL=index.js.map