@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.
- package/README.md +51 -0
- package/docs/api-ref/go.md +399 -0
- package/docs/api-ref/python.md +399 -0
- package/docs/api-ref/rust.md +399 -0
- package/docs/api-ref/typescript.md +399 -0
- package/docs/invalidate-spec.md +303 -0
- package/docs/sdk.md +722 -0
- package/package.json +120 -0
- package/src/accepts.ts +78 -0
- package/src/body-limit.ts +100 -0
- package/src/build/index.ts +174 -0
- package/src/cli-go/auth.go +15 -0
- package/src/cli-go/config.go +165 -0
- package/src/cli-go/errors.go +57 -0
- package/src/cli-go/multipart.go +55 -0
- package/src/cli-go/output.go +109 -0
- package/src/cli-go/root.go +30 -0
- package/src/cli-go/stream.go +88 -0
- package/src/cli-go/version.go +5 -0
- package/src/cli.ts +204 -0
- package/src/client/error.ts +27 -0
- package/src/client/http.ts +502 -0
- package/src/client/index.ts +127 -0
- package/src/client/sdk.ts +265 -0
- package/src/client/sse.ts +102 -0
- package/src/client/types.ts +211 -0
- package/src/client/ws.ts +115 -0
- package/src/client-go/errors.go +207 -0
- package/src/client-go/invalidation.go +373 -0
- package/src/client-go/realtime.go +317 -0
- package/src/client-go/result.go +20 -0
- package/src/client-go/runtime.go +413 -0
- package/src/client-go/sse.go +149 -0
- package/src/client-go/transport.go +452 -0
- package/src/client-go/ws.go +104 -0
- package/src/client-mcp/README.md +10 -0
- package/src/client-mcp/server.ts +94 -0
- package/src/client-mcp/types.ts +19 -0
- package/src/client-python/_errors.py +115 -0
- package/src/client-python/_invalidation.py +578 -0
- package/src/client-python/_realtime.py +156 -0
- package/src/client-python/_runtime.py +469 -0
- package/src/client-python/_sse.py +99 -0
- package/src/client-python/_transport.py +335 -0
- package/src/client-python/_ws.py +131 -0
- package/src/client-python/py.typed +0 -0
- package/src/client-rust/errors.rs +200 -0
- package/src/client-rust/invalidation.rs +441 -0
- package/src/client-rust/invalidation_sync.rs +306 -0
- package/src/client-rust/realtime.rs +695 -0
- package/src/client-rust/result.rs +22 -0
- package/src/client-rust/runtime.rs +547 -0
- package/src/client-rust/runtime_sync.rs +452 -0
- package/src/client-rust/sse.rs +167 -0
- package/src/client-rust/ws.rs +143 -0
- package/src/cloudflare/realtime.ts +236 -0
- package/src/codegen-go-cli.ts +1180 -0
- package/src/codegen-go.ts +1010 -0
- package/src/codegen-hash.ts +8 -0
- package/src/codegen-ir.ts +517 -0
- package/src/codegen-loaders.ts +53 -0
- package/src/codegen-mcp.ts +364 -0
- package/src/codegen-python.ts +1028 -0
- package/src/codegen-rust.ts +1652 -0
- package/src/codegen-sanitize.ts +67 -0
- package/src/codegen-schema-naming.ts +146 -0
- package/src/codegen.ts +5015 -0
- package/src/context.ts +210 -0
- package/src/cookie-sign.ts +58 -0
- package/src/cookie.ts +89 -0
- package/src/cors.ts +96 -0
- package/src/crypto.ts +19 -0
- package/src/csrf.ts +54 -0
- package/src/curl-logger.ts +235 -0
- package/src/detect-runtime.ts +19 -0
- package/src/error.ts +52 -0
- package/src/errors.ts +87 -0
- package/src/etag.ts +74 -0
- package/src/feature-load.ts +23 -0
- package/src/go-type-emitter.ts +404 -0
- package/src/honey-response.ts +354 -0
- package/src/i18n-slot.ts +20 -0
- package/src/i18n.ts +234 -0
- package/src/index.ts +3843 -0
- package/src/init.ts +149 -0
- package/src/input.ts +8 -0
- package/src/ip-restrict.ts +192 -0
- package/src/logger.ts +149 -0
- package/src/middleware.ts +199 -0
- package/src/node-request.ts +240 -0
- package/src/node.ts +212 -0
- package/src/openapi/register.ts +19 -0
- package/src/openapi/scalar.ts +33 -0
- package/src/openapi/spec-factory.ts +46 -0
- package/src/openapi/spec.ts +31 -0
- package/src/openapi/swagger.ts +34 -0
- package/src/plugin.ts +771 -0
- package/src/powered-by.ts +17 -0
- package/src/pretty-json.ts +39 -0
- package/src/proxy.ts +155 -0
- package/src/python-type-emitter.ts +144 -0
- package/src/realtime/buffer.ts +101 -0
- package/src/realtime/bus.ts +93 -0
- package/src/realtime/longpoll.ts +148 -0
- package/src/realtime/protocol.ts +128 -0
- package/src/realtime/route.ts +104 -0
- package/src/realtime/runtime.ts +430 -0
- package/src/request-id.ts +23 -0
- package/src/request-to-curl.ts +34 -0
- package/src/response.ts +414 -0
- package/src/rust-type-emitter.ts +412 -0
- package/src/secure-headers.ts +69 -0
- package/src/serve-register.ts +8 -0
- package/src/serve-slot.ts +20 -0
- package/src/serve.ts +141 -0
- package/src/server-timing.ts +66 -0
- package/src/static.ts +60 -0
- package/src/telemetry/otel.ts +139 -0
- package/src/testing.ts +114 -0
- package/src/timeout.ts +36 -0
- package/src/tree.ts +464 -0
- package/src/ts-type-emitter.ts +106 -0
- package/src/type-emitter.ts +524 -0
- package/src/type-extractor.ts +749 -0
- package/src/types.ts +638 -0
- package/src/validation.ts +300 -0
- package/src/ws/bun.ts +89 -0
- package/src/ws/cloudflare.ts +133 -0
- package/src/ws/deno.ts +109 -0
- package/src/ws/node.ts +161 -0
- package/src/yaml.ts +80 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import type { StandardSchemaLike } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
const SAFE_KEY_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
4
|
+
|
|
5
|
+
function quoteKey(key: string): string {
|
|
6
|
+
return SAFE_KEY_RE.test(key) ? key : `"${key}"`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Wrap element type in parens when it contains union/intersection before appending [] */
|
|
10
|
+
function arrayOf(el: string): string {
|
|
11
|
+
return el.includes("|") || el.includes("&") ? `(${el})[]` : `${el}[]`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Emit a TypeScript type string from a Standard Schema compatible schema.
|
|
16
|
+
* Supports Zod (+ mini), Valibot, ArkType, Yup, and Effect.
|
|
17
|
+
* Unknown vendors return "unknown".
|
|
18
|
+
*/
|
|
19
|
+
export function emitSchemaType(schema: StandardSchemaLike): string {
|
|
20
|
+
const vendor = schema["~standard"].vendor;
|
|
21
|
+
if (vendor === "zod") return emitZod(schema);
|
|
22
|
+
if (vendor === "valibot") return emitValibot(schema);
|
|
23
|
+
if (vendor === "arktype") return emitArkType(schema);
|
|
24
|
+
if (vendor === "yup") return emitYup(schema);
|
|
25
|
+
if (vendor === "effect") return emitEffect(schema);
|
|
26
|
+
return "unknown";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* ---- Zod emitter (handles both full zod and zod/mini) ---- */
|
|
30
|
+
|
|
31
|
+
/** Unwrap nullable/default/catch wrappers to detect if optional is nested inside */
|
|
32
|
+
function zodIsOptional(def: Record<string, unknown>): boolean {
|
|
33
|
+
const t = (def.typeName as string) ?? (def.type as string)
|
|
34
|
+
if (t === "ZodOptional" || t === "optional") return true
|
|
35
|
+
if (t === "ZodNullable" || t === "nullable" || t === "ZodDefault" || t === "default" || t === "ZodCatch" || t === "catch") {
|
|
36
|
+
const inner = def.innerType as unknown
|
|
37
|
+
if (inner) return zodIsOptional(zodDef(inner))
|
|
38
|
+
}
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function zodDef(schema: unknown): Record<string, unknown> {
|
|
43
|
+
const s = schema as Record<string, unknown>;
|
|
44
|
+
return (s._def ?? s.def) as Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function emitZod(schema: unknown): string {
|
|
48
|
+
const def = zodDef(schema);
|
|
49
|
+
if (!def) return "unknown";
|
|
50
|
+
const typeName = (def.typeName ?? def.type) as string;
|
|
51
|
+
|
|
52
|
+
switch (typeName) {
|
|
53
|
+
case "ZodString":
|
|
54
|
+
case "string":
|
|
55
|
+
return "string";
|
|
56
|
+
case "ZodNumber":
|
|
57
|
+
case "number":
|
|
58
|
+
return "number";
|
|
59
|
+
case "ZodBoolean":
|
|
60
|
+
case "boolean":
|
|
61
|
+
return "boolean";
|
|
62
|
+
case "ZodBigInt":
|
|
63
|
+
case "bigint":
|
|
64
|
+
return "bigint";
|
|
65
|
+
case "ZodDate":
|
|
66
|
+
case "date":
|
|
67
|
+
return "Date";
|
|
68
|
+
case "ZodUndefined":
|
|
69
|
+
case "undefined":
|
|
70
|
+
return "undefined";
|
|
71
|
+
case "ZodNull":
|
|
72
|
+
case "null":
|
|
73
|
+
return "null";
|
|
74
|
+
case "ZodVoid":
|
|
75
|
+
case "void":
|
|
76
|
+
return "void";
|
|
77
|
+
case "ZodAny":
|
|
78
|
+
case "ZodUnknown":
|
|
79
|
+
case "any":
|
|
80
|
+
case "unknown":
|
|
81
|
+
return "unknown";
|
|
82
|
+
case "ZodNever":
|
|
83
|
+
case "never":
|
|
84
|
+
return "never";
|
|
85
|
+
case "ZodLiteral":
|
|
86
|
+
case "literal": {
|
|
87
|
+
/* v4: single value, v3: values array */
|
|
88
|
+
const val = def.value as unknown;
|
|
89
|
+
if (val !== undefined)
|
|
90
|
+
return typeof val === "string" ? `"${val}"` : String(val);
|
|
91
|
+
const values = def.values as unknown[];
|
|
92
|
+
if (values.length === 1) {
|
|
93
|
+
const v0 = values[0];
|
|
94
|
+
return typeof v0 === "string" ? `"${v0}"` : String(v0);
|
|
95
|
+
}
|
|
96
|
+
return values
|
|
97
|
+
.map((v0) => (typeof v0 === "string" ? `"${v0}"` : String(v0)))
|
|
98
|
+
.join(" | ");
|
|
99
|
+
}
|
|
100
|
+
case "ZodEnum":
|
|
101
|
+
case "enum": {
|
|
102
|
+
/* v4: values array, v3/mini: entries record */
|
|
103
|
+
const vals = (def.values ?? def.entries) as unknown;
|
|
104
|
+
if (Array.isArray(vals)) return vals.map((v0) => `"${v0}"`).join(" | ");
|
|
105
|
+
return Object.values(vals as Record<string, string>)
|
|
106
|
+
.map((v0) => `"${v0}"`)
|
|
107
|
+
.join(" | ");
|
|
108
|
+
}
|
|
109
|
+
case "ZodObject":
|
|
110
|
+
case "object": {
|
|
111
|
+
const rawShape = def.shape as
|
|
112
|
+
| Record<string, unknown>
|
|
113
|
+
| (() => Record<string, unknown>);
|
|
114
|
+
const shape = typeof rawShape === "function" ? rawShape() : rawShape;
|
|
115
|
+
const keys = Object.keys(shape);
|
|
116
|
+
if (keys.length === 0) return "{}";
|
|
117
|
+
return `{ ${keys
|
|
118
|
+
.map((k) => {
|
|
119
|
+
const propDef = zodDef(shape[k]);
|
|
120
|
+
const isOpt = propDef && zodIsOptional(propDef);
|
|
121
|
+
return `${quoteKey(k)}${isOpt ? "?" : ""}: ${emitZod(shape[k])}`;
|
|
122
|
+
})
|
|
123
|
+
.join("; ")} }`;
|
|
124
|
+
}
|
|
125
|
+
case "ZodArray":
|
|
126
|
+
/* v4: _def.type is the element schema */
|
|
127
|
+
return arrayOf(emitZod((def.type ?? def.element) as unknown));
|
|
128
|
+
case "array":
|
|
129
|
+
return arrayOf(emitZod(def.element as unknown));
|
|
130
|
+
case "ZodOptional":
|
|
131
|
+
case "optional":
|
|
132
|
+
return `${emitZod(def.innerType as unknown)} | undefined`;
|
|
133
|
+
case "ZodNullable":
|
|
134
|
+
case "nullable":
|
|
135
|
+
return `${emitZod(def.innerType as unknown)} | null`;
|
|
136
|
+
case "ZodUnion":
|
|
137
|
+
case "ZodDiscriminatedUnion":
|
|
138
|
+
case "union":
|
|
139
|
+
return (def.options as unknown[]).map((o) => emitZod(o)).join(" | ");
|
|
140
|
+
case "ZodIntersection":
|
|
141
|
+
case "intersection":
|
|
142
|
+
return `${emitZod(def.left as unknown)} & ${emitZod(def.right as unknown)}`;
|
|
143
|
+
case "ZodRecord":
|
|
144
|
+
case "record":
|
|
145
|
+
return `Partial<Record<${emitZod(def.keyType as unknown)}, ${emitZod(def.valueType as unknown)}>>`;
|
|
146
|
+
case "ZodTuple":
|
|
147
|
+
case "tuple":
|
|
148
|
+
return `[${(def.items as unknown[]).map((i) => emitZod(i)).join(", ")}]`;
|
|
149
|
+
case "ZodDefault":
|
|
150
|
+
case "ZodCatch":
|
|
151
|
+
case "default":
|
|
152
|
+
case "catch":
|
|
153
|
+
return emitZod(def.innerType as unknown);
|
|
154
|
+
case "ZodReadonly":
|
|
155
|
+
case "readonly":
|
|
156
|
+
return `Readonly<${emitZod(def.innerType as unknown)}>`;
|
|
157
|
+
case "ZodPipeline":
|
|
158
|
+
case "pipe":
|
|
159
|
+
return emitZod(def.out as unknown);
|
|
160
|
+
case "ZodBranded":
|
|
161
|
+
return emitZod(def.type as unknown);
|
|
162
|
+
case "ZodLazy":
|
|
163
|
+
case "lazy":
|
|
164
|
+
return "unknown";
|
|
165
|
+
default:
|
|
166
|
+
return "unknown";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ---- Valibot emitter ---- */
|
|
171
|
+
|
|
172
|
+
function emitValibot(schema: unknown): string {
|
|
173
|
+
const s = schema as Record<string, unknown>;
|
|
174
|
+
const type = s.type as string | undefined;
|
|
175
|
+
if (!type) return "unknown";
|
|
176
|
+
|
|
177
|
+
switch (type) {
|
|
178
|
+
case "string":
|
|
179
|
+
return "string";
|
|
180
|
+
case "number":
|
|
181
|
+
return "number";
|
|
182
|
+
case "boolean":
|
|
183
|
+
return "boolean";
|
|
184
|
+
case "bigint":
|
|
185
|
+
return "bigint";
|
|
186
|
+
case "date":
|
|
187
|
+
return "Date";
|
|
188
|
+
case "undefined":
|
|
189
|
+
return "undefined";
|
|
190
|
+
case "null":
|
|
191
|
+
return "null";
|
|
192
|
+
case "void":
|
|
193
|
+
return "void";
|
|
194
|
+
case "any":
|
|
195
|
+
case "unknown":
|
|
196
|
+
return "unknown";
|
|
197
|
+
case "never":
|
|
198
|
+
return "never";
|
|
199
|
+
case "literal": {
|
|
200
|
+
const val = s.literal as unknown;
|
|
201
|
+
return typeof val === "string" ? `"${val}"` : String(val);
|
|
202
|
+
}
|
|
203
|
+
case "enum": {
|
|
204
|
+
const enumObj = s.enum as Record<string, string>;
|
|
205
|
+
return Object.values(enumObj)
|
|
206
|
+
.map((val) => `"${val}"`)
|
|
207
|
+
.join(" | ");
|
|
208
|
+
}
|
|
209
|
+
case "picklist": {
|
|
210
|
+
const options = s.options as unknown[];
|
|
211
|
+
return options
|
|
212
|
+
.map((val) => (typeof val === "string" ? `"${val}"` : String(val)))
|
|
213
|
+
.join(" | ");
|
|
214
|
+
}
|
|
215
|
+
case "object": {
|
|
216
|
+
const entries = s.entries as Record<string, unknown>;
|
|
217
|
+
const keys = Object.keys(entries);
|
|
218
|
+
if (keys.length === 0) return "{}";
|
|
219
|
+
return `{ ${keys
|
|
220
|
+
.map((k) => {
|
|
221
|
+
const propType = (entries[k] as Record<string, unknown>).type as
|
|
222
|
+
| string
|
|
223
|
+
| undefined;
|
|
224
|
+
const isOpt = propType === "optional" || propType === "nullish";
|
|
225
|
+
return `${quoteKey(k)}${isOpt ? "?" : ""}: ${emitValibot(entries[k])}`;
|
|
226
|
+
})
|
|
227
|
+
.join("; ")} }`;
|
|
228
|
+
}
|
|
229
|
+
case "array":
|
|
230
|
+
return arrayOf(emitValibot(s.item as unknown));
|
|
231
|
+
case "optional":
|
|
232
|
+
return `${emitValibot(s.wrapped as unknown)} | undefined`;
|
|
233
|
+
case "nullable":
|
|
234
|
+
return `${emitValibot(s.wrapped as unknown)} | null`;
|
|
235
|
+
case "nullish":
|
|
236
|
+
return `${emitValibot(s.wrapped as unknown)} | null | undefined`;
|
|
237
|
+
case "union":
|
|
238
|
+
return (s.options as unknown[]).map((o) => emitValibot(o)).join(" | ");
|
|
239
|
+
case "intersect":
|
|
240
|
+
return (s.options as unknown[]).map((o) => emitValibot(o)).join(" & ");
|
|
241
|
+
case "record":
|
|
242
|
+
return `Record<${emitValibot(s.key as unknown)}, ${emitValibot(s.value as unknown)}>`;
|
|
243
|
+
case "tuple":
|
|
244
|
+
return `[${(s.items as unknown[]).map((i) => emitValibot(i)).join(", ")}]`;
|
|
245
|
+
default:
|
|
246
|
+
return "unknown";
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* ---- ArkType emitter ---- */
|
|
251
|
+
|
|
252
|
+
function emitArkType(schema: unknown): string {
|
|
253
|
+
const s = schema as Record<string, unknown>;
|
|
254
|
+
const json = s.json as unknown;
|
|
255
|
+
if (json === undefined || json === null) return "unknown";
|
|
256
|
+
return emitArkJson(json);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
type ArkEntry = { key: string; value: unknown };
|
|
260
|
+
|
|
261
|
+
function isBooleanUnion(node: unknown[]): boolean {
|
|
262
|
+
if (node.length !== 2) return false;
|
|
263
|
+
const units = node.map((n) => {
|
|
264
|
+
if (typeof n === "object" && n !== null && "unit" in n) {
|
|
265
|
+
return (n as Record<string, unknown>).unit;
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
});
|
|
269
|
+
return (
|
|
270
|
+
(units[0] === false && units[1] === true) ||
|
|
271
|
+
(units[0] === true && units[1] === false)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function emitArkJson(node: unknown): string {
|
|
276
|
+
if (typeof node === "string") return mapArkDomain(node);
|
|
277
|
+
|
|
278
|
+
if (Array.isArray(node)) {
|
|
279
|
+
if (node.length === 0) return "never";
|
|
280
|
+
if (isBooleanUnion(node)) return "boolean";
|
|
281
|
+
return node.map((n) => emitArkJson(n)).join(" | ");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const obj = node as Record<string, unknown>;
|
|
285
|
+
|
|
286
|
+
if ("unit" in obj) {
|
|
287
|
+
const val = obj.unit;
|
|
288
|
+
if (val === null) return "null";
|
|
289
|
+
/* arktype serializes undefined as the string "undefined" in JSON */
|
|
290
|
+
if (val === "undefined") return "undefined";
|
|
291
|
+
return typeof val === "string" ? `"${val}"` : String(val);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if ("sequence" in obj) {
|
|
295
|
+
return arrayOf(emitArkJson(obj.sequence));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if ("proto" in obj) {
|
|
299
|
+
return mapArkProto(obj.proto as string);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if ("domain" in obj) {
|
|
303
|
+
const domain = obj.domain as string;
|
|
304
|
+
|
|
305
|
+
if (domain === "object") {
|
|
306
|
+
const required = (obj.required as ArkEntry[] | undefined) ?? [];
|
|
307
|
+
const optional = (obj.optional as ArkEntry[] | undefined) ?? [];
|
|
308
|
+
if (required.length === 0 && optional.length === 0) return "{}";
|
|
309
|
+
const parts: string[] = [];
|
|
310
|
+
for (const entry of required) {
|
|
311
|
+
parts.push(
|
|
312
|
+
`${quoteKey(String(entry.key))}: ${emitArkJson(entry.value)}`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
for (const entry of optional) {
|
|
316
|
+
parts.push(
|
|
317
|
+
`${quoteKey(String(entry.key))}?: ${emitArkJson(entry.value)}`,
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
return `{ ${parts.join("; ")} }`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return mapArkDomain(domain);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return "unknown";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function mapArkDomain(domain: string): string {
|
|
330
|
+
switch (domain) {
|
|
331
|
+
case "string":
|
|
332
|
+
return "string";
|
|
333
|
+
case "number":
|
|
334
|
+
case "integer":
|
|
335
|
+
return "number";
|
|
336
|
+
case "boolean":
|
|
337
|
+
return "boolean";
|
|
338
|
+
case "bigint":
|
|
339
|
+
return "bigint";
|
|
340
|
+
case "symbol":
|
|
341
|
+
return "symbol";
|
|
342
|
+
case "undefined":
|
|
343
|
+
return "undefined";
|
|
344
|
+
case "null":
|
|
345
|
+
return "null";
|
|
346
|
+
case "object":
|
|
347
|
+
return "object";
|
|
348
|
+
default:
|
|
349
|
+
return "unknown";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function mapArkProto(proto: string): string {
|
|
354
|
+
switch (proto) {
|
|
355
|
+
case "Date":
|
|
356
|
+
return "Date";
|
|
357
|
+
case "Array":
|
|
358
|
+
return "unknown[]";
|
|
359
|
+
case "RegExp":
|
|
360
|
+
return "RegExp";
|
|
361
|
+
case "Map":
|
|
362
|
+
return "Map<unknown, unknown>";
|
|
363
|
+
case "Set":
|
|
364
|
+
return "Set<unknown>";
|
|
365
|
+
default:
|
|
366
|
+
return "unknown";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* ---- Yup emitter ---- */
|
|
371
|
+
|
|
372
|
+
type YupDesc = {
|
|
373
|
+
fields?: Record<string, YupDesc>;
|
|
374
|
+
innerType?: YupDesc | YupDesc[];
|
|
375
|
+
nullable?: boolean;
|
|
376
|
+
oneOf?: unknown[];
|
|
377
|
+
optional?: boolean;
|
|
378
|
+
type: string;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
function emitYup(schema: unknown): string {
|
|
382
|
+
const s = schema as Record<string, unknown>;
|
|
383
|
+
if (typeof s.describe !== "function") return "unknown";
|
|
384
|
+
const desc = s.describe() as YupDesc;
|
|
385
|
+
return emitYupDesc(desc);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function emitYupDesc(desc: YupDesc): string {
|
|
389
|
+
let base = emitYupBase(desc);
|
|
390
|
+
if (desc.nullable) base = `${base} | null`;
|
|
391
|
+
return base;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function emitYupBase(desc: YupDesc): string {
|
|
395
|
+
switch (desc.type) {
|
|
396
|
+
case "string":
|
|
397
|
+
return "string";
|
|
398
|
+
case "number":
|
|
399
|
+
return "number";
|
|
400
|
+
case "boolean":
|
|
401
|
+
return "boolean";
|
|
402
|
+
case "date":
|
|
403
|
+
return "Date";
|
|
404
|
+
case "object": {
|
|
405
|
+
if (!desc.fields) return "{}";
|
|
406
|
+
const keys = Object.keys(desc.fields);
|
|
407
|
+
if (keys.length === 0) return "{}";
|
|
408
|
+
const parts = keys.map((k) => {
|
|
409
|
+
const field = desc.fields?.[k];
|
|
410
|
+
if (!field) return `${quoteKey(k)}: unknown`;
|
|
411
|
+
const opt = field.optional ? "?" : "";
|
|
412
|
+
return `${quoteKey(k)}${opt}: ${emitYupDesc(field)}`;
|
|
413
|
+
});
|
|
414
|
+
return `{ ${parts.join("; ")} }`;
|
|
415
|
+
}
|
|
416
|
+
case "array": {
|
|
417
|
+
if (!desc.innerType || Array.isArray(desc.innerType)) return "unknown[]";
|
|
418
|
+
return arrayOf(emitYupDesc(desc.innerType));
|
|
419
|
+
}
|
|
420
|
+
case "tuple": {
|
|
421
|
+
if (!Array.isArray(desc.innerType)) return "unknown";
|
|
422
|
+
return `[${desc.innerType.map((i) => emitYupDesc(i)).join(", ")}]`;
|
|
423
|
+
}
|
|
424
|
+
case "mixed": {
|
|
425
|
+
if (desc.oneOf && desc.oneOf.length > 0) {
|
|
426
|
+
return desc.oneOf
|
|
427
|
+
.map((val) => (typeof val === "string" ? `"${val}"` : String(val)))
|
|
428
|
+
.join(" | ");
|
|
429
|
+
}
|
|
430
|
+
return "unknown";
|
|
431
|
+
}
|
|
432
|
+
case "lazy":
|
|
433
|
+
return "unknown";
|
|
434
|
+
default:
|
|
435
|
+
return "unknown";
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* ---- Effect Schema emitter ---- */
|
|
440
|
+
|
|
441
|
+
type EffectAST = {
|
|
442
|
+
_tag: string;
|
|
443
|
+
elements?: Array<{ isOptional: boolean; type: EffectAST }>;
|
|
444
|
+
indexSignatures?: Array<{ parameter: EffectAST; type: EffectAST }>;
|
|
445
|
+
literal?: unknown;
|
|
446
|
+
propertySignatures?: Array<{
|
|
447
|
+
isOptional: boolean;
|
|
448
|
+
name: string;
|
|
449
|
+
type: EffectAST;
|
|
450
|
+
}>;
|
|
451
|
+
rest?: Array<{ type: EffectAST }>;
|
|
452
|
+
types?: EffectAST[];
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
function emitEffect(schema: unknown): string {
|
|
456
|
+
const s = schema as Record<string, unknown>;
|
|
457
|
+
const ast = s.ast as EffectAST | undefined;
|
|
458
|
+
if (!ast) return "unknown";
|
|
459
|
+
return emitEffectAst(ast);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function emitEffectAst(ast: EffectAST): string {
|
|
463
|
+
switch (ast._tag) {
|
|
464
|
+
case "StringKeyword":
|
|
465
|
+
return "string";
|
|
466
|
+
case "NumberKeyword":
|
|
467
|
+
return "number";
|
|
468
|
+
case "BooleanKeyword":
|
|
469
|
+
return "boolean";
|
|
470
|
+
case "BigIntKeyword":
|
|
471
|
+
return "bigint";
|
|
472
|
+
case "SymbolKeyword":
|
|
473
|
+
return "symbol";
|
|
474
|
+
case "UndefinedKeyword":
|
|
475
|
+
return "undefined";
|
|
476
|
+
case "VoidKeyword":
|
|
477
|
+
return "void";
|
|
478
|
+
case "UnknownKeyword":
|
|
479
|
+
case "AnyKeyword":
|
|
480
|
+
return "unknown";
|
|
481
|
+
case "NeverKeyword":
|
|
482
|
+
return "never";
|
|
483
|
+
case "ObjectKeyword":
|
|
484
|
+
return "object";
|
|
485
|
+
case "Literal": {
|
|
486
|
+
const val = ast.literal;
|
|
487
|
+
if (val === null) return "null";
|
|
488
|
+
return typeof val === "string" ? `"${val}"` : String(val);
|
|
489
|
+
}
|
|
490
|
+
case "Union":
|
|
491
|
+
return (ast.types ?? []).map((t) => emitEffectAst(t)).join(" | ");
|
|
492
|
+
case "TypeLiteral": {
|
|
493
|
+
const props = ast.propertySignatures ?? [];
|
|
494
|
+
const idxSigs = ast.indexSignatures ?? [];
|
|
495
|
+
if (props.length === 0 && idxSigs.length === 0) return "{}";
|
|
496
|
+
if (props.length === 0 && idxSigs.length === 1) {
|
|
497
|
+
return `Record<${emitEffectAst(idxSigs[0].parameter)}, ${emitEffectAst(idxSigs[0].type)}>`;
|
|
498
|
+
}
|
|
499
|
+
const parts = props.map((p) => {
|
|
500
|
+
const opt = p.isOptional ? "?" : "";
|
|
501
|
+
return `${quoteKey(p.name as string)}${opt}: ${emitEffectAst(p.type)}`;
|
|
502
|
+
});
|
|
503
|
+
return `{ ${parts.join("; ")} }`;
|
|
504
|
+
}
|
|
505
|
+
case "TupleType": {
|
|
506
|
+
const elems = ast.elements ?? [];
|
|
507
|
+
const rest = ast.rest;
|
|
508
|
+
if (elems.length === 0 && rest && rest.length === 1) {
|
|
509
|
+
return arrayOf(emitEffectAst(rest[0].type));
|
|
510
|
+
}
|
|
511
|
+
if (elems.length > 0 && (!rest || rest.length === 0)) {
|
|
512
|
+
return `[${elems.map((e) => emitEffectAst(e.type)).join(", ")}]`;
|
|
513
|
+
}
|
|
514
|
+
if (elems.length === 0) return "unknown[]";
|
|
515
|
+
return `[${elems.map((e) => emitEffectAst(e.type)).join(", ")}]`;
|
|
516
|
+
}
|
|
517
|
+
case "Declaration":
|
|
518
|
+
return "unknown";
|
|
519
|
+
case "Suspend":
|
|
520
|
+
return "unknown";
|
|
521
|
+
default:
|
|
522
|
+
return "unknown";
|
|
523
|
+
}
|
|
524
|
+
}
|