@lovrozagar/honey 0.1.0 → 0.2.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 (263) hide show
  1. package/README.md +1596 -6
  2. package/dist/accepts.d.ts +6 -0
  3. package/dist/accepts.d.ts.map +1 -0
  4. package/dist/body-limit.d.ts +22 -0
  5. package/dist/body-limit.d.ts.map +1 -0
  6. package/dist/build/index.d.ts +42 -0
  7. package/dist/build/index.d.ts.map +1 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/client/error.d.ts +14 -0
  11. package/dist/client/error.d.ts.map +1 -0
  12. package/dist/client/http.d.ts +88 -0
  13. package/dist/client/http.d.ts.map +1 -0
  14. package/dist/client/index.d.ts +22 -0
  15. package/dist/client/index.d.ts.map +1 -0
  16. package/dist/client/sdk.d.ts +34 -0
  17. package/dist/client/sdk.d.ts.map +1 -0
  18. package/dist/client/sse.d.ts +10 -0
  19. package/dist/client/sse.d.ts.map +1 -0
  20. package/dist/client/types.d.ts +124 -0
  21. package/dist/client/types.d.ts.map +1 -0
  22. package/dist/client/ws.d.ts +15 -0
  23. package/dist/client/ws.d.ts.map +1 -0
  24. package/dist/client-mcp/server.d.ts +10 -0
  25. package/dist/client-mcp/server.d.ts.map +1 -0
  26. package/dist/client-mcp/types.d.ts +16 -0
  27. package/dist/client-mcp/types.d.ts.map +1 -0
  28. package/dist/cloudflare/realtime.d.ts +30 -0
  29. package/dist/cloudflare/realtime.d.ts.map +1 -0
  30. package/dist/codegen-go-cli.d.ts +22 -0
  31. package/dist/codegen-go-cli.d.ts.map +1 -0
  32. package/dist/codegen-go.d.ts +29 -0
  33. package/dist/codegen-go.d.ts.map +1 -0
  34. package/dist/codegen-hash.d.ts +3 -0
  35. package/dist/codegen-hash.d.ts.map +1 -0
  36. package/dist/codegen-ir.d.ts +123 -0
  37. package/dist/codegen-ir.d.ts.map +1 -0
  38. package/dist/codegen-loaders.d.ts +12 -0
  39. package/dist/codegen-loaders.d.ts.map +1 -0
  40. package/dist/codegen-mcp.d.ts +13 -0
  41. package/dist/codegen-mcp.d.ts.map +1 -0
  42. package/dist/codegen-python.d.ts +21 -0
  43. package/dist/codegen-python.d.ts.map +1 -0
  44. package/dist/codegen-rust.d.ts +18 -0
  45. package/dist/codegen-rust.d.ts.map +1 -0
  46. package/dist/codegen-sanitize.d.ts +3 -0
  47. package/dist/codegen-sanitize.d.ts.map +1 -0
  48. package/dist/codegen-schema-naming.d.ts +22 -0
  49. package/dist/codegen-schema-naming.d.ts.map +1 -0
  50. package/dist/codegen.d.ts +178 -0
  51. package/dist/codegen.d.ts.map +1 -0
  52. package/dist/context.d.ts +56 -0
  53. package/dist/context.d.ts.map +1 -0
  54. package/dist/cookie-sign.d.ts +3 -0
  55. package/dist/cookie-sign.d.ts.map +1 -0
  56. package/dist/cookie.d.ts +12 -0
  57. package/dist/cookie.d.ts.map +1 -0
  58. package/dist/cors.d.ts +13 -0
  59. package/dist/cors.d.ts.map +1 -0
  60. package/dist/crypto.d.ts +2 -0
  61. package/dist/crypto.d.ts.map +1 -0
  62. package/dist/csrf.d.ts +9 -0
  63. package/dist/csrf.d.ts.map +1 -0
  64. package/dist/curl-logger.d.ts +33 -0
  65. package/dist/curl-logger.d.ts.map +1 -0
  66. package/dist/detect-runtime.d.ts +16 -0
  67. package/dist/detect-runtime.d.ts.map +1 -0
  68. package/dist/error.d.ts +21 -0
  69. package/dist/error.d.ts.map +1 -0
  70. package/dist/errors.d.ts +40 -0
  71. package/dist/errors.d.ts.map +1 -0
  72. package/dist/etag.d.ts +9 -0
  73. package/dist/etag.d.ts.map +1 -0
  74. package/dist/feature-load.d.ts +3 -0
  75. package/dist/feature-load.d.ts.map +1 -0
  76. package/dist/go-type-emitter.d.ts +33 -0
  77. package/dist/go-type-emitter.d.ts.map +1 -0
  78. package/dist/honey-response.d.ts +82 -0
  79. package/dist/honey-response.d.ts.map +1 -0
  80. package/dist/i18n-slot.d.ts +7 -0
  81. package/dist/i18n-slot.d.ts.map +1 -0
  82. package/dist/i18n.d.ts +26 -0
  83. package/dist/i18n.d.ts.map +1 -0
  84. package/dist/index.d.ts +470 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/init.d.ts +7 -0
  87. package/dist/init.d.ts.map +1 -0
  88. package/dist/input.d.ts +6 -0
  89. package/dist/input.d.ts.map +1 -0
  90. package/dist/ip-restrict.d.ts +12 -0
  91. package/dist/ip-restrict.d.ts.map +1 -0
  92. package/dist/logger.d.ts +45 -0
  93. package/dist/logger.d.ts.map +1 -0
  94. package/dist/meta-spec.d.ts +81 -0
  95. package/dist/meta-spec.d.ts.map +1 -0
  96. package/dist/middleware.d.ts +60 -0
  97. package/dist/middleware.d.ts.map +1 -0
  98. package/dist/node-request.d.ts +59 -0
  99. package/dist/node-request.d.ts.map +1 -0
  100. package/dist/node.d.ts +13 -0
  101. package/dist/node.d.ts.map +1 -0
  102. package/dist/openapi/register.d.ts +3 -0
  103. package/dist/openapi/register.d.ts.map +1 -0
  104. package/dist/openapi/scalar.d.ts +12 -0
  105. package/dist/openapi/scalar.d.ts.map +1 -0
  106. package/dist/openapi/spec-factory.d.ts +31 -0
  107. package/dist/openapi/spec-factory.d.ts.map +1 -0
  108. package/dist/openapi/spec.d.ts +19 -0
  109. package/dist/openapi/spec.d.ts.map +1 -0
  110. package/dist/openapi/swagger.d.ts +12 -0
  111. package/dist/openapi/swagger.d.ts.map +1 -0
  112. package/dist/plugin.d.ts +167 -0
  113. package/dist/plugin.d.ts.map +1 -0
  114. package/dist/powered-by.d.ts +7 -0
  115. package/dist/powered-by.d.ts.map +1 -0
  116. package/dist/pretty-json.d.ts +10 -0
  117. package/dist/pretty-json.d.ts.map +1 -0
  118. package/dist/proxy.d.ts +51 -0
  119. package/dist/proxy.d.ts.map +1 -0
  120. package/dist/python-type-emitter.d.ts +11 -0
  121. package/dist/python-type-emitter.d.ts.map +1 -0
  122. package/dist/realtime/buffer.d.ts +18 -0
  123. package/dist/realtime/buffer.d.ts.map +1 -0
  124. package/dist/realtime/bus.d.ts +11 -0
  125. package/dist/realtime/bus.d.ts.map +1 -0
  126. package/dist/realtime/longpoll.d.ts +23 -0
  127. package/dist/realtime/longpoll.d.ts.map +1 -0
  128. package/dist/realtime/protocol.d.ts +50 -0
  129. package/dist/realtime/protocol.d.ts.map +1 -0
  130. package/dist/realtime/route.d.ts +42 -0
  131. package/dist/realtime/route.d.ts.map +1 -0
  132. package/dist/realtime/runtime.d.ts +74 -0
  133. package/dist/realtime/runtime.d.ts.map +1 -0
  134. package/dist/request-id.d.ts +12 -0
  135. package/dist/request-id.d.ts.map +1 -0
  136. package/dist/request-to-curl.d.ts +9 -0
  137. package/dist/request-to-curl.d.ts.map +1 -0
  138. package/dist/response.d.ts +57 -0
  139. package/dist/response.d.ts.map +1 -0
  140. package/dist/rust-type-emitter.d.ts +32 -0
  141. package/dist/rust-type-emitter.d.ts.map +1 -0
  142. package/dist/secure-headers.d.ts +16 -0
  143. package/dist/secure-headers.d.ts.map +1 -0
  144. package/dist/serve-register.d.ts +2 -0
  145. package/dist/serve-register.d.ts.map +1 -0
  146. package/dist/serve-slot.d.ts +6 -0
  147. package/dist/serve-slot.d.ts.map +1 -0
  148. package/dist/serve.d.ts +21 -0
  149. package/dist/serve.d.ts.map +1 -0
  150. package/dist/server-timing.d.ts +9 -0
  151. package/dist/server-timing.d.ts.map +1 -0
  152. package/dist/static.d.ts +12 -0
  153. package/dist/static.d.ts.map +1 -0
  154. package/dist/telemetry/otel.d.ts +59 -0
  155. package/dist/telemetry/otel.d.ts.map +1 -0
  156. package/dist/testing.d.ts +24 -0
  157. package/dist/testing.d.ts.map +1 -0
  158. package/dist/timeout.d.ts +7 -0
  159. package/dist/timeout.d.ts.map +1 -0
  160. package/dist/tree.d.ts +90 -0
  161. package/dist/tree.d.ts.map +1 -0
  162. package/dist/ts-type-emitter.d.ts +4 -0
  163. package/dist/ts-type-emitter.d.ts.map +1 -0
  164. package/dist/type-emitter.d.ts +8 -0
  165. package/dist/type-emitter.d.ts.map +1 -0
  166. package/dist/type-extractor.d.ts +39 -0
  167. package/dist/type-extractor.d.ts.map +1 -0
  168. package/dist/types.d.ts +451 -0
  169. package/dist/types.d.ts.map +1 -0
  170. package/dist/validation.d.ts +18 -0
  171. package/dist/validation.d.ts.map +1 -0
  172. package/dist/ws/bun.d.ts +28 -0
  173. package/dist/ws/bun.d.ts.map +1 -0
  174. package/dist/ws/cloudflare.d.ts +55 -0
  175. package/dist/ws/cloudflare.d.ts.map +1 -0
  176. package/dist/ws/deno.d.ts +13 -0
  177. package/dist/ws/deno.d.ts.map +1 -0
  178. package/dist/ws/node.d.ts +10 -0
  179. package/dist/ws/node.d.ts.map +1 -0
  180. package/dist/yaml.d.ts +4 -0
  181. package/dist/yaml.d.ts.map +1 -0
  182. package/docs/api-ref/go.md +82 -82
  183. package/docs/api-ref/python.md +82 -82
  184. package/docs/api-ref/rust.md +82 -82
  185. package/docs/api-ref/typescript.md +82 -82
  186. package/docs/invalidate-spec.md +13 -13
  187. package/docs/meta-spec.md +513 -0
  188. package/docs/packaging.md +85 -0
  189. package/docs/sdk.md +87 -73
  190. package/package.json +195 -51
  191. package/src/body-limit.ts +2 -1
  192. package/src/build/index.ts +2 -9
  193. package/src/cli.ts +2 -3
  194. package/src/client/http.ts +204 -223
  195. package/src/client/index.ts +6 -14
  196. package/src/client/sdk.ts +24 -17
  197. package/src/client/types.ts +4 -9
  198. package/src/client/ws.ts +3 -5
  199. package/src/client-mcp/server.ts +1 -4
  200. package/src/cloudflare/realtime.ts +8 -10
  201. package/src/codegen-go-cli.ts +74 -67
  202. package/src/codegen-go.ts +71 -51
  203. package/src/codegen-ir.ts +16 -27
  204. package/src/codegen-loaders.ts +5 -8
  205. package/src/codegen-mcp.ts +9 -27
  206. package/src/codegen-python.ts +47 -27
  207. package/src/codegen-rust.ts +110 -66
  208. package/src/codegen-sanitize.ts +1 -7
  209. package/src/codegen-schema-naming.ts +8 -2
  210. package/src/codegen.ts +299 -196
  211. package/src/context.ts +91 -95
  212. package/src/cookie-sign.ts +5 -13
  213. package/src/cookie.ts +1 -2
  214. package/src/cors.ts +4 -6
  215. package/src/crypto.ts +1 -4
  216. package/src/csrf.ts +3 -3
  217. package/src/curl-logger.ts +14 -32
  218. package/src/detect-runtime.ts +1 -3
  219. package/src/errors.ts +1 -3
  220. package/src/etag.ts +3 -4
  221. package/src/go-type-emitter.ts +45 -40
  222. package/src/honey-response.ts +1 -3
  223. package/src/i18n.ts +3 -12
  224. package/src/index.ts +1168 -1412
  225. package/src/ip-restrict.ts +2 -1
  226. package/src/logger.ts +13 -13
  227. package/src/meta-spec.ts +0 -0
  228. package/src/middleware.ts +111 -79
  229. package/src/node-request.ts +2 -1
  230. package/src/node.ts +9 -10
  231. package/src/openapi/register.ts +1 -2
  232. package/src/openapi/scalar.ts +9 -9
  233. package/src/openapi/spec-factory.ts +2 -2
  234. package/src/openapi/spec.ts +26 -19
  235. package/src/openapi/swagger.ts +9 -9
  236. package/src/plugin.ts +226 -256
  237. package/src/powered-by.ts +2 -1
  238. package/src/pretty-json.ts +2 -1
  239. package/src/proxy.ts +1 -3
  240. package/src/python-type-emitter.ts +37 -12
  241. package/src/realtime/route.ts +1 -1
  242. package/src/realtime/runtime.ts +9 -12
  243. package/src/request-id.ts +3 -4
  244. package/src/request-to-curl.ts +1 -4
  245. package/src/response.ts +150 -199
  246. package/src/rust-type-emitter.ts +66 -25
  247. package/src/secure-headers.ts +2 -1
  248. package/src/serve.ts +3 -12
  249. package/src/server-timing.ts +2 -1
  250. package/src/static.ts +3 -4
  251. package/src/telemetry/otel.ts +1 -6
  252. package/src/testing.ts +2 -11
  253. package/src/timeout.ts +2 -1
  254. package/src/tree.ts +12 -27
  255. package/src/ts-type-emitter.ts +71 -15
  256. package/src/type-emitter.ts +226 -237
  257. package/src/type-extractor.ts +260 -334
  258. package/src/types.ts +168 -68
  259. package/src/validation.ts +7 -30
  260. package/src/ws/bun.ts +2 -9
  261. package/src/ws/cloudflare.ts +13 -16
  262. package/src/ws/deno.ts +31 -40
  263. package/src/ws/node.ts +4 -19
