silgi 0.0.13 → 0.1.0-beta.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.
- package/README.md +102 -1
- package/dist/_virtual/_rolldown/runtime.mjs +5 -0
- package/dist/adapters/astro.d.mts +17 -0
- package/dist/adapters/astro.mjs +24 -0
- package/dist/adapters/aws-lambda.d.mts +31 -0
- package/dist/adapters/aws-lambda.mjs +85 -0
- package/dist/adapters/elysia.d.mts +17 -0
- package/dist/adapters/elysia.mjs +76 -0
- package/dist/adapters/express.d.mts +16 -0
- package/dist/adapters/express.mjs +78 -0
- package/dist/adapters/fastify.d.mts +15 -0
- package/dist/adapters/fastify.mjs +78 -0
- package/dist/adapters/message-port.d.mts +37 -0
- package/dist/adapters/message-port.mjs +129 -0
- package/dist/adapters/nestjs.d.mts +25 -0
- package/dist/adapters/nestjs.mjs +91 -0
- package/dist/adapters/nextjs.d.mts +21 -0
- package/dist/adapters/nextjs.mjs +30 -0
- package/dist/adapters/peer.d.mts +27 -0
- package/dist/adapters/peer.mjs +36 -0
- package/dist/adapters/remix.d.mts +17 -0
- package/dist/adapters/remix.mjs +24 -0
- package/dist/adapters/solidstart.d.mts +14 -0
- package/dist/adapters/solidstart.mjs +30 -0
- package/dist/adapters/sveltekit.d.mts +18 -0
- package/dist/adapters/sveltekit.mjs +33 -0
- package/dist/analyze.mjs +26 -0
- package/dist/broker/index.d.mts +62 -0
- package/dist/broker/index.mjs +153 -0
- package/dist/broker/nats.d.mts +33 -0
- package/dist/broker/nats.mjs +31 -0
- package/dist/broker/redis.d.mts +51 -0
- package/dist/broker/redis.mjs +92 -0
- package/dist/builder.d.mts +36 -0
- package/dist/builder.mjs +51 -0
- package/dist/callable.d.mts +17 -0
- package/dist/callable.mjs +42 -0
- package/dist/client/adapters/fetch/index.d.mts +17 -0
- package/dist/client/adapters/fetch/index.mjs +61 -0
- package/dist/client/adapters/ofetch/index.d.mts +41 -0
- package/dist/client/adapters/ofetch/index.mjs +92 -0
- package/dist/client/client.d.mts +29 -0
- package/dist/client/client.mjs +54 -0
- package/dist/client/dynamic-link.d.mts +15 -0
- package/dist/client/dynamic-link.mjs +16 -0
- package/dist/client/index.d.mts +7 -0
- package/dist/client/index.mjs +6 -0
- package/dist/client/interceptor.d.mts +31 -0
- package/dist/client/interceptor.mjs +34 -0
- package/dist/client/merge.d.mts +28 -0
- package/dist/client/merge.mjs +30 -0
- package/dist/client/openapi.d.mts +29 -0
- package/dist/client/openapi.mjs +89 -0
- package/dist/client/plugins/batch.d.mts +20 -0
- package/dist/client/plugins/batch.mjs +64 -0
- package/dist/client/plugins/csrf.d.mts +13 -0
- package/dist/client/plugins/csrf.mjs +20 -0
- package/dist/client/plugins/dedupe.d.mts +10 -0
- package/dist/client/plugins/dedupe.mjs +28 -0
- package/dist/client/plugins/index.d.mts +5 -0
- package/dist/client/plugins/index.mjs +5 -0
- package/dist/client/plugins/retry.d.mts +11 -0
- package/dist/client/plugins/retry.mjs +21 -0
- package/dist/client/server.d.mts +16 -0
- package/dist/client/server.mjs +60 -0
- package/dist/client/types.d.mts +29 -0
- package/dist/codec/devalue.d.mts +21 -0
- package/dist/codec/devalue.mjs +32 -0
- package/dist/codec/msgpack.d.mts +21 -0
- package/dist/codec/msgpack.mjs +59 -0
- package/dist/compile.d.mts +52 -0
- package/dist/compile.mjs +304 -0
- package/dist/contract.d.mts +36 -0
- package/dist/contract.mjs +40 -0
- package/dist/core/error.d.mts +104 -0
- package/dist/core/error.mjs +139 -0
- package/dist/core/handler.mjs +546 -0
- package/dist/core/iterator.d.mts +17 -0
- package/dist/core/iterator.mjs +79 -0
- package/dist/core/router-utils.mjs +16 -0
- package/dist/core/schema.d.mts +19 -0
- package/dist/core/schema.mjs +26 -0
- package/dist/core/serve.mjs +38 -0
- package/dist/core/sse.d.mts +16 -0
- package/dist/core/sse.mjs +95 -0
- package/dist/core/storage.d.mts +21 -0
- package/dist/core/storage.mjs +63 -0
- package/dist/core/utils.mjs +21 -0
- package/dist/fast-stringify.mjs +125 -0
- package/dist/index.d.mts +15 -37
- package/dist/index.mjs +13 -7
- package/dist/integrations/ai/index.d.mts +25 -0
- package/dist/integrations/ai/index.mjs +116 -0
- package/dist/integrations/react/index.d.mts +83 -0
- package/dist/integrations/react/index.mjs +197 -0
- package/dist/integrations/tanstack-query/index.d.mts +120 -0
- package/dist/integrations/tanstack-query/index.mjs +100 -0
- package/dist/integrations/tanstack-query/ssr.d.mts +51 -0
- package/dist/integrations/tanstack-query/ssr.mjs +89 -0
- package/dist/integrations/zod/converter.d.mts +75 -0
- package/dist/integrations/zod/converter.mjs +345 -0
- package/dist/integrations/zod/index.d.mts +2 -0
- package/dist/integrations/zod/index.mjs +2 -0
- package/dist/lazy.d.mts +24 -0
- package/dist/lazy.mjs +27 -0
- package/dist/lifecycle.d.mts +36 -0
- package/dist/lifecycle.mjs +46 -0
- package/dist/map-input.d.mts +17 -0
- package/dist/map-input.mjs +24 -0
- package/dist/plugins/analytics.d.mts +168 -0
- package/dist/plugins/analytics.mjs +459 -0
- package/dist/plugins/batch-server.d.mts +20 -0
- package/dist/plugins/batch-server.mjs +86 -0
- package/dist/plugins/body-limit.d.mts +16 -0
- package/dist/plugins/body-limit.mjs +44 -0
- package/dist/plugins/cache.d.mts +170 -0
- package/dist/plugins/cache.mjs +200 -0
- package/dist/plugins/coerce.d.mts +21 -0
- package/dist/plugins/coerce.mjs +46 -0
- package/dist/plugins/compression.d.mts +19 -0
- package/dist/plugins/compression.mjs +23 -0
- package/dist/plugins/cookies.d.mts +44 -0
- package/dist/plugins/cookies.mjs +67 -0
- package/dist/plugins/cors.d.mts +39 -0
- package/dist/plugins/cors.mjs +56 -0
- package/dist/plugins/custom-serializer.d.mts +57 -0
- package/dist/plugins/custom-serializer.mjs +40 -0
- package/dist/plugins/file-upload.d.mts +38 -0
- package/dist/plugins/file-upload.mjs +100 -0
- package/dist/plugins/index.d.mts +16 -0
- package/dist/plugins/index.mjs +16 -0
- package/dist/plugins/otel.d.mts +35 -0
- package/dist/plugins/otel.mjs +40 -0
- package/dist/plugins/pino.d.mts +60 -0
- package/dist/plugins/pino.mjs +42 -0
- package/dist/plugins/pubsub.d.mts +50 -0
- package/dist/plugins/pubsub.mjs +53 -0
- package/dist/plugins/ratelimit.d.mts +51 -0
- package/dist/plugins/ratelimit.mjs +81 -0
- package/dist/plugins/signing.d.mts +41 -0
- package/dist/plugins/signing.mjs +115 -0
- package/dist/plugins/strict-get.d.mts +10 -0
- package/dist/plugins/strict-get.mjs +33 -0
- package/dist/route/add.mjs +240 -0
- package/dist/route/compiler.mjs +373 -0
- package/dist/route/context.mjs +12 -0
- package/dist/route/types.d.mts +11 -0
- package/dist/route/utils.mjs +17 -0
- package/dist/scalar.d.mts +53 -0
- package/dist/scalar.mjs +315 -0
- package/dist/silgi.d.mts +139 -0
- package/dist/silgi.mjs +113 -0
- package/dist/trpc-interop.d.mts +22 -0
- package/dist/trpc-interop.mjs +68 -0
- package/dist/types.d.mts +82 -0
- package/dist/ws.d.mts +42 -0
- package/dist/ws.mjs +137 -0
- package/lib/dashboard/index.html +123 -0
- package/lib/ocache.d.mts +1 -0
- package/lib/ocache.mjs +1 -0
- package/lib/ofetch.d.mts +1 -0
- package/lib/ofetch.mjs +1 -0
- package/lib/srvx.d.mts +1 -0
- package/lib/srvx.mjs +1 -0
- package/lib/unstorage.d.mts +1 -0
- package/lib/unstorage.mjs +1 -0
- package/package.json +291 -65
- package/bin/silgi.mjs +0 -3
- package/dist/chunks/generate.mjs +0 -933
- package/dist/chunks/init.mjs +0 -21
- package/dist/cli/config.d.mts +0 -19
- package/dist/cli/config.d.ts +0 -19
- package/dist/cli/config.mjs +0 -5
- package/dist/cli/index.d.mts +0 -2
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.mjs +0 -119
- package/dist/index.d.ts +0 -37
- package/dist/plugins/openapi.d.mts +0 -138
- package/dist/plugins/openapi.d.ts +0 -138
- package/dist/plugins/openapi.mjs +0 -204
- package/dist/plugins/scalar.d.mts +0 -14
- package/dist/plugins/scalar.d.ts +0 -14
- package/dist/plugins/scalar.mjs +0 -66
- package/dist/shared/silgi.BMCYk2cR.mjs +0 -841
- package/dist/shared/silgi.D5qK9QOm.d.mts +0 -301
- package/dist/shared/silgi.D5qK9QOm.d.ts +0 -301
package/dist/scalar.mjs
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
//#region src/scalar.ts
|
|
2
|
+
/**
|
|
3
|
+
* Generate OpenAPI 3.1.0 document from a v2 RouterDef.
|
|
4
|
+
*/
|
|
5
|
+
function generateOpenAPI(router, options = {}) {
|
|
6
|
+
const paths = {};
|
|
7
|
+
const tags = /* @__PURE__ */ new Map();
|
|
8
|
+
collectProcedures(router, [], (path, proc) => {
|
|
9
|
+
const route = proc.route;
|
|
10
|
+
const httpPath = route?.path ?? "/" + path.join("/");
|
|
11
|
+
const method = route?.method?.toLowerCase() ?? "post";
|
|
12
|
+
const operationId = path.join("_");
|
|
13
|
+
if (path.length > 1) {
|
|
14
|
+
const tagName = path[0];
|
|
15
|
+
if (!tags.has(tagName)) tags.set(tagName, {});
|
|
16
|
+
}
|
|
17
|
+
let description = route?.description;
|
|
18
|
+
if (route?.ws) {
|
|
19
|
+
const wsNote = "Also available over WebSocket (`ws://`). Send `{ id, path: \"" + path.join("/") + "\", input }` as JSON.";
|
|
20
|
+
description = description ? `${description}\n\n${wsNote}` : wsNote;
|
|
21
|
+
}
|
|
22
|
+
const operation = {
|
|
23
|
+
operationId,
|
|
24
|
+
tags: path.length > 1 ? [path[0]] : void 0,
|
|
25
|
+
summary: route?.summary,
|
|
26
|
+
description,
|
|
27
|
+
deprecated: route?.deprecated || void 0,
|
|
28
|
+
responses: {}
|
|
29
|
+
};
|
|
30
|
+
if (!operation.summary) delete operation.summary;
|
|
31
|
+
if (!operation.description) delete operation.description;
|
|
32
|
+
if (!operation.deprecated) delete operation.deprecated;
|
|
33
|
+
if (options.security) operation.security = [{ auth: [] }];
|
|
34
|
+
if (proc.input) {
|
|
35
|
+
const schema = zodToJsonSchema(proc.input);
|
|
36
|
+
if (method === "get") operation.parameters = objectSchemaToParams(schema);
|
|
37
|
+
else operation.requestBody = {
|
|
38
|
+
required: true,
|
|
39
|
+
content: { "application/json": { schema } }
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const successStatus = route?.successStatus ?? 200;
|
|
43
|
+
const successDesc = route?.successDescription ?? "Successful response";
|
|
44
|
+
if (proc.output) {
|
|
45
|
+
const schema = zodToJsonSchema(proc.output);
|
|
46
|
+
operation.responses[String(successStatus)] = {
|
|
47
|
+
description: successDesc,
|
|
48
|
+
content: { "application/json": { schema } }
|
|
49
|
+
};
|
|
50
|
+
} else operation.responses[String(successStatus)] = { description: successDesc };
|
|
51
|
+
const guards = (proc.use ?? []).filter((m) => m.kind === "guard" && m.errors);
|
|
52
|
+
let allErrors = proc.errors ? { ...proc.errors } : null;
|
|
53
|
+
for (const guard of guards) {
|
|
54
|
+
const ge = guard.errors;
|
|
55
|
+
if (ge) allErrors = allErrors ? {
|
|
56
|
+
...allErrors,
|
|
57
|
+
...ge
|
|
58
|
+
} : { ...ge };
|
|
59
|
+
}
|
|
60
|
+
if (allErrors) {
|
|
61
|
+
const byStatus = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const [code, def] of Object.entries(allErrors)) {
|
|
63
|
+
const status = typeof def === "number" ? def : def.status;
|
|
64
|
+
if (!byStatus.has(status)) byStatus.set(status, []);
|
|
65
|
+
const entry = { code };
|
|
66
|
+
if (typeof def === "object" && def.data) entry.schema = zodToJsonSchema(def.data);
|
|
67
|
+
byStatus.get(status).push(entry);
|
|
68
|
+
}
|
|
69
|
+
for (const [status, errors] of byStatus) {
|
|
70
|
+
const errorSchemas = errors.map((e) => {
|
|
71
|
+
const s = {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {
|
|
74
|
+
code: {
|
|
75
|
+
const: e.code,
|
|
76
|
+
type: "string"
|
|
77
|
+
},
|
|
78
|
+
status: {
|
|
79
|
+
const: status,
|
|
80
|
+
type: "integer"
|
|
81
|
+
},
|
|
82
|
+
message: { type: "string" }
|
|
83
|
+
},
|
|
84
|
+
required: [
|
|
85
|
+
"code",
|
|
86
|
+
"status",
|
|
87
|
+
"message"
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
if (e.schema) {
|
|
91
|
+
s.properties.data = e.schema;
|
|
92
|
+
s.required.push("data");
|
|
93
|
+
}
|
|
94
|
+
return s;
|
|
95
|
+
});
|
|
96
|
+
operation.responses[String(status)] = {
|
|
97
|
+
description: errors.map((e) => e.code).join(" | "),
|
|
98
|
+
content: { "application/json": { schema: errorSchemas.length === 1 ? errorSchemas[0] : { oneOf: errorSchemas } } }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (proc.type === "subscription") operation.responses[String(successStatus)] = {
|
|
103
|
+
description: "SSE event stream",
|
|
104
|
+
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
105
|
+
};
|
|
106
|
+
paths[httpPath] ??= {};
|
|
107
|
+
paths[httpPath][method] = operation;
|
|
108
|
+
});
|
|
109
|
+
const doc = {
|
|
110
|
+
openapi: "3.1.0",
|
|
111
|
+
info: {
|
|
112
|
+
title: options.title ?? "Silgi API",
|
|
113
|
+
version: options.version ?? "1.0.0",
|
|
114
|
+
...options.description ? { description: options.description } : {},
|
|
115
|
+
...options.contact ? { contact: options.contact } : {},
|
|
116
|
+
...options.license ? { license: options.license } : {}
|
|
117
|
+
},
|
|
118
|
+
paths
|
|
119
|
+
};
|
|
120
|
+
if (options.servers?.length) doc.servers = options.servers;
|
|
121
|
+
if (options.externalDocs) doc.externalDocs = options.externalDocs;
|
|
122
|
+
if (tags.size > 0) doc.tags = [...tags.entries()].map(([name, meta]) => ({
|
|
123
|
+
name,
|
|
124
|
+
...meta.description ? { description: meta.description } : {}
|
|
125
|
+
}));
|
|
126
|
+
if (options.security) {
|
|
127
|
+
const scheme = { type: options.security.type };
|
|
128
|
+
if (options.security.type === "http") {
|
|
129
|
+
scheme.scheme = options.security.scheme ?? "bearer";
|
|
130
|
+
if (options.security.bearerFormat) scheme.bearerFormat = options.security.bearerFormat;
|
|
131
|
+
} else if (options.security.type === "apiKey") {
|
|
132
|
+
scheme.in = options.security.in ?? "header";
|
|
133
|
+
scheme.name = options.security.name ?? "x-api-key";
|
|
134
|
+
}
|
|
135
|
+
if (options.security.description) scheme.description = options.security.description;
|
|
136
|
+
doc.components = { securitySchemes: { auth: scheme } };
|
|
137
|
+
}
|
|
138
|
+
return doc;
|
|
139
|
+
}
|
|
140
|
+
const SCALAR_CDN_SOURCES = {
|
|
141
|
+
cdn: "https://cdn.jsdelivr.net/npm/@scalar/api-reference",
|
|
142
|
+
unpkg: "https://unpkg.com/@scalar/api-reference",
|
|
143
|
+
local: "/__silgi/scalar.js"
|
|
144
|
+
};
|
|
145
|
+
function scalarHTML(specUrl, options = {}) {
|
|
146
|
+
const title = escapeHtml(options.title ?? "Silgi API");
|
|
147
|
+
const safeUrl = escapeHtml(specUrl);
|
|
148
|
+
const cdnOption = options.cdn ?? "cdn";
|
|
149
|
+
return `<!DOCTYPE html>
|
|
150
|
+
<html>
|
|
151
|
+
<head>
|
|
152
|
+
<title>${title} — Scalar</title>
|
|
153
|
+
<meta charset="utf-8" />
|
|
154
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
155
|
+
</head>
|
|
156
|
+
<body>
|
|
157
|
+
<script id="api-reference" data-url="${safeUrl}"><\/script>
|
|
158
|
+
<script src="${escapeHtml(SCALAR_CDN_SOURCES[cdnOption] ?? cdnOption)}"><\/script>
|
|
159
|
+
</body>
|
|
160
|
+
</html>`;
|
|
161
|
+
}
|
|
162
|
+
function escapeHtml(s) {
|
|
163
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
164
|
+
}
|
|
165
|
+
function isProcedureDef(value) {
|
|
166
|
+
return typeof value === "object" && value !== null && "type" in value && "resolve" in value && typeof value.resolve === "function";
|
|
167
|
+
}
|
|
168
|
+
function collectProcedures(node, path, cb) {
|
|
169
|
+
if (isProcedureDef(node)) {
|
|
170
|
+
cb(path, node);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (typeof node === "object" && node !== null) for (const [key, child] of Object.entries(node)) collectProcedures(child, [...path, key], cb);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Convert a Zod / Standard Schema to JSON Schema.
|
|
177
|
+
*/
|
|
178
|
+
function zodToJsonSchema(schema) {
|
|
179
|
+
const zod = schema._zod ?? schema._def;
|
|
180
|
+
if (!zod) return {};
|
|
181
|
+
return convertZodDef(zod.def ?? zod);
|
|
182
|
+
}
|
|
183
|
+
function convertZodDef(def) {
|
|
184
|
+
if (!def) return {};
|
|
185
|
+
switch (def.type ?? def.typeName) {
|
|
186
|
+
case "string": return applyStringChecks({ type: "string" }, def.checks);
|
|
187
|
+
case "number":
|
|
188
|
+
case "float": return applyNumberChecks({ type: "number" }, def.checks);
|
|
189
|
+
case "int": return applyNumberChecks({ type: "integer" }, def.checks);
|
|
190
|
+
case "boolean": return { type: "boolean" };
|
|
191
|
+
case "bigint": return {
|
|
192
|
+
type: "integer",
|
|
193
|
+
format: "int64"
|
|
194
|
+
};
|
|
195
|
+
case "date": return {
|
|
196
|
+
type: "string",
|
|
197
|
+
format: "date-time"
|
|
198
|
+
};
|
|
199
|
+
case "object": {
|
|
200
|
+
const schema = {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {},
|
|
203
|
+
required: []
|
|
204
|
+
};
|
|
205
|
+
if (def.shape) for (const [key, fieldSchema] of Object.entries(def.shape)) {
|
|
206
|
+
schema.properties[key] = zodToJsonSchema(fieldSchema);
|
|
207
|
+
const fz = fieldSchema?._zod?.def ?? fieldSchema?._def;
|
|
208
|
+
if (!(fz?.type === "optional" || fz?.typeName === "ZodOptional" || fz?.optional)) schema.required.push(key);
|
|
209
|
+
}
|
|
210
|
+
if (!schema.required.length) delete schema.required;
|
|
211
|
+
if (def.description) schema.description = def.description;
|
|
212
|
+
return schema;
|
|
213
|
+
}
|
|
214
|
+
case "array": return {
|
|
215
|
+
type: "array",
|
|
216
|
+
...def.element ? { items: zodToJsonSchema(def.element) } : {}
|
|
217
|
+
};
|
|
218
|
+
case "tuple": return {
|
|
219
|
+
type: "array",
|
|
220
|
+
prefixItems: (def.items ?? []).map((item) => zodToJsonSchema(item))
|
|
221
|
+
};
|
|
222
|
+
case "record": return {
|
|
223
|
+
type: "object",
|
|
224
|
+
additionalProperties: def.valueType ? zodToJsonSchema(def.valueType) : true
|
|
225
|
+
};
|
|
226
|
+
case "map": return {
|
|
227
|
+
type: "object",
|
|
228
|
+
description: "Map (serialized as object)"
|
|
229
|
+
};
|
|
230
|
+
case "set": return {
|
|
231
|
+
type: "array",
|
|
232
|
+
uniqueItems: true,
|
|
233
|
+
...def.valueType ? { items: zodToJsonSchema(def.valueType) } : {}
|
|
234
|
+
};
|
|
235
|
+
case "optional": return zodToJsonSchema(def.innerType ?? def.inner);
|
|
236
|
+
case "nullable": return { anyOf: [zodToJsonSchema(def.innerType ?? def.inner), { type: "null" }] };
|
|
237
|
+
case "default": {
|
|
238
|
+
const inner = zodToJsonSchema(def.innerType ?? def.inner);
|
|
239
|
+
const defaultVal = typeof def.defaultValue === "function" ? def.defaultValue() : def.default;
|
|
240
|
+
return {
|
|
241
|
+
...inner,
|
|
242
|
+
default: defaultVal
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
case "enum": return {
|
|
246
|
+
type: "string",
|
|
247
|
+
enum: def.values ?? def.entries
|
|
248
|
+
};
|
|
249
|
+
case "nativeEnum": return { enum: Object.values(def.values ?? {}).filter((v) => typeof v !== "number" || !def.values[v]) };
|
|
250
|
+
case "literal": return { const: def.value };
|
|
251
|
+
case "union": return { anyOf: (def.options ?? def.members ?? []).map((o) => zodToJsonSchema(o)) };
|
|
252
|
+
case "discriminatedUnion": return { oneOf: (def.options ?? []).map((o) => zodToJsonSchema(o)) };
|
|
253
|
+
case "intersection": return { allOf: [zodToJsonSchema(def.left), zodToJsonSchema(def.right)] };
|
|
254
|
+
case "pipe":
|
|
255
|
+
case "transform": return zodToJsonSchema(def.in ?? def.innerType ?? def.input);
|
|
256
|
+
case "lazy": return def.getter ? zodToJsonSchema(def.getter()) : {};
|
|
257
|
+
case "any":
|
|
258
|
+
case "unknown": return {};
|
|
259
|
+
case "void":
|
|
260
|
+
case "undefined": return { type: "null" };
|
|
261
|
+
case "never": return { not: {} };
|
|
262
|
+
case "null": return { type: "null" };
|
|
263
|
+
default: return {};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function applyStringChecks(schema, checks) {
|
|
267
|
+
if (!checks) return schema;
|
|
268
|
+
for (const c of checks) {
|
|
269
|
+
const k = c.kind ?? c.type;
|
|
270
|
+
if (k === "min" || k === "min_length") schema.minLength = c.value ?? c.minimum;
|
|
271
|
+
if (k === "max" || k === "max_length") schema.maxLength = c.value ?? c.maximum;
|
|
272
|
+
if (k === "length") {
|
|
273
|
+
schema.minLength = c.value;
|
|
274
|
+
schema.maxLength = c.value;
|
|
275
|
+
}
|
|
276
|
+
if (k === "email" || c.format === "email") schema.format = "email";
|
|
277
|
+
if (k === "url") schema.format = "uri";
|
|
278
|
+
if (k === "uuid") schema.format = "uuid";
|
|
279
|
+
if (k === "cuid") schema.format = "cuid";
|
|
280
|
+
if (k === "ulid") schema.format = "ulid";
|
|
281
|
+
if (k === "datetime" || k === "iso_datetime") schema.format = "date-time";
|
|
282
|
+
if (k === "ip") schema.format = "ipv4";
|
|
283
|
+
if (k === "regex") schema.pattern = String(c.value ?? c.regex);
|
|
284
|
+
if (k === "includes") schema.pattern = c.value;
|
|
285
|
+
if (k === "startsWith") schema.pattern = `^${c.value}`;
|
|
286
|
+
if (k === "endsWith") schema.pattern = `${c.value}$`;
|
|
287
|
+
}
|
|
288
|
+
return schema;
|
|
289
|
+
}
|
|
290
|
+
function applyNumberChecks(schema, checks) {
|
|
291
|
+
if (!checks) return schema;
|
|
292
|
+
for (const c of checks) {
|
|
293
|
+
const k = c.kind ?? c.type;
|
|
294
|
+
if (k === "min" || k === "minimum" || k === "gte") schema.minimum = c.value ?? c.minimum;
|
|
295
|
+
if (k === "max" || k === "maximum" || k === "lte") schema.maximum = c.value ?? c.maximum;
|
|
296
|
+
if (k === "int") schema.type = "integer";
|
|
297
|
+
if (k === "positive") schema.minimum = 0;
|
|
298
|
+
if (k === "negative") schema.maximum = 0;
|
|
299
|
+
if (k === "multipleOf") schema.multipleOf = c.value;
|
|
300
|
+
}
|
|
301
|
+
return schema;
|
|
302
|
+
}
|
|
303
|
+
function objectSchemaToParams(schema) {
|
|
304
|
+
if (schema.type !== "object" || !schema.properties) return [];
|
|
305
|
+
const required = new Set(schema.required ?? []);
|
|
306
|
+
return Object.entries(schema.properties).map(([name, propSchema]) => ({
|
|
307
|
+
name,
|
|
308
|
+
in: "query",
|
|
309
|
+
required: required.has(name),
|
|
310
|
+
schema: propSchema,
|
|
311
|
+
...propSchema.description ? { description: propSchema.description } : {}
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
export { generateOpenAPI, scalarHTML };
|
package/dist/silgi.d.mts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { AnySchema, InferSchemaInput, InferSchemaOutput } from "./core/schema.mjs";
|
|
2
|
+
import { ErrorDef, GuardDef, GuardFn, ProcedureDef, ResolveContext, RouterDef, WrapDef, WrapFn } from "./types.mjs";
|
|
3
|
+
import { ProcedureBuilder } from "./builder.mjs";
|
|
4
|
+
import { StorageConfig, useStorage } from "./core/storage.mjs";
|
|
5
|
+
import { AnalyticsOptions } from "./plugins/analytics.mjs";
|
|
6
|
+
import { ScalarOptions } from "./scalar.mjs";
|
|
7
|
+
import { Hookable } from "hookable";
|
|
8
|
+
|
|
9
|
+
//#region src/silgi.d.ts
|
|
10
|
+
interface SilgiHooks {
|
|
11
|
+
/** Called before a request is processed */
|
|
12
|
+
request: (event: {
|
|
13
|
+
path: string;
|
|
14
|
+
input: unknown;
|
|
15
|
+
}) => void;
|
|
16
|
+
/** Called after a successful response */
|
|
17
|
+
response: (event: {
|
|
18
|
+
path: string;
|
|
19
|
+
output: unknown;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
}) => void;
|
|
22
|
+
/** Called when an error occurs */
|
|
23
|
+
error: (event: {
|
|
24
|
+
path: string;
|
|
25
|
+
error: unknown;
|
|
26
|
+
}) => void;
|
|
27
|
+
/** Called when the server starts */
|
|
28
|
+
'serve:start': (event: {
|
|
29
|
+
url: string;
|
|
30
|
+
port: number;
|
|
31
|
+
hostname: string;
|
|
32
|
+
}) => void;
|
|
33
|
+
}
|
|
34
|
+
interface SilgiConfig<TCtx extends Record<string, unknown>> {
|
|
35
|
+
context: (req: Request) => TCtx | Promise<TCtx>;
|
|
36
|
+
/** Register lifecycle hooks */
|
|
37
|
+
hooks?: Partial<{ [K in keyof SilgiHooks]: SilgiHooks[K] | SilgiHooks[K][] }>;
|
|
38
|
+
/**
|
|
39
|
+
* Storage configuration — mount drivers by path prefix.
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* import redisDriver from 'unstorage/drivers/redis'
|
|
43
|
+
* import memoryDriver from 'unstorage/drivers/memory'
|
|
44
|
+
*
|
|
45
|
+
* storage: {
|
|
46
|
+
* cache: redisDriver({ url: 'redis://localhost' }),
|
|
47
|
+
* sessions: memoryDriver(),
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* Or pass a pre-built unstorage instance:
|
|
52
|
+
* ```ts
|
|
53
|
+
* storage: myStorageInstance
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
storage?: StorageConfig;
|
|
57
|
+
}
|
|
58
|
+
interface SilgiInstance<TBaseCtx extends Record<string, unknown>> {
|
|
59
|
+
/** Register a lifecycle hook */
|
|
60
|
+
hook: Hookable<SilgiHooks>['hook'];
|
|
61
|
+
/** Remove a lifecycle hook */
|
|
62
|
+
removeHook: Hookable<SilgiHooks>['removeHook'];
|
|
63
|
+
/** Access storage with optional prefix — uses configured mounts */
|
|
64
|
+
useStorage: typeof useStorage;
|
|
65
|
+
/** Create a guard middleware (flat, zero-closure) */
|
|
66
|
+
guard: GuardFactory<TBaseCtx>;
|
|
67
|
+
/** Create a wrap middleware (onion, before+after) */
|
|
68
|
+
wrap: (fn: WrapFn<TBaseCtx>) => WrapDef<TBaseCtx>;
|
|
69
|
+
/** Start a builder — resolve only */
|
|
70
|
+
$resolve: ProcedureBuilder<'query', TBaseCtx>['$resolve'];
|
|
71
|
+
/** Start a builder — set input schema */
|
|
72
|
+
$input: ProcedureBuilder<'query', TBaseCtx>['$input'];
|
|
73
|
+
/** Start a builder — add middleware */
|
|
74
|
+
$use: ProcedureBuilder<'query', TBaseCtx>['$use'];
|
|
75
|
+
/** Start a builder — set output schema */
|
|
76
|
+
$output: ProcedureBuilder<'query', TBaseCtx>['$output'];
|
|
77
|
+
/** Start a builder — set errors */
|
|
78
|
+
$errors: ProcedureBuilder<'query', TBaseCtx>['$errors'];
|
|
79
|
+
/** Start a builder — set route metadata */
|
|
80
|
+
$route: ProcedureBuilder<'query', TBaseCtx>['$route'];
|
|
81
|
+
/** Start a builder — set custom metadata */
|
|
82
|
+
$meta: ProcedureBuilder<'query', TBaseCtx>['$meta'];
|
|
83
|
+
/** Define a subscription (SSE stream) */
|
|
84
|
+
subscription: SubscriptionFactory<TBaseCtx>;
|
|
85
|
+
/** Assemble router and compile pipelines */
|
|
86
|
+
router: <T extends RouterDef>(def: T) => T;
|
|
87
|
+
/** Create a Fetch API handler: (Request) => Response */
|
|
88
|
+
handler: (router: RouterDef, options?: {
|
|
89
|
+
/** Enable Scalar API Reference UI at /reference and /openapi.json */scalar?: boolean | ScalarOptions; /** Enable analytics dashboard at /analytics */
|
|
90
|
+
analytics?: boolean | AnalyticsOptions;
|
|
91
|
+
}) => (request: Request) => Response | Promise<Response>;
|
|
92
|
+
/** Create & start a Node.js HTTP server */
|
|
93
|
+
serve: (router: RouterDef, options?: {
|
|
94
|
+
port?: number;
|
|
95
|
+
hostname?: string; /** Enable Scalar API Reference UI at /reference and /openapi.json */
|
|
96
|
+
scalar?: boolean | ScalarOptions; /** Enable analytics dashboard at /analytics */
|
|
97
|
+
analytics?: boolean | AnalyticsOptions; /** Enable WebSocket RPC (requires crossws) */
|
|
98
|
+
ws?: boolean; /** Enable HTTP/2 (requires cert + key for TLS) */
|
|
99
|
+
http2?: {
|
|
100
|
+
cert: string;
|
|
101
|
+
key: string;
|
|
102
|
+
};
|
|
103
|
+
}) => void;
|
|
104
|
+
}
|
|
105
|
+
interface GuardConfig<TBaseCtx, TReturn extends Record<string, unknown> | void, TErrors extends ErrorDef> {
|
|
106
|
+
errors?: TErrors;
|
|
107
|
+
fn: GuardFn<TBaseCtx, TReturn>;
|
|
108
|
+
}
|
|
109
|
+
interface GuardFactory<TBaseCtx> {
|
|
110
|
+
/** Simple guard: guard(fn) */
|
|
111
|
+
<TReturn extends Record<string, unknown> | void>(fn: GuardFn<TBaseCtx, TReturn>): GuardDef<TBaseCtx, TReturn, {}>;
|
|
112
|
+
/** Guard with typed errors: guard({ errors, fn }) */
|
|
113
|
+
<TReturn extends Record<string, unknown> | void, TErrors extends ErrorDef>(config: GuardConfig<TBaseCtx, TReturn, TErrors>): GuardDef<TBaseCtx, TReturn, TErrors>;
|
|
114
|
+
}
|
|
115
|
+
interface SubscriptionFactory<TBaseCtx extends Record<string, unknown>> {
|
|
116
|
+
/** Builder: `subscription()` — returns chainable builder */
|
|
117
|
+
(): ProcedureBuilder<'subscription', TBaseCtx>;
|
|
118
|
+
/** Short: `subscription(resolve)` */
|
|
119
|
+
<TOutput>(resolve: (opts: ResolveContext<TBaseCtx, undefined, {}>) => AsyncIterableIterator<TOutput>): ProcedureDef<'subscription', undefined, TOutput, {}>;
|
|
120
|
+
/** Short: `subscription(input, resolve)` */
|
|
121
|
+
<TSchema extends AnySchema, TOutput>(input: TSchema, resolve: (opts: ResolveContext<TBaseCtx, InferSchemaOutput<TSchema>, {}>) => AsyncIterableIterator<TOutput>): ProcedureDef<'subscription', InferSchemaInput<TSchema>, TOutput, {}>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Create a Silgi RPC instance with typed context.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const k = silgi({
|
|
129
|
+
* context: (req) => ({ db: getDB(), user: getUser(req) }),
|
|
130
|
+
* hooks: {
|
|
131
|
+
* request: ({ path }) => console.log(`-> ${path}`),
|
|
132
|
+
* },
|
|
133
|
+
* })
|
|
134
|
+
* const { query, mutation, guard, wrap, router, serve } = k
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
declare function silgi<TBaseCtx extends Record<string, unknown>>(config: SilgiConfig<TBaseCtx>): SilgiInstance<TBaseCtx>;
|
|
138
|
+
//#endregion
|
|
139
|
+
export { SilgiConfig, SilgiInstance, silgi };
|
package/dist/silgi.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createProcedureBuilder } from "./builder.mjs";
|
|
2
|
+
import { compileRouter } from "./compile.mjs";
|
|
3
|
+
import { assignPaths, routerCache } from "./core/router-utils.mjs";
|
|
4
|
+
import { createFetchHandler } from "./core/handler.mjs";
|
|
5
|
+
import { createHooks } from "hookable";
|
|
6
|
+
//#region src/silgi.ts
|
|
7
|
+
/**
|
|
8
|
+
* silgi() — the main entry point.
|
|
9
|
+
*
|
|
10
|
+
* Creates a Silgi instance with typed context.
|
|
11
|
+
* All procedure/middleware factories are methods on this instance,
|
|
12
|
+
* so context type flows automatically.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const k = silgi({ context: (req) => ({ db, headers }) })
|
|
16
|
+
* export const { query, mutation, guard, wrap, router, handler } = k
|
|
17
|
+
*/
|
|
18
|
+
function createProcedure(type, ...args) {
|
|
19
|
+
if (args.length === 0) return createProcedureBuilder(type);
|
|
20
|
+
if (args.length === 1 && typeof args[0] === "function") return {
|
|
21
|
+
type,
|
|
22
|
+
input: null,
|
|
23
|
+
output: null,
|
|
24
|
+
errors: null,
|
|
25
|
+
use: null,
|
|
26
|
+
resolve: args[0],
|
|
27
|
+
route: null,
|
|
28
|
+
meta: null
|
|
29
|
+
};
|
|
30
|
+
if (args.length === 2 && typeof args[1] === "function") return {
|
|
31
|
+
type,
|
|
32
|
+
input: args[0],
|
|
33
|
+
output: null,
|
|
34
|
+
errors: null,
|
|
35
|
+
use: null,
|
|
36
|
+
resolve: args[1],
|
|
37
|
+
route: null,
|
|
38
|
+
meta: null
|
|
39
|
+
};
|
|
40
|
+
throw new TypeError(`Invalid arguments for ${type}()`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a Silgi RPC instance with typed context.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const k = silgi({
|
|
48
|
+
* context: (req) => ({ db: getDB(), user: getUser(req) }),
|
|
49
|
+
* hooks: {
|
|
50
|
+
* request: ({ path }) => console.log(`-> ${path}`),
|
|
51
|
+
* },
|
|
52
|
+
* })
|
|
53
|
+
* const { query, mutation, guard, wrap, router, serve } = k
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function silgi(config) {
|
|
57
|
+
const contextFactory = config.context;
|
|
58
|
+
const hooks = createHooks();
|
|
59
|
+
if (config.hooks) {
|
|
60
|
+
for (const [name, fn] of Object.entries(config.hooks)) if (Array.isArray(fn)) for (const f of fn) hooks.hook(name, f);
|
|
61
|
+
else if (fn) hooks.hook(name, fn);
|
|
62
|
+
}
|
|
63
|
+
if (config.storage) import("./core/storage.mjs").then((m) => m.initStorage(config.storage));
|
|
64
|
+
return {
|
|
65
|
+
hook: hooks.hook,
|
|
66
|
+
removeHook: hooks.removeHook.bind(hooks),
|
|
67
|
+
useStorage: (...args) => {
|
|
68
|
+
return import("./core/storage.mjs").then((m) => m.useStorage(...args));
|
|
69
|
+
},
|
|
70
|
+
guard: (fnOrConfig) => {
|
|
71
|
+
if (typeof fnOrConfig === "function") return {
|
|
72
|
+
kind: "guard",
|
|
73
|
+
fn: fnOrConfig
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
kind: "guard",
|
|
77
|
+
fn: fnOrConfig.fn,
|
|
78
|
+
errors: fnOrConfig.errors
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
wrap: (fn) => ({
|
|
82
|
+
kind: "wrap",
|
|
83
|
+
fn
|
|
84
|
+
}),
|
|
85
|
+
$resolve: ((fn) => createProcedure("query", fn)),
|
|
86
|
+
$input: ((schema) => createProcedureBuilder("query").$input(schema)),
|
|
87
|
+
$use: ((...middleware) => createProcedureBuilder("query").$use(...middleware)),
|
|
88
|
+
$output: ((schema) => createProcedureBuilder("query").$output(schema)),
|
|
89
|
+
$errors: ((errors) => createProcedureBuilder("query").$errors(errors)),
|
|
90
|
+
$route: ((route) => createProcedureBuilder("query").$route(route)),
|
|
91
|
+
$meta: ((meta) => createProcedureBuilder("query").$meta(meta)),
|
|
92
|
+
subscription: ((...args) => createProcedure("subscription", ...args)),
|
|
93
|
+
router: (def) => {
|
|
94
|
+
assignPaths(def);
|
|
95
|
+
const flat = compileRouter(def);
|
|
96
|
+
routerCache.set(def, flat);
|
|
97
|
+
return def;
|
|
98
|
+
},
|
|
99
|
+
handler: (routerDef, options) => {
|
|
100
|
+
let fetchHandler;
|
|
101
|
+
return (request) => {
|
|
102
|
+
fetchHandler ??= createFetchHandler(routerDef, contextFactory, hooks, options);
|
|
103
|
+
return fetchHandler(request);
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
serve: async (routerDef, options) => {
|
|
107
|
+
const { createServeHandler } = await import("./core/serve.mjs");
|
|
108
|
+
return createServeHandler(routerDef, contextFactory, hooks, options);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { silgi };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RouterDef } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/trpc-interop.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Convert a tRPC router to a Silgi RouterDef.
|
|
6
|
+
*
|
|
7
|
+
* Walks the tRPC router's `_def.procedures` and wraps each one as
|
|
8
|
+
* a Silgi ProcedureDef that calls the tRPC procedure's resolver.
|
|
9
|
+
*
|
|
10
|
+
* Supports:
|
|
11
|
+
* - tRPC v10 and v11 routers
|
|
12
|
+
* - Queries, mutations, and subscriptions
|
|
13
|
+
* - Input schemas (passed through as-is)
|
|
14
|
+
* - Middleware (runs inside tRPC, not Silgi's pipeline)
|
|
15
|
+
*
|
|
16
|
+
* Does NOT support:
|
|
17
|
+
* - Converting tRPC middleware to Silgi guards/wraps
|
|
18
|
+
* - tRPC context factories (use Silgi's context instead)
|
|
19
|
+
*/
|
|
20
|
+
declare function fromTRPC(trpcRouter: unknown): RouterDef;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { fromTRPC };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//#region src/trpc-interop.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert a tRPC router to a Silgi RouterDef.
|
|
4
|
+
*
|
|
5
|
+
* Walks the tRPC router's `_def.procedures` and wraps each one as
|
|
6
|
+
* a Silgi ProcedureDef that calls the tRPC procedure's resolver.
|
|
7
|
+
*
|
|
8
|
+
* Supports:
|
|
9
|
+
* - tRPC v10 and v11 routers
|
|
10
|
+
* - Queries, mutations, and subscriptions
|
|
11
|
+
* - Input schemas (passed through as-is)
|
|
12
|
+
* - Middleware (runs inside tRPC, not Silgi's pipeline)
|
|
13
|
+
*
|
|
14
|
+
* Does NOT support:
|
|
15
|
+
* - Converting tRPC middleware to Silgi guards/wraps
|
|
16
|
+
* - tRPC context factories (use Silgi's context instead)
|
|
17
|
+
*/
|
|
18
|
+
function fromTRPC(trpcRouter) {
|
|
19
|
+
if (!trpcRouter || typeof trpcRouter !== "object") throw new Error("fromTRPC: expected a tRPC router object");
|
|
20
|
+
const router = trpcRouter;
|
|
21
|
+
return walkTRPCRouter(router._def?.procedures ?? router);
|
|
22
|
+
}
|
|
23
|
+
function walkTRPCRouter(node) {
|
|
24
|
+
const result = {};
|
|
25
|
+
for (const [key, value] of Object.entries(node)) {
|
|
26
|
+
if (!value || typeof value !== "object") continue;
|
|
27
|
+
const proc = value;
|
|
28
|
+
if (isTRPCProcedure(proc)) result[key] = convertProcedure(proc);
|
|
29
|
+
else if (isTRPCRouter(proc)) result[key] = walkTRPCRouter(proc._def?.procedures ?? proc);
|
|
30
|
+
else result[key] = walkTRPCRouter(proc);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function isTRPCProcedure(value) {
|
|
35
|
+
const def = value._def;
|
|
36
|
+
if (!def) return false;
|
|
37
|
+
return def.type === "query" || def.type === "mutation" || def.type === "subscription";
|
|
38
|
+
}
|
|
39
|
+
function isTRPCRouter(value) {
|
|
40
|
+
return !!value._def?.procedures;
|
|
41
|
+
}
|
|
42
|
+
function convertProcedure(trpcProc) {
|
|
43
|
+
const def = trpcProc._def;
|
|
44
|
+
const type = def.type === "subscription" ? "subscription" : def.type === "mutation" ? "mutation" : "query";
|
|
45
|
+
return {
|
|
46
|
+
type,
|
|
47
|
+
input: def.inputs?.[0] ?? null,
|
|
48
|
+
output: null,
|
|
49
|
+
errors: null,
|
|
50
|
+
use: null,
|
|
51
|
+
resolve: async ({ input, ctx }) => {
|
|
52
|
+
if (typeof def.resolver === "function") return def.resolver({
|
|
53
|
+
input,
|
|
54
|
+
ctx,
|
|
55
|
+
type
|
|
56
|
+
});
|
|
57
|
+
if (typeof trpcProc.call === "function") return trpcProc.call({
|
|
58
|
+
input,
|
|
59
|
+
ctx
|
|
60
|
+
});
|
|
61
|
+
throw new Error(`Cannot resolve tRPC procedure: ${JSON.stringify(Object.keys(def))}`);
|
|
62
|
+
},
|
|
63
|
+
route: null,
|
|
64
|
+
meta: { __trpc: true }
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { fromTRPC };
|