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,48 @@
|
|
|
1
|
+
import { parse, parseSetCookie, serialize, splitSetCookieString } from "cookie-es";
|
|
2
|
+
//#region src/plugins/cookies.ts
|
|
3
|
+
/**
|
|
4
|
+
* Cookie helpers — thin wrapper over cookie-es (unjs).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { getCookie, setCookie, deleteCookie } from "silgi/cookies"
|
|
9
|
+
*
|
|
10
|
+
* const auth = k.guard((ctx) => {
|
|
11
|
+
* const token = getCookie(ctx.headers, "session")
|
|
12
|
+
* if (!token) throw new SilgiError("UNAUTHORIZED")
|
|
13
|
+
* return { sessionToken: token }
|
|
14
|
+
* })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
/** Parse a specific cookie value by name. */
|
|
18
|
+
function getCookie(headers, name) {
|
|
19
|
+
const str = typeof headers === "string" ? headers : headers.cookie ?? headers.Cookie ?? "";
|
|
20
|
+
if (!str) return void 0;
|
|
21
|
+
return parse(str)[name];
|
|
22
|
+
}
|
|
23
|
+
/** Parse all cookies into a key-value object. */
|
|
24
|
+
function parseCookies(headers) {
|
|
25
|
+
const str = typeof headers === "string" ? headers : headers.cookie ?? headers.Cookie ?? "";
|
|
26
|
+
if (!str) return {};
|
|
27
|
+
return parse(str);
|
|
28
|
+
}
|
|
29
|
+
/** Serialize a Set-Cookie header. Defaults: path="/", httpOnly, sameSite=lax. */
|
|
30
|
+
function setCookie(name, value, options = {}) {
|
|
31
|
+
const merged = {
|
|
32
|
+
path: "/",
|
|
33
|
+
httpOnly: true,
|
|
34
|
+
sameSite: "lax",
|
|
35
|
+
...options
|
|
36
|
+
};
|
|
37
|
+
if (merged.sameSite === "none") merged.secure = true;
|
|
38
|
+
return serialize(name, value, merged);
|
|
39
|
+
}
|
|
40
|
+
/** Delete a cookie by setting maxAge=0. */
|
|
41
|
+
function deleteCookie(name, options = {}) {
|
|
42
|
+
return setCookie(name, "", {
|
|
43
|
+
...options,
|
|
44
|
+
maxAge: 0
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { deleteCookie, getCookie, parseCookies, parseSetCookie, setCookie, splitSetCookieString };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/plugins/cors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* CORS plugin — response header helper.
|
|
4
|
+
*
|
|
5
|
+
* Returns CORS headers to merge into responses.
|
|
6
|
+
* Handles preflight OPTIONS requests automatically via the handler.
|
|
7
|
+
*/
|
|
8
|
+
interface CORSOptions {
|
|
9
|
+
origin?: string | string[] | ((origin: string) => boolean);
|
|
10
|
+
methods?: string[];
|
|
11
|
+
allowedHeaders?: string[];
|
|
12
|
+
exposedHeaders?: string[];
|
|
13
|
+
credentials?: boolean;
|
|
14
|
+
maxAge?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create CORS headers config for silgi().
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { cors } from "silgi/cors"
|
|
22
|
+
*
|
|
23
|
+
* const k = silgi({
|
|
24
|
+
* context: (req) => ({}),
|
|
25
|
+
* hooks: cors({ origin: "https://app.example.com", credentials: true }),
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare function cors(options?: CORSOptions): {
|
|
30
|
+
headers: Record<string, string>;
|
|
31
|
+
options: CORSOptions;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* CORS header map — use in custom serve() or middleware.
|
|
35
|
+
* Returns headers object to merge into responses.
|
|
36
|
+
*
|
|
37
|
+
* For dynamic origins (array/function), omits Access-Control-Allow-Origin
|
|
38
|
+
* entirely when the request origin is not allowed — this is the correct
|
|
39
|
+
* behavior per the CORS spec (browsers reject missing header).
|
|
40
|
+
*/
|
|
41
|
+
declare function corsHeaders(options?: CORSOptions, requestOrigin?: string): Record<string, string>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { CORSOptions, cors, corsHeaders };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/plugins/cors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create CORS headers config for silgi().
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { cors } from "silgi/cors"
|
|
8
|
+
*
|
|
9
|
+
* const k = silgi({
|
|
10
|
+
* context: (req) => ({}),
|
|
11
|
+
* hooks: cors({ origin: "https://app.example.com", credentials: true }),
|
|
12
|
+
* })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
function cors(options = {}) {
|
|
16
|
+
const origin = options.origin ?? "*";
|
|
17
|
+
if (options.credentials && typeof origin === "string" && origin === "*") throw new Error("[silgi/cors] Cannot use credentials: true with origin: \"*\". Set an explicit origin.");
|
|
18
|
+
return {
|
|
19
|
+
headers: corsHeaders(options),
|
|
20
|
+
options
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* CORS header map — use in custom serve() or middleware.
|
|
25
|
+
* Returns headers object to merge into responses.
|
|
26
|
+
*
|
|
27
|
+
* For dynamic origins (array/function), omits Access-Control-Allow-Origin
|
|
28
|
+
* entirely when the request origin is not allowed — this is the correct
|
|
29
|
+
* behavior per the CORS spec (browsers reject missing header).
|
|
30
|
+
*/
|
|
31
|
+
function corsHeaders(options = {}, requestOrigin) {
|
|
32
|
+
const origin = options.origin ?? "*";
|
|
33
|
+
const methods = options.methods ?? [
|
|
34
|
+
"GET",
|
|
35
|
+
"POST",
|
|
36
|
+
"PUT",
|
|
37
|
+
"PATCH",
|
|
38
|
+
"DELETE",
|
|
39
|
+
"OPTIONS"
|
|
40
|
+
];
|
|
41
|
+
const allowedHeaders = options.allowedHeaders ?? ["Content-Type", "Authorization"];
|
|
42
|
+
const headers = {
|
|
43
|
+
"access-control-allow-methods": methods.join(", "),
|
|
44
|
+
"access-control-allow-headers": allowedHeaders.join(", ")
|
|
45
|
+
};
|
|
46
|
+
if (typeof origin === "string") {
|
|
47
|
+
headers["access-control-allow-origin"] = origin;
|
|
48
|
+
if (origin !== "*") headers["vary"] = "Origin";
|
|
49
|
+
} else if (Array.isArray(origin)) {
|
|
50
|
+
if (requestOrigin && origin.includes(requestOrigin)) headers["access-control-allow-origin"] = requestOrigin;
|
|
51
|
+
headers["vary"] = "Origin";
|
|
52
|
+
} else if (typeof origin === "function") {
|
|
53
|
+
if (requestOrigin && origin(requestOrigin)) headers["access-control-allow-origin"] = requestOrigin;
|
|
54
|
+
headers["vary"] = "Origin";
|
|
55
|
+
}
|
|
56
|
+
if (options.credentials) headers["access-control-allow-credentials"] = "true";
|
|
57
|
+
if (options.maxAge !== void 0) headers["access-control-max-age"] = String(options.maxAge);
|
|
58
|
+
if (options.exposedHeaders) headers["access-control-expose-headers"] = options.exposedHeaders.join(", ");
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { cors, corsHeaders };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { GuardDef } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/file-upload.d.ts
|
|
4
|
+
interface FileGuardOptions {
|
|
5
|
+
/** Maximum file size in bytes. Default: 10MB */
|
|
6
|
+
maxFileSize?: number;
|
|
7
|
+
/** Allowed MIME types (supports wildcards like "image/*"). Default: all */
|
|
8
|
+
allowedTypes?: string[];
|
|
9
|
+
/** Maximum number of files. Default: 1 */
|
|
10
|
+
maxFiles?: number;
|
|
11
|
+
/** Form field name for the file. Default: "file" */
|
|
12
|
+
fieldName?: string;
|
|
13
|
+
}
|
|
14
|
+
interface UploadedFile {
|
|
15
|
+
name: string;
|
|
16
|
+
size: number;
|
|
17
|
+
type: string;
|
|
18
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
19
|
+
text(): Promise<string>;
|
|
20
|
+
stream(): ReadableStream<Uint8Array>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Guard that parses multipart file uploads from the request.
|
|
24
|
+
*
|
|
25
|
+
* Adds `ctx.file` (single) or `ctx.files` (multiple) to the context.
|
|
26
|
+
* Validates file size and MIME type before the procedure runs.
|
|
27
|
+
*/
|
|
28
|
+
declare function fileGuard(options?: FileGuardOptions): GuardDef<Record<string, unknown>>;
|
|
29
|
+
/**
|
|
30
|
+
* Parse multipart form data from a Request.
|
|
31
|
+
* Returns files and fields separately.
|
|
32
|
+
*/
|
|
33
|
+
declare function parseMultipart(request: Request): Promise<{
|
|
34
|
+
files: UploadedFile[];
|
|
35
|
+
fields: Record<string, string>;
|
|
36
|
+
}>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { FileGuardOptions, UploadedFile, fileGuard, parseMultipart };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { SilgiError } from "../core/error.mjs";
|
|
2
|
+
//#region src/plugins/file-upload.ts
|
|
3
|
+
/**
|
|
4
|
+
* File upload/download — type-safe File/Blob handling.
|
|
5
|
+
*
|
|
6
|
+
* Parse multipart/form-data requests and pass File objects
|
|
7
|
+
* to procedure handlers. Works with serve() and handler().
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { fileInput, fileGuard } from "silgi/plugins"
|
|
12
|
+
*
|
|
13
|
+
* const uploadAvatar = k
|
|
14
|
+
* .$use(fileGuard({ maxFileSize: 5 * 1024 * 1024, allowedTypes: ["image/*"] }))
|
|
15
|
+
* .$resolve(async ({ ctx }) => {
|
|
16
|
+
* const file = ctx.file
|
|
17
|
+
* const buffer = await file.arrayBuffer()
|
|
18
|
+
* return { name: file.name, size: file.size, type: file.type }
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Guard that parses multipart file uploads from the request.
|
|
24
|
+
*
|
|
25
|
+
* Adds `ctx.file` (single) or `ctx.files` (multiple) to the context.
|
|
26
|
+
* Validates file size and MIME type before the procedure runs.
|
|
27
|
+
*/
|
|
28
|
+
function fileGuard(options = {}) {
|
|
29
|
+
const { maxFileSize = 10 * 1024 * 1024, allowedTypes, maxFiles = 1, fieldName: _fieldName = "file" } = options;
|
|
30
|
+
return {
|
|
31
|
+
kind: "guard",
|
|
32
|
+
fn: (ctx) => {
|
|
33
|
+
const files = ctx.__files;
|
|
34
|
+
if (!files || files.length === 0) throw new SilgiError("BAD_REQUEST", {
|
|
35
|
+
status: 400,
|
|
36
|
+
message: "No file uploaded"
|
|
37
|
+
});
|
|
38
|
+
if (files.length > maxFiles) throw new SilgiError("BAD_REQUEST", {
|
|
39
|
+
status: 400,
|
|
40
|
+
message: `Too many files: ${files.length} (max ${maxFiles})`
|
|
41
|
+
});
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
if (file.size > maxFileSize) throw new SilgiError("PAYLOAD_TOO_LARGE", {
|
|
44
|
+
status: 413,
|
|
45
|
+
message: `File too large: ${file.size} bytes (max ${maxFileSize})`,
|
|
46
|
+
data: {
|
|
47
|
+
maxFileSize,
|
|
48
|
+
actualSize: file.size,
|
|
49
|
+
fileName: file.name
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (allowedTypes && !matchesMimeType(file.type, allowedTypes)) throw new SilgiError("BAD_REQUEST", {
|
|
53
|
+
status: 400,
|
|
54
|
+
message: `File type not allowed: ${file.type}`,
|
|
55
|
+
data: {
|
|
56
|
+
allowedTypes,
|
|
57
|
+
actualType: file.type,
|
|
58
|
+
fileName: file.name
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return maxFiles === 1 ? { file: files[0] } : { files };
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse multipart form data from a Request.
|
|
68
|
+
* Returns files and fields separately.
|
|
69
|
+
*/
|
|
70
|
+
async function parseMultipart(request) {
|
|
71
|
+
const formData = await request.formData();
|
|
72
|
+
const files = [];
|
|
73
|
+
const fields = {};
|
|
74
|
+
for (const [key, value] of formData.entries()) if (typeof value !== "string") {
|
|
75
|
+
const file = value;
|
|
76
|
+
files.push({
|
|
77
|
+
name: file.name,
|
|
78
|
+
size: file.size,
|
|
79
|
+
type: file.type,
|
|
80
|
+
arrayBuffer: () => file.arrayBuffer(),
|
|
81
|
+
text: () => file.text(),
|
|
82
|
+
stream: () => file.stream()
|
|
83
|
+
});
|
|
84
|
+
} else fields[key] = value;
|
|
85
|
+
return {
|
|
86
|
+
files,
|
|
87
|
+
fields
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function matchesMimeType(actual, patterns) {
|
|
91
|
+
for (const pattern of patterns) {
|
|
92
|
+
if (pattern === "*" || pattern === "*/*") return true;
|
|
93
|
+
if (pattern === actual) return true;
|
|
94
|
+
if (pattern.endsWith("/*")) {
|
|
95
|
+
const prefix = pattern.slice(0, -2);
|
|
96
|
+
if (actual.startsWith(prefix + "/")) return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { fileGuard, parseMultipart };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AnalyticsCollector, AnalyticsOptions, AnalyticsSnapshot, ErrorEntry, ProcedureSnapshot, RequestTrace, TraceSpan, analyticsHTML, errorToMarkdown, trace } from "./analytics.mjs";
|
|
2
|
+
import { Span, Tracer, otelWrap } from "./otel.mjs";
|
|
3
|
+
import { CookieOptions, deleteCookie, getCookie, parseCookies, setCookie } from "./cookies.mjs";
|
|
4
|
+
import { CORSOptions, cors, corsHeaders } from "./cors.mjs";
|
|
5
|
+
import { Logger, LoggingOptions, loggingHooks } from "./pino.mjs";
|
|
6
|
+
import { MemoryRateLimiter, MemoryRateLimiterOptions, RateLimitGuardOptions, RateLimitResult, RateLimiter, rateLimitGuard } from "./ratelimit.mjs";
|
|
7
|
+
import { BodyLimitOptions, bodyLimitGuard } from "./body-limit.mjs";
|
|
8
|
+
import { strictGetGuard } from "./strict-get.mjs";
|
|
9
|
+
import { decrypt, encrypt, sign, unsign } from "./signing.mjs";
|
|
10
|
+
import { coerceGuard, coerceObject, coerceValue } from "./coerce.mjs";
|
|
11
|
+
import { BatchHandlerOptions, createBatchHandler } from "./batch-server.mjs";
|
|
12
|
+
import { MemoryPubSub, PubSubBackend, Publisher, createPublisher } from "./pubsub.mjs";
|
|
13
|
+
import { FileGuardOptions, UploadedFile, fileGuard, parseMultipart } from "./file-upload.mjs";
|
|
14
|
+
export { AnalyticsCollector, type AnalyticsOptions, type AnalyticsSnapshot, type BatchHandlerOptions, type BodyLimitOptions, type CORSOptions, type CookieOptions, type ErrorEntry, type FileGuardOptions, type Logger, type LoggingOptions, MemoryPubSub, MemoryRateLimiter, type MemoryRateLimiterOptions, type ProcedureSnapshot, type PubSubBackend, type Publisher, type RateLimitGuardOptions, type RateLimitResult, type RateLimiter, RequestTrace, type Span, type TraceSpan, type Tracer, type UploadedFile, analyticsHTML, bodyLimitGuard, coerceGuard, coerceObject, coerceValue, cors, corsHeaders, createBatchHandler, createPublisher, decrypt, deleteCookie, encrypt, errorToMarkdown, fileGuard, getCookie, loggingHooks, otelWrap, parseCookies, parseMultipart, rateLimitGuard, setCookie, sign, strictGetGuard, trace, unsign };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AnalyticsCollector, RequestTrace, analyticsHTML, errorToMarkdown, trace } from "./analytics.mjs";
|
|
2
|
+
import { cors, corsHeaders } from "./cors.mjs";
|
|
3
|
+
import { otelWrap } from "./otel.mjs";
|
|
4
|
+
import { loggingHooks } from "./pino.mjs";
|
|
5
|
+
import { MemoryRateLimiter, rateLimitGuard } from "./ratelimit.mjs";
|
|
6
|
+
import { bodyLimitGuard } from "./body-limit.mjs";
|
|
7
|
+
import { strictGetGuard } from "./strict-get.mjs";
|
|
8
|
+
import { deleteCookie, getCookie, parseCookies, setCookie } from "./cookies.mjs";
|
|
9
|
+
import { decrypt, encrypt, sign, unsign } from "./signing.mjs";
|
|
10
|
+
import { coerceGuard, coerceObject, coerceValue } from "./coerce.mjs";
|
|
11
|
+
import { createBatchHandler } from "./batch-server.mjs";
|
|
12
|
+
import { MemoryPubSub, createPublisher } from "./pubsub.mjs";
|
|
13
|
+
import { fileGuard, parseMultipart } from "./file-upload.mjs";
|
|
14
|
+
export { AnalyticsCollector, MemoryPubSub, MemoryRateLimiter, RequestTrace, analyticsHTML, bodyLimitGuard, coerceGuard, coerceObject, coerceValue, cors, corsHeaders, createBatchHandler, createPublisher, decrypt, deleteCookie, encrypt, errorToMarkdown, fileGuard, getCookie, loggingHooks, otelWrap, parseCookies, parseMultipart, rateLimitGuard, setCookie, sign, strictGetGuard, trace, unsign };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { WrapDef } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/otel.d.ts
|
|
4
|
+
interface Span {
|
|
5
|
+
setAttribute(key: string, value: string | number | boolean): void;
|
|
6
|
+
setStatus(status: {
|
|
7
|
+
code: number;
|
|
8
|
+
message?: string;
|
|
9
|
+
}): void;
|
|
10
|
+
addEvent(name: string, attributes?: Record<string, unknown>): void;
|
|
11
|
+
end(): void;
|
|
12
|
+
}
|
|
13
|
+
interface Tracer {
|
|
14
|
+
startSpan(name: string, options?: {
|
|
15
|
+
attributes?: Record<string, string | number | boolean>;
|
|
16
|
+
}): Span;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create an OTel tracing wrap middleware.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { otelWrap } from "silgi/otel"
|
|
24
|
+
* import { trace } from "@opentelemetry/api"
|
|
25
|
+
*
|
|
26
|
+
* const tracing = otelWrap(trace.getTracer("my-service"))
|
|
27
|
+
*
|
|
28
|
+
* const proc = k
|
|
29
|
+
* .$use(tracing)
|
|
30
|
+
* .$resolve(({ ctx }) => ctx.db.find())
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function otelWrap(tracer: Tracer, spanName?: string): WrapDef;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { Span, Tracer, otelWrap };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/plugins/otel.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create an OTel tracing wrap middleware.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { otelWrap } from "silgi/otel"
|
|
8
|
+
* import { trace } from "@opentelemetry/api"
|
|
9
|
+
*
|
|
10
|
+
* const tracing = otelWrap(trace.getTracer("my-service"))
|
|
11
|
+
*
|
|
12
|
+
* const proc = k
|
|
13
|
+
* .$use(tracing)
|
|
14
|
+
* .$resolve(({ ctx }) => ctx.db.find())
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function otelWrap(tracer, spanName = "rpc.call") {
|
|
18
|
+
return {
|
|
19
|
+
kind: "wrap",
|
|
20
|
+
fn: async (_ctx, next) => {
|
|
21
|
+
const span = tracer.startSpan(spanName, { attributes: { "rpc.system": "silgi" } });
|
|
22
|
+
try {
|
|
23
|
+
const result = await next();
|
|
24
|
+
span.setStatus({ code: 0 });
|
|
25
|
+
return result;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
span.setStatus({
|
|
28
|
+
code: 2,
|
|
29
|
+
message: String(error)
|
|
30
|
+
});
|
|
31
|
+
span.addEvent("exception", { "exception.message": error instanceof Error ? error.message : String(error) });
|
|
32
|
+
throw error;
|
|
33
|
+
} finally {
|
|
34
|
+
span.end();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { otelWrap };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/plugins/pino.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pino logging plugin — v2 hook-based.
|
|
4
|
+
*
|
|
5
|
+
* Logs request lifecycle events via silgi hooks.
|
|
6
|
+
*/
|
|
7
|
+
interface Logger {
|
|
8
|
+
child(bindings: Record<string, unknown>): Logger;
|
|
9
|
+
info(obj: Record<string, unknown>, msg?: string): void;
|
|
10
|
+
error(obj: Record<string, unknown>, msg?: string): void;
|
|
11
|
+
warn(obj: Record<string, unknown>, msg?: string): void;
|
|
12
|
+
debug(obj: Record<string, unknown>, msg?: string): void;
|
|
13
|
+
}
|
|
14
|
+
interface LoggingOptions {
|
|
15
|
+
/** The root logger instance */
|
|
16
|
+
logger: Logger;
|
|
17
|
+
/** Log request received events (default: true) */
|
|
18
|
+
logRequests?: boolean;
|
|
19
|
+
/** Log response events (default: true) */
|
|
20
|
+
logResponses?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create logging hooks for silgi().
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import pino from "pino"
|
|
28
|
+
* import { loggingHooks } from "silgi/pino"
|
|
29
|
+
*
|
|
30
|
+
* const k = silgi({
|
|
31
|
+
* context: (req) => ({}),
|
|
32
|
+
* hooks: loggingHooks({ logger: pino() }),
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function loggingHooks(options: LoggingOptions): {
|
|
37
|
+
error: ({
|
|
38
|
+
path,
|
|
39
|
+
error
|
|
40
|
+
}: {
|
|
41
|
+
path: string;
|
|
42
|
+
error: unknown;
|
|
43
|
+
}) => void;
|
|
44
|
+
response?: (({
|
|
45
|
+
path,
|
|
46
|
+
durationMs
|
|
47
|
+
}: {
|
|
48
|
+
path: string;
|
|
49
|
+
durationMs: number;
|
|
50
|
+
}) => void) | undefined;
|
|
51
|
+
request?: (({
|
|
52
|
+
path,
|
|
53
|
+
input
|
|
54
|
+
}: {
|
|
55
|
+
path: string;
|
|
56
|
+
input: unknown;
|
|
57
|
+
}) => void) | undefined;
|
|
58
|
+
};
|
|
59
|
+
//#endregion
|
|
60
|
+
export { Logger, LoggingOptions, loggingHooks };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/plugins/pino.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create logging hooks for silgi().
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import pino from "pino"
|
|
8
|
+
* import { loggingHooks } from "silgi/pino"
|
|
9
|
+
*
|
|
10
|
+
* const k = silgi({
|
|
11
|
+
* context: (req) => ({}),
|
|
12
|
+
* hooks: loggingHooks({ logger: pino() }),
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
function loggingHooks(options) {
|
|
17
|
+
const { logger } = options;
|
|
18
|
+
const logRequests = options.logRequests ?? true;
|
|
19
|
+
const logResponses = options.logResponses ?? true;
|
|
20
|
+
return {
|
|
21
|
+
...logRequests && { request: ({ path, input }) => {
|
|
22
|
+
logger.info({
|
|
23
|
+
path,
|
|
24
|
+
hasInput: input !== void 0
|
|
25
|
+
}, "request received");
|
|
26
|
+
} },
|
|
27
|
+
...logResponses && { response: ({ path, durationMs }) => {
|
|
28
|
+
logger.info({
|
|
29
|
+
path,
|
|
30
|
+
durationMs: Math.round(durationMs * 100) / 100
|
|
31
|
+
}, "response sent");
|
|
32
|
+
} },
|
|
33
|
+
error: ({ path, error }) => {
|
|
34
|
+
logger.error({
|
|
35
|
+
path,
|
|
36
|
+
error: error instanceof Error ? error.message : String(error)
|
|
37
|
+
}, "request error");
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { loggingHooks };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/plugins/pubsub.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Publisher/PubSub — event pub/sub with pluggable backends.
|
|
4
|
+
*
|
|
5
|
+
* Publish events from any procedure, subscribe from SSE/WebSocket.
|
|
6
|
+
* Built-in memory adapter. Redis/Upstash adapters plug in via interface.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createPublisher, MemoryPubSub } from "silgi/plugins"
|
|
11
|
+
*
|
|
12
|
+
* const pubsub = createPublisher(new MemoryPubSub())
|
|
13
|
+
*
|
|
14
|
+
* // Publish from a mutation
|
|
15
|
+
* const createUser = k.$resolve(async ({ input, ctx }) => {
|
|
16
|
+
* const user = await ctx.db.users.create(input)
|
|
17
|
+
* await pubsub.publish("user:created", user)
|
|
18
|
+
* return user
|
|
19
|
+
* })
|
|
20
|
+
*
|
|
21
|
+
* // Subscribe via SSE
|
|
22
|
+
* const onUserCreated = k.subscription(async function* () {
|
|
23
|
+
* yield* pubsub.subscribe("user:created")
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
interface PubSubBackend {
|
|
28
|
+
publish(channel: string, data: unknown): Promise<void>;
|
|
29
|
+
subscribe(channel: string, callback: (data: unknown) => void): () => void;
|
|
30
|
+
}
|
|
31
|
+
declare class MemoryPubSub implements PubSubBackend {
|
|
32
|
+
#private;
|
|
33
|
+
publish(channel: string, data: unknown): Promise<void>;
|
|
34
|
+
subscribe(channel: string, callback: (data: unknown) => void): () => void;
|
|
35
|
+
}
|
|
36
|
+
interface Publisher {
|
|
37
|
+
/** Publish an event to a channel */
|
|
38
|
+
publish(channel: string, data: unknown): Promise<void>;
|
|
39
|
+
/** Subscribe to a channel — returns an async iterable for use in subscriptions */
|
|
40
|
+
subscribe<T = unknown>(channel: string): AsyncGenerator<T, void, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a publisher from a PubSub backend.
|
|
44
|
+
*
|
|
45
|
+
* The publisher exposes `publish()` for mutations and `subscribe()`
|
|
46
|
+
* as an async generator for SSE/WebSocket subscriptions.
|
|
47
|
+
*/
|
|
48
|
+
declare function createPublisher(backend: PubSubBackend): Publisher;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { MemoryPubSub, PubSubBackend, Publisher, createPublisher };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/plugins/pubsub.ts
|
|
2
|
+
var MemoryPubSub = class {
|
|
3
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
4
|
+
async publish(channel, data) {
|
|
5
|
+
const listeners = this.#listeners.get(channel);
|
|
6
|
+
if (!listeners) return;
|
|
7
|
+
for (const cb of listeners) cb(data);
|
|
8
|
+
}
|
|
9
|
+
subscribe(channel, callback) {
|
|
10
|
+
let set = this.#listeners.get(channel);
|
|
11
|
+
if (!set) {
|
|
12
|
+
set = /* @__PURE__ */ new Set();
|
|
13
|
+
this.#listeners.set(channel, set);
|
|
14
|
+
}
|
|
15
|
+
set.add(callback);
|
|
16
|
+
return () => {
|
|
17
|
+
set.delete(callback);
|
|
18
|
+
if (set.size === 0) this.#listeners.delete(channel);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Create a publisher from a PubSub backend.
|
|
24
|
+
*
|
|
25
|
+
* The publisher exposes `publish()` for mutations and `subscribe()`
|
|
26
|
+
* as an async generator for SSE/WebSocket subscriptions.
|
|
27
|
+
*/
|
|
28
|
+
function createPublisher(backend) {
|
|
29
|
+
return {
|
|
30
|
+
publish: (channel, data) => backend.publish(channel, data),
|
|
31
|
+
async *subscribe(channel) {
|
|
32
|
+
const queue = [];
|
|
33
|
+
let resolve = null;
|
|
34
|
+
const unsubscribe = backend.subscribe(channel, (data) => {
|
|
35
|
+
queue.push(data);
|
|
36
|
+
if (resolve) {
|
|
37
|
+
resolve();
|
|
38
|
+
resolve = null;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
try {
|
|
42
|
+
while (true) if (queue.length > 0) yield queue.shift();
|
|
43
|
+
else await new Promise((r) => {
|
|
44
|
+
resolve = r;
|
|
45
|
+
});
|
|
46
|
+
} finally {
|
|
47
|
+
unsubscribe();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { MemoryPubSub, createPublisher };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { GuardDef } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/ratelimit.d.ts
|
|
4
|
+
interface RateLimitResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
limit: number;
|
|
7
|
+
remaining: number;
|
|
8
|
+
reset: number;
|
|
9
|
+
}
|
|
10
|
+
interface RateLimiter {
|
|
11
|
+
limit(key: string): Promise<RateLimitResult>;
|
|
12
|
+
}
|
|
13
|
+
interface MemoryRateLimiterOptions {
|
|
14
|
+
/** Maximum requests per window */
|
|
15
|
+
limit: number;
|
|
16
|
+
/** Window duration in milliseconds */
|
|
17
|
+
windowMs: number;
|
|
18
|
+
}
|
|
19
|
+
declare class MemoryRateLimiter implements RateLimiter {
|
|
20
|
+
#private;
|
|
21
|
+
constructor(options: MemoryRateLimiterOptions);
|
|
22
|
+
limit(key: string): Promise<RateLimitResult>;
|
|
23
|
+
/** Remove entries with no active timestamps to prevent unbounded Map growth */
|
|
24
|
+
evict(): void;
|
|
25
|
+
}
|
|
26
|
+
interface RateLimitGuardOptions {
|
|
27
|
+
/** The rate limiter instance */
|
|
28
|
+
limiter: RateLimiter;
|
|
29
|
+
/** Extract rate limit key from context */
|
|
30
|
+
keyFn: (ctx: Record<string, unknown>) => string | Promise<string>;
|
|
31
|
+
/** Custom error message */
|
|
32
|
+
message?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a rate limiting guard.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { rateLimitGuard, MemoryRateLimiter } from "silgi/ratelimit"
|
|
40
|
+
*
|
|
41
|
+
* const rateLimit = rateLimitGuard({
|
|
42
|
+
* limiter: new MemoryRateLimiter({ limit: 100, windowMs: 60_000 }),
|
|
43
|
+
* keyFn: (ctx) => (ctx as any).ip ?? "anonymous",
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* const proc = k
|
|
47
|
+
* .$use(rateLimit)
|
|
48
|
+
* .$resolve(() => ({ ok: true }))
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare function rateLimitGuard(options: RateLimitGuardOptions): GuardDef<any, any>;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { MemoryRateLimiter, MemoryRateLimiterOptions, RateLimitGuardOptions, RateLimitResult, RateLimiter, rateLimitGuard };
|