@lovrozagar/honey 0.1.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 (131) hide show
  1. package/README.md +51 -0
  2. package/docs/api-ref/go.md +399 -0
  3. package/docs/api-ref/python.md +399 -0
  4. package/docs/api-ref/rust.md +399 -0
  5. package/docs/api-ref/typescript.md +399 -0
  6. package/docs/invalidate-spec.md +303 -0
  7. package/docs/sdk.md +722 -0
  8. package/package.json +120 -0
  9. package/src/accepts.ts +78 -0
  10. package/src/body-limit.ts +100 -0
  11. package/src/build/index.ts +174 -0
  12. package/src/cli-go/auth.go +15 -0
  13. package/src/cli-go/config.go +165 -0
  14. package/src/cli-go/errors.go +57 -0
  15. package/src/cli-go/multipart.go +55 -0
  16. package/src/cli-go/output.go +109 -0
  17. package/src/cli-go/root.go +30 -0
  18. package/src/cli-go/stream.go +88 -0
  19. package/src/cli-go/version.go +5 -0
  20. package/src/cli.ts +204 -0
  21. package/src/client/error.ts +27 -0
  22. package/src/client/http.ts +502 -0
  23. package/src/client/index.ts +127 -0
  24. package/src/client/sdk.ts +265 -0
  25. package/src/client/sse.ts +102 -0
  26. package/src/client/types.ts +211 -0
  27. package/src/client/ws.ts +115 -0
  28. package/src/client-go/errors.go +207 -0
  29. package/src/client-go/invalidation.go +373 -0
  30. package/src/client-go/realtime.go +317 -0
  31. package/src/client-go/result.go +20 -0
  32. package/src/client-go/runtime.go +413 -0
  33. package/src/client-go/sse.go +149 -0
  34. package/src/client-go/transport.go +452 -0
  35. package/src/client-go/ws.go +104 -0
  36. package/src/client-mcp/README.md +10 -0
  37. package/src/client-mcp/server.ts +94 -0
  38. package/src/client-mcp/types.ts +19 -0
  39. package/src/client-python/_errors.py +115 -0
  40. package/src/client-python/_invalidation.py +578 -0
  41. package/src/client-python/_realtime.py +156 -0
  42. package/src/client-python/_runtime.py +469 -0
  43. package/src/client-python/_sse.py +99 -0
  44. package/src/client-python/_transport.py +335 -0
  45. package/src/client-python/_ws.py +131 -0
  46. package/src/client-python/py.typed +0 -0
  47. package/src/client-rust/errors.rs +200 -0
  48. package/src/client-rust/invalidation.rs +441 -0
  49. package/src/client-rust/invalidation_sync.rs +306 -0
  50. package/src/client-rust/realtime.rs +695 -0
  51. package/src/client-rust/result.rs +22 -0
  52. package/src/client-rust/runtime.rs +547 -0
  53. package/src/client-rust/runtime_sync.rs +452 -0
  54. package/src/client-rust/sse.rs +167 -0
  55. package/src/client-rust/ws.rs +143 -0
  56. package/src/cloudflare/realtime.ts +236 -0
  57. package/src/codegen-go-cli.ts +1180 -0
  58. package/src/codegen-go.ts +1010 -0
  59. package/src/codegen-hash.ts +8 -0
  60. package/src/codegen-ir.ts +517 -0
  61. package/src/codegen-loaders.ts +53 -0
  62. package/src/codegen-mcp.ts +364 -0
  63. package/src/codegen-python.ts +1028 -0
  64. package/src/codegen-rust.ts +1652 -0
  65. package/src/codegen-sanitize.ts +67 -0
  66. package/src/codegen-schema-naming.ts +146 -0
  67. package/src/codegen.ts +5015 -0
  68. package/src/context.ts +210 -0
  69. package/src/cookie-sign.ts +58 -0
  70. package/src/cookie.ts +89 -0
  71. package/src/cors.ts +96 -0
  72. package/src/crypto.ts +19 -0
  73. package/src/csrf.ts +54 -0
  74. package/src/curl-logger.ts +235 -0
  75. package/src/detect-runtime.ts +19 -0
  76. package/src/error.ts +52 -0
  77. package/src/errors.ts +87 -0
  78. package/src/etag.ts +74 -0
  79. package/src/feature-load.ts +23 -0
  80. package/src/go-type-emitter.ts +404 -0
  81. package/src/honey-response.ts +354 -0
  82. package/src/i18n-slot.ts +20 -0
  83. package/src/i18n.ts +234 -0
  84. package/src/index.ts +3843 -0
  85. package/src/init.ts +149 -0
  86. package/src/input.ts +8 -0
  87. package/src/ip-restrict.ts +192 -0
  88. package/src/logger.ts +149 -0
  89. package/src/middleware.ts +199 -0
  90. package/src/node-request.ts +240 -0
  91. package/src/node.ts +212 -0
  92. package/src/openapi/register.ts +19 -0
  93. package/src/openapi/scalar.ts +33 -0
  94. package/src/openapi/spec-factory.ts +46 -0
  95. package/src/openapi/spec.ts +31 -0
  96. package/src/openapi/swagger.ts +34 -0
  97. package/src/plugin.ts +771 -0
  98. package/src/powered-by.ts +17 -0
  99. package/src/pretty-json.ts +39 -0
  100. package/src/proxy.ts +155 -0
  101. package/src/python-type-emitter.ts +144 -0
  102. package/src/realtime/buffer.ts +101 -0
  103. package/src/realtime/bus.ts +93 -0
  104. package/src/realtime/longpoll.ts +148 -0
  105. package/src/realtime/protocol.ts +128 -0
  106. package/src/realtime/route.ts +104 -0
  107. package/src/realtime/runtime.ts +430 -0
  108. package/src/request-id.ts +23 -0
  109. package/src/request-to-curl.ts +34 -0
  110. package/src/response.ts +414 -0
  111. package/src/rust-type-emitter.ts +412 -0
  112. package/src/secure-headers.ts +69 -0
  113. package/src/serve-register.ts +8 -0
  114. package/src/serve-slot.ts +20 -0
  115. package/src/serve.ts +141 -0
  116. package/src/server-timing.ts +66 -0
  117. package/src/static.ts +60 -0
  118. package/src/telemetry/otel.ts +139 -0
  119. package/src/testing.ts +114 -0
  120. package/src/timeout.ts +36 -0
  121. package/src/tree.ts +464 -0
  122. package/src/ts-type-emitter.ts +106 -0
  123. package/src/type-emitter.ts +524 -0
  124. package/src/type-extractor.ts +749 -0
  125. package/src/types.ts +638 -0
  126. package/src/validation.ts +300 -0
  127. package/src/ws/bun.ts +89 -0
  128. package/src/ws/cloudflare.ts +133 -0
  129. package/src/ws/deno.ts +109 -0
  130. package/src/ws/node.ts +161 -0
  131. package/src/yaml.ts +80 -0
