@automate.ax/integration-contracts 0.152.5 → 0.153.1

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.
@@ -57,9 +57,9 @@ export declare const OUTLOOK_MESSAGE_CREATED_EVENT_SCHEMA: z.ZodObject<{
57
57
  normal: "normal";
58
58
  }>>;
59
59
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
60
- focused: "focused";
61
60
  other: "other";
62
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
61
+ focused: "focused";
62
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
63
63
  isDraft: z.ZodPrefault<z.ZodBoolean>;
64
64
  isRead: z.ZodPrefault<z.ZodBoolean>;
65
65
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -168,9 +168,9 @@ export declare const OUTLOOK_MESSAGE_UPDATED_EVENT_SCHEMA: z.ZodObject<{
168
168
  normal: "normal";
169
169
  }>>;
170
170
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
171
- focused: "focused";
172
171
  other: "other";
173
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
172
+ focused: "focused";
173
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
174
174
  isDraft: z.ZodPrefault<z.ZodBoolean>;
175
175
  isRead: z.ZodPrefault<z.ZodBoolean>;
176
176
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -284,9 +284,9 @@ export declare const OUTLOOK_MESSAGE_CHANGE_EVENT_SCHEMA: z.ZodDiscriminatedUnio
284
284
  normal: "normal";
285
285
  }>>;
286
286
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
287
- focused: "focused";
288
287
  other: "other";
289
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
288
+ focused: "focused";
289
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
290
290
  isDraft: z.ZodPrefault<z.ZodBoolean>;
291
291
  isRead: z.ZodPrefault<z.ZodBoolean>;
292
292
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -394,9 +394,9 @@ export declare const OUTLOOK_MESSAGE_CHANGE_EVENT_SCHEMA: z.ZodDiscriminatedUnio
394
394
  normal: "normal";
395
395
  }>>;
396
396
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
397
- focused: "focused";
398
397
  other: "other";
399
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
398
+ focused: "focused";
399
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
400
400
  isDraft: z.ZodPrefault<z.ZodBoolean>;
401
401
  isRead: z.ZodPrefault<z.ZodBoolean>;
402
402
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -510,9 +510,9 @@ export declare const outlookTriggerContracts: {
510
510
  normal: "normal";
511
511
  }>>;
512
512
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
513
- focused: "focused";
514
513
  other: "other";
515
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
514
+ focused: "focused";
515
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
516
516
  isDraft: z.ZodPrefault<z.ZodBoolean>;
517
517
  isRead: z.ZodPrefault<z.ZodBoolean>;
518
518
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -623,9 +623,9 @@ export declare const outlookTriggerContracts: {
623
623
  normal: "normal";
624
624
  }>>;
625
625
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
626
- focused: "focused";
627
626
  other: "other";
628
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
627
+ focused: "focused";
628
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
629
629
  isDraft: z.ZodPrefault<z.ZodBoolean>;
630
630
  isRead: z.ZodPrefault<z.ZodBoolean>;
631
631
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -733,9 +733,9 @@ export declare const outlookTriggerContracts: {
733
733
  normal: "normal";
734
734
  }>>;
735
735
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
736
- focused: "focused";
737
736
  other: "other";
738
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
737
+ focused: "focused";
738
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
739
739
  isDraft: z.ZodPrefault<z.ZodBoolean>;
740
740
  isRead: z.ZodPrefault<z.ZodBoolean>;
741
741
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -853,9 +853,9 @@ export declare const outlookTriggerContracts: {
853
853
  normal: "normal";
854
854
  }>>;
855
855
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
856
- focused: "focused";
857
856
  other: "other";
858
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
857
+ focused: "focused";
858
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
859
859
  isDraft: z.ZodPrefault<z.ZodBoolean>;
860
860
  isRead: z.ZodPrefault<z.ZodBoolean>;
861
861
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -969,9 +969,9 @@ export declare const outlookTriggerContracts: {
969
969
  normal: "normal";
970
970
  }>>;
