@superblocksteam/sdk-api 2.0.165-next.0 → 2.0.166-next.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.
Files changed (125) hide show
  1. package/README.md +1 -2
  2. package/dist/errors.d.ts +36 -3
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +244 -8
  5. package/dist/errors.js.map +1 -1
  6. package/dist/errors.test.d.ts +2 -0
  7. package/dist/errors.test.d.ts.map +1 -0
  8. package/dist/errors.test.js +189 -0
  9. package/dist/errors.test.js.map +1 -0
  10. package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -1
  11. package/dist/integrations/base/decode-worker-binary-response.js +2 -5
  12. package/dist/integrations/base/decode-worker-binary-response.js.map +1 -1
  13. package/dist/integrations/base/index.d.ts +2 -1
  14. package/dist/integrations/base/index.d.ts.map +1 -1
  15. package/dist/integrations/base/index.js +1 -0
  16. package/dist/integrations/base/index.js.map +1 -1
  17. package/dist/integrations/base/request-body.d.ts +29 -0
  18. package/dist/integrations/base/request-body.d.ts.map +1 -0
  19. package/dist/integrations/base/request-body.js +238 -0
  20. package/dist/integrations/base/request-body.js.map +1 -0
  21. package/dist/integrations/base/request-body.test.d.ts +2 -0
  22. package/dist/integrations/base/request-body.test.d.ts.map +1 -0
  23. package/dist/integrations/base/request-body.test.js +466 -0
  24. package/dist/integrations/base/request-body.test.js.map +1 -0
  25. package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -1
  26. package/dist/integrations/base/rest-api-client-base.js +15 -14
  27. package/dist/integrations/base/rest-api-client-base.js.map +1 -1
  28. package/dist/integrations/base/rest-api-integration-client.d.ts +2 -2
  29. package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
  30. package/dist/integrations/base/rest-api-integration-client.js +5 -6
  31. package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
  32. package/dist/integrations/base/types.d.ts +55 -11
  33. package/dist/integrations/base/types.d.ts.map +1 -1
  34. package/dist/integrations/base/types.js +7 -0
  35. package/dist/integrations/base/types.js.map +1 -1
  36. package/dist/integrations/cosmosdb/client.d.ts.map +1 -1
  37. package/dist/integrations/cosmosdb/client.js +2 -5
  38. package/dist/integrations/cosmosdb/client.js.map +1 -1
  39. package/dist/integrations/documentation-resolver.test.js +106 -1
  40. package/dist/integrations/documentation-resolver.test.js.map +1 -1
  41. package/dist/integrations/dynamodb/client.d.ts.map +1 -1
  42. package/dist/integrations/dynamodb/client.js +2 -5
  43. package/dist/integrations/dynamodb/client.js.map +1 -1
  44. package/dist/integrations/gcs/client.d.ts.map +1 -1
  45. package/dist/integrations/gcs/client.js +2 -5
  46. package/dist/integrations/gcs/client.js.map +1 -1
  47. package/dist/integrations/gsheets/client.d.ts.map +1 -1
  48. package/dist/integrations/gsheets/client.js +2 -5
  49. package/dist/integrations/gsheets/client.js.map +1 -1
  50. package/dist/integrations/mongodb/client.d.ts.map +1 -1
  51. package/dist/integrations/mongodb/client.js +2 -5
  52. package/dist/integrations/mongodb/client.js.map +1 -1
  53. package/dist/integrations/restapiintegration/client.body-types.test.d.ts +2 -0
  54. package/dist/integrations/restapiintegration/client.body-types.test.d.ts.map +1 -0
  55. package/dist/integrations/restapiintegration/client.body-types.test.js +227 -0
  56. package/dist/integrations/restapiintegration/client.body-types.test.js.map +1 -0
  57. package/dist/integrations/restapiintegration/client.test.js +56 -30
  58. package/dist/integrations/restapiintegration/client.test.js.map +1 -1
  59. package/dist/integrations/s3/client.d.ts.map +1 -1
  60. package/dist/integrations/s3/client.js +2 -5
  61. package/dist/integrations/s3/client.js.map +1 -1
  62. package/dist/integrations/salesforce/client.d.ts.map +1 -1
  63. package/dist/integrations/salesforce/client.js +2 -5
  64. package/dist/integrations/salesforce/client.js.map +1 -1
  65. package/dist/integrations/slack/client.d.ts.map +1 -1
  66. package/dist/integrations/slack/client.js +4 -13
  67. package/dist/integrations/slack/client.js.map +1 -1
  68. package/dist/integrations/slack/client.test.js +20 -0
  69. package/dist/integrations/slack/client.test.js.map +1 -1
  70. package/dist/integrations/utils.d.ts +8 -3
  71. package/dist/integrations/utils.d.ts.map +1 -1
  72. package/dist/integrations/utils.js +42 -1
  73. package/dist/integrations/utils.js.map +1 -1
  74. package/package.json +2 -2
  75. package/src/errors.test.ts +257 -0
  76. package/src/errors.ts +304 -10
  77. package/src/integrations/base/decode-worker-binary-response.ts +5 -7
  78. package/src/integrations/base/index.ts +3 -0
  79. package/src/integrations/base/request-body.test.ts +565 -0
  80. package/src/integrations/base/request-body.ts +392 -0
  81. package/src/integrations/base/rest-api-client-base.ts +24 -19
  82. package/src/integrations/base/rest-api-integration-client.ts +14 -10
  83. package/src/integrations/base/types.ts +66 -11
  84. package/src/integrations/box/README.md +4 -60
  85. package/src/integrations/cosmosdb/client.ts +8 -7
  86. package/src/integrations/documentation-resolver.test.ts +130 -1
  87. package/src/integrations/dropbox/README.md +1 -71
  88. package/src/integrations/dropbox/docs.manifest.json +10 -1
  89. package/src/integrations/dropbox/overlays/upload-unsupported.md +3 -0
  90. package/src/integrations/dropbox/overlays/upload.md +89 -0
  91. package/src/integrations/dynamodb/client.ts +8 -7
  92. package/src/integrations/elasticsearch/README.md +0 -80
  93. package/src/integrations/elasticsearch/docs.manifest.json +10 -1
  94. package/src/integrations/elasticsearch/overlays/bulk-unsupported.md +3 -0
  95. package/src/integrations/elasticsearch/overlays/bulk.md +80 -0
  96. package/src/integrations/gcs/client.ts +8 -7
  97. package/src/integrations/googledrive/README.md +10 -36
  98. package/src/integrations/gsheets/client.ts +8 -7
  99. package/src/integrations/jira/README.md +0 -26
  100. package/src/integrations/jira/docs.manifest.json +10 -1
  101. package/src/integrations/jira/overlays/attachments-unsupported.md +3 -0
  102. package/src/integrations/jira/overlays/attachments.md +30 -0
  103. package/src/integrations/lakebase/README.md +1 -3
  104. package/src/integrations/mongodb/client.ts +8 -7
  105. package/src/integrations/postgres/README.md +1 -3
  106. package/src/integrations/restapiintegration/client.body-types.test.ts +323 -0
  107. package/src/integrations/restapiintegration/client.test.ts +68 -32
  108. package/src/integrations/restapiintegration/docs.manifest.json +8 -0
  109. package/src/integrations/restapiintegration/overlays/request-body-types-unsupported.md +7 -0
  110. package/src/integrations/restapiintegration/overlays/request-body-types.md +115 -0
  111. package/src/integrations/restapiintegration/overlays/response-types-binary.md +4 -8
  112. package/src/integrations/s3/client.ts +8 -7
  113. package/src/integrations/salesforce/client.ts +9 -7
  114. package/src/integrations/slack/client.test.ts +30 -0
  115. package/src/integrations/slack/client.ts +13 -19
  116. package/src/integrations/snowflakepostgres/README.md +1 -3
  117. package/src/integrations/stabilityai/README.md +0 -87
  118. package/src/integrations/stabilityai/docs.manifest.json +10 -1
  119. package/src/integrations/stabilityai/overlays/image-uploads-unsupported.md +3 -0
  120. package/src/integrations/stabilityai/overlays/image-uploads.md +120 -0
  121. package/src/integrations/stripe/README.md +0 -201
  122. package/src/integrations/stripe/docs.manifest.json +10 -1
  123. package/src/integrations/stripe/overlays/request-bodies-unsupported.md +3 -0
  124. package/src/integrations/stripe/overlays/request-bodies.md +204 -0
  125. package/src/integrations/utils.ts +51 -1
