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