971
971
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
972
- focused: "focused";
973
972
  other: "other";
974
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
973
+ focused: "focused";
974
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
975
975
  isDraft: z.ZodPrefault<z.ZodBoolean>;
976
976
  isRead: z.ZodPrefault<z.ZodBoolean>;
977
977
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -88,9 +88,9 @@ export declare const OUTLOOK_MESSAGE_SCHEMA: z.ZodObject<{
88
88
  normal: "normal";
89
89
  }>>;
90
90
  inferenceClassification: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
91
- focused: "focused";
92
91
  other: "other";
93
- }>>>, z.ZodTransform<"focused" | "other" | undefined, "focused" | "other" | null | undefined>>;
92
+ focused: "focused";
93
+ }>>>, z.ZodTransform<"other" | "focused" | undefined, "other" | "focused" | null | undefined>>;
94
94
  isDraft: z.ZodPrefault<z.ZodBoolean>;
95
95
  isRead: z.ZodPrefault<z.ZodBoolean>;
96
96
  modifiedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>>, z.ZodTransform<Date | undefined, Date | null | undefined>>;
@@ -201,8 +201,8 @@ export declare const GRAPH_MESSAGE_SCHEMA: z.ZodPipe<z.ZodObject<{
201
201
  normal: "normal";
202
202
  }>>;
203
203
  inferenceClassification: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
204
- focused: "focused";
205
204
  other: "other";
205
+ focused: "focused";
206
206
  }>>>;
207
207
  internetMessageId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
208
208
  isDraft: z.ZodOptional<z.ZodBoolean>;