@@ -0,0 +1,120 @@
1
+ ## Image-to-Image, Upscale, and Inpainting
2
+
3
+ The `/image-to-image`, `/image-to-image/upscale`, and `/image-to-image/masking` endpoints expect `multipart/form-data`, so pass `bodyType: "multipartForm"`. Image parts are `{ value, filename }` with raw bytes. Multipart fields are flat, so `text_prompts` uses bracketed keys such as `"text_prompts[0][text]"`. Do not set `Content-Type`. The worker generates the multipart boundary.
4
+
5
+ For an uploaded file, pass `await file.readContentsAsync("raw")` as the part value. For an image returned by a previous generation, decode its `base64` artifact with `Buffer.from(artifact.base64, "base64")`.
6
+
7
+ The engine/model ID is part of the URL path:
8
+
9
+ ```typescript
10
+ // Image-to-image
11
+ const path = "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image";
12
+
13
+ // Upscaling
14
+ const path = "/v1/generation/esrgan-v1-x2plus/image-to-image/upscale";
15
+ ```
16
+
17
+ ### Image-to-Image (Transform Existing Image)
18
+
19
+ ```typescript
20
+ const source = input.upload.files[0];
21
+
22
+ const result = await ctx.integrations.stability.apiRequest(
23
+ {
24
+ method: "POST",
25
+ path: "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image",
26
+ bodyType: "multipartForm",
27
+ body: {
28
+ init_image: {
29
+ value: await source.readContentsAsync("raw"),
30
+ filename: source.name,
31
+ },
32
+ "text_prompts[0][text]": "A vibrant oil painting style",
33
+ "text_prompts[0][weight]": 1,
34
+ image_strength: 0.35, // How much to modify (0-1)
35
+ cfg_scale: 7,
36
+ samples: 1,
37
+ steps: 30,
38
+ },
39
+ },
40
+ { response: GenerationResponseSchema },
41
+ );
42
+ ```
43
+
44
+ ### Upscale an Image
45
+
46
+ ```typescript
47
+ const UpscaleResponseSchema = z.object({
48
+ artifacts: z.array(
49
+ z.object({
50
+ base64: z.string(),
51
+ finishReason: z.string(),
52
+ seed: z.number(),
53
+ }),
54
+ ),
55
+ });
56
+
57
+ // Response of an earlier text-to-image apiRequest call.
58
+ const generated = await ctx.integrations.stability.apiRequest(
59
+ {
60
+ method: "POST",
61
+ path: "/v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image",
62
+ body: {
63
+ text_prompts: [{ text: "A vibrant oil painting style", weight: 1 }],
64
+ cfg_scale: 7,
65
+ samples: 1,
66
+ steps: 30,
67
+ },
68
+ },
69
+ { response: GenerationResponseSchema },
70
+ );
71
+
72
+ const result = await ctx.integrations.stability.apiRequest(
73
+ {
74
+ method: "POST",
75
+ path: "/v1/generation/esrgan-v1-x2plus/image-to-image/upscale",
76
+ bodyType: "multipartForm",
77
+ body: {
78
+ image: {
79
+ value: Buffer.from(generated.artifacts[0].base64, "base64"),
80
+ filename: "generated.png",
81
+ },
82
+ width: 2048, // Target width (optional)
83
+ },
84
+ },
85
+ { response: UpscaleResponseSchema },
86
+ );
87
+
88
+ const upscaledImage = result.artifacts[0].base64;
89
+ ```
90
+
91
+ ### Inpainting (Edit Parts of an Image)
92
+
93
+ ```typescript
94
+ const [source, mask] = input.upload.files;
95
+
96
+ const result = await ctx.integrations.stability.apiRequest(
97
+ {
98
+ method: "POST",
99
+ path: "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image/masking",
100
+ bodyType: "multipartForm",
101
+ body: {
102
+ init_image: {
103
+ value: await source.readContentsAsync("raw"),
104
+ filename: source.name,
105
+ },
106
+ mask_image: {
107
+ value: await mask.readContentsAsync("raw"),
108
+ filename: mask.name,
109
+ },
110
+ mask_source: "MASK_IMAGE_WHITE", // White areas will be regenerated
111
+ "text_prompts[0][text]": "A golden retriever sitting",
112
+ "text_prompts[0][weight]": 1,
113
+ cfg_scale: 7,
114
+ samples: 1,
115
+ steps: 30,
116
+ },
117
+ },
118
+ { response: GenerationResponseSchema },
119
+ );
120
+ ```
@@ -10,57 +10,6 @@ Process payments, manage customers, and interact with Stripe's payment platform.
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Create a Customer
14
-
15
- ```typescript
16
- import { api, z, stripe } from "@superblocksteam/sdk-api";
17
-
18
- // Integration ID from the integrations panel
19
- const PROD_STRIPE = "a1b2c3d4-5678-90ab-cdef-stripe000001";
20
-
21
- const CustomerSchema = z.object({
22
- id: z.string(),
23
- object: z.literal("customer"),
24
- email: z.string().nullable(),
25
- name: z.string().nullable(),
26
- created: z.number(),
27
- metadata: z.record(z.string()),
28
- default_source: z.string().nullable(),
29
- });
30
-
31
- export default api({
32
- name: "StripeExample",
33
- integrations: {
34
- stripe: stripe(PROD_STRIPE),
35
- },
36
- input: z.object({
37
- email: z.string().email(),
38
- name: z.string(),
39
- }),
40
- output: z.object({
41
- customerId: z.string(),
42
- }),
43
- async run(ctx, { email, name }) {
44
- const result = await ctx.integrations.stripe.apiRequest(
45
- {
46
- method: "POST",
47
- path: "/v1/customers",
48
- body: {
49
- email: email,
50
- name: name,
51
- metadata: {
52
- source: "api",
53
- },
54
- },
55
- },
56
- { response: CustomerSchema },
57
- );
58
-
59
- return { customerId: result.id };
60
- },
61
- });
62
- ```
63
-
64
13
  ### Get a Customer