package/src/codegen-go.ts CHANGED
@@ -47,7 +47,16 @@ let runtimeCache: Map<string, string> | null = null
47
47
  export function loadGoRuntimeTemplates(): Map<string, string> {
48
48
  if (runtimeCache) return runtimeCache
49
49
 
50
- const names = ["result.go", "errors.go", "runtime.go", "invalidation.go", "sse.go", "ws.go", "transport.go", "realtime.go"]
50
+ const names = [
51
+ "result.go",
52
+ "errors.go",
53
+ "runtime.go",
54
+ "invalidation.go",
55
+ "sse.go",
56
+ "ws.go",
57
+ "transport.go",
58
+ "realtime.go",
59
+ ]
51
60
  const cache = new Map<string, string>()
52
61
  for (const name of names) {
53
62
  const filePath = fileURLToPath(new URL(`./client-go/${name}`, import.meta.url))
@@ -304,14 +313,7 @@ function buildGoTypes(spec: OpenApiSpec): string {
304
313
 
305
314
  function buildGoMod(modulePath?: string): string {
306
315
  const mod = modulePath ?? "example.com/sdk"
307
- return [
308
- `module ${mod}`,
309
- ``,
310
- `go 1.23`,
311
- ``,
312
- `require nhooyr.io/websocket v1.8.17`,
313
- ``,
314
- ].join("\n")
316
+ return [`module ${mod}`, ``, `go 1.23`, ``, `require nhooyr.io/websocket v1.8.17`, ``].join("\n")
315
317
  }
316
318
 
317
319
  /* ── buildGoDoc ── */
@@ -366,10 +368,7 @@ type MethodInfo = {
366
368
  }
367
369
 
368
370
  /** Build a MethodInfo from an IROperation + the raw spec op object. */
369
- function methodInfoFromIROp(
370
- irOp: IROperation,
371
- rawOp: Record<string, unknown>,
372
- ): MethodInfo {
371
+ function methodInfoFromIROp(irOp: IROperation, rawOp: Record<string, unknown>): MethodInfo {
373
372
  const segments = irOp.id.split(".")
374
373
  const isTopLevel = segments.length === 1
375
374
  const resource = segments[0] ?? irOp.id
@@ -401,10 +400,7 @@ function methodInfoFromIROp(
401
400
  }
402
401
 
403
402
  /** Walk the IR tree and collect all MethodInfos, pairing with raw spec ops. */
404
- function collectMethodsFromIR(
405
- ns: IRNamespace,
406
- spec: OpenApiSpec,
407
- ): MethodInfo[] {
403
+ function collectMethodsFromIR(ns: IRNamespace, spec: OpenApiSpec): MethodInfo[] {
408
404
  const rawOpById = new Map<string, Record<string, unknown>>()
409
405
  for (const [, pathMethods] of Object.entries(spec.paths ?? {})) {
410
406
  for (const [, operation] of Object.entries(pathMethods)) {
@@ -473,16 +469,17 @@ function buildGoClient(spec: OpenApiSpec, options: GoSDKOptions): string {
473
469
  body.push(`var invalidationMap = map[string]map[string]serviceEntry{`)
474
470
  for (const [resource, actions] of Object.entries(serviceMap).sort(([a], [b]) => a.localeCompare(b))) {
475
471
  body.push(`\t"${resource}": {`)
476
- for (const [action, entry] of Object.entries(actions as Record<string, Record<string, unknown>>).sort(([a], [b]) => a.localeCompare(b))) {
472
+ for (const [action, entry] of Object.entries(actions as Record<string, Record<string, unknown>>).sort(([a], [b]) =>
473
+ a.localeCompare(b),
474
+ )) {
477
475
  const inv = entry.invalidate as string[] | undefined
478
- const invStr = inv && inv.length > 0
479
- ? `[]string{${inv.map((s) => JSON.stringify(s)).join(", ")}}`
480
- : `nil`
476
+ const invStr = inv && inv.length > 0 ? `[]string{${inv.map((s) => JSON.stringify(s)).join(", ")}}` : `nil`
481
477
  const params = entry.params as string[] | undefined
482
- const paramsStr = params && params.length > 0
483
- ? `[]string{${params.map((s) => JSON.stringify(s)).join(", ")}}`
484
- : `nil`
485
- body.push(`\t\t"${action}": {Method: ${JSON.stringify(String(entry.method))}, Path: ${JSON.stringify(String(entry.path))}, Params: ${paramsStr}, SSE: ${entry.sse === true}, WS: ${entry.ws === true}, Invalidate: ${invStr}},`)
478
+ const paramsStr =
479
+ params && params.length > 0 ? `[]string{${params.map((s) => JSON.stringify(s)).join(", ")}}` : `nil`
480
+ body.push(
481
+ `\t\t"${action}": {Method: ${JSON.stringify(String(entry.method))}, Path: ${JSON.stringify(String(entry.path))}, Params: ${paramsStr}, SSE: ${entry.sse === true}, WS: ${entry.ws === true}, Invalidate: ${invStr}},`,
482
+ )
486
483
  }
487
484
  body.push(`\t},`)
488
485
  }
@@ -570,7 +567,11 @@ function buildGoClient(spec: OpenApiSpec, options: GoSDKOptions): string {
570
567
 
571
568
  /* direct methods on this struct */
572
569
  for (const [, op] of methodsOf(ns)) {
573
- const raw = (methods.find((m) => m.opId === op.id)) ?? (() => { throw new Error(`missing MethodInfo for ${op.id}`) })()
570
+ const raw =
571
+ methods.find((m) => m.opId === op.id) ??
572
+ (() => {
573
+ throw new Error(`missing MethodInfo for ${op.id}`)
574
+ })()
574
575
  body.push(...emitMethod(raw, recvName, `*${structName}`, throwOnError))
575
576
  }
576
577
 
@@ -613,9 +614,7 @@ function buildGoClient(spec: OpenApiSpec, options: GoSDKOptions): string {
613
614
  { line: `var _ = sync.Mutex{}`, symbol: "sync." },
614
615
  { line: `var _ = time.Second`, symbol: "time." },
615
616
  ]
616
- const suppressionLines = suppressions
617
- .filter((s) => !bodyStr.includes(s.symbol))
618
- .map((s) => s.line)
617
+ const suppressionLines = suppressions.filter((s) => !bodyStr.includes(s.symbol)).map((s) => s.line)
619
618
 
620
619
  const out: string[] = []
621
620
  /* blank line after generator header detaches it from package-doc on pkg.go.dev */
@@ -772,16 +771,19 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
772
771
  for (const qp of sseQueryParams) {
773
772
  const fieldName = goPascal(qp.name)
774
773
  if (qp.required) {
775
- l.push(`\t\tif opts != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`opts.${fieldName}`, qp.schema)}) }`)
774
+ l.push(
775
+ `\t\tif opts != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`opts.${fieldName}`, qp.schema)}) }`,
776
+ )
776
777
  } else {
777
- l.push(`\t\tif opts != nil && opts.${fieldName} != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`*opts.${fieldName}`, qp.schema)}) }`)
778
+ l.push(
779
+ `\t\tif opts != nil && opts.${fieldName} != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`*opts.${fieldName}`, qp.schema)}) }`,
780
+ )
778
781
  }
779
782
  }
780
783
  }
781
784
  const urlExpr = pathToGoExpr(pathStr)
782
- const sseURLExpr = sseQueryParams.length > 0
783
- ? `${cfgAccess}.BaseURL+${urlExpr}+"?"+q.Encode()`
784
- : `${cfgAccess}.BaseURL+${urlExpr}`
785
+ const sseURLExpr =
786
+ sseQueryParams.length > 0 ? `${cfgAccess}.BaseURL+${urlExpr}+"?"+q.Encode()` : `${cfgAccess}.BaseURL+${urlExpr}`
785
787
  l.push(`\t\tcallHeaders := map[string]string{"Accept": "text/event-stream"}`)
786
788
  l.push(`\t\tif opts != nil && opts.LastEventID != "" {`)
787
789
  l.push(`\t\t\tcallHeaders["Last-Event-ID"] = opts.LastEventID`)
@@ -793,7 +795,9 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
793
795
  l.push(`\t\tif err != nil { yield(SSEEvent{}, err); return }`)
794
796
  l.push(`\t\tfor k, v := range ${cfgAccess}.Headers { req.Header.Set(k, v) }`)
795
797
  l.push(`\t\tfor k, v := range callHeaders { req.Header.Set(k, v) }`)
796
- l.push(`\t\tif ${cfgAccess}.BearerToken != "" { req.Header.Set(${cfgAccess}.AuthHeaderName, ${cfgAccess}.AuthHeaderPrefix+${cfgAccess}.BearerToken) }`)
798
+ l.push(
799
+ `\t\tif ${cfgAccess}.BearerToken != "" { req.Header.Set(${cfgAccess}.AuthHeaderName, ${cfgAccess}.AuthHeaderPrefix+${cfgAccess}.BearerToken) }`,
800
+ )
797
801
  l.push(`\t\tresp, err := ${cfgAccess}.HTTPClient.Do(req)`)
798
802
  l.push(`\t\tif err != nil { yield(SSEEvent{}, err); return }`)
799
803
  l.push(`\t\tfor ev, err := range parseSSEStream(ctx, resp) {`)
@@ -812,9 +816,13 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
812
816
  if (qp.name === "reconnect_token") continue
813
817
  const fieldName = goPascal(qp.name)
814
818
  if (qp.required) {
815
- l.push(`\tif opts != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`opts.${fieldName}`, qp.schema)}) }`)
819
+ l.push(
820
+ `\tif opts != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`opts.${fieldName}`, qp.schema)}) }`,
821
+ )
816
822
  } else {
817
- l.push(`\tif opts != nil && opts.${fieldName} != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`*opts.${fieldName}`, qp.schema)}) }`)
823
+ l.push(
824
+ `\tif opts != nil && opts.${fieldName} != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`*opts.${fieldName}`, qp.schema)}) }`,
825
+ )
818
826
  }
819
827
  }
820
828
  l.push(`\tif opts != nil && opts.ReconnectToken != "" { q.Set("reconnect_token", opts.ReconnectToken) }`)
@@ -841,7 +849,9 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
841
849
  if (qp.required) {
842
850
  l.push(`\tif opts != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`opts.${fieldName}`, qp.schema)}) }`)
843
851
  } else {
844
- l.push(`\tif opts != nil && opts.${fieldName} != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`*opts.${fieldName}`, qp.schema)}) }`)
852
+ l.push(
853
+ `\tif opts != nil && opts.${fieldName} != nil { q.Set(${JSON.stringify(qp.name)}, ${qpStringExpr(`*opts.${fieldName}`, qp.schema)}) }`,
854
+ )
845
855
  }
846
856
  }
847
857
 
@@ -881,21 +891,26 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
881
891
  * so consumer can flip cfg.Invalidation on/off without regen; tracker's
882
892
  * BuildRequestMeta short-circuits to nil when disabled. */
883
893
  const hasInvalidate = Array.isArray(m.op["x-invalidate"]) && (m.op["x-invalidate"] as unknown[]).length > 0
884
- const pathParamsMapExpr = m.pathParams.length > 0
885
- ? `map[string]string{${m.pathParams.map((p) => `${JSON.stringify(p)}: ${p}`).join(", ")}}`
886
- : `map[string]string(nil)`
894
+ const pathParamsMapExpr =
895
+ m.pathParams.length > 0
896
+ ? `map[string]string{${m.pathParams.map((p) => `${JSON.stringify(p)}: ${p}`).join(", ")}}`
897
+ : `map[string]string(nil)`
887
898
  if (hasInvalidate) {
888
899
  l.push(`\t_pathParamsMap := ${pathParamsMapExpr}`)
889
900
  l.push(`\t_concretePath, _ := interpolatePath(${JSON.stringify(pathStr)}, _pathParamsMap)`)
890
901
  l.push(`\t_concreteSelector := ${JSON.stringify(`${httpMethodStr} `)} + _concretePath`)
891
902
  l.push(`\tvar _requestMeta *RequestMeta`)
892
- l.push(`\tif ${staleAccess} != nil { _requestMeta = ${staleAccess}.BuildRequestMeta(_concreteSelector, _concretePath, ${JSON.stringify(httpMethodStr)}) }`)
903
+ l.push(
904
+ `\tif ${staleAccess} != nil { _requestMeta = ${staleAccess}.BuildRequestMeta(_concreteSelector, _concretePath, ${JSON.stringify(httpMethodStr)}) }`,
905
+ )
893
906
  }
894
907
 
895
908
  const resultBind = m.responseType === "" ? "_, err" : "result, err"
896
909
  const operationStr = JSON.stringify(`${httpMethodStr} ${pathStr}`)
897
910
  const requestMetaArg = hasInvalidate ? "_requestMeta" : "nil"
898
- l.push(`\t${resultBind} := doRequest(ctx, ${cfgAccess}, "${httpMethodStr}", ${urlExpr}, q, ${bodyArg}, ${bodyReaderArg}, ${contentTypeArg}, callHeaders, ${operationStr}, ${requestMetaArg})`)
911
+ l.push(
912
+ `\t${resultBind} := doRequest(ctx, ${cfgAccess}, "${httpMethodStr}", ${urlExpr}, q, ${bodyArg}, ${bodyReaderArg}, ${contentTypeArg}, callHeaders, ${operationStr}, ${requestMetaArg})`,
913
+ )
899
914
  l.push(`\tif err != nil {`)
900
915
 
901
916
  if (throwOnError) {
@@ -912,7 +927,9 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
912
927
  l.push(`\t\tif apiErr, ok := err.(APIError); ok {`)
913
928
  l.push(`\t\t\treturn SDKResult[${m.responseType}]{Err: apiErr, Status: 0, Response: nil}, nil`)
914
929
  l.push(`\t\t}`)
915
- l.push(`\t\treturn SDKResult[${m.responseType}]{Err: &StatusError{StatusCode: 0, Message: err.Error()}, Status: 0, Response: nil}, nil`)
930
+ l.push(
931
+ `\t\treturn SDKResult[${m.responseType}]{Err: &StatusError{StatusCode: 0, Message: err.Error()}, Status: 0, Response: nil}, nil`,
932
+ )
916
933
  } else {
917
934
  l.push(`\t\treturn err`)
918
935
  }
@@ -925,9 +942,13 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
925
942
  * mutation completed (seq-bounded to avoid clobbering newer invalidations). */
926
943
  if (hasInvalidate) {
927
944
  l.push(`\tif ${staleAccess} != nil {`)
928
- l.push(`\t\t${staleAccess}.MarkStale(invalidationMap["${m.resource}"]["${m.action}"].Invalidate, _pathParamsMap, _concreteSelector)`)
945
+ l.push(
946
+ `\t\t${staleAccess}.MarkStale(invalidationMap["${m.resource}"]["${m.action}"].Invalidate, _pathParamsMap, _concreteSelector)`,
947
+ )
929
948
  l.push(`\t\tif _requestMeta != nil && _requestMeta.IsStale {`)
930
- l.push(`\t\t\t${staleAccess}.ClearStale(_concreteSelector, _concretePath, ${JSON.stringify(httpMethodStr)}, _requestMeta.SeqSnapshot)`)
949
+ l.push(
950
+ `\t\t\t${staleAccess}.ClearStale(_concreteSelector, _concretePath, ${JSON.stringify(httpMethodStr)}, _requestMeta.SeqSnapshot)`,
951
+ )
931
952
  l.push(`\t\t}`)
932
953
  l.push(`\t}`)
933
954
  }
@@ -949,7 +970,9 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
949
970
  l.push(`\tvar out ${m.responseType}`)
950
971
  l.push(`\tif len(result.body) > 0 {`)
951
972
  l.push(`\t\tif err := json.Unmarshal(result.body, &out); err != nil {`)
952
- l.push(`\t\t\treturn SDKResult[${m.responseType}]{Err: &StatusError{StatusCode: result.status, Body: result.body, Response: result.resp, Message: err.Error()}, Status: result.status, Response: result.resp}, nil`)
973
+ l.push(
974
+ `\t\t\treturn SDKResult[${m.responseType}]{Err: &StatusError{StatusCode: result.status, Body: result.body, Response: result.resp, Message: err.Error()}, Status: result.status, Response: result.resp}, nil`,
975
+ )
953
976
  l.push(`\t\t}`)
954
977
  l.push(`\t}`)
955
978
  l.push(`\treturn SDKResult[${m.responseType}]{Data: &out, Status: result.status, Response: result.resp}, nil`)
@@ -966,10 +989,7 @@ function emitMethod(m: MethodInfo, recv: string, recvType: string, throwOnError:
966
989
  * Generates a complete Go SDK module from an OpenAPI 3.1 spec.
967
990
  * Returns a file map (filename → content) ready to write to disk as a Go module.
968
991
  */
969
- export function generateGoSDK(
970
- spec: Record<string, unknown>,
971
- options: GoSDKOptions = {},
972
- ): GeneratedGoSDK {
992
+ export function generateGoSDK(spec: Record<string, unknown>, options: GoSDKOptions = {}): GeneratedGoSDK {
973
993
  const resolved = resolveRefs(spec as Parameters<typeof resolveRefs>[0]) as unknown as OpenApiSpec
974
994
 
975
995
  const { serviceMap } = collectSDKMethods(spec as Parameters<typeof resolveRefs>[0])
package/src/codegen-ir.ts CHANGED
@@ -48,7 +48,7 @@ export type IRBody =
48
48
  contentType: "multipart/form-data"
49
49
  parts: IRMultipartPart[]
50
50
  required: boolean
51
- }
51
+ }
52
52
 
53
53
  export type IRResponse = {
54
54
  status: string /* "200" | "204" | "default" | ... — preserved verbatim */
@@ -81,9 +81,7 @@ export type IROperation = {
81
81
  extensions: IROperationExtensions
82
82
  }
83
83
 
84
- export type IRTreeEntry =
85
- | { kind: "method"; op: IROperation }
86
- | { kind: "namespace"; ns: IRNamespace }
84
+ export type IRTreeEntry = { kind: "method"; op: IROperation } | { kind: "namespace"; ns: IRNamespace }
87
85
 
88
86
  export type IRNamespace = {
89
87
  entries: Map<string, IRTreeEntry>
@@ -132,7 +130,7 @@ export function buildResourceTree(operations: IROperation[]): IRNamespace {
132
130
  if (existing.kind === "method") {
133
131
  throw new Error(
134
132
  `operationId conflict: "${existing.op.id}" is callable but also a namespace prefix of "${op.id}".\n` +
135
- `Rename one. Suggested: "${existing.op.id}" → "${existing.op.id}One" or drop "${op.id}".`,
133
+ `Rename one. Suggested: "${existing.op.id}" → "${existing.op.id}One" or drop "${op.id}".`,
136
134
  )
137
135
  }
138
136
  cur = existing.ns
@@ -149,7 +147,7 @@ export function buildResourceTree(operations: IROperation[]): IRNamespace {
149
147
  const otherId = firstDescendantId(existing.ns)
150
148
  throw new Error(
151
149
  `operationId conflict: "${op.id}" is callable but also a namespace prefix of "${otherId}".\n` +
152
- `Rename one. Suggested: "${op.id}" → "${op.id}One" or drop "${otherId}".`,
150
+ `Rename one. Suggested: "${op.id}" → "${op.id}One" or drop "${otherId}".`,
153
151
  )
154
152
  }
155
153
  cur.entries.set(leafName, { kind: "method", op })
@@ -281,7 +279,10 @@ export function schemaToIR(schema: Record<string, unknown> | undefined): IRSchem
281
279
  if (isStringEnum(schema)) return { enum: enumVals, kind: "scalar", type: "string" }
282
280
  if (isIntEnum(schema)) return { enum: enumVals, kind: "scalar", type: "integer" }
283
281
  /* mixed enum (string + number values) — emit as union of const literals */
284
- if (enumVals.length > 0 && enumVals.every((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean")) {
282
+ if (
283
+ enumVals.length > 0 &&
284
+ enumVals.every((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean")
285
+ ) {
285
286
  return { kind: "union", variants: enumVals.map((v) => ({ kind: "const" as const, value: v })) }
286
287
  }
287
288
  }
@@ -299,11 +300,7 @@ export function schemaToIR(schema: Record<string, unknown> | undefined): IRSchem
299
300
  return { items, kind: "array" }
300
301
  }
301
302
 
302
- if (
303
- schema.type === "object" ||
304
- schema.properties !== undefined ||
305
- schema.additionalProperties !== undefined
306
- ) {
303
+ if (schema.type === "object" || schema.properties !== undefined || schema.additionalProperties !== undefined) {
307
304
  const props = (schema.properties ?? {}) as Record<string, Record<string, unknown>>
308
305
  const requiredKeys = new Set((schema.required as string[] | undefined) ?? [])
309
306
  const fields: IRField[] = Object.entries(props).map(([name, propSchema]) => {
@@ -363,28 +360,20 @@ function hasBinaryMultipartPart(mediaType: Record<string, unknown> | undefined):
363
360
  }
364
361
 
365
362
  /** Normalise a multipart/form-data object schema into typed parts. */
366
- function extractMultipartParts(
367
- mediaType: Record<string, unknown> | undefined,
368
- ): IRMultipartPart[] {
363
+ function extractMultipartParts(mediaType: Record<string, unknown> | undefined): IRMultipartPart[] {
369
364
  const schema = mediaType?.schema as Record<string, unknown> | undefined
370
365
  const props = schema?.properties as Record<string, Record<string, unknown>> | undefined
371
366
  if (!props) return []
372
367
  const parts: IRMultipartPart[] = []
373
368
  for (const [name, propSchema] of Object.entries(props)) {
374
369
  const ir = schemaToIR(propSchema)
375
- const isBinary =
376
- ir.kind === "binary" || (ir.kind === "array" && ir.items.kind === "binary")
370
+ const isBinary = ir.kind === "binary" || (ir.kind === "array" && ir.items.kind === "binary")
377
371
  parts.push({ name, schema: ir, type: isBinary ? "file" : "text" })
378
372
  }
379
373
  return parts
380
374
  }
381
375
 
382
- function buildOperation(
383
- id: string,
384
- method: string,
385
- path: string,
386
- op: Record<string, unknown>,
387
- ): IROperation {
376
+ function buildOperation(id: string, method: string, path: string, op: Record<string, unknown>): IROperation {
388
377
  const upperMethod = method.toUpperCase()
389
378
 
390
379
  const declaredParams = (op.parameters as Record<string, unknown>[] | undefined) ?? []
@@ -396,7 +385,9 @@ function buildOperation(
396
385
  for (const p of declaredParams) {
397
386
  const pIn = p.in as string
398
387
  const pName = p.name as string
399
- const pSchema = p.schema ? schemaToIR(p.schema as Record<string, unknown>) : { kind: "scalar" as const, type: "string" as const }
388
+ const pSchema = p.schema
389
+ ? schemaToIR(p.schema as Record<string, unknown>)
390
+ : { kind: "scalar" as const, type: "string" as const }
400
391
  const param: IRParam = { name: pName, schema: pSchema }
401
392
  if (typeof p.description === "string") param.description = p.description
402
393
  if (pIn === "path") {
@@ -458,9 +449,7 @@ function buildOperation(
458
449
  /* SSE detected by response content-type, not a request extension */
459
450
  if (firstCt === "text/event-stream") extensions.sse = true
460
451
  const mediaType = content[firstCt]
461
- const respSchema = mediaType?.schema
462
- ? schemaToIR(mediaType.schema as Record<string, unknown>)
463
- : undefined
452
+ const respSchema = mediaType?.schema ? schemaToIR(mediaType.schema as Record<string, unknown>) : undefined
464
453
  const resp: IRResponse = { contentType: firstCt, status }
465
454
  if (respSchema !== undefined) resp.schema = respSchema
466
455
  responses[status] = resp
@@ -9,13 +9,11 @@ export async function loadToJSONSchema(): Promise<
9
9
  if (toJSONSchemaLoaded) return toJSONSchemaFn
10
10
  toJSONSchemaLoaded = true
11
11
  try {
12
- const id = "zod"
13
- const zod = await import(id)
12
+ /* opaque specifier — a plain `const id = "zod"` is constant-folded by rollup/esbuild,
13
+ which pulls the whole optional dependency into webworker-target bundles */
14
+ const zod = await import(["z", "od"].join(""))
14
15
  if (typeof zod.toJSONSchema === "function") {
15
- toJSONSchemaFn = zod.toJSONSchema as (
16
- schema: unknown,
17
- opts?: { io?: "input" | "output" },
18
- ) => unknown
16
+ toJSONSchemaFn = zod.toJSONSchema as (schema: unknown, opts?: { io?: "input" | "output" }) => unknown
19
17
  }
20
18
  } catch {
21
19
  /* zod not available */
@@ -30,8 +28,7 @@ export async function loadEffectJsonSchema(): Promise<((schema: unknown) => unkn
30
28
  if (effectLoaded) return effectJsonSchemaFn
31
29
  effectLoaded = true
32
30
  try {
33
- const id = "effect"
34
- const effect = await import(id)
31
+ const effect = await import(["eff", "ect"].join(""))
35
32
  if (effect.JSONSchema && typeof effect.JSONSchema.make === "function") {
36
33
  effectJsonSchemaFn = effect.JSONSchema.make as (schema: unknown) => unknown
37
34
  }
@@ -71,10 +71,7 @@ function toSnakeCase(id: string): string {
71
71
  .toLowerCase()
72
72
  }
73
73
 
74
- function resolveSchema(
75
- spec: SpecRecord,
76
- schema: SpecRecord | undefined,
77
- ): SpecRecord | undefined {
74
+ function resolveSchema(spec: SpecRecord, schema: SpecRecord | undefined): SpecRecord | undefined {
78
75
  if (!schema) return undefined
79
76
  if (!schema.$ref) return schema
80
77
  const ref = schema.$ref as string
@@ -108,9 +105,7 @@ function deepResolveSchema(
108
105
  out[k] = v
109
106
  } else if (Array.isArray(v)) {
110
107
  out[k] = v.map((item) =>
111
- item !== null && typeof item === "object"
112
- ? deepResolveSchema(spec, item as SpecRecord, seen)
113
- : item,
108
+ item !== null && typeof item === "object" ? deepResolveSchema(spec, item as SpecRecord, seen) : item,
114
109
  )
115
110
  } else if (typeof v === "object") {
116
111
  out[k] = deepResolveSchema(spec, v as SpecRecord, seen)
@@ -169,12 +164,7 @@ function emptyParamGroup(): ParamGroup {
169
164
  return { props: {}, required: [] }
170
165
  }
171
166
 
172
- function assignGroup(
173
- properties: SpecRecord,
174
- required: string[],
175
- key: string,
176
- group: ParamGroup,
177
- ): void {
167
+ function assignGroup(properties: SpecRecord, required: string[], key: string, group: ParamGroup): void {
178
168
  if (Object.keys(group.props).length === 0) return
179
169
  const out: SpecRecord = { properties: group.props, type: "object" }
180
170
  if (group.required.length > 0) {
@@ -184,10 +174,7 @@ function assignGroup(
184
174
  properties[key] = out
185
175
  }
186
176
 
187
- function buildToolInputSchema(
188
- op: MCPOp,
189
- spec: OpenApiSpecInput,
190
- ): SpecRecord {
177
+ function buildToolInputSchema(op: MCPOp, spec: OpenApiSpecInput): SpecRecord {
191
178
  const properties: SpecRecord = {}
192
179
  const required: string[] = []
193
180
  const specRec = spec as unknown as SpecRecord
@@ -220,11 +207,7 @@ function buildToolInputSchema(
220
207
  return schema
221
208
  }
222
209
 
223
- function buildToolsGen(
224
- ops: MCPOp[],
225
- spec: OpenApiSpecInput,
226
- projectName: string,
227
- ): string {
210
+ function buildToolsGen(ops: MCPOp[], spec: OpenApiSpecInput, projectName: string): string {
228
211
  const l: string[] = []
229
212
  l.push(`/* Generated by honey codegen-mcp.ts — do not edit by hand. */`)
230
213
  l.push(``)
@@ -254,7 +237,9 @@ function buildToolsGen(
254
237
  l.push(`\t\t\thandler: async (args) => {`)
255
238
  l.push(`\t\t\t\tconst method = ${callExpr}`)
256
239
  l.push(`\t\t\t\tif (typeof method !== "function") {`)
257
- l.push(`\t\t\t\t\treturn { content: [{ type: "text", text: JSON.stringify({ message: "SDK missing method ${op.operationId}" }) }], isError: true }`)
240
+ l.push(
241
+ `\t\t\t\t\treturn { content: [{ type: "text", text: JSON.stringify({ message: "SDK missing method ${op.operationId}" }) }], isError: true }`,
242
+ )
258
243
  l.push(`\t\t\t\t}`)
259
244
  l.push(`\t\t\t\tconst result = await method.call(sdk, args)`)
260
245
  l.push(`\t\t\t\treturn { content: [{ type: "text", text: JSON.stringify(result) }] }`)
@@ -331,10 +316,7 @@ function buildTsconfig(): string {
331
316
  )
332
317
  }
333
318
 
334
- export function generateMCPServer(
335
- spec: OpenApiSpecInput,
336
- options: MCPOptions,
337
- ): MCPResult {
319
+ export function generateMCPServer(spec: OpenApiSpecInput, options: MCPOptions): MCPResult {
338
320
  toIR(spec)
339
321
  const { projectName, sdkClassName, sdkPackageName } = options
340
322
  const version = options.version ?? "0.1.0"
@@ -53,7 +53,15 @@ interface ParamInfo {
53
53
  }
54
54
 
55
55
  const PYTHON_FUTURE = "from __future__ import annotations"
56
- const STATIC_FILE_NAMES = ["_runtime.py", "_errors.py", "_invalidation.py", "_sse.py", "_ws.py", "_transport.py", "_realtime.py"] as const
56
+ const STATIC_FILE_NAMES = [
57
+ "_runtime.py",
58
+ "_errors.py",
59
+ "_invalidation.py",
60
+ "_sse.py",
61
+ "_ws.py",
62
+ "_transport.py",
63
+ "_realtime.py",
64
+ ] as const
57
65
  const RUNTIME_TEMPLATE_CACHE = new Map<string, string>()
58
66
  const WS_EXTENSION = "x-websocket"
59
67
  const REALTIME_EXTENSION = "x-realtime"
@@ -100,10 +108,7 @@ function resolveSchema(
100
108
  return cur as Record<string, unknown> | undefined
101
109
  }
102
110
 
103
- function getSuccessType(
104
- spec: Record<string, unknown>,
105
- operation: Record<string, unknown>,
106
- ): string {
111
+ function getSuccessType(spec: Record<string, unknown>, operation: Record<string, unknown>): string {
107
112
  const responses = operation.responses as Record<string, Record<string, unknown>> | undefined
108
113
  if (!responses) return "None"
109
114
  const successCodes = ["200", "201", "202", "204"]
@@ -193,7 +198,12 @@ function collectOperations(spec: Record<string, unknown>): OperationInfo[] {
193
198
  isWS: !isRealtime && operation[WS_EXTENSION] === true,
194
199
  method: httpMethod.toUpperCase(),
195
200
  operationId,
196
- parameters: rawParams as Array<{ in: string; name: string; required: boolean; schema: Record<string, unknown> }>,
201
+ parameters: rawParams as Array<{
202
+ in: string
203
+ name: string
204
+ required: boolean
205
+ schema: Record<string, unknown>
206
+ }>,
197
207
  path,
198
208
  pathParams,
199
209
  queryParams,
@@ -207,10 +217,12 @@ function collectOperations(spec: Record<string, unknown>): OperationInfo[] {
207
217
  }
208
218
 
209
219
  function nestedClassName(path: string[]): string {
210
- const pascal = path.map((seg) => {
211
- const safe = pyIdent(seg)
212
- return safe.charAt(0).toUpperCase() + safe.slice(1)
213
- }).join("")
220
+ const pascal = path
221
+ .map((seg) => {
222
+ const safe = pyIdent(seg)
223
+ return safe.charAt(0).toUpperCase() + safe.slice(1)
224
+ })
225
+ .join("")
214
226
  return `_${pascal}Resource`
215
227
  }
216
228
 
@@ -392,10 +404,7 @@ function buildUrlLine(op: OperationInfo, isTopLevel: boolean): string {
392
404
 
393
405
  function buildResponseLines(throwOnError: boolean): string[] {
394
406
  if (throwOnError) {
395
- return [
396
- ` _raise_for_status(_response)`,
397
- ` return _parse_body(_response)`,
398
- ]
407
+ return [` _raise_for_status(_response)`, ` return _parse_body(_response)`]
399
408
  }
400
409
  return [
401
410
  ` _body = _parse_body(_response)`,
@@ -461,7 +470,9 @@ function buildAsyncMethodBody(op: OperationInfo, throwOnError: boolean, isTopLev
461
470
  } else if (op.isWS) {
462
471
  lines.push(` _headers = _build_headers(${configRef}, extra=None)`)
463
472
  } else if (op.bodyIsStream) {
464
- lines.push(` _headers = _build_headers(${configRef}, extra={"Content-Type": "application/octet-stream", **(headers or {})})`)
473
+ lines.push(
474
+ ` _headers = _build_headers(${configRef}, extra={"Content-Type": "application/octet-stream", **(headers or {})})`,
475
+ )
465
476
  } else {
466
477
  lines.push(` _headers = _build_headers(${configRef}, extra=headers)`)
467
478
  }
@@ -585,7 +596,8 @@ function buildSyncMethodBody(op: OperationInfo, throwOnError: boolean, isTopLeve
585
596
 
586
597
  function buildPyTypes(spec: Record<string, unknown>): string {
587
598
  const schemas = (spec.components as Record<string, unknown> | undefined)?.schemas as
588
- Record<string, Record<string, unknown>> | undefined
599
+ | Record<string, Record<string, unknown>>
600
+ | undefined
589
601
 
590
602
  const body: string[] = []
591
603
  const allNames: string[] = []
@@ -656,13 +668,13 @@ function buildPyClient(
656
668
  const { opsByOpId, topLevel, tree } = collectSDKMethods(spec)
657
669
 
658
670
  const schemas = (spec.components as Record<string, unknown> | undefined)?.schemas as
659
- Record<string, Record<string, unknown>> | undefined
671
+ | Record<string, Record<string, unknown>>
672
+ | undefined
660
673
  const schemaNames = schemas ? Object.keys(schemas) : []
661
674
 
662
675
  const body: string[] = []
663
676
 
664
- const invEntries = Object.entries(serviceMap)
665
- .filter(([, v]) => v.invalidate && v.invalidate.length > 0)
677
+ const invEntries = Object.entries(serviceMap).filter(([, v]) => v.invalidate && v.invalidate.length > 0)
666
678
 
667
679
  body.push("_INVALIDATION_MAP: dict[str, dict[str, Any]] = {")
668
680
  for (const [opId, entry] of invEntries) {
@@ -779,7 +791,7 @@ function buildPyClient(
779
791
  }
780
792
  body.push("")
781
793
  body.push(" def is_stale(self, method: str, path: str) -> bool:")
782
- body.push(" \"\"\"Returns True when (method, path) sits inside an active stale window.\"\"\"")
794
+ body.push(' """Returns True when (method, path) sits inside an active stale window."""')
783
795
  body.push(" return self._stale_tracker.is_stale(method, path)")
784
796
  body.push("")
785
797
 
@@ -816,7 +828,7 @@ function buildPyClient(
816
828
  }
817
829
  body.push("")
818
830
  body.push(" async def is_stale(self, method: str, path: str) -> bool:")
819
- body.push(" \"\"\"Returns True when (method, path) sits inside an active stale window.\"\"\"")
831
+ body.push(' """Returns True when (method, path) sits inside an active stale window."""')
820
832
  body.push(" return await self._stale_tracker.is_stale(method, path)")
821
833
  body.push("")
822
834
 
@@ -845,7 +857,17 @@ function buildPyClient(
845
857
  if (typingUsed.length > 0) imports.push(`from typing import ${typingUsed.join(", ")}`)
846
858
 
847
859
  const runtimeUsed: string[] = []
848
- for (const sym of ["ClientConfig", "InvalidationConfig", "SDKResult", "_build_headers", "_build_url", "_do_request_async", "_do_request_sync", "_parse_body", "_raise_for_status"]) {
860
+ for (const sym of [
861
+ "ClientConfig",
862
+ "InvalidationConfig",
863
+ "SDKResult",
864
+ "_build_headers",
865
+ "_build_url",
866
+ "_do_request_async",
867
+ "_do_request_sync",
868
+ "_parse_body",
869
+ "_raise_for_status",
870
+ ]) {
849
871
  if (new RegExp(`\\b${sym}\\b`).test(bodyText)) runtimeUsed.push(sym)
850
872
  }
851
873
  if (runtimeUsed.length > 0) {
@@ -972,7 +994,8 @@ function buildPyInit(spec: Record<string, unknown>): string {
972
994
  l.push(")")
973
995
 
974
996
  const schemas = (spec.components as Record<string, unknown> | undefined)?.schemas as
975
- Record<string, Record<string, unknown>> | undefined
997
+ | Record<string, Record<string, unknown>>
998
+ | undefined
976
999
  const schemaNames = schemas ? Object.keys(schemas).sort() : []
977
1000
  if (schemaNames.length > 0) {
978
1001
  l.push("from .types import (")
@@ -1007,10 +1030,7 @@ function buildServiceMap(spec: Record<string, unknown>): Record<string, ServiceM
1007
1030
  return result
1008
1031
  }
1009
1032
 
1010
- export function generatePythonSDK(
1011
- spec: Record<string, unknown>,
1012
- options: PySDKOptions = {},
1013
- ): PySDKResult {
1033
+ export function generatePythonSDK(spec: Record<string, unknown>, options: PySDKOptions = {}): PySDKResult {
1014
1034
  const templates = loadPythonRuntimeTemplates()
1015
1035
  const serviceMap = buildServiceMap(spec)
1016
1036