silgi 0.0.14 → 0.1.0-beta.10
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/_fetch-adapter.d.mts +18 -0
- package/dist/adapters/_fetch-adapter.mjs +53 -0
- package/dist/adapters/astro.d.mts +15 -0
- package/dist/adapters/astro.mjs +31 -0
- package/dist/adapters/aws-lambda.d.mts +42 -0
- package/dist/adapters/aws-lambda.mjs +92 -0
- package/dist/adapters/express.d.mts +16 -0
- package/dist/adapters/express.mjs +120 -0
- package/dist/adapters/message-port.d.mts +42 -0
- package/dist/adapters/message-port.mjs +132 -0
- package/dist/adapters/nestjs.d.mts +25 -0
- package/dist/adapters/nestjs.mjs +75 -0
- package/dist/adapters/nextjs.d.mts +14 -0
- package/dist/adapters/nextjs.mjs +29 -0
- package/dist/adapters/peer.d.mts +27 -0
- package/dist/adapters/peer.mjs +36 -0
- package/dist/adapters/remix.d.mts +15 -0
- package/dist/adapters/remix.mjs +30 -0
- package/dist/adapters/solidstart.d.mts +12 -0
- package/dist/adapters/solidstart.mjs +29 -0
- package/dist/adapters/sveltekit.d.mts +14 -0
- package/dist/adapters/sveltekit.mjs +30 -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 +55 -0
- package/dist/builder.mjs +70 -0
- package/dist/callable.d.mts +19 -0
- package/dist/callable.mjs +42 -0
- package/dist/caller.mjs +90 -0
- package/dist/client/adapters/fetch/index.d.mts +19 -0
- package/dist/client/adapters/fetch/index.mjs +82 -0
- package/dist/client/adapters/ofetch/index.d.mts +57 -0
- package/dist/client/adapters/ofetch/index.mjs +100 -0
- package/dist/client/adapters/websocket/index.d.mts +20 -0
- package/dist/client/adapters/websocket/index.mjs +101 -0
- package/dist/client/client.d.mts +37 -0
- package/dist/client/client.mjs +80 -0
- package/dist/client/consume.d.mts +50 -0
- package/dist/client/consume.mjs +66 -0
- package/dist/client/dynamic-link.d.mts +16 -0
- package/dist/client/dynamic-link.mjs +19 -0
- package/dist/client/index.d.mts +6 -0
- package/dist/client/index.mjs +5 -0
- package/dist/client/interceptor.d.mts +31 -0
- package/dist/client/interceptor.mjs +34 -0
- package/dist/client/openapi.d.mts +29 -0
- package/dist/client/openapi.mjs +89 -0
- package/dist/client/plugins/batch.d.mts +26 -0
- package/dist/client/plugins/batch.mjs +64 -0
- package/dist/client/plugins/circuit-breaker.d.mts +24 -0
- package/dist/client/plugins/circuit-breaker.mjs +60 -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 +8 -0
- package/dist/client/plugins/index.mjs +8 -0
- package/dist/client/plugins/otel.d.mts +12 -0
- package/dist/client/plugins/otel.mjs +27 -0
- package/dist/client/plugins/retry.d.mts +34 -0
- package/dist/client/plugins/retry.mjs +79 -0
- package/dist/client/plugins/timeout.d.mts +10 -0
- package/dist/client/plugins/timeout.mjs +14 -0
- package/dist/client/server.d.mts +16 -0
- package/dist/client/server.mjs +62 -0
- package/dist/client/types.d.mts +29 -0
- package/dist/codec/devalue.d.mts +21 -0
- package/dist/codec/devalue.mjs +33 -0
- package/dist/codec/msgpack.d.mts +18 -0
- package/dist/codec/msgpack.mjs +45 -0
- package/dist/codec/sanitize.mjs +38 -0
- package/dist/compile.d.mts +46 -0
- package/dist/compile.mjs +332 -0
- package/dist/contract.d.mts +36 -0
- package/dist/contract.mjs +40 -0
- package/dist/core/codec.mjs +67 -0
- package/dist/core/dispatch.mjs +62 -0
- package/dist/core/error.d.mts +104 -0
- package/dist/core/error.mjs +139 -0
- package/dist/core/handler.d.mts +6 -0
- package/dist/core/handler.mjs +148 -0
- package/dist/core/input.mjs +49 -0
- package/dist/core/iterator.d.mts +17 -0
- package/dist/core/iterator.mjs +79 -0
- package/dist/core/router-utils.d.mts +25 -0
- package/dist/core/router-utils.mjs +98 -0
- package/dist/core/schema.d.mts +20 -0
- package/dist/core/schema.mjs +33 -0
- package/dist/core/serve.d.mts +51 -0
- package/dist/core/serve.mjs +76 -0
- package/dist/core/sse.d.mts +18 -0
- package/dist/core/sse.mjs +98 -0
- package/dist/core/storage.d.mts +20 -0
- package/dist/core/storage.mjs +61 -0
- package/dist/core/task.d.mts +62 -0
- package/dist/core/task.mjs +165 -0
- package/dist/core/utils.mjs +24 -0
- package/dist/index.d.mts +18 -37
- package/dist/index.mjs +15 -7
- package/dist/integrations/ai/index.d.mts +25 -0
- package/dist/integrations/ai/index.mjs +117 -0
- package/dist/integrations/better-auth/index.d.mts +61 -0
- package/dist/integrations/better-auth/index.mjs +332 -0
- package/dist/integrations/drizzle/index.d.mts +27 -0
- package/dist/integrations/drizzle/index.mjs +286 -0
- package/dist/integrations/hey-api/index.d.mts +2 -0
- package/dist/integrations/hey-api/index.mjs +2 -0
- package/dist/integrations/hey-api/to-client.d.mts +20 -0
- package/dist/integrations/hey-api/to-client.mjs +39 -0
- package/dist/integrations/pinia-colada/general-utils.d.mts +13 -0
- package/dist/integrations/pinia-colada/general-utils.mjs +9 -0
- package/dist/integrations/pinia-colada/index.d.mts +6 -0
- package/dist/integrations/pinia-colada/index.mjs +5 -0
- package/dist/integrations/pinia-colada/key.d.mts +11 -0
- package/dist/integrations/pinia-colada/key.mjs +11 -0
- package/dist/integrations/pinia-colada/procedure-utils.d.mts +25 -0
- package/dist/integrations/pinia-colada/procedure-utils.mjs +33 -0
- package/dist/integrations/pinia-colada/router-utils.d.mts +17 -0
- package/dist/integrations/pinia-colada/router-utils.mjs +30 -0
- package/dist/integrations/pinia-colada/types.d.mts +25 -0
- package/dist/integrations/react/index.d.mts +83 -0
- package/dist/integrations/react/index.mjs +196 -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 +60 -0
- package/dist/integrations/tanstack-query/ssr.mjs +102 -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 +22 -0
- package/dist/lazy.mjs +34 -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 +47 -0
- package/dist/plugins/analytics.d.mts +236 -0
- package/dist/plugins/analytics.mjs +816 -0
- package/dist/plugins/batch-server.d.mts +20 -0
- package/dist/plugins/batch-server.mjs +91 -0
- package/dist/plugins/body-limit.d.mts +19 -0
- package/dist/plugins/body-limit.mjs +49 -0
- package/dist/plugins/cache.d.mts +170 -0
- package/dist/plugins/cache.mjs +212 -0
- package/dist/plugins/coerce.d.mts +24 -0
- package/dist/plugins/coerce.mjs +70 -0
- package/dist/plugins/cookies.d.mts +14 -0
- package/dist/plugins/cookies.mjs +48 -0
- package/dist/plugins/cors.d.mts +43 -0
- package/dist/plugins/cors.mjs +62 -0
- package/dist/plugins/file-upload.d.mts +38 -0
- package/dist/plugins/file-upload.mjs +102 -0
- package/dist/plugins/index.d.mts +14 -0
- package/dist/plugins/index.mjs +14 -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 +53 -0
- package/dist/plugins/ratelimit.mjs +92 -0
- package/dist/plugins/signing.d.mts +41 -0
- package/dist/plugins/signing.mjs +118 -0
- package/dist/plugins/strict-get.d.mts +10 -0
- package/dist/plugins/strict-get.mjs +33 -0
- package/dist/scalar.d.mts +49 -0
- package/dist/scalar.mjs +311 -0
- package/dist/silgi.d.mts +144 -0
- package/dist/silgi.mjs +156 -0
- package/dist/trpc-interop.d.mts +22 -0
- package/dist/trpc-interop.mjs +68 -0
- package/dist/types.d.mts +108 -0
- package/dist/ws.d.mts +88 -0
- package/dist/ws.mjs +205 -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 +324 -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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ProcedureDef } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/compile.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Compiled pipeline — called per request.
|
|
6
|
+
* May return sync value OR Promise — caller uses instanceof check.
|
|
7
|
+
*/
|
|
8
|
+
type CompiledHandler = (ctx: Record<string, unknown>, rawInput: unknown, signal: AbortSignal) => unknown | Promise<unknown>;
|
|
9
|
+
/**
|
|
10
|
+
* Compile a procedure into the fastest possible handler.
|
|
11
|
+
*
|
|
12
|
+
* Optimizations applied:
|
|
13
|
+
* - Guard count specialization (unrolled for 0-4)
|
|
14
|
+
* - Separate fast path for no-wrap case (zero closures per request)
|
|
15
|
+
* - Pre-computed fail function (singleton per procedure)
|
|
16
|
+
* - Sync fast path when all guards are sync
|
|
17
|
+
*/
|
|
18
|
+
declare function compileProcedure(procedure: ProcedureDef): CompiledHandler;
|
|
19
|
+
interface CompiledRoute {
|
|
20
|
+
handler: CompiledHandler;
|
|
21
|
+
/** Pre-computed Cache-Control header value, or undefined if no caching */
|
|
22
|
+
cacheControl?: string;
|
|
23
|
+
/** Procedure is accessible over WebSocket */
|
|
24
|
+
ws?: boolean;
|
|
25
|
+
/** Skip body parsing — procedure receives raw request (e.g. catch-all proxy) */
|
|
26
|
+
passthrough?: boolean;
|
|
27
|
+
/** HTTP method this route is registered for (uppercase) */
|
|
28
|
+
method: string;
|
|
29
|
+
}
|
|
30
|
+
/** Match result from the router */
|
|
31
|
+
interface MatchedRoute<T = unknown> {
|
|
32
|
+
data: T;
|
|
33
|
+
params?: Record<string, string>;
|
|
34
|
+
}
|
|
35
|
+
/** Compiled router function — returns matched route + params */
|
|
36
|
+
type CompiledRouterFn = (method: string, path: string) => MatchedRoute<CompiledRoute> | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Compile a router tree into a rou3 radix router.
|
|
39
|
+
*
|
|
40
|
+
* Powered by rou3 (unjs) — battle-tested, fast, minimal.
|
|
41
|
+
*/
|
|
42
|
+
declare function compileRouter(def: Record<string, unknown>): CompiledRouterFn;
|
|
43
|
+
/** Acquire a context object from the pool (or create one). */
|
|
44
|
+
declare function createContext(): Record<string, unknown>;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { compileProcedure, compileRouter, createContext };
|
package/dist/compile.mjs
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { validateSchema } from "./core/schema.mjs";
|
|
2
|
+
import { SilgiError } from "./core/error.mjs";
|
|
3
|
+
import { isProcedureDef } from "./core/router-utils.mjs";
|
|
4
|
+
import { addRoute, createRouter, findRoute } from "rou3";
|
|
5
|
+
//#region src/compile.ts
|
|
6
|
+
/**
|
|
7
|
+
* Pipeline Compiler — guard unrolling, context pooling, rou3 routing.
|
|
8
|
+
*
|
|
9
|
+
* 1. UNROLLED GUARDS — 0-4 guard specialization (no loop, V8 inlines)
|
|
10
|
+
* 2. ZERO-ALLOC CONTEXT — Object.create(null) + pool reuse
|
|
11
|
+
* 3. ROU3 ROUTER — unjs radix tree (same as h3/nitro)
|
|
12
|
+
*/
|
|
13
|
+
/** Internal symbol for pipeline raw input — prevents collision with user context keys */
|
|
14
|
+
const RAW_INPUT = Symbol.for("silgi.rawInput");
|
|
15
|
+
function isThenable(value) {
|
|
16
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
17
|
+
}
|
|
18
|
+
function createFail(errors) {
|
|
19
|
+
return (code, data) => {
|
|
20
|
+
const def = errors[code];
|
|
21
|
+
throw new SilgiError(code, {
|
|
22
|
+
status: typeof def === "number" ? def : def?.status ?? 500,
|
|
23
|
+
message: typeof def === "object" && def !== null && "message" in def ? def.message : void 0,
|
|
24
|
+
data,
|
|
25
|
+
defined: true
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function noopFail(code, data) {
|
|
30
|
+
throw new SilgiError(code, {
|
|
31
|
+
data,
|
|
32
|
+
defined: false
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
36
|
+
"__proto__",
|
|
37
|
+
"constructor",
|
|
38
|
+
"prototype"
|
|
39
|
+
]);
|
|
40
|
+
/** Pre-frozen empty params — avoids per-request {} allocation */
|
|
41
|
+
const EMPTY_PARAMS = /* @__PURE__ */ Object.freeze(Object.create(null));
|
|
42
|
+
/** Sanitize a value to prevent prototype pollution from nested __proto__ keys */
|
|
43
|
+
function sanitizeValue(value) {
|
|
44
|
+
if (typeof value !== "object" || value === null) return value;
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
for (let i = 0; i < value.length; i++) value[i] = sanitizeValue(value[i]);
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
const proto = Object.getPrototypeOf(value);
|
|
50
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
51
|
+
const obj = value;
|
|
52
|
+
if (Object.prototype.hasOwnProperty.call(obj, "__proto__")) {
|
|
53
|
+
const clean = Object.create(null);
|
|
54
|
+
const keys = Object.keys(obj);
|
|
55
|
+
for (let i = 0; i < keys.length; i++) {
|
|
56
|
+
const k = keys[i];
|
|
57
|
+
if (!UNSAFE_KEYS.has(k)) clean[k] = sanitizeValue(obj[k]);
|
|
58
|
+
}
|
|
59
|
+
return clean;
|
|
60
|
+
}
|
|
61
|
+
const keys = Object.keys(obj);
|
|
62
|
+
for (let i = 0; i < keys.length; i++) {
|
|
63
|
+
const k = keys[i];
|
|
64
|
+
obj[k] = sanitizeValue(obj[k]);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
/** Apply a single guard result to context — direct property set */
|
|
69
|
+
function applyGuardResult(ctx, result) {
|
|
70
|
+
if (result === null || result === void 0 || typeof result !== "object") return;
|
|
71
|
+
const keys = Object.keys(result);
|
|
72
|
+
for (let i = 0; i < keys.length; i++) {
|
|
73
|
+
const k = keys[i];
|
|
74
|
+
if (UNSAFE_KEYS.has(k)) continue;
|
|
75
|
+
ctx[k] = sanitizeValue(result[k]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Apply a single guard (sync fast-path, async fallback) */
|
|
79
|
+
async function applyGuard(ctx, guard) {
|
|
80
|
+
const result = guard.fn(ctx);
|
|
81
|
+
applyGuardResult(ctx, isThenable(result) ? await result : result);
|
|
82
|
+
}
|
|
83
|
+
function runGuards0() {}
|
|
84
|
+
function runGuards1(ctx, g0) {
|
|
85
|
+
const r0 = g0.fn(ctx);
|
|
86
|
+
if (isThenable(r0)) return r0.then((v) => applyGuardResult(ctx, v));
|
|
87
|
+
applyGuardResult(ctx, r0);
|
|
88
|
+
}
|
|
89
|
+
function runGuards2(ctx, g0, g1) {
|
|
90
|
+
const r0 = g0.fn(ctx);
|
|
91
|
+
if (isThenable(r0)) return r0.then((v) => {
|
|
92
|
+
applyGuardResult(ctx, v);
|
|
93
|
+
return applyGuard(ctx, g1);
|
|
94
|
+
});
|
|
95
|
+
applyGuardResult(ctx, r0);
|
|
96
|
+
const r1 = g1.fn(ctx);
|
|
97
|
+
if (isThenable(r1)) return r1.then((v) => applyGuardResult(ctx, v));
|
|
98
|
+
applyGuardResult(ctx, r1);
|
|
99
|
+
}
|
|
100
|
+
function runGuards3(ctx, g0, g1, g2) {
|
|
101
|
+
const r0 = g0.fn(ctx);
|
|
102
|
+
if (isThenable(r0)) return r0.then(async (v) => {
|
|
103
|
+
applyGuardResult(ctx, v);
|
|
104
|
+
await applyGuard(ctx, g1);
|
|
105
|
+
await applyGuard(ctx, g2);
|
|
106
|
+
});
|
|
107
|
+
applyGuardResult(ctx, r0);
|
|
108
|
+
const r1 = g1.fn(ctx);
|
|
109
|
+
if (isThenable(r1)) return r1.then(async (v) => {
|
|
110
|
+
applyGuardResult(ctx, v);
|
|
111
|
+
await applyGuard(ctx, g2);
|
|
112
|
+
});
|
|
113
|
+
applyGuardResult(ctx, r1);
|
|
114
|
+
const r2 = g2.fn(ctx);
|
|
115
|
+
if (isThenable(r2)) return r2.then((v) => applyGuardResult(ctx, v));
|
|
116
|
+
applyGuardResult(ctx, r2);
|
|
117
|
+
}
|
|
118
|
+
function runGuards4(ctx, g0, g1, g2, g3) {
|
|
119
|
+
const r0 = g0.fn(ctx);
|
|
120
|
+
if (isThenable(r0)) return r0.then(async (v) => {
|
|
121
|
+
applyGuardResult(ctx, v);
|
|
122
|
+
await applyGuard(ctx, g1);
|
|
123
|
+
await applyGuard(ctx, g2);
|
|
124
|
+
await applyGuard(ctx, g3);
|
|
125
|
+
});
|
|
126
|
+
applyGuardResult(ctx, r0);
|
|
127
|
+
const r1 = g1.fn(ctx);
|
|
128
|
+
if (isThenable(r1)) return r1.then(async (v) => {
|
|
129
|
+
applyGuardResult(ctx, v);
|
|
130
|
+
await applyGuard(ctx, g2);
|
|
131
|
+
await applyGuard(ctx, g3);
|
|
132
|
+
});
|
|
133
|
+
applyGuardResult(ctx, r1);
|
|
134
|
+
const r2 = g2.fn(ctx);
|
|
135
|
+
if (isThenable(r2)) return r2.then(async (v) => {
|
|
136
|
+
applyGuardResult(ctx, v);
|
|
137
|
+
await applyGuard(ctx, g3);
|
|
138
|
+
});
|
|
139
|
+
applyGuardResult(ctx, r2);
|
|
140
|
+
const r3 = g3.fn(ctx);
|
|
141
|
+
if (isThenable(r3)) return r3.then((v) => applyGuardResult(ctx, v));
|
|
142
|
+
applyGuardResult(ctx, r3);
|
|
143
|
+
}
|
|
144
|
+
/** Fallback for 5+ guards — loop */
|
|
145
|
+
async function runGuardsN(ctx, guards) {
|
|
146
|
+
for (const guard of guards) {
|
|
147
|
+
const result = guard.fn(ctx);
|
|
148
|
+
applyGuardResult(ctx, isThenable(result) ? await result : result);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Select the optimal guard runner based on count.
|
|
153
|
+
* Returns a function that applies all guards to a context.
|
|
154
|
+
*/
|
|
155
|
+
function selectGuardRunner(guards) {
|
|
156
|
+
switch (guards.length) {
|
|
157
|
+
case 0: return runGuards0;
|
|
158
|
+
case 1: {
|
|
159
|
+
const g0 = guards[0];
|
|
160
|
+
return (ctx) => runGuards1(ctx, g0);
|
|
161
|
+
}
|
|
162
|
+
case 2: {
|
|
163
|
+
const [g0, g1] = guards;
|
|
164
|
+
return (ctx) => runGuards2(ctx, g0, g1);
|
|
165
|
+
}
|
|
166
|
+
case 3: {
|
|
167
|
+
const [g0, g1, g2] = guards;
|
|
168
|
+
return (ctx) => runGuards3(ctx, g0, g1, g2);
|
|
169
|
+
}
|
|
170
|
+
case 4: {
|
|
171
|
+
const [g0, g1, g2, g3] = guards;
|
|
172
|
+
return (ctx) => runGuards4(ctx, g0, g1, g2, g3);
|
|
173
|
+
}
|
|
174
|
+
default: return (ctx) => runGuardsN(ctx, guards);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Call resolve, then validate output (sync-first, async fallback) */
|
|
178
|
+
function _resolveWithOutput(resolveFn, input, ctx, failFn, signal, outputSchema) {
|
|
179
|
+
const output = resolveFn({
|
|
180
|
+
input,
|
|
181
|
+
ctx,
|
|
182
|
+
fail: failFn,
|
|
183
|
+
signal,
|
|
184
|
+
params: ctx.params ?? EMPTY_PARAMS
|
|
185
|
+
});
|
|
186
|
+
if (!outputSchema) return output;
|
|
187
|
+
if (isThenable(output)) return output.then((o) => validateSchema(outputSchema, o));
|
|
188
|
+
return validateSchema(outputSchema, output);
|
|
189
|
+
}
|
|
190
|
+
/** Validate input, resolve, validate output — sync-first with rejected Promise fallback.
|
|
191
|
+
* All sync throws (validation errors, fail() calls, resolver errors) are converted
|
|
192
|
+
* to rejected Promises for consistent error handling in .then().catch() chains. */
|
|
193
|
+
function _validateAndResolve(inputSchema, outputSchema, resolveFn, rawInput, ctx, failFn, signal) {
|
|
194
|
+
try {
|
|
195
|
+
const input = inputSchema ? validateSchema(inputSchema, rawInput ?? {}) : rawInput;
|
|
196
|
+
if (isThenable(input)) return input.then((resolvedInput) => _resolveWithOutput(resolveFn, resolvedInput, ctx, failFn, signal, outputSchema));
|
|
197
|
+
return _resolveWithOutput(resolveFn, input, ctx, failFn, signal, outputSchema);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
return Promise.reject(e);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Compile a procedure into the fastest possible handler.
|
|
204
|
+
*
|
|
205
|
+
* Optimizations applied:
|
|
206
|
+
* - Guard count specialization (unrolled for 0-4)
|
|
207
|
+
* - Separate fast path for no-wrap case (zero closures per request)
|
|
208
|
+
* - Pre-computed fail function (singleton per procedure)
|
|
209
|
+
* - Sync fast path when all guards are sync
|
|
210
|
+
*/
|
|
211
|
+
function compileProcedure(procedure) {
|
|
212
|
+
const middlewares = procedure.use ?? [];
|
|
213
|
+
const guards = [];
|
|
214
|
+
const wraps = [];
|
|
215
|
+
for (const mw of middlewares) if (mw.kind === "guard") guards.push(mw);
|
|
216
|
+
else wraps.push(mw);
|
|
217
|
+
const inputSchema = procedure.input;
|
|
218
|
+
const outputSchema = procedure.output;
|
|
219
|
+
const resolveFn = procedure.resolve;
|
|
220
|
+
let mergedErrors = procedure.errors;
|
|
221
|
+
for (const guard of guards) if (guard.errors) mergedErrors = mergedErrors ? {
|
|
222
|
+
...mergedErrors,
|
|
223
|
+
...guard.errors
|
|
224
|
+
} : guard.errors;
|
|
225
|
+
const failFn = mergedErrors ? createFail(mergedErrors) : noopFail;
|
|
226
|
+
const runGuards = selectGuardRunner(guards);
|
|
227
|
+
if (wraps.length === 0 && !inputSchema && !outputSchema) return (ctx, rawInput, signal) => {
|
|
228
|
+
try {
|
|
229
|
+
const guardResult = runGuards(ctx);
|
|
230
|
+
if (guardResult && isThenable(guardResult)) return guardResult.then(() => resolveFn({
|
|
231
|
+
input: rawInput,
|
|
232
|
+
ctx,
|
|
233
|
+
fail: failFn,
|
|
234
|
+
signal,
|
|
235
|
+
params: ctx.params ?? EMPTY_PARAMS
|
|
236
|
+
}));
|
|
237
|
+
return resolveFn({
|
|
238
|
+
input: rawInput,
|
|
239
|
+
ctx,
|
|
240
|
+
fail: failFn,
|
|
241
|
+
signal,
|
|
242
|
+
params: ctx.params ?? EMPTY_PARAMS
|
|
243
|
+
});
|
|
244
|
+
} catch (e) {
|
|
245
|
+
return Promise.reject(e);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
if (wraps.length === 0) return (ctx, rawInput, signal) => {
|
|
249
|
+
try {
|
|
250
|
+
const guardResult = runGuards(ctx);
|
|
251
|
+
if (guardResult && isThenable(guardResult)) return guardResult.then(() => _validateAndResolve(inputSchema, outputSchema, resolveFn, rawInput, ctx, failFn, signal));
|
|
252
|
+
return _validateAndResolve(inputSchema, outputSchema, resolveFn, rawInput, ctx, failFn, signal);
|
|
253
|
+
} catch (e) {
|
|
254
|
+
return Promise.reject(e);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
return async (ctx, rawInput, signal) => {
|
|
258
|
+
const guardResult = runGuards(ctx);
|
|
259
|
+
if (guardResult && isThenable(guardResult)) await guardResult;
|
|
260
|
+
let input;
|
|
261
|
+
if (inputSchema) {
|
|
262
|
+
const validated = validateSchema(inputSchema, rawInput ?? {});
|
|
263
|
+
input = isThenable(validated) ? await validated : validated;
|
|
264
|
+
} else input = rawInput;
|
|
265
|
+
ctx[RAW_INPUT] = input;
|
|
266
|
+
let execute = () => {
|
|
267
|
+
const resolvedInput = ctx[RAW_INPUT] ?? input;
|
|
268
|
+
return Promise.resolve(resolveFn({
|
|
269
|
+
input: resolvedInput,
|
|
270
|
+
ctx,
|
|
271
|
+
fail: failFn,
|
|
272
|
+
signal,
|
|
273
|
+
params: ctx.params ?? EMPTY_PARAMS
|
|
274
|
+
}));
|
|
275
|
+
};
|
|
276
|
+
for (let i = wraps.length - 1; i >= 0; i--) {
|
|
277
|
+
const wrapFn = wraps[i].fn;
|
|
278
|
+
const next = execute;
|
|
279
|
+
execute = () => wrapFn(ctx, next);
|
|
280
|
+
}
|
|
281
|
+
const output = await execute();
|
|
282
|
+
if (!outputSchema) return output;
|
|
283
|
+
const validated = validateSchema(outputSchema, output);
|
|
284
|
+
return isThenable(validated) ? await validated : validated;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Compile a router tree into a rou3 radix router.
|
|
289
|
+
*
|
|
290
|
+
* Powered by rou3 (unjs) — battle-tested, fast, minimal.
|
|
291
|
+
*/
|
|
292
|
+
function compileRouter(def) {
|
|
293
|
+
const router = createRouter();
|
|
294
|
+
function walk(node, path) {
|
|
295
|
+
if (isProcedureDef(node)) {
|
|
296
|
+
const proc = node;
|
|
297
|
+
const route = proc.route;
|
|
298
|
+
const routePath = route?.path || "/" + path.join("/");
|
|
299
|
+
const method = route?.method?.toUpperCase() || "POST";
|
|
300
|
+
let cacheControl;
|
|
301
|
+
if (route?.cache != null) cacheControl = typeof route.cache === "number" ? `public, max-age=${route.cache}` : route.cache;
|
|
302
|
+
const compiled = {
|
|
303
|
+
handler: compileProcedure(proc),
|
|
304
|
+
cacheControl,
|
|
305
|
+
ws: route?.ws ?? void 0,
|
|
306
|
+
passthrough: routePath.includes("**") || void 0,
|
|
307
|
+
method
|
|
308
|
+
};
|
|
309
|
+
addRoute(router, method, routePath, compiled);
|
|
310
|
+
addRoute(router, "", routePath, compiled);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (typeof node === "object" && node !== null) for (const [k, v] of Object.entries(node)) walk(v, [...path, k]);
|
|
314
|
+
}
|
|
315
|
+
walk(def, []);
|
|
316
|
+
return (method, path) => findRoute(router, method, path);
|
|
317
|
+
}
|
|
318
|
+
/** Pool of pre-allocated null-prototype context objects — eliminates per-request GC pressure. */
|
|
319
|
+
const CTX_POOL = [];
|
|
320
|
+
const CTX_POOL_MAX = 128;
|
|
321
|
+
/** Acquire a context object from the pool (or create one). */
|
|
322
|
+
function createContext() {
|
|
323
|
+
return CTX_POOL.length > 0 ? CTX_POOL.pop() : Object.create(null);
|
|
324
|
+
}
|
|
325
|
+
/** Return a context object to the pool after request completes. */
|
|
326
|
+
function releaseContext(ctx) {
|
|
327
|
+
for (const key of Object.keys(ctx)) delete ctx[key];
|
|
328
|
+
for (const sym of Object.getOwnPropertySymbols(ctx)) delete ctx[sym];
|
|
329
|
+
if (CTX_POOL.length < CTX_POOL_MAX) CTX_POOL.push(ctx);
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
export { RAW_INPUT, compileProcedure, compileRouter, createContext, releaseContext };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AnySchema, InferSchemaInput, InferSchemaOutput } from "./core/schema.mjs";
|
|
2
|
+
import { ErrorDef, ProcedureType, Route, RouterDef } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/contract.d.ts
|
|
5
|
+
interface ProcedureContract<TType extends ProcedureType = ProcedureType, TInput extends AnySchema | undefined = AnySchema | undefined, TOutput extends AnySchema | undefined = AnySchema | undefined, TErrors extends ErrorDef = ErrorDef> {
|
|
6
|
+
type?: TType;
|
|
7
|
+
input?: TInput;
|
|
8
|
+
output?: TOutput;
|
|
9
|
+
errors?: TErrors;
|
|
10
|
+
route?: Route;
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
type ContractRouter = {
|
|
14
|
+
[key: string]: ProcedureContract<any, any, any, any> | ContractRouter;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Define an API contract — shared between client and server.
|
|
18
|
+
* Pure type information, no runtime behavior.
|
|
19
|
+
*/
|
|
20
|
+
declare function contract<T extends ContractRouter>(definition: T): T;
|
|
21
|
+
type ImplementProcedure<T extends ProcedureContract> = T extends ProcedureContract<infer _TType, infer TInput, infer TOutput, infer TErrors> ? (opts: {
|
|
22
|
+
input: TInput extends AnySchema ? InferSchemaOutput<TInput> : undefined;
|
|
23
|
+
ctx: Record<string, unknown>;
|
|
24
|
+
fail: TErrors extends ErrorDef ? <K extends keyof TErrors & string>(code: K, ...args: any[]) => never : never;
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
}) => TOutput extends AnySchema ? InferSchemaOutput<TOutput> | Promise<InferSchemaOutput<TOutput>> : unknown : never;
|
|
27
|
+
type ImplementRouter<T extends ContractRouter> = { [K in keyof T]: T[K] extends ProcedureContract ? ImplementProcedure<T[K]> : T[K] extends ContractRouter ? ImplementRouter<T[K]> : never };
|
|
28
|
+
/**
|
|
29
|
+
* Implement a contract — returns a silgi RouterDef.
|
|
30
|
+
* Type-safe: implementation must match the contract.
|
|
31
|
+
*/
|
|
32
|
+
declare function implement<T extends ContractRouter>(contractDef: T, implementations: ImplementRouter<T>): RouterDef;
|
|
33
|
+
/** Infer client type from a contract (no server code needed) */
|
|
34
|
+
type InferContractClient<T extends ContractRouter> = { [K in keyof T]: T[K] extends ProcedureContract<any, infer TInput, infer TOutput> ? TInput extends AnySchema ? (input: InferSchemaInput<TInput>) => Promise<TOutput extends AnySchema ? InferSchemaOutput<TOutput> : unknown> : () => Promise<TOutput extends AnySchema ? InferSchemaOutput<TOutput> : unknown> : T[K] extends ContractRouter ? InferContractClient<T[K]> : never };
|
|
35
|
+
//#endregion
|
|
36
|
+
export { ContractRouter, InferContractClient, ProcedureContract, contract, implement };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/contract.ts
|
|
2
|
+
/**
|
|
3
|
+
* Define an API contract — shared between client and server.
|
|
4
|
+
* Pure type information, no runtime behavior.
|
|
5
|
+
*/
|
|
6
|
+
function contract(definition) {
|
|
7
|
+
return definition;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Implement a contract — returns a silgi RouterDef.
|
|
11
|
+
* Type-safe: implementation must match the contract.
|
|
12
|
+
*/
|
|
13
|
+
function implement(contractDef, implementations) {
|
|
14
|
+
return buildRouter(contractDef, implementations);
|
|
15
|
+
}
|
|
16
|
+
function buildRouter(contractDef, impls) {
|
|
17
|
+
const router = {};
|
|
18
|
+
for (const [key, contractEntry] of Object.entries(contractDef)) {
|
|
19
|
+
const impl = impls[key];
|
|
20
|
+
if (!impl) continue;
|
|
21
|
+
if (isProcedureContract(contractEntry)) router[key] = {
|
|
22
|
+
type: contractEntry.type ?? "query",
|
|
23
|
+
input: contractEntry.input ?? null,
|
|
24
|
+
output: contractEntry.output ?? null,
|
|
25
|
+
errors: contractEntry.errors ?? null,
|
|
26
|
+
use: null,
|
|
27
|
+
resolve: impl,
|
|
28
|
+
route: contractEntry.route ?? null,
|
|
29
|
+
meta: null
|
|
30
|
+
};
|
|
31
|
+
else router[key] = buildRouter(contractEntry, impl);
|
|
32
|
+
}
|
|
33
|
+
return router;
|
|
34
|
+
}
|
|
35
|
+
function isProcedureContract(v) {
|
|
36
|
+
if (typeof v !== "object" || v === null) return false;
|
|
37
|
+
return "input" in v || "output" in v || "type" in v || "errors" in v;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { contract, implement };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ValidationError } from "./schema.mjs";
|
|
2
|
+
import { SilgiError, toSilgiError } from "./error.mjs";
|
|
3
|
+
import { stringifyJSON } from "./utils.mjs";
|
|
4
|
+
//#region src/core/codec.ts
|
|
5
|
+
/**
|
|
6
|
+
* Response encoding — JSON, MessagePack, devalue.
|
|
7
|
+
*
|
|
8
|
+
* Lazy-loads binary codecs on first non-JSON request.
|
|
9
|
+
*/
|
|
10
|
+
let _msgpack;
|
|
11
|
+
let _devalue;
|
|
12
|
+
/** Detect response format from Accept header */
|
|
13
|
+
function detectResponseFormat(request) {
|
|
14
|
+
const accept = request.headers.get("accept");
|
|
15
|
+
if (!accept) return "json";
|
|
16
|
+
if (accept.includes("msgpack")) return "msgpack";
|
|
17
|
+
if (accept.includes("x-devalue")) return "devalue";
|
|
18
|
+
return "json";
|
|
19
|
+
}
|
|
20
|
+
/** Encode data into a Response with the given format */
|
|
21
|
+
async function encodeResponse(data, status, format, extraHeaders) {
|
|
22
|
+
switch (format) {
|
|
23
|
+
case "msgpack":
|
|
24
|
+
_msgpack ??= await import("../codec/msgpack.mjs");
|
|
25
|
+
return new Response(_msgpack.encode(data), {
|
|
26
|
+
status,
|
|
27
|
+
headers: {
|
|
28
|
+
"content-type": _msgpack.MSGPACK_CONTENT_TYPE,
|
|
29
|
+
...extraHeaders
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
case "devalue":
|
|
33
|
+
_devalue ??= await import("../codec/devalue.mjs");
|
|
34
|
+
return new Response(_devalue.encode(data), {
|
|
35
|
+
status,
|
|
36
|
+
headers: {
|
|
37
|
+
"content-type": _devalue.DEVALUE_CONTENT_TYPE,
|
|
38
|
+
...extraHeaders
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
default: return new Response(stringifyJSON(data), {
|
|
42
|
+
status,
|
|
43
|
+
headers: {
|
|
44
|
+
"content-type": "application/json",
|
|
45
|
+
...extraHeaders
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Build error Response from any thrown error */
|
|
51
|
+
function makeErrorResponse(error, format) {
|
|
52
|
+
if (error instanceof ValidationError) return encodeResponse({
|
|
53
|
+
code: "BAD_REQUEST",
|
|
54
|
+
status: 400,
|
|
55
|
+
message: error.message,
|
|
56
|
+
data: { issues: error.issues }
|
|
57
|
+
}, 400, format);
|
|
58
|
+
if (error instanceof SyntaxError) return encodeResponse({
|
|
59
|
+
code: "BAD_REQUEST",
|
|
60
|
+
status: 400,
|
|
61
|
+
message: "Invalid JSON body"
|
|
62
|
+
}, 400, format);
|
|
63
|
+
const e = error instanceof SilgiError ? error : toSilgiError(error);
|
|
64
|
+
return encodeResponse(e.toJSON(), e.status, format);
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { detectResponseFormat, encodeResponse, makeErrorResponse };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ValidationError } from "./schema.mjs";
|
|
2
|
+
import { SilgiError, toSilgiError } from "./error.mjs";
|
|
3
|
+
//#region src/core/dispatch.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared dispatch utilities for adapters.
|
|
6
|
+
*
|
|
7
|
+
* Centralizes: context building, method enforcement, input parsing, error serialization.
|
|
8
|
+
* Every non-Fetch adapter (express, nestjs, bun, lambda, message-port) uses these
|
|
9
|
+
* instead of reimplementing the same logic.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Copy properties from source into target context via direct property set.
|
|
13
|
+
* V8 monomorphic access — no Object.assign, no spread.
|
|
14
|
+
*/
|
|
15
|
+
function applyContext(target, source) {
|
|
16
|
+
const keys = Object.keys(source);
|
|
17
|
+
for (let i = 0; i < keys.length; i++) target[keys[i]] = source[keys[i]];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build a null-prototype context object from a base context and route params.
|
|
21
|
+
* Uses direct property assignment (no Object.assign) for V8 monomorphic access.
|
|
22
|
+
*/
|
|
23
|
+
function buildContext(baseCtx, params) {
|
|
24
|
+
const ctx = Object.create(null);
|
|
25
|
+
if (baseCtx) applyContext(ctx, baseCtx);
|
|
26
|
+
if (params) ctx.params = params;
|
|
27
|
+
return ctx;
|
|
28
|
+
}
|
|
29
|
+
/** Check if an HTTP method is allowed for a compiled route. */
|
|
30
|
+
function isMethodAllowed(reqMethod, routeMethod) {
|
|
31
|
+
if (routeMethod === "*") return true;
|
|
32
|
+
if (reqMethod === routeMethod) return true;
|
|
33
|
+
if (reqMethod === "OPTIONS") return true;
|
|
34
|
+
if (reqMethod === "GET" && routeMethod === "POST") return true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/** Convert any thrown error to a serializable error body + status. */
|
|
38
|
+
function serializeError(error) {
|
|
39
|
+
if (error instanceof ValidationError) return {
|
|
40
|
+
code: "BAD_REQUEST",
|
|
41
|
+
status: 400,
|
|
42
|
+
message: error.message,
|
|
43
|
+
data: { issues: error.issues }
|
|
44
|
+
};
|
|
45
|
+
return (error instanceof SilgiError ? error : toSilgiError(error)).toJSON();
|
|
46
|
+
}
|
|
47
|
+
/** Max allowed size for GET ?data= parameter (bytes). Prevents JSON bomb via URL. */
|
|
48
|
+
const MAX_QUERY_DATA_LENGTH = 8192;
|
|
49
|
+
/**
|
|
50
|
+
* Parse a JSON string from a query parameter.
|
|
51
|
+
* Throws SilgiError(BAD_REQUEST) on invalid JSON or oversized input.
|
|
52
|
+
*/
|
|
53
|
+
function parseQueryData(dataValue) {
|
|
54
|
+
if (dataValue.length > MAX_QUERY_DATA_LENGTH) throw new SilgiError("BAD_REQUEST", { message: "Query data parameter too large" });
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(dataValue);
|
|
57
|
+
} catch {
|
|
58
|
+
throw new SilgiError("BAD_REQUEST", { message: "Invalid JSON in data parameter" });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { applyContext, buildContext, isMethodAllowed, parseQueryData, serializeError };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//#region src/core/error.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* SilgiError — unified RPC error with cross-realm instanceof.
|
|
4
|
+
*/
|
|
5
|
+
declare const COMMON_ERRORS: Readonly<{
|
|
6
|
+
readonly BAD_REQUEST: {
|
|
7
|
+
readonly status: 400;
|
|
8
|
+
readonly message: "Bad Request";
|
|
9
|
+
};
|
|
10
|
+
readonly UNAUTHORIZED: {
|
|
11
|
+
readonly status: 401;
|
|
12
|
+
readonly message: "Unauthorized";
|
|
13
|
+
};
|
|
14
|
+
readonly FORBIDDEN: {
|
|
15
|
+
readonly status: 403;
|
|
16
|
+
readonly message: "Forbidden";
|
|
17
|
+
};
|
|
18
|
+
readonly NOT_FOUND: {
|
|
19
|
+
readonly status: 404;
|
|
20
|
+
readonly message: "Not Found";
|
|
21
|
+
};
|
|
22
|
+
readonly METHOD_NOT_ALLOWED: {
|
|
23
|
+
readonly status: 405;
|
|
24
|
+
readonly message: "Method Not Allowed";
|
|
25
|
+
};
|
|
26
|
+
readonly NOT_ACCEPTABLE: {
|
|
27
|
+
readonly status: 406;
|
|
28
|
+
readonly message: "Not Acceptable";
|
|
29
|
+
};
|
|
30
|
+
readonly CONFLICT: {
|
|
31
|
+
readonly status: 409;
|
|
32
|
+
readonly message: "Conflict";
|
|
33
|
+
};
|
|
34
|
+
readonly GONE: {
|
|
35
|
+
readonly status: 410;
|
|
36
|
+
readonly message: "Gone";
|
|
37
|
+
};
|
|
38
|
+
readonly UNPROCESSABLE_CONTENT: {
|
|
39
|
+
readonly status: 422;
|
|
40
|
+
readonly message: "Unprocessable Content";
|
|
41
|
+
};
|
|
42
|
+
readonly PRECONDITION_REQUIRED: {
|
|
43
|
+
readonly status: 428;
|
|
44
|
+
readonly message: "Precondition Required";
|
|
45
|
+
};
|
|
46
|
+
readonly TOO_MANY_REQUESTS: {
|
|
47
|
+
readonly status: 429;
|
|
48
|
+
readonly message: "Too Many Requests";
|
|
49
|
+
};
|
|
50
|
+
readonly CLIENT_CLOSED_REQUEST: {
|
|
51
|
+
readonly status: 499;
|
|
52
|
+
readonly message: "Client Closed Request";
|
|
53
|
+
};
|
|
54
|
+
readonly INTERNAL_SERVER_ERROR: {
|
|
55
|
+
readonly status: 500;
|
|
56
|
+
readonly message: "Internal Server Error";
|
|
57
|
+
};
|
|
58
|
+
readonly NOT_IMPLEMENTED: {
|
|
59
|
+
readonly status: 501;
|
|
60
|
+
readonly message: "Not Implemented";
|
|
61
|
+
};
|
|
62
|
+
readonly BAD_GATEWAY: {
|
|
63
|
+
readonly status: 502;
|
|
64
|
+
readonly message: "Bad Gateway";
|
|
65
|
+
};
|
|
66
|
+
readonly SERVICE_UNAVAILABLE: {
|
|
67
|
+
readonly status: 503;
|
|
68
|
+
readonly message: "Service Unavailable";
|
|
69
|
+
};
|
|
70
|
+
readonly GATEWAY_TIMEOUT: {
|
|
71
|
+
readonly status: 504;
|
|
72
|
+
readonly message: "Gateway Timeout";
|
|
73
|
+
};
|
|
74
|
+
}>;
|
|
75
|
+
type SilgiErrorCode = keyof typeof COMMON_ERRORS | (string & {});
|
|
76
|
+
interface SilgiErrorOptions<TData = unknown> {
|
|
77
|
+
status?: number;
|
|
78
|
+
message?: string;
|
|
79
|
+
data?: TData;
|
|
80
|
+
cause?: unknown;
|
|
81
|
+
defined?: boolean;
|
|
82
|
+
}
|
|
83
|
+
interface SilgiErrorJSON<TCode extends string = string, TData = unknown> {
|
|
84
|
+
defined: boolean;
|
|
85
|
+
code: TCode;
|
|
86
|
+
status: number;
|
|
87
|
+
message: string;
|
|
88
|
+
data: TData;
|
|
89
|
+
}
|
|
90
|
+
declare class SilgiError<TCode extends string = string, TData = unknown> extends Error {
|
|
91
|
+
readonly code: TCode;
|
|
92
|
+
readonly status: number;
|
|
93
|
+
readonly data: TData;
|
|
94
|
+
readonly defined: boolean;
|
|
95
|
+
constructor(code: TCode, options?: SilgiErrorOptions<TData>);
|
|
96
|
+
toJSON(): SilgiErrorJSON<TCode, TData>;
|
|
97
|
+
static [Symbol.hasInstance](instance: unknown): boolean;
|
|
98
|
+
}
|
|
99
|
+
declare function isDefinedError<TError>(error: TError): error is TError & SilgiError & {
|
|
100
|
+
defined: true;
|
|
101
|
+
};
|
|
102
|
+
declare function toSilgiError(error: unknown): SilgiError;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { SilgiError, SilgiErrorCode, SilgiErrorJSON, SilgiErrorOptions, isDefinedError, toSilgiError };
|