65
14
 
66
15
  ```typescript
@@ -75,43 +24,6 @@ const customer = await ctx.integrations.stripe.apiRequest(
75
24
  console.log(`Customer: ${customer.name} (${customer.email})`);
76
25
  ```
77
26
 
78
- ### Create a Payment Intent
79
-
80
- ```typescript
81
- const PaymentIntentSchema = z.object({
82
- id: z.string(),
83
- object: z.literal("payment_intent"),
84
- amount: z.number(),
85
- currency: z.string(),
86
- status: z.string(),
87
- client_secret: z.string(),
88
- customer: z.string().nullable(),
89
- metadata: z.record(z.string()),
90
- });
91
-
92
- const paymentIntent = await ctx.integrations.stripe.apiRequest(
93
- {
94
- method: "POST",
95
- path: "/v1/payment_intents",
96
- body: {
97
- amount: 2000, // Amount in cents ($20.00)
98
- currency: "usd",
99
- customer: customerId,
100
- automatic_payment_methods: {
101
- enabled: true,
102
- },
103
- metadata: {
104
- order_id: "order_123",
105
- },
106
- },
107
- },
108
- { response: PaymentIntentSchema },
109
- );
110
-
111
- // Return client_secret to frontend for Stripe.js
112
- console.log(`Client secret: ${paymentIntent.client_secret}`);
113
- ```
114
-
115
27
  ### List Charges