package/src/ws/node.ts ADDED
@@ -0,0 +1,161 @@
1
+ export {
2
+ type RawSocket,
3
+ type WSAdapter,
4
+ type WSContext,
5
+ WSContextImpl,
6
+ type WSHandler,
7
+ } from "./cloudflare.ts"
8
+
9
+ import type { WSAdapter, WSHandler } from "./cloudflare.ts"
10
+ import { WSContextImpl } from "./cloudflare.ts"
11
+
12
+ type WsModule = {
13
+ WebSocketServer: new (opts: {
14
+ noServer: true
15
+ perMessageDeflate: boolean
16
+ }) => {
17
+ handleUpgrade(req: unknown, socket: unknown, head: unknown, cb: (ws: NodeWS) => void): void
18
+ }
19
+ }
20
+
21
+ type NodeWS = {
22
+ close(code?: number, reason?: string): void
23
+ on(event: "close", cb: (code: number, reason: Buffer) => void): void
24
+ on(event: "error", cb: (err: unknown) => void): void
25
+ on(event: "message", cb: (data: Buffer | ArrayBuffer | Buffer[], isBinary: boolean) => void): void
26
+ on(event: "pong", cb: () => void): void
27
+ ping(): void
28
+ readyState: number
29
+ send(data: ArrayBuffer | Buffer | Uint8Array | string): void
30
+ }
31
+
32
+ type NodeUpgradeData = {
33
+ head: Buffer
34
+ req: unknown
35
+ socket: unknown
36
+ }
37
+
38
+ /**
39
+ * Node.js WebSocket adapter.
40
+ * Requires the `ws` package. Creates a no-server WebSocketServer internally.
41
+ * Reads __nodeUpgrade from env (placed there by serve()).
42
+ */
43
+ type WssInstance = {
44
+ handleUpgrade(req: unknown, socket: unknown, head: unknown, cb: (ws: NodeWS) => void): void
45
+ }
46
+
47
+ type KeepaliveConfig = {
48
+ interval: number
49
+ timeout: number
50
+ }
51
+
52
+ export function nodeWebSocket(opts?: { keepalive?: KeepaliveConfig }): WSAdapter {
53
+ let wss: WssInstance | null = null
54
+ let wssReady: Promise<void> | null = null
55
+
56
+ const ensureWss = async (): Promise<WssInstance> => {
57
+ if (wss) return wss
58
+ if (!wssReady) {
59
+ wssReady = import("ws").then((mod) => {
60
+ const wsModule = mod as unknown as WsModule
61
+ wss = new wsModule.WebSocketServer({ noServer: true, perMessageDeflate: false })
62
+ })
63
+ }
64
+ await wssReady
65
+ if (!wss) throw new Error("Failed to initialize WebSocketServer")
66
+ return wss
67
+ }
68
+
69
+ return {
70
+ async upgrade(_req: Request, env: unknown, handler: WSHandler<unknown>) {
71
+ const server = await ensureWss()
72
+
73
+ const nodeEnv = env as Record<string, unknown>
74
+ const upgrade = nodeEnv.__nodeUpgrade as NodeUpgradeData
75
+
76
+ return new Promise((resolve, reject) => {
77
+ try {
78
+ server.handleUpgrade(upgrade.req, upgrade.socket, upgrade.head, (ws: NodeWS) => {
79
+ const socket = new WSContextImpl(ws)
80
+
81
+ ws.on("message", (data: Buffer | ArrayBuffer | Buffer[], isBinary: boolean) => {
82
+ if (!isBinary) {
83
+ /* text frame — decode to string to match other runtimes */
84
+ const text = Buffer.isBuffer(data) ? data.toString("utf-8") : String(data)
85
+ handler.onMessage?.(undefined, socket, text)
86
+ } else if (Buffer.isBuffer(data)) {
87
+ /* binary frame — slice from pool to get exact bytes */
88
+ handler.onMessage?.(
89
+ undefined,
90
+ socket,
91
+ data.buffer.slice(
92
+ data.byteOffset,
93
+ data.byteOffset + data.byteLength,
94
+ ) as ArrayBuffer,
95
+ )
96
+ } else if (Array.isArray(data)) {
97
+ const merged = Buffer.concat(data)
98
+ handler.onMessage?.(
99
+ undefined,
100
+ socket,
101
+ merged.buffer.slice(
102
+ merged.byteOffset,
103
+ merged.byteOffset + merged.byteLength,
104
+ ) as ArrayBuffer,
105
+ )
106
+ } else {
107
+ handler.onMessage?.(undefined, socket, data)
108
+ }
109
+ })
110
+
111
+ /* keepalive ping/pong */
112
+ let pingTimer: ReturnType<typeof setInterval> | undefined
113
+ let pongTimeout: ReturnType<typeof setTimeout> | undefined
114
+
115
+ if (opts?.keepalive) {
116
+ const { interval, timeout } = opts.keepalive
117
+ ws.on("pong", () => {
118
+ if (pongTimeout !== undefined) {
119
+ clearTimeout(pongTimeout)
120
+ pongTimeout = undefined
121
+ }
122
+ })
123
+ pingTimer = setInterval(() => {
124
+ if (ws.readyState !== 1) {
125
+ clearInterval(pingTimer)
126
+ return
127
+ }
128
+ ws.ping()
129
+ pongTimeout = setTimeout(() => {
130
+ ws.close(1001, "keepalive timeout")
131
+ }, timeout)
132
+ }, interval)
133
+ }
134
+
135
+ ws.on("close", (code: number, reason: Buffer) => {
136
+ if (pingTimer !== undefined) clearInterval(pingTimer)
137
+ if (pongTimeout !== undefined) clearTimeout(pongTimeout)
138
+ handler.onClose?.(undefined, socket, code, reason.toString())
139
+ })
140
+
141
+ ws.on("error", (err: unknown) => {
142
+ if (pingTimer !== undefined) clearInterval(pingTimer)
143
+ if (pongTimeout !== undefined) clearTimeout(pongTimeout)
144
+ handler.onError?.(undefined, socket, err)
145
+ })
146
+
147
+ handler.onOpen?.(undefined, socket)
148
+
149
+ /* Node's Response rejects status 101 (Fetch spec: 200-599 only).
150
+ Create a valid Response and override status for the sentinel check. */
151
+ const resp = new Response(null)
152
+ Object.defineProperty(resp, "status", { value: 101 })
153
+ resolve({ response: resp, socket })
154
+ })
155
+ } catch (err) {
156
+ reject(err)
157
+ }
158
+ })
159
+ },
160
+ }
161
+ }
package/src/yaml.ts ADDED
@@ -0,0 +1,80 @@
1
+ /** JSON-compatible YAML 1.2 emitter — same document as JSON.stringify, different encoding. */
2
+
3
+ const INDENT = " "
4
+
5
+ export function yamlSiblingPath(jsonPath: string): string {
6
+ if (jsonPath.endsWith(".yaml") || jsonPath.endsWith(".yml")) return jsonPath.replace(/\.yml$/, ".yaml")
7
+ return jsonPath.endsWith(".json") ? `${jsonPath.slice(0, -5)}.yaml` : `${jsonPath}.yaml`
8
+ }
9
+
10
+ export function toYaml(value: unknown): string {
11
+ return `${emit(value, 0)}\n`
12
+ }
13
+
14
+ function emit(value: unknown, indent: number): string {
15
+ if (value === null || value === undefined) return "null"
16
+ if (typeof value === "boolean") return value ? "true" : "false"
17
+ if (typeof value === "number") {
18
+ if (!Number.isFinite(value)) return JSON.stringify(String(value))
19
+ return Object.is(value, -0) ? "-0" : String(value)
20
+ }
21
+ if (typeof value === "string") return emitString(value)
22
+ if (Array.isArray(value)) return emitArray(value, indent)
23
+ if (typeof value === "object") return emitObject(value as Record<string, unknown>, indent)
24
+ return JSON.stringify(String(value))
25
+ }
26
+
27
+ function emitString(value: string): string {
28
+ if (value === "") return '""'
29
+ if (needsQuotes(value)) return JSON.stringify(value)
30
+ return value
31
+ }
32
+
33
+ function needsQuotes(value: string): boolean {
34
+ if (/^[-?:{},[\],&*#|!>'%@`]/.test(value)) return true
35
+ if (/[\n\r\t]/.test(value)) return true
36
+ if (/^\s|\s$/.test(value)) return true
37
+ if (value.includes(": ") || value.includes(" #")) return true
38
+ if (/^(true|false|null|yes|no|on|off)$/i.test(value)) return true
39
+ if (/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)) return true
40
+ if (/^\d/.test(value)) return true
41
+ return false
42
+ }
43
+
44
+ function isNonEmptyCollection(value: unknown): boolean {
45
+ if (Array.isArray(value)) return value.length > 0
46
+ return value !== null && typeof value === "object" && Object.keys(value).length > 0
47
+ }
48
+
49
+ function emitArray(value: unknown[], indent: number): string {
50
+ if (value.length === 0) return "[]"
51
+ const pad = INDENT.repeat(indent)
52
+ const childPad = INDENT.repeat(indent + 1)
53
+ return value
54
+ .map((item) => {
55
+ if (isNonEmptyCollection(item)) {
56
+ const inner = emit(item, indent + 1)
57
+ const lines = inner.split("\n")
58
+ const first = lines[0]?.startsWith(childPad) ? lines[0].slice(childPad.length) : (lines[0] ?? "")
59
+ return [`${pad}- ${first}`, ...lines.slice(1)].join("\n")
60
+ }
61
+ return `${pad}- ${emit(item, 0)}`
62
+ })
63
+ .join("\n")
64
+ }
65
+
66
+ function emitObject(value: Record<string, unknown>, indent: number): string {
67
+ const keys = Object.keys(value)
68
+ if (keys.length === 0) return "{}"
69
+ const pad = INDENT.repeat(indent)
70
+ return keys
71
+ .map((key) => {
72
+ const child = value[key]
73
+ const renderedKey = needsQuotes(key) || key === "" ? JSON.stringify(key) : key
74
+ if (isNonEmptyCollection(child)) {
75
+ return `${pad}${renderedKey}:\n${emit(child, indent + 1)}`
76
+ }
77
+ return `${pad}${renderedKey}: ${emit(child, 0)}`
78
+ })
79
+ .join("\n")
80
+ }