@garuhq/node 0.11.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -3,6 +3,49 @@
3
3
  All notable changes to `@garuhq/node` are documented in this file. Format:
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
5
5
 
6
+ ## [0.12.0] — 2026-05-19
7
+
8
+ ### Added
9
+
10
+ - `webhookEvents.resend(id)` — `POST /api/webhook-events/{id}/resend`,
11
+ the audit-trail-preserving counterpart to `retry()`. The backend
12
+ inserts a *clone* event (new numeric id) that points back at the
13
+ source via `manualResendOf`, then dispatches that clone. The
14
+ original row is untouched, so the historical record of the prior
15
+ failure (status, response status/body, attempts) survives. Works on
16
+ any source status (`success` / `failed` / `pending`).
17
+ - Outbound delivery uses `Idempotency-Key: resend_<originalId>`, so
18
+ recipient handlers can distinguish a resend from a fresh delivery
19
+ both by the header prefix and by reading the response payload's
20
+ `manualResendOf` field.
21
+ - `WebhookEvent.manualResendOf: number | null` — populated with the
22
+ source event's numeric id on rows produced by `resend()`, `null`
23
+ everywhere else (originally-fired events and legacy `retry()`
24
+ outputs).
25
+
26
+ ### Deprecated
27
+
28
+ - `webhookEvents.retry(id)` is soft-deprecated. It still works and is
29
+ not scheduled for removal — older CLI / MCP releases depend on it —
30
+ but new integrations should prefer `resend()`, which preserves the
31
+ original event's audit trail by cloning instead of mutating the row
32
+ in place.
33
+
34
+ ## [0.11.1] — 2026-05-19
35
+
36
+ ### Fixed
37
+
38
+ - Empty-body mutations (`webhookEvents.retry`, `scheduledCharges.resume`,
39
+ `customers.delete`, `products.portalConfig.clear`,
40
+ `scheduledCharges.clearPaymentMethod`) now send an explicit `{}` body.
41
+ `openapi-fetch` sets `Content-Type: application/json` as a default
42
+ header on every request, and the backend body-parser rejects
43
+ `Content-Type: json` + empty body with
44
+ `Body cannot be empty when content-type is set to 'application/json'`.
45
+ Previously these calls failed against production; the SDK's mock-fetch
46
+ tests didn't surface the regression because the mock never hits the
47
+ body-parser middleware.
48
+
6
49
  ## [0.11.0] — 2026-05-19
7
50
 
8
51
  ### Added
package/dist/index.cjs CHANGED
@@ -435,9 +435,10 @@ var Customers = class {
435
435
  */
436
436
  async delete(id) {
437
437
  await this.http.call(
438
- (signal) => this.http.client.DELETE(`/api/customers/${id}`, { signal }).then(
439
- (r) => r
440
- )
438
+ (signal) => this.http.client.DELETE(`/api/customers/${id}`, {
439
+ body: {},
440
+ signal
441
+ }).then((r) => r)
441
442
  );
442
443
  }
443
444
  };