116
28
 
117
29
  ```typescript
@@ -151,67 +63,6 @@ charges.data.forEach((charge) => {
151
63
  });
152
64
  ```
153
65
 
154
- ### Create a Subscription
155
-
156
- ```typescript
157
- const SubscriptionSchema = z.object({
158
- id: z.string(),
159
- object: z.literal("subscription"),
160
- customer: z.string(),
161
- status: z.string(), // active, past_due, canceled, etc.
162
- current_period_start: z.number(),
163
- current_period_end: z.number(),
164
- items: z.object({
165
- data: z.array(
166
- z.object({
167
- id: z.string(),
168
- price: z.object({ id: z.string() }),
169
- }),
170
- ),
171
- }),
172
- });
173
-
174
- const subscription = await ctx.integrations.stripe.apiRequest(
175
- {
176
- method: "POST",
177
- path: "/v1/subscriptions",
178
- body: {
179
- customer: customerId,
180
- items: [{ price: "price_abc123" }],
181
- payment_behavior: "default_incomplete",
182
- expand: ["latest_invoice.payment_intent"],
183
- },
184
- },
185
- { response: SubscriptionSchema },
186
- );
187
- ```
188
-
189
- ### Create a Refund
190
-
191
- ```typescript
192
- const RefundSchema = z.object({
193
- id: z.string(),
194
- object: z.literal("refund"),
195
- amount: z.number(),
196
- charge: z.string(),
197
- status: z.string(), // succeeded, pending, failed, canceled
198
- created: z.number(),
199
- });
200
-
201
- const refund = await ctx.integrations.stripe.apiRequest(
202
- {
203
- method: "POST",
204
- path: "/v1/refunds",
205
- body: {
206
- charge: chargeId,
207
- amount: 1000, // Partial refund of $10.00
208
- reason: "requested_by_customer",
209
- },
210
- },
211
- { response: RefundSchema },
212
- );
213
- ```
214
-
215
66
  ### List Products