@@ -243,7 +243,7 @@ export declare const GRAPH_MESSAGE_SCHEMA: z.ZodPipe<z.ZodObject<{
243
243
  messageId: string;
244
244
  internetMessageId: string | undefined;
245
245
  importance: "high" | "low" | "normal";
246
- inferenceClassification: "focused" | "other" | undefined;
246
+ inferenceClassification: "other" | "focused" | undefined;
247
247
  isDraft: boolean;
248
248
  isRead: boolean;
249
249
  modifiedAt: Date | undefined;
@@ -332,7 +332,7 @@ export declare const GRAPH_MESSAGE_SCHEMA: z.ZodPipe<z.ZodObject<{
332
332
  } | null | undefined;
333
333
  hasAttachments?: boolean | undefined;
334
334
  importance?: "high" | "low" | "normal" | undefined;
335
- inferenceClassification?: "focused" | "other" | null | undefined;
335
+ inferenceClassification?: "other" | "focused" | null | undefined;
336
336
  internetMessageId?: string | null | undefined;
337
337
  isDraft?: boolean | undefined;
338
338
  isRead?: boolean | undefined;
@@ -378,7 +378,7 @@ export declare function normalizeOutlookMessage(payload: unknown): {
378
378
  messageId: string;
379
379
  internetMessageId: string | undefined;
380
380
  importance: "high" | "low" | "normal";
381
- inferenceClassification: "focused" | "other" | undefined;
381
+ inferenceClassification: "other" | "focused" | undefined;
382
382
  isDraft: boolean;
383
383
  isRead: boolean;
384
384
  modifiedAt: Date | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automate.ax/integration-contracts",
3
- "version": "0.152.5",
3
+ "version": "0.153.1",
4
4
  "description": "Shared integration payload contracts and provider primitives for Automate.ax.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@anthropic-ai/sdk": "0.123.0",
75
- "@automate.ax/codec": "0.152.5",
75
+ "@automate.ax/codec": "0.153.1",
76
76
  "@cfworker/json-schema": "^4.1.1",
77
77
  "@googleapis/calendar": "^16.0.0",
78
78
  "@googleapis/forms": "^6.0.1",
@@ -53,16 +53,40 @@ export interface CloudflareRequestOptions<TSchema extends z.ZodType> {
53
53
  /** Public camelCase JSON body. */
54
54
  body?: Encodable
55
55
 
56
+ /** Provider request-key convention. Defaults to Cloudflare snake_case. */
57
+ keyFormat?: "browser-rendering"
58
+
56
59
  /** HTTP verb. Defaults to `GET`. */
57
60
  method?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT"
58
61
 
59
62
  /** Public camelCase query parameters. */
60
63
  query?: Record<string, CloudflareQueryValue>
61
64
 
65
+ /** Provider result-key convention. Defaults to recursive camelCase. */
66
+ responseKeyFormat?: "preserve"
67
+
62
68
  /** Schema for the normalized provider result. */
63
69
  responseSchema: TSchema
64
70
  }
65
71
 
72
+ /** Options for one authenticated Cloudflare binary REST request. */
73
+ export interface CloudflareBlobRequestOptions {
74
+ /** Expected response media type. */
75
+ accept: string
76
+
77
+ /** Public camelCase JSON body. */
78
+ body?: Encodable
79
+
80
+ /** Provider request-key convention. Defaults to Cloudflare snake_case. */
81
+ keyFormat?: "browser-rendering"
82
+
83
+ /** HTTP verb. Defaults to `GET`. */
84
+ method?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT"
85
+
86
+ /** Public camelCase query parameters. */
87
+ query?: Record<string, CloudflareQueryValue>
88
+ }
89
+
66
90
  /** One normalized Cloudflare result page. */
67
91
  export interface CloudflarePage<T> {
68
92
  result: T
@@ -166,6 +190,64 @@ export function getCloudflareApi(account: CloudflareResolvedAccount) {
166
190
  path: string,
167
191
  options: CloudflareRequestOptions<TSchema>,
168
192
  ): Promise<CloudflarePage<z.output<TSchema>>> {
193
+ const response = await runRequest(path, options, "application/json")
194
+ const parsed = CLOUDFLARE_ENVELOPE_SCHEMA.safeParse(
195
+ parseJson(await response.text()),
196
+ )
197
+ if (!response.ok || !parsed.success || !parsed.data.success) {
198
+ throw createApiError(response, parsed.success ? parsed.data.errors : [])
199
+ }
200
+ return {
201
+ result: options.responseSchema.parse(
202
+ options.responseKeyFormat === "preserve"
203
+ ? convertFromCloudflare(parsed.data.result, true)
204
+ : fromCloudflare(parsed.data.result),
205
+ ),
206
+ ...(parsed.data.result_info && {
207
+ resultInfo: CLOUDFLARE_RESULT_INFO_SCHEMA.parse(
208
+ fromCloudflare(parsed.data.result_info),
209
+ ),
210
+ }),
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Runs one request and returns a binary response body.
216
+ *
217
+ * @param path - Cloudflare API path relative to the v4 root.
218
+ * @param options - Method, parameters, and expected media type.
219
+ */
220
+ async function requestBlob(
221
+ path: string,
222
+ options: CloudflareBlobRequestOptions,
223
+ ): Promise<Blob> {
224
+ const response = await runRequest(path, options, options.accept)
225
+ if (
226
+ !response.ok ||
227
+ response.headers.get("Content-Type")?.includes("application/json")
228
+ ) {
229
+ const parsed = CLOUDFLARE_ENVELOPE_SCHEMA.safeParse(
230
+ parseJson(await response.text()),
231
+ )
232
+ if (!response.ok || !parsed.success || !parsed.data.success)
233
+ throw createApiError(response, parsed.success ? parsed.data.errors : [])
234
+ throw new Error("Cloudflare returned JSON instead of binary content.")
235
+ }
236
+ return response.blob()
237
+ }
238
+
239
+ /**
240
+ * Runs one authenticated Cloudflare request.
241
+ *
242
+ * @param path - Cloudflare API path relative to the v4 root.
243
+ * @param options - Method, body, and query parameters.
244
+ * @param accept - Expected response media type.
245
+ */
246
+ async function runRequest(
247
+ path: string,
248
+ options: Omit<CloudflareBlobRequestOptions, "accept">,
249
+ accept: string,
250
+ ) {
169
251
  const url = new URL(path.replace(/^\//, ""), CLOUDFLARE_API_BASE_URL)
170
252
  if (url.origin !== CLOUDFLARE_API_ORIGIN) {
171
253
  throw new Error(
@@ -174,15 +256,24 @@ export function getCloudflareApi(account: CloudflareResolvedAccount) {
174
256
  }
175
257
  for (const [name, value] of Object.entries(options.query ?? {})) {
176
258
  if (value !== undefined)
177
- url.searchParams.set(toSnakeCase(name), String(value))
259
+ url.searchParams.set(
260
+ options.keyFormat === "browser-rendering"
261
+ ? toBrowserRenderingQueryKey(name)
262
+ : toSnakeCase(name),
263
+ String(value),
264
+ )
178
265
  }
179
- const response = await fetch(url, {
266
+ return fetch(url, {
180
267
  body:
181
268
  options.body === undefined
182
269
  ? undefined
183
- : JSON.stringify(toCloudflare(options.body)),
270
+ : JSON.stringify(
271
+ options.keyFormat === "browser-rendering"
272
+ ? toCloudflareBrowserRendering(options.body)
273
+ : toCloudflare(options.body),
274
+ ),
184
275
  headers: {
185
- Accept: "application/json",
276
+ Accept: accept,
186
277
  Authorization: `Bearer ${accessToken}`,
187
278
  ...(options.body === undefined
188
279
  ? {}
@@ -190,29 +281,28 @@ export function getCloudflareApi(account: CloudflareResolvedAccount) {
190
281
  },
191
282
  method: options.method ?? "GET",
192
283
  })
193
- const parsed = CLOUDFLARE_ENVELOPE_SCHEMA.safeParse(
194
- parseJson(await response.text()),
195
- )
196
- if (!response.ok || !parsed.success || !parsed.data.success) {
197
- throw new CloudflareApiError({
198
- errors: parsed.success ? parsed.data.errors : undefined,
199
- rateLimit: response.headers.get("Ratelimit") ?? undefined,
200
- rateLimitPolicy: response.headers.get("Ratelimit-Policy") ?? undefined,
201
- retryAfter: parseFiniteNumber(response.headers.get("Retry-After")),
202
- status: response.status,
203
- })
204
- }
205
- return {
206
- result: options.responseSchema.parse(fromCloudflare(parsed.data.result)),
207
- ...(parsed.data.result_info && {
208
- resultInfo: CLOUDFLARE_RESULT_INFO_SCHEMA.parse(
209
- fromCloudflare(parsed.data.result_info),
210
- ),
211
- }),
212
- }
213
284
  }
214
285
 
215
- return { request, requestPage }
286
+ return { request, requestBlob, requestPage }
287
+ }
288
+
289
+ /**
290
+ * Creates a structured error from one Cloudflare response.
291
+ *
292
+ * @param response - Failed Cloudflare response.
293
+ * @param errors - Parsed provider errors.
294
+ */
295
+ function createApiError(
296
+ response: Response,
297
+ errors: z.output<typeof CLOUDFLARE_RESPONSE_INFO_SCHEMA>[],
298
+ ) {
299
+ return new CloudflareApiError({
300
+ errors,
301
+ rateLimit: response.headers.get("Ratelimit") ?? undefined,
302
+ rateLimitPolicy: response.headers.get("Ratelimit-Policy") ?? undefined,
303
+ retryAfter: parseFiniteNumber(response.headers.get("Retry-After")),
304
+ status: response.status,
305
+ })
216
306
  }
217
307
 
218
308
  /**
@@ -224,6 +314,42 @@ export function toCloudflare(value: Encodable): Encodable {
224
314
  return convertToCloudflare(value, false)
225
315
  }
226
316
 
317
+ /**
318
+ * Converts public JSON to Cloudflare Browser Run's mixed wire keys.
319
+ *
320
+ * @param value - Codec-safe public value.
321
+ */
322
+ export function toCloudflareBrowserRendering(value: Encodable): Encodable {
323
+ return convertToCloudflareBrowserRendering(value, false)
324
+ }
325
+
326
+ /**
327
+ * Converts one Browser Run value while optionally preserving arbitrary keys.
328
+ *
329
+ * @param value - Public value to convert.
330
+ * @param preserveKeys - Whether this object is a user-defined map.
331
+ */
332
+ function convertToCloudflareBrowserRendering(
333
+ value: Encodable,
334
+ preserveKeys: boolean,
335
+ ): Encodable {
336
+ if (value instanceof Date) return value.toISOString()
337
+ if (Array.isArray(value))
338
+ return value.map((item) =>
339
+ convertToCloudflareBrowserRendering(item, preserveKeys),
340
+ )
341
+ if (!isPlainObject(value)) return value
342
+ return Object.fromEntries(
343
+ Object.entries(value).map(([key, item]) => [
344
+ preserveKeys ? key : toBrowserRenderingKey(key),
345
+ convertToCloudflareBrowserRendering(
346
+ item,
347
+ preserveKeys || key === "jsonSchema" || key === "setExtraHttpHeaders",
348
+ ),
349
+ ]),
350
+ )
351
+ }
352
+
227
353
  /**
228
354
  * Converts one value while optionally preserving object keys.
229
355
  *
@@ -241,7 +367,7 @@ function convertToCloudflare(
241
367
  return Object.fromEntries(
242
368
  Object.entries(value).map(([key, item]) => [
243
369
  preserveKeys ? key : toSnakeCase(key),
244
- convertToCloudflare(item, key === "header" || key === "headers"),
370
+ convertToCloudflare(item, preserveKeys || preservesNestedKeys(key)),
245
371
  ]),
246
372
  )
247
373
  }
@@ -271,11 +397,28 @@ function convertFromCloudflare(
271
397
  return Object.fromEntries(
272
398
  Object.entries(value).map(([key, item]) => [
273
399
  preserveKeys ? key : toCamelCase(key),
274
- convertFromCloudflare(item, key === "header" || key === "headers"),
400
+ convertFromCloudflare(item, preserveKeys || preservesNestedKeys(key)),
275
401
  ]),
276
402
  )
277
403
  }
278
404
 
405
+ /**
406
+ * Returns whether a provider-defined object map must retain its keys.
407
+ *
408
+ * @param key - Public or provider parent key.
409
+ */
410
+ function preservesNestedKeys(key: string) {
411
+ return [
412
+ "header",
413
+ "headers",
414
+ "json",
415
+ "jsonSchema",
416
+ "json_schema",
417
+ "setExtraHttpHeaders",
418
+ "set_extra_http_headers",
419
+ ].includes(key)
420
+ }
421
+
279
422
  const CLOUDFLARE_VALUE_SCHEMA = z.union([
280
423
  z.boolean(),
281
424
  z.number(),
@@ -338,3 +481,25 @@ function toSnakeCase(value: string) {
338
481
  .replace(/([a-zA-Z])([0-9])/g, "$1_$2")
339
482
  .replace(/[A-Z]/g, (character) => `_${character.toLowerCase()}`)
340
483
  }
484
+
485
+ /**
486
+ * Converts a public query key to Cloudflare's endpoint spelling.
487
+ *
488
+ * @param value - Public query key.
489
+ */
490
+ function toBrowserRenderingQueryKey(value: string) {
491
+ return value === "cacheTtl" ? "cacheTTL" : toSnakeCase(value)
492
+ }
493
+
494
+ /**
495
+ * Converts one public Browser Run body key to its exact provider spelling.
496
+ *
497
+ * @param value - Public key.
498
+ */
499
+ function toBrowserRenderingKey(value: string) {
500
+ if (value === "customAi") return "custom_ai"
501
+ if (value === "jsonSchema") return "json_schema"
502
+ if (value === "responseFormat") return "response_format"
503
+ if (value === "setExtraHttpHeaders") return "setExtraHTTPHeaders"
504
+ return value
505
+ }