@@ -525,6 +526,7 @@ var ProductPortalConfigResource = class {
525
526
  async clear(productId) {
526
527
  return this.http.call(
527
528
  (signal) => this.http.client.DELETE(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
529
+ body: {},
528
530
  signal
529
531
  }).then((r) => r)
530
532
  );
@@ -700,6 +702,7 @@ var ScheduledCharges = class {
700
702
  async resume(id) {
701
703
  return this.http.call(
702
704
  (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/resume`, {
705
+ body: {},
703
706
  signal
704
707
  }).then((r) => r)
705
708
  );
@@ -798,6 +801,7 @@ var ScheduledCharges = class {
798
801
  async clearPaymentMethod(id) {
799
802
  return this.http.call(
800
803
  (signal) => this.http.client.DELETE(`/api/scheduled-charges/${id}/payment-method`, {
804
+ body: {},
801
805
  signal
802
806
  }).then((r) => r)
803
807
  );
@@ -894,10 +898,16 @@ var WebhookEvents = class {
894
898
  );
895
899
  }
896
900
  /**
901
+ * @deprecated For most cases prefer {@link resend}, which preserves the
902
+ * original event's audit trail by cloning rather than mutating. `retry()`
903
+ * resets the original row in place — once it succeeds, the historical
904
+ * record of the prior failure is gone. Kept here for callers that
905
+ * explicitly want the legacy in-place semantics (and for backwards
906
+ * compatibility with older CLI / MCP releases).
907
+ *
897
908
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
898
909
  * retry schedule, and triggers an immediate delivery attempt. Works on
899
- * any status (`success`, `failed`, `pending`) — use this when a
900
- * customer reports a missed or unprocessed event.
910
+ * any status (`success`, `failed`, `pending`).
901
911
  *
902
912
  * @example
903
913
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -907,9 +917,47 @@ var WebhookEvents = class {
907
917
  */
908
918
  async retry(id) {
909
919
  return this.http.call(
910
- (signal) => this.http.client.POST(`/api/webhook-events/${id}/retry`, { signal }).then(
911
- (r) => r
912
- )
920
+ (signal) => this.http.client.POST(`/api/webhook-events/${id}/retry`, {
921
+ body: {},
922
+ signal
923
+ }).then((r) => r)
924
+ );
925
+ }
926
+ /**
927
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
928
+ * {@link retry}, this does *not* mutate the original row — it inserts a
929
+ * fresh event (new numeric id) that points back at the source via
930
+ * `manualResendOf`, then dispatches that clone. The original row is
931
+ * untouched, so the historical record of the prior failure (and its
932
+ * response status / body) is preserved.
933
+ *
934
+ * Works on any source status (`success`, `failed`, `pending`). Use this
935
+ * when a customer reports a missed or unprocessed event, or to replay an
936
+ * event during a backfill — both reasons where you want the original
937
+ * delivery's outcome to remain on the record.
938
+ *
939
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
940
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
941
+ * of the source event, not the clone). Recipient handlers that key off
942
+ * `Idempotency-Key` will see this as a distinct delivery from the
943
+ * original — distinguishable both by the `resend_` prefix and by reading
944
+ * the response payload's `manualResendOf` field.
945
+ *
946
+ * Returns the *clone* event (new id), not the original. The original is
947
+ * unchanged on the server.
948
+ *
949
+ * @example
950
+ * const event = await garu.webhookEvents.get(42);
951
+ * const clone = await garu.webhookEvents.resend(42);
952
+ * clone.id !== event.id; // true — clone has its own id
953
+ * clone.manualResendOf === event.id; // true — points back at the source
954
+ */
955
+ async resend(id) {
956
+ return this.http.call(
957
+ (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
958
+ body: {},
959
+ signal
960
+ }).then((r) => r)
913
961
  );
914
962
  }
915
963
  };
@@ -972,7 +1020,7 @@ function parseSignatureHeader(header) {
972
1020
  var DEFAULT_BASE_URL = "https://garu.com.br";
973
1021
  var DEFAULT_TIMEOUT_MS = 3e4;
974
1022
  var DEFAULT_MAX_RETRIES = 2;
975
- var SDK_VERSION = "0.11.0";
1023
+ var SDK_VERSION = "0.11.1";
976
1024
  var Garu = class {
977
1025
  charges;
978
1026
  customers;
package/dist/index.d.cts CHANGED
@@ -596,6 +596,14 @@ interface WebhookEvent {
596
596
  responseStatus: number | null;
597
597
  /** Response body from the most recent attempt, truncated by the gateway. */
598
598
  responseBody: string | null;
599
+ /**
600
+ * When this row is a clone produced by `webhookEvents.resend(id)`, this is
601
+ * the numeric id of the original event the clone was forked from. `null`
602
+ * on every originally-fired event (and on events resurrected via the
603
+ * legacy `webhookEvents.retry(id)` mutation, which mutates in place
604
+ * instead of cloning).
605
+ */
606
+ manualResendOf: number | null;
599
607
  createdAt: string;
600
608
  [key: string]: unknown;
601
609
  }
@@ -1117,10 +1125,16 @@ declare class WebhookEvents {
1117
1125
  */
1118
1126
  get(id: number): Promise<WebhookEvent>;
1119
1127
  /**
1128
+ * @deprecated For most cases prefer {@link resend}, which preserves the
1129
+ * original event's audit trail by cloning rather than mutating. `retry()`
1130
+ * resets the original row in place — once it succeeds, the historical
1131
+ * record of the prior failure is gone. Kept here for callers that
1132
+ * explicitly want the legacy in-place semantics (and for backwards
1133
+ * compatibility with older CLI / MCP releases).
1134
+ *
1120
1135
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
1121
1136
  * retry schedule, and triggers an immediate delivery attempt. Works on
1122
- * any status (`success`, `failed`, `pending`) — use this when a
1123
- * customer reports a missed or unprocessed event.
1137
+ * any status (`success`, `failed`, `pending`).
1124
1138
  *
1125
1139
  * @example
1126
1140
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -1129,6 +1143,36 @@ declare class WebhookEvents {
1129
1143
  * }
1130
1144
  */
1131
1145
  retry(id: number): Promise<WebhookEvent>;
1146
+ /**
1147
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
1148
+ * {@link retry}, this does *not* mutate the original row — it inserts a
1149
+ * fresh event (new numeric id) that points back at the source via
1150
+ * `manualResendOf`, then dispatches that clone. The original row is
1151
+ * untouched, so the historical record of the prior failure (and its
1152
+ * response status / body) is preserved.
1153
+ *
1154
+ * Works on any source status (`success`, `failed`, `pending`). Use this
1155
+ * when a customer reports a missed or unprocessed event, or to replay an
1156
+ * event during a backfill — both reasons where you want the original
1157
+ * delivery's outcome to remain on the record.
1158
+ *
1159
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
1160
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
1161
+ * of the source event, not the clone). Recipient handlers that key off
1162
+ * `Idempotency-Key` will see this as a distinct delivery from the
1163
+ * original — distinguishable both by the `resend_` prefix and by reading
1164
+ * the response payload's `manualResendOf` field.
1165
+ *
1166
+ * Returns the *clone* event (new id), not the original. The original is
1167
+ * unchanged on the server.
1168
+ *
1169
+ * @example
1170
+ * const event = await garu.webhookEvents.get(42);
1171
+ * const clone = await garu.webhookEvents.resend(42);
1172
+ * clone.id !== event.id; // true — clone has its own id
1173
+ * clone.manualResendOf === event.id; // true — points back at the source
1174
+ */
1175
+ resend(id: number): Promise<WebhookEvent>;
1132
1176
  }
1133
1177
 
1134
1178
  interface GaruOptions {
package/dist/index.d.ts CHANGED
@@ -596,6 +596,14 @@ interface WebhookEvent {
596
596
  responseStatus: number | null;
597
597
  /** Response body from the most recent attempt, truncated by the gateway. */
598
598
  responseBody: string | null;
599
+ /**
600
+ * When this row is a clone produced by `webhookEvents.resend(id)`, this is
601
+ * the numeric id of the original event the clone was forked from. `null`
602
+ * on every originally-fired event (and on events resurrected via the
603
+ * legacy `webhookEvents.retry(id)` mutation, which mutates in place
604
+ * instead of cloning).
605
+ */
606
+ manualResendOf: number | null;
599
607
  createdAt: string;
600
608
  [key: string]: unknown;
601
609
  }
@@ -1117,10 +1125,16 @@ declare class WebhookEvents {
1117
1125
  */
1118
1126
  get(id: number): Promise<WebhookEvent>;
1119
1127
  /**
1128
+ * @deprecated For most cases prefer {@link resend}, which preserves the
1129
+ * original event's audit trail by cloning rather than mutating. `retry()`
1130
+ * resets the original row in place — once it succeeds, the historical
1131
+ * record of the prior failure is gone. Kept here for callers that
1132
+ * explicitly want the legacy in-place semantics (and for backwards
1133
+ * compatibility with older CLI / MCP releases).
1134
+ *
1120
1135
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
1121
1136
  * retry schedule, and triggers an immediate delivery attempt. Works on
1122
- * any status (`success`, `failed`, `pending`) — use this when a
1123
- * customer reports a missed or unprocessed event.
1137
+ * any status (`success`, `failed`, `pending`).
1124
1138
  *
1125
1139
  * @example
1126
1140
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -1129,6 +1143,36 @@ declare class WebhookEvents {
1129
1143
  * }
1130
1144
  */
1131
1145
  retry(id: number): Promise<WebhookEvent>;
1146
+ /**
1147
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
1148
+ * {@link retry}, this does *not* mutate the original row — it inserts a
1149
+ * fresh event (new numeric id) that points back at the source via
1150
+ * `manualResendOf`, then dispatches that clone. The original row is
1151
+ * untouched, so the historical record of the prior failure (and its
1152
+ * response status / body) is preserved.
1153
+ *
1154
+ * Works on any source status (`success`, `failed`, `pending`). Use this
1155
+ * when a customer reports a missed or unprocessed event, or to replay an
1156
+ * event during a backfill — both reasons where you want the original
1157
+ * delivery's outcome to remain on the record.
1158
+ *
1159
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
1160
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
1161
+ * of the source event, not the clone). Recipient handlers that key off
1162
+ * `Idempotency-Key` will see this as a distinct delivery from the
1163
+ * original — distinguishable both by the `resend_` prefix and by reading
1164
+ * the response payload's `manualResendOf` field.
1165
+ *
1166
+ * Returns the *clone* event (new id), not the original. The original is
1167
+ * unchanged on the server.
1168
+ *
1169
+ * @example
1170
+ * const event = await garu.webhookEvents.get(42);
1171
+ * const clone = await garu.webhookEvents.resend(42);
1172
+ * clone.id !== event.id; // true — clone has its own id
1173
+ * clone.manualResendOf === event.id; // true — points back at the source
1174
+ */
1175
+ resend(id: number): Promise<WebhookEvent>;
1132
1176
  }
1133
1177
 
1134
1178
  interface GaruOptions {
package/dist/index.js CHANGED
@@ -429,9 +429,10 @@ var Customers = class {
429
429
  */
430
430
  async delete(id) {
431
431
  await this.http.call(
432
- (signal) => this.http.client.DELETE(`/api/customers/${id}`, { signal }).then(
433
- (r) => r
434
- )
432
+ (signal) => this.http.client.DELETE(`/api/customers/${id}`, {
433
+ body: {},
434
+ signal
435
+ }).then((r) => r)
435
436
  );
436
437
  }
437
438
  };
@@ -519,6 +520,7 @@ var ProductPortalConfigResource = class {
519
520
  async clear(productId) {
520
521
  return this.http.call(
521
522
  (signal) => this.http.client.DELETE(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
523
+ body: {},
522
524
  signal
523
525
  }).then((r) => r)
524
526
  );
@@ -694,6 +696,7 @@ var ScheduledCharges = class {
694
696
  async resume(id) {
695
697
  return this.http.call(
696
698
  (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/resume`, {
699
+ body: {},
697
700
  signal
698
701
  }).then((r) => r)
699
702
  );
@@ -792,6 +795,7 @@ var ScheduledCharges = class {
792
795
  async clearPaymentMethod(id) {
793
796
  return this.http.call(
794
797
  (signal) => this.http.client.DELETE(`/api/scheduled-charges/${id}/payment-method`, {
798
+ body: {},
795
799
  signal
796
800
  }).then((r) => r)
797
801
  );
@@ -888,10 +892,16 @@ var WebhookEvents = class {
888
892
  );
889
893
  }
890
894
  /**
895
+ * @deprecated For most cases prefer {@link resend}, which preserves the
896
+ * original event's audit trail by cloning rather than mutating. `retry()`
897
+ * resets the original row in place — once it succeeds, the historical
898
+ * record of the prior failure is gone. Kept here for callers that
899
+ * explicitly want the legacy in-place semantics (and for backwards
900
+ * compatibility with older CLI / MCP releases).
901
+ *
891
902
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
892
903
  * retry schedule, and triggers an immediate delivery attempt. Works on
893
- * any status (`success`, `failed`, `pending`) — use this when a
894
- * customer reports a missed or unprocessed event.
904
+ * any status (`success`, `failed`, `pending`).
895
905
  *
896
906
  * @example
897
907
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -901,9 +911,47 @@ var WebhookEvents = class {
901
911
  */
902
912
  async retry(id) {
903
913
  return this.http.call(
904
- (signal) => this.http.client.POST(`/api/webhook-events/${id}/retry`, { signal }).then(
905
- (r) => r
906
- )
914
+ (signal) => this.http.client.POST(`/api/webhook-events/${id}/retry`, {
915
+ body: {},
916
+ signal
917
+ }).then((r) => r)
918
+ );
919
+ }
920
+ /**
921
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
922
+ * {@link retry}, this does *not* mutate the original row — it inserts a
923
+ * fresh event (new numeric id) that points back at the source via
924
+ * `manualResendOf`, then dispatches that clone. The original row is
925
+ * untouched, so the historical record of the prior failure (and its
926
+ * response status / body) is preserved.
927
+ *
928
+ * Works on any source status (`success`, `failed`, `pending`). Use this
929
+ * when a customer reports a missed or unprocessed event, or to replay an
930
+ * event during a backfill — both reasons where you want the original
931
+ * delivery's outcome to remain on the record.
932
+ *
933
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
934
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
935
+ * of the source event, not the clone). Recipient handlers that key off
936
+ * `Idempotency-Key` will see this as a distinct delivery from the
937
+ * original — distinguishable both by the `resend_` prefix and by reading
938
+ * the response payload's `manualResendOf` field.
939
+ *
940
+ * Returns the *clone* event (new id), not the original. The original is
941
+ * unchanged on the server.
942
+ *
943
+ * @example
944
+ * const event = await garu.webhookEvents.get(42);
945
+ * const clone = await garu.webhookEvents.resend(42);
946
+ * clone.id !== event.id; // true — clone has its own id
947
+ * clone.manualResendOf === event.id; // true — points back at the source
948
+ */
949
+ async resend(id) {
950
+ return this.http.call(
951
+ (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
952
+ body: {},
953
+ signal
954
+ }).then((r) => r)
907
955
  );
908
956
  }
909
957
  };
@@ -966,7 +1014,7 @@ function parseSignatureHeader(header) {
966
1014
  var DEFAULT_BASE_URL = "https://garu.com.br";
967
1015
  var DEFAULT_TIMEOUT_MS = 3e4;
968
1016
  var DEFAULT_MAX_RETRIES = 2;
969
- var SDK_VERSION = "0.11.0";
1017
+ var SDK_VERSION = "0.11.1";
970
1018
  var Garu = class {
971
1019
  charges;
972
1020
  customers;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Official Node.js / TypeScript SDK for the Garu payment gateway.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://garu.com.br",