216
67
 
217
68
  ```typescript
@@ -243,58 +94,6 @@ const products = await ctx.integrations.stripe.apiRequest(
243
94
  );
244
95
  ```
245
96
 
246
- ### Create Invoice
247
-
248
- ```typescript
249
- const InvoiceSchema = z.object({
250
- id: z.string(),
251
- object: z.literal("invoice"),
252
- customer: z.string(),
253
- status: z.string(), // draft, open, paid, uncollectible, void
254
- total: z.number(),
255
- amount_due: z.number(),
256
- hosted_invoice_url: z.string().nullable(),
257
- });
258
-
259
- const invoice = await ctx.integrations.stripe.apiRequest(
260
- {
261
- method: "POST",
262
- path: "/v1/invoices",
263
- body: {
264
- customer: customerId,
265
- collection_method: "send_invoice",
266
- days_until_due: 30,
267
- },
268
- },
269
- { response: InvoiceSchema },
270
- );
271
-
272
- // Add line items
273
- await ctx.integrations.stripe.apiRequest(
274
- {
275
- method: "POST",
276
- path: "/v1/invoiceitems",
277
- body: {
278
- customer: customerId,
279
- invoice: invoice.id,
280
- amount: 5000,
281
- currency: "usd",
282
- description: "Consulting services",
283
- },
284
- },
285
- { response: z.object({ id: z.string() }) },
286
- );
287
-
288
- // Finalize and send
289
- await ctx.integrations.stripe.apiRequest(
290
- {
291
- method: "POST",
292
- path: `/v1/invoices/${invoice.id}/finalize`,
293
- },
294
- { response: InvoiceSchema },
295
- );
296
- ```
297
-
298
97
  ## Trace Metadata
299
98
 
