@senrijs/senri 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +377 -2
- package/dist/adapters/node/index.d.ts +14 -0
- package/dist/adapters/node/index.js +152 -0
- package/dist/adapters/serve/index.d.ts +28 -0
- package/dist/adapters/serve/index.js +137 -0
- package/dist/adapters/static/index.d.ts +9 -0
- package/dist/adapters/static/index.js +120 -0
- package/dist/src/app.d.ts +86 -0
- package/dist/src/app.js +325 -0
- package/dist/src/body.d.ts +3 -0
- package/dist/src/body.js +45 -0
- package/dist/src/compose.d.ts +22 -0
- package/dist/src/compose.js +80 -0
- package/dist/src/connection.d.ts +3 -0
- package/dist/src/connection.js +49 -0
- package/dist/src/context.d.ts +58 -0
- package/dist/src/context.js +235 -0
- package/dist/src/cookie.d.ts +18 -0
- package/dist/src/cookie.js +73 -0
- package/dist/src/dev/dashboard/access.d.ts +6 -0
- package/dist/src/dev/dashboard/access.js +46 -0
- package/dist/src/dev/dashboard/index.d.ts +27 -0
- package/dist/src/dev/dashboard/index.js +51 -0
- package/dist/src/dev/dashboard/links.d.ts +3 -0
- package/dist/src/dev/dashboard/links.js +8 -0
- package/dist/src/dev/dashboard/log.d.ts +20 -0
- package/dist/src/dev/dashboard/log.js +53 -0
- package/dist/src/dev/dashboard/mask.d.ts +4 -0
- package/dist/src/dev/dashboard/mask.js +43 -0
- package/dist/src/dev/dashboard/record.d.ts +37 -0
- package/dist/src/dev/dashboard/record.js +50 -0
- package/dist/src/dev/dashboard/routes.d.ts +7 -0
- package/dist/src/dev/dashboard/routes.js +121 -0
- package/dist/src/dev/dashboard/sse.d.ts +7 -0
- package/dist/src/dev/dashboard/sse.js +73 -0
- package/dist/src/dev/editor-url.d.ts +14 -0
- package/dist/src/dev/editor-url.js +61 -0
- package/dist/src/dev/editor.d.ts +6 -0
- package/dist/src/dev/editor.js +14 -0
- package/dist/src/dev/error-page.d.ts +3 -0
- package/dist/src/dev/error-page.js +69 -0
- package/dist/src/dev/explain.d.ts +2 -0
- package/dist/src/dev/explain.js +37 -0
- package/dist/src/dev/fallback.d.ts +3 -0
- package/dist/src/dev/fallback.js +40 -0
- package/dist/src/dev/hints.d.ts +6 -0
- package/dist/src/dev/hints.js +85 -0
- package/dist/src/dev/html.d.ts +1 -0
- package/dist/src/dev/html.js +14 -0
- package/dist/src/dev/index.d.ts +3 -0
- package/dist/src/dev/index.js +185 -0
- package/dist/src/dev/messages.d.ts +2 -0
- package/dist/src/dev/messages.js +6 -0
- package/dist/src/dev/not-found-page.d.ts +7 -0
- package/dist/src/dev/not-found-page.js +33 -0
- package/dist/src/dev/not-found.d.ts +3 -0
- package/dist/src/dev/not-found.js +15 -0
- package/dist/src/dev/page-data.d.ts +95 -0
- package/dist/src/dev/page-data.js +0 -0
- package/dist/src/dev/page.generated.d.ts +1 -0
- package/dist/src/dev/page.generated.js +3 -0
- package/dist/src/dev/pages.d.ts +4 -0
- package/dist/src/dev/pages.js +59 -0
- package/dist/src/dev/registry.d.ts +3 -0
- package/dist/src/dev/registry.js +7 -0
- package/dist/src/dev/render.d.ts +5 -0
- package/dist/src/dev/render.js +42 -0
- package/dist/src/dev/server-timing.d.ts +6 -0
- package/dist/src/dev/server-timing.js +45 -0
- package/dist/src/dev/source.d.ts +20 -0
- package/dist/src/dev/source.js +106 -0
- package/dist/src/dev/stack.d.ts +12 -0
- package/dist/src/dev/stack.js +93 -0
- package/dist/src/dev/suggest.d.ts +12 -0
- package/dist/src/dev/suggest.js +95 -0
- package/dist/src/dev/validation.d.ts +3 -0
- package/dist/src/dev/validation.js +47 -0
- package/dist/src/errors.d.ts +25 -0
- package/dist/src/errors.js +97 -0
- package/dist/src/group.d.ts +37 -0
- package/dist/src/group.js +75 -0
- package/dist/src/health.d.ts +9 -0
- package/dist/src/health.js +40 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +12 -0
- package/dist/src/ip.d.ts +2 -0
- package/dist/src/ip.js +12 -0
- package/dist/src/logger.d.ts +18 -0
- package/dist/src/logger.js +110 -0
- package/dist/src/matcher.d.ts +16 -0
- package/dist/src/matcher.js +86 -0
- package/dist/src/middleware/body-limit.d.ts +6 -0
- package/dist/src/middleware/body-limit.js +47 -0
- package/dist/src/middleware/cors.d.ts +10 -0
- package/dist/src/middleware/cors.js +79 -0
- package/dist/src/middleware/response.d.ts +2 -0
- package/dist/src/middleware/response.js +28 -0
- package/dist/src/middleware/secure-headers.d.ts +19 -0
- package/dist/src/middleware/secure-headers.js +46 -0
- package/dist/src/router.d.ts +30 -0
- package/dist/src/router.js +177 -0
- package/dist/src/runtime.d.ts +5 -0
- package/dist/src/runtime.js +55 -0
- package/dist/src/validate.d.ts +46 -0
- package/dist/src/validate.js +55 -0
- package/package.json +90 -4
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function isWorkers(): boolean;
|
|
2
|
+
export declare function cwd(): string | null;
|
|
3
|
+
export declare function env(name: string): string | undefined;
|
|
4
|
+
export declare function canRead(path: string): boolean;
|
|
5
|
+
export declare function canReadEnv(name: string): boolean;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// ランタイムごとに違う処理(カレントディレクトリ、環境変数、ファイルの読み込み権限)。開発用ページと serve() で使う
|
|
2
|
+
//
|
|
3
|
+
// Deno は、許可されていない環境変数やファイルを読もうとすると、ターミナルで「許可しますか?」と聞いてきて
|
|
4
|
+
// その間リクエストが止まってしまう。なので先に permissions.querySync() で「許可済みか」だけを確かめ、
|
|
5
|
+
// 許可されていなければ読まずにあきらめる(聞き返しはしない)
|
|
6
|
+
const g = globalThis;
|
|
7
|
+
// Cloudflare Workers(workerd)で動いているか。Workers の navigator.userAgent は決まった値になる
|
|
8
|
+
export function isWorkers() {
|
|
9
|
+
return g.navigator?.userAgent === "Cloudflare-Workers";
|
|
10
|
+
}
|
|
11
|
+
function denoAllows(desc) {
|
|
12
|
+
try {
|
|
13
|
+
return g.Deno.permissions.querySync(desc).state === "granted";
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// スラッシュ区切りのカレントディレクトリ。分からなければ null
|
|
20
|
+
export function cwd() {
|
|
21
|
+
try {
|
|
22
|
+
// Deno 2 には process もあるが、権限の確認をしたいので Deno を先に見る
|
|
23
|
+
// 全体({ name: "read" })で聞くと、--allow-read=. のように範囲を絞ったときに "prompt" になってしまう。
|
|
24
|
+
// カレントディレクトリが読めればよいので、"." で聞く
|
|
25
|
+
if (g.Deno)
|
|
26
|
+
return denoAllows({ name: "read", path: "." }) ? g.Deno.cwd().replaceAll("\\", "/") : null;
|
|
27
|
+
if (g.process)
|
|
28
|
+
return g.process.cwd().replaceAll("\\", "/");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// 読めなければあきらめる
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
export function env(name) {
|
|
36
|
+
try {
|
|
37
|
+
if (g.Deno)
|
|
38
|
+
return denoAllows({ name: "env", variable: name }) ? g.Deno.env.get(name) : undefined;
|
|
39
|
+
return g.process?.env[name];
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function canRead(path) {
|
|
46
|
+
if (g.Deno)
|
|
47
|
+
return denoAllows({ name: "read", path });
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// 環境変数を読めるか。Deno で許可(--allow-env)がないときは false(聞き返しはしない)
|
|
51
|
+
export function canReadEnv(name) {
|
|
52
|
+
if (g.Deno)
|
|
53
|
+
return denoAllows({ name: "env", variable: name });
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Handler } from "./context.ts";
|
|
2
|
+
import { HttpError } from "./errors.ts";
|
|
3
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
4
|
+
readonly "~standard": {
|
|
5
|
+
readonly version: 1;
|
|
6
|
+
readonly vendor: string;
|
|
7
|
+
readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
|
|
8
|
+
readonly types?: {
|
|
9
|
+
readonly input: Input;
|
|
10
|
+
readonly output: Output;
|
|
11
|
+
} | undefined;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
type StandardResult<Output> = {
|
|
15
|
+
readonly value: Output;
|
|
16
|
+
readonly issues?: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
readonly issues: readonly StandardIssue[];
|
|
19
|
+
};
|
|
20
|
+
export type StandardIssue = {
|
|
21
|
+
readonly message: string;
|
|
22
|
+
readonly path?: readonly (PropertyKey | {
|
|
23
|
+
readonly key: PropertyKey;
|
|
24
|
+
})[] | undefined;
|
|
25
|
+
};
|
|
26
|
+
export type InferOutput<S> = S extends StandardSchemaV1<any, infer Output> ? Output : never;
|
|
27
|
+
export type RouteSchemas = {
|
|
28
|
+
params?: StandardSchemaV1;
|
|
29
|
+
query?: StandardSchemaV1;
|
|
30
|
+
body?: StandardSchemaV1;
|
|
31
|
+
};
|
|
32
|
+
export type Validated<S extends RouteSchemas> = {
|
|
33
|
+
[K in keyof S & ("query" | "body")]: InferOutput<S[K]>;
|
|
34
|
+
};
|
|
35
|
+
export type ValidatedParams<S extends RouteSchemas, Fallback> = S extends {
|
|
36
|
+
params: StandardSchemaV1;
|
|
37
|
+
} ? InferOutput<S["params"]> : Fallback;
|
|
38
|
+
export type ValidationTarget = "params" | "query" | "body";
|
|
39
|
+
export declare class ValidationError extends HttpError {
|
|
40
|
+
readonly target: ValidationTarget;
|
|
41
|
+
readonly issues: readonly StandardIssue[];
|
|
42
|
+
constructor(target: ValidationTarget, issues: readonly StandardIssue[]);
|
|
43
|
+
}
|
|
44
|
+
export declare function withValidation(schemas: RouteSchemas, handler: Handler): Handler;
|
|
45
|
+
export declare function routeHandler(path: string, a: Handler | RouteSchemas, b: Handler | undefined): Handler;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Standard Schema(https://standardschema.dev)によるバリデーション
|
|
2
|
+
// Zod・Valibot・ArkType など、Standard Schema に対応したライブラリのスキーマをそのまま使える(Senri はどれにも依存しない)
|
|
3
|
+
//
|
|
4
|
+
// app.post("/users/:id", { params, query, body }, handler) のように、ルートにスキーマを渡す
|
|
5
|
+
// - params: パスパラメーター。通ればハンドラーの第1引数(と c.params)が変換後の値になる
|
|
6
|
+
// - query: c.query() の結果。通れば c.valid("query") で読める
|
|
7
|
+
// - body: c.body() の結果(Content-Type に合わせて読んだもの)。通れば c.valid("body") で読める
|
|
8
|
+
// どれかが通らなければ、ハンドラーは呼ばずに 400 にする(ValidationError)
|
|
9
|
+
import { setValidated } from "./context.js";
|
|
10
|
+
import { HttpError, senriError } from "./errors.js";
|
|
11
|
+
// バリデーションに通らなかった。issues はスキーマのライブラリが返したもの
|
|
12
|
+
// app.onError で受けて、自分の形(JSON など)で返すこともできる
|
|
13
|
+
// (開発モードの「どこが違うか」の一覧は @senrijs/senri/dev が組み立てる)
|
|
14
|
+
export class ValidationError extends HttpError {
|
|
15
|
+
target;
|
|
16
|
+
issues;
|
|
17
|
+
constructor(target, issues) {
|
|
18
|
+
// 本番の本文(message)には中身を出さない(スキーマの作りをそのまま外に見せないため)
|
|
19
|
+
super(400, "400 Bad Request");
|
|
20
|
+
this.name = "ValidationError";
|
|
21
|
+
this.target = target;
|
|
22
|
+
this.issues = issues;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function run(schema, target, value) {
|
|
26
|
+
const result = await schema["~standard"].validate(value);
|
|
27
|
+
if (result.issues)
|
|
28
|
+
throw new ValidationError(target, result.issues);
|
|
29
|
+
return result.value;
|
|
30
|
+
}
|
|
31
|
+
// スキーマで確かめてからハンドラーを呼ぶハンドラーにする。params → query → body の順に確かめる
|
|
32
|
+
export function withValidation(schemas, handler) {
|
|
33
|
+
return async (params, c) => {
|
|
34
|
+
let validParams = params;
|
|
35
|
+
if (schemas.params) {
|
|
36
|
+
validParams = (await run(schemas.params, "params", params));
|
|
37
|
+
c.params = validParams;
|
|
38
|
+
}
|
|
39
|
+
if (schemas.query)
|
|
40
|
+
setValidated(c, "query", await run(schemas.query, "query", c.query()));
|
|
41
|
+
if (schemas.body)
|
|
42
|
+
setValidated(c, "body", await run(schemas.body, "body", await c.body()));
|
|
43
|
+
return handler(validParams, c);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// app.get(path, handler) と app.get(path, { params, query, body }, handler) のどちらか
|
|
47
|
+
// 2つ目の引数がスキーマなら、確かめてからハンドラーを呼ぶものにする
|
|
48
|
+
// (スキーマの書き間違いは、開発モードで登録した時点で知らせる。@senrijs/senri/dev の validation.ts)
|
|
49
|
+
export function routeHandler(path, a, b) {
|
|
50
|
+
if (typeof a === "function")
|
|
51
|
+
return a;
|
|
52
|
+
if (typeof b !== "function")
|
|
53
|
+
throw senriError("no_handler", `Missing handler: ${path}`);
|
|
54
|
+
return withValidation(a, b);
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,92 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@senrijs/senri",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Web 標準の Request / Response で書かれた、小さな Web フレームワーク。Bun・Deno・Node.js・Cloudflare Workers で動く",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "ibuki-hum4",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Senrijs/Senri.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Senrijs/Senri#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Senrijs/Senri/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"web",
|
|
17
|
+
"framework",
|
|
18
|
+
"router",
|
|
19
|
+
"http",
|
|
20
|
+
"fetch",
|
|
21
|
+
"bun",
|
|
22
|
+
"deno",
|
|
23
|
+
"node",
|
|
24
|
+
"cloudflare-workers",
|
|
25
|
+
"standard-schema"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"private": false,
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/src/index.d.ts",
|
|
36
|
+
"import": "./dist/src/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./dev": {
|
|
39
|
+
"types": "./dist/src/dev/index.d.ts",
|
|
40
|
+
"import": "./dist/src/dev/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./cookie": {
|
|
43
|
+
"types": "./dist/src/cookie.d.ts",
|
|
44
|
+
"import": "./dist/src/cookie.js"
|
|
45
|
+
},
|
|
46
|
+
"./ip": {
|
|
47
|
+
"types": "./dist/src/ip.d.ts",
|
|
48
|
+
"import": "./dist/src/ip.js"
|
|
49
|
+
},
|
|
50
|
+
"./node": {
|
|
51
|
+
"types": "./dist/adapters/node/index.d.ts",
|
|
52
|
+
"import": "./dist/adapters/node/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./serve": {
|
|
55
|
+
"types": "./dist/adapters/serve/index.d.ts",
|
|
56
|
+
"import": "./dist/adapters/serve/index.js"
|
|
57
|
+
},
|
|
58
|
+
"./static": {
|
|
59
|
+
"types": "./dist/adapters/static/index.d.ts",
|
|
60
|
+
"import": "./dist/adapters/static/index.js"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"dist"
|
|
65
|
+
],
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsc -p tsconfig.build.json",
|
|
68
|
+
"build:pages": "vite build -c ui/vite.config.ts && node scripts/build-pages.ts",
|
|
69
|
+
"typecheck": "bun run build && bun check && bun check -p test && bun check -p test/exact && bun check -p test/node && bun check -p adapters/node && bun check -p examples/bun && bun check -p examples/node && bun check -p scripts && deno check examples/deno/index.ts && bun check -p adapters/serve && bun check -p adapters/static && bun check -p ui",
|
|
70
|
+
"dev": "bun --watch examples/bun/index.ts",
|
|
71
|
+
"test": "bun test",
|
|
72
|
+
"test:node": "bun run build && node --test --test-timeout=5000 \"test/node/**/*.test.ts\"",
|
|
73
|
+
"smoke": "node scripts/smoke.ts",
|
|
74
|
+
"browser": "node scripts/browser.ts",
|
|
75
|
+
"size": "bun scripts/size.ts"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@types/bun": "latest",
|
|
79
|
+
"@types/node": "^26.6.5",
|
|
80
|
+
"@vitejs/plugin-vue": "6.0.10",
|
|
81
|
+
"arktype": "2.2.8",
|
|
82
|
+
"playwright": "1.64.0",
|
|
83
|
+
"typescript": "^7",
|
|
84
|
+
"valibot": "1.5.0",
|
|
85
|
+
"vite": "8.3.5",
|
|
86
|
+
"vite-plugin-singlefile": "2.3.4",
|
|
87
|
+
"vue": "3.5.44",
|
|
88
|
+
"workerd": "1.20261010.1",
|
|
89
|
+
"wrangler": "4.149.0",
|
|
90
|
+
"zod": "4.6.6"
|
|
91
|
+
}
|
|
92
|
+
}
|