300
99
  All methods accept an optional `metadata` parameter as the last argument for diagnostics labeling. See the [root SDK README](../../../README.md#trace-metadata) for details.
@@ -1,5 +1,14 @@
1
1
  {
2
2
  "pluginId": "stripe",
3
3
  "base": "README.md",
4
- "overlays": []
4
+ "overlays": [
5
+ {
6
+ "file": "overlays/request-bodies.md",
7
+ "sdkVersionRange": ">=0.0.7"
8
+ },
9
+ {
10
+ "file": "overlays/request-bodies-unsupported.md",
11
+ "sdkVersionRange": "<0.0.7"
12
+ }
13
+ ]
5
14
  }
@@ -0,0 +1,3 @@
1
+ ## Creating Customers, Payment Intents, Subscriptions, Refunds, and Invoices
2
+
3
+ Stripe rejects any request whose body is not `application/x-www-form-urlencoded`. This agent's sdk-api cannot send that encoding through `apiRequest()` because it serializes every body as JSON, so creating a customer, payment intent, subscription, refund, or invoice always fails. Do not generate Stripe write code until the agent is upgraded. Getting a customer, listing charges, and listing products work as documented above; they send no body.
@@ -0,0 +1,204 @@
1
+ ## Creating Customers, Payment Intents, Subscriptions, Refunds, and Invoices
2
+
3
+ Stripe expects `application/x-www-form-urlencoded` bodies, not JSON, so pass `bodyType: "formUrlencoded"` on every write request. Flatten nested fields with bracket notation, such as `"metadata[order_id]"`. An array field whose API name ends in `[]`, such as `expand`, needs the brackets in the key: `"expand[]": [...]`. The SDK adds `Content-Type: application/x-www-form-urlencoded` unless you already provided a `Content-Type` header.
4
+
5
+ ### Create a Customer
6
+
7
+ ```typescript
8
+ import { api, z, stripe } from "@superblocksteam/sdk-api";
9
+
10
+ // Integration ID from the integrations panel
11
+ const PROD_STRIPE = "a1b2c3d4-5678-90ab-cdef-stripe000001";
12
+
13
+ const CustomerSchema = z.object({
14
+ id: z.string(),
15
+ object: z.literal("customer"),
16
+ email: z.string().nullable(),
17
+ name: z.string().nullable(),
18
+ created: z.number(),
19
+ metadata: z.record(z.string()),
20
+ default_source: z.string().nullable(),
21
+ });
22
+
23
+ export default api({
24
+ name: "StripeExample",
25
+ integrations: {
26
+ stripe: stripe(PROD_STRIPE),
27
+ },
28
+ input: z.object({
29
+ email: z.string().email(),
30
+ name: z.string(),
31
+ }),
32
+ output: z.object({
33
+ customerId: z.string(),
34
+ }),
35
+ async run(ctx, { email, name }) {
36
+ const result = await ctx.integrations.stripe.apiRequest(
37
+ {
38
+ method: "POST",
39
+ path: "/v1/customers",
40
+ bodyType: "formUrlencoded",
41
+ body: {
42
+ email: email,
43
+ name: name,
44
+ "metadata[source]": "api",
45
+ },
46
+ },
47
+ { response: CustomerSchema },
48
+ );
49
+
50
+ return { customerId: result.id };
51
+ },
52
+ });
53
+ ```
54
+
55
+ ### Create a Payment Intent
56
+
57
+ ```typescript
58
+ const PaymentIntentSchema = z.object({
59
+ id: z.string(),
60
+ object: z.literal("payment_intent"),
61
+ amount: z.number(),
62
+ currency: z.string(),
63
+ status: z.string(),
64
+ client_secret: z.string(),
65
+ customer: z.string().nullable(),
66
+ metadata: z.record(z.string()),
67
+ });
68
+
69
+ const paymentIntent = await ctx.integrations.stripe.apiRequest(
70
+ {
71
+ method: "POST",
72
+ path: "/v1/payment_intents",
73
+ bodyType: "formUrlencoded",
74
+ body: {
75
+ amount: 2000, // Amount in cents ($20.00)
76
+ currency: "usd",
77
+ customer: customerId,
78
+ "automatic_payment_methods[enabled]": true,
79
+ "metadata[order_id]": "order_123",
80
+ },
81
+ },
82
+ { response: PaymentIntentSchema },
83
+ );
84
+
85
+ // Return client_secret to frontend for Stripe.js
86
+ console.log(`Client secret: ${paymentIntent.client_secret}`);
87
+ ```
88
+
89
+ ### Create a Subscription
90
+
91
+ ```typescript
92
+ const SubscriptionSchema = z.object({
93
+ id: z.string(),
94
+ object: z.literal("subscription"),
95
+ customer: z.string(),
96
+ status: z.string(), // active, past_due, canceled, etc.
97
+ current_period_start: z.number(),
98
+ current_period_end: z.number(),
99
+ items: z.object({
100
+ data: z.array(
101
+ z.object({
102
+ id: z.string(),
103
+ price: z.object({ id: z.string() }),
104
+ }),
105
+ ),
106
+ }),
107
+ });
108
+
109
+ const subscription = await ctx.integrations.stripe.apiRequest(
110
+ {
111
+ method: "POST",
112
+ path: "/v1/subscriptions",
113
+ bodyType: "formUrlencoded",
114
+ body: {
115
+ customer: customerId,
116
+ "items[0][price]": "price_abc123",
117
+ payment_behavior: "default_incomplete",
118
+ "expand[]": ["latest_invoice.payment_intent"],
119
+ },
120
+ },
121
+ { response: SubscriptionSchema },
122
+ );
123
+ ```
124
+
125
+ ### Create a Refund
126
+
127
+ ```typescript
128
+ const RefundSchema = z.object({
129
+ id: z.string(),
130
+ object: z.literal("refund"),
131
+ amount: z.number(),
132
+ charge: z.string(),
133
+ status: z.string(), // succeeded, pending, failed, canceled
134
+ created: z.number(),
135
+ });
136
+
137
+ const refund = await ctx.integrations.stripe.apiRequest(
138
+ {
139
+ method: "POST",
140
+ path: "/v1/refunds",
141
+ bodyType: "formUrlencoded",
142
+ body: {
143
+ charge: chargeId,
144
+ amount: 1000, // Partial refund of $10.00
145
+ reason: "requested_by_customer",
146
+ },
147
+ },
148
+ { response: RefundSchema },
149
+ );
150
+ ```
151
+
152
+ ### Create Invoice
153
+
154
+ ```typescript
155
+ const InvoiceSchema = z.object({
156
+ id: z.string(),
157
+ object: z.literal("invoice"),
158
+ customer: z.string(),
159
+ status: z.string(), // draft, open, paid, uncollectible, void
160
+ total: z.number(),
161
+ amount_due: z.number(),
162
+ hosted_invoice_url: z.string().nullable(),
163
+ });
164
+
165
+ const invoice = await ctx.integrations.stripe.apiRequest(
166
+ {
167
+ method: "POST",
168
+ path: "/v1/invoices",
169
+ bodyType: "formUrlencoded",
170
+ body: {
171
+ customer: customerId,
172
+ collection_method: "send_invoice",
173
+ days_until_due: 30,
174
+ },
175
+ },
176
+ { response: InvoiceSchema },
177
+ );
178
+
179
+ // Add line items
180
+ await ctx.integrations.stripe.apiRequest(
181
+ {
182
+ method: "POST",
183
+ path: "/v1/invoiceitems",
184
+ bodyType: "formUrlencoded",
185
+ body: {
186
+ customer: customerId,
187
+ invoice: invoice.id,
188
+ amount: 5000,
189
+ currency: "usd",
190
+ description: "Consulting services",
191
+ },
192
+ },
193
+ { response: z.object({ id: z.string() }) },
194
+ );
195
+
196
+ // Finalize and send; this call has no body
197
+ await ctx.integrations.stripe.apiRequest(
198
+ {
199
+ method: "POST",
200
+ path: `/v1/invoices/${invoice.id}/finalize`,
201
+ },
202
+ { response: InvoiceSchema },
203
+ );
204
+ ```
@@ -1,7 +1,23 @@
1
+ import { z } from "zod";
2
+
3
+ import { isBinaryData, RestApiValidationError } from "../errors.js";
4
+
5
+ const MAX_OPTION_VALUE_CHARS = 64;
6
+
1
7
  /**
2
8
  * Integration utility functions.
3
9
  */
4
10
 
11
+ /**
12
+ * Truncates a string to at most `max` characters, appending "..." when it
13
+ * was cut. The single place that defines how caller-supplied strings are
14
+ * bounded for display (`describeType`, `describeOptionValue`) and for the
15
+ * Zod issue `path` (`boundedPathSegment`).
16
+ */
17
+ function truncate(value: string, max = MAX_OPTION_VALUE_CHARS): string {
18
+ return value.length > max ? `${value.slice(0, max)}...` : value;
19
+ }
20
+
5
21
  /**
6
22
  * Produces a safe, human-readable description of a value's type
7
23
  * without exposing potentially sensitive data.
@@ -18,8 +34,42 @@ export function describeType(value: unknown): string {
18
34
  if (t === "object") {
19
35
  const keys = Object.keys(value as Record<string, unknown>);
20
36
  if (keys.length === 0) return "empty object";
21
- return `object with keys [${keys.slice(0, 5).join(", ")}${keys.length > 5 ? ", ..." : ""}]`;
37
+ const shown = keys.slice(0, 5).map((k) => truncate(k));
38
+ return `object with keys [${shown.join(", ")}${keys.length > 5 ? ", ..." : ""}]`;
22
39
  }
23
40
 
24
41
  return t;
25
42
  }
43
+
44
+ /**
45
+ * Describes a caller-supplied option value for an error message with bounded
46
+ * length. Strings are quoted and truncated; everything else is described by type
47
+ * so arbitrary payloads never land in the message.
48
+ */
49
+ export function describeOptionValue(value: unknown): string {
50
+ if (isBinaryData(value)) {
51
+ return `binary data (${value.byteLength} bytes)`;
52
+ }
53
+ if (typeof value !== "string") return describeType(value);
54
+ return JSON.stringify(truncate(value));
55
+ }
56
+
57
+ function boundedPathSegment(segment: string | number): string | number {
58
+ return typeof segment === "string" ? truncate(segment) : segment;
59
+ }
60
+
61
+ export function validationError(
62
+ message: string,
63
+ path: (string | number)[],
64
+ ): RestApiValidationError {
65
+ return new RestApiValidationError(message, {
66
+ data: undefined,
67
+ zodError: new z.ZodError([
68
+ {
69
+ code: z.ZodIssueCode.custom,
70
+ message,
71
+ path: path.map(boundedPathSegment),
72
+ },
73
+ ]),
74
+ });
75
+ }