@senrijs/senri 0.1.0 → 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/README.md +62 -16
- package/dist/src/app.d.ts +41 -4
- package/dist/src/app.js +67 -117
- package/dist/src/compose.d.ts +5 -5
- package/dist/src/compose.js +15 -30
- package/dist/src/context.d.ts +4 -6
- package/dist/src/context.js +15 -34
- package/dist/src/cookie.d.ts +5 -0
- package/dist/src/cookie.js +15 -3
- package/dist/src/dev/explain.d.ts +2 -0
- package/dist/src/dev/explain.js +37 -0
- package/dist/src/dev/hints.js +6 -3
- package/dist/src/dev/index.d.ts +3 -6
- package/dist/src/dev/index.js +184 -10
- package/dist/src/dev/not-found.d.ts +3 -0
- package/dist/src/dev/not-found.js +15 -0
- package/dist/src/dev/registry.d.ts +3 -0
- package/dist/src/dev/registry.js +7 -0
- package/dist/src/dev/validation.d.ts +3 -0
- package/dist/src/dev/validation.js +47 -0
- package/dist/src/errors.d.ts +7 -5
- package/dist/src/errors.js +16 -55
- package/dist/src/group.d.ts +0 -1
- package/dist/src/group.js +8 -16
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +3 -1
- package/dist/src/ip.d.ts +2 -0
- package/dist/src/ip.js +12 -0
- package/dist/src/router.js +5 -5
- package/dist/src/runtime.d.ts +1 -0
- package/dist/src/runtime.js +6 -0
- package/dist/src/validate.d.ts +1 -2
- package/dist/src/validate.js +12 -44
- package/package.json +19 -3
- package/dist/src/dev/runtime.d.ts +0 -3
- package/dist/src/dev/runtime.js +0 -45
package/dist/src/errors.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { suggest } from "./dev/suggest.js";
|
|
2
|
-
import { wantsHtml } from "./dev/render.js";
|
|
3
1
|
// status 付きで投げられるエラー。message はそのままクライアントに見せる前提のもの
|
|
4
2
|
export class HttpError extends Error {
|
|
5
3
|
status;
|
|
@@ -43,44 +41,28 @@ export async function handleError(err, c, onError) {
|
|
|
43
41
|
return new Response("500 Internal Server Error", { status: 500 });
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// validate.ts を import すると循環するので、形で見分ける
|
|
49
|
-
function isValidationError(err) {
|
|
50
|
-
return err.name === "ValidationError" && typeof err.describe === "function";
|
|
44
|
+
export function senriError(code, error) {
|
|
45
|
+
return Object.assign(typeof error === "string" ? new Error(error) : error, { code });
|
|
51
46
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (dev && isValidationError(err))
|
|
57
|
-
return c.text(err.describe());
|
|
58
|
-
return c.text(err.message);
|
|
59
|
-
}
|
|
60
|
-
if (!dev) {
|
|
61
|
-
return c.text("500 Internal Server Error");
|
|
62
|
-
}
|
|
63
|
-
return c.text(`500 Internal Server Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
64
|
-
};
|
|
47
|
+
// Senri が投げたエラーの code(なければ undefined)
|
|
48
|
+
export function errorCode(err) {
|
|
49
|
+
const code = err?.code;
|
|
50
|
+
return typeof code === "string" ? code : undefined;
|
|
65
51
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
// 本番の既定。エラーの中身(DB のパスワードなど)がもれないよう、固定の文言だけ返す
|
|
53
|
+
// (開発モードの詳しい文言とエラーページは @senrijs/senri/dev が差し替える)
|
|
54
|
+
export const defaultOnError = (err, c) => c.text(err instanceof HttpError ? err.message : "500 Internal Server Error");
|
|
55
|
+
export const defaultNotFound = (c) => c.text("404 Not Found");
|
|
56
|
+
export function methodNotAllowed(methods) {
|
|
57
|
+
return senriError("method_not_allowed", new HttpError(405, "405 Method Not Allowed", { headers: { allow: methods } }));
|
|
71
58
|
}
|
|
72
59
|
// QUERY(RFC 10008)は、検索の条件をボディに入れて送るメソッド。ボディと、その形式(Content-Type)が必須
|
|
73
60
|
// ハンドラーを呼ぶ前に確かめる。どちらも問題なければ null
|
|
74
|
-
export async function checkQueryRequest(c
|
|
75
|
-
if (await isEmptyBody(c))
|
|
76
|
-
return new HttpError(400,
|
|
77
|
-
? "400 Bad Request: QUERY リクエストにボディがありません\nQUERY は、検索の条件をボディに入れて送るメソッドです"
|
|
78
|
-
: "400 Bad Request");
|
|
79
|
-
}
|
|
61
|
+
export async function checkQueryRequest(c) {
|
|
62
|
+
if (await isEmptyBody(c))
|
|
63
|
+
return senriError("query_no_body", new HttpError(400, "400 Bad Request"));
|
|
80
64
|
if (!c.req.headers.get("content-type")) {
|
|
81
|
-
return new HttpError(415,
|
|
82
|
-
? "415 Unsupported Media Type: QUERY リクエストに Content-Type がありません\nボディの形式を指定してください(例: Content-Type: application/json)"
|
|
83
|
-
: "415 Unsupported Media Type");
|
|
65
|
+
return senriError("query_no_type", new HttpError(415, "415 Unsupported Media Type"));
|
|
84
66
|
}
|
|
85
67
|
return null;
|
|
86
68
|
}
|
|
@@ -113,24 +95,3 @@ async function isEmptyBody(c) {
|
|
|
113
95
|
reader.cancel().catch(() => { });
|
|
114
96
|
}
|
|
115
97
|
}
|
|
116
|
-
export function defaultNotFound(routes, dev) {
|
|
117
|
-
return async (c) => {
|
|
118
|
-
if (!dev) {
|
|
119
|
-
return c.text("404 Not Found");
|
|
120
|
-
}
|
|
121
|
-
// ブラウザで開いたときは、ルートの一覧付きの404ページ(HTML)にする
|
|
122
|
-
// ページの HTML は大きいので、本番では読み込まないよう、ここで初めて import() する
|
|
123
|
-
if (wantsHtml(c.req)) {
|
|
124
|
-
const { notFoundPage } = await import("./dev/not-found-page.js");
|
|
125
|
-
return notFoundPage(c, routes);
|
|
126
|
-
}
|
|
127
|
-
// ルートと同じく、デコード・正規化したパスで比べる(notFound に来るのはデコードできたリクエストだけ)
|
|
128
|
-
const path = c.path;
|
|
129
|
-
let message = `404 Not Found: ${c.req.method} ${path}`;
|
|
130
|
-
const closest = suggest(routes, path);
|
|
131
|
-
if (closest) {
|
|
132
|
-
message += `\nもしかして: ${closest.method} ${closest.path} ?`;
|
|
133
|
-
}
|
|
134
|
-
return c.text(message);
|
|
135
|
-
};
|
|
136
|
-
}
|
package/dist/src/group.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Handler, Middleware } from "./context.ts";
|
|
2
2
|
import { type ExtractParams, type Simplify } from "./router.ts";
|
|
3
3
|
import { type RouteSchemas, type Validated, type ValidatedParams } from "./validate.ts";
|
|
4
|
-
export declare function routeHandler(path: string, a: Handler | RouteSchemas, b: Handler | undefined): Handler;
|
|
5
4
|
type GroupParams<Prefix extends string, Path extends string> = Simplify<ExtractParams<`${Prefix}/${Path}`>>;
|
|
6
5
|
type GroupMethod<Prefix extends string, V extends object> = {
|
|
7
6
|
<Path extends string>(path: Path, handler: Handler<GroupParams<Prefix, Path>, V>): void;
|
package/dist/src/group.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { normalizeMethod, splitPattern } from "./router.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export function routeHandler(path, a, b) {
|
|
5
|
-
if (typeof a === "function")
|
|
6
|
-
return a;
|
|
7
|
-
if (typeof b !== "function")
|
|
8
|
-
throw new TypeError(`${path}: ハンドラーがありません`);
|
|
9
|
-
checkSchemas(path, a);
|
|
10
|
-
return withValidation(a, b);
|
|
11
|
-
}
|
|
2
|
+
import { routeHandler } from "./validate.js";
|
|
3
|
+
import { senriError } from "./errors.js";
|
|
12
4
|
const entries = new WeakMap();
|
|
13
5
|
export function joinPath(...parts) {
|
|
14
6
|
return "/" + parts.flatMap(splitPattern).join("/");
|
|
@@ -17,7 +9,7 @@ export function joinPath(...parts) {
|
|
|
17
9
|
export function groupEntries(router) {
|
|
18
10
|
const list = entries.get(router);
|
|
19
11
|
if (!list)
|
|
20
|
-
throw new TypeError("
|
|
12
|
+
throw senriError("not_router", new TypeError("Not a router from createRouter()"));
|
|
21
13
|
return list("");
|
|
22
14
|
}
|
|
23
15
|
// V は c.set / c.get で使う変数の型。アプリと同じものを指定する: createRouter<"/admin", Vars>("/admin")
|
|
@@ -34,7 +26,7 @@ export function createRouter(prefix) {
|
|
|
34
26
|
const path = typeof pathOrMw === "string" ? pathOrMw : "/";
|
|
35
27
|
const fn = typeof pathOrMw === "string" ? mw : pathOrMw;
|
|
36
28
|
if (!fn)
|
|
37
|
-
throw
|
|
29
|
+
throw senriError("no_middleware", `Missing middleware: ${path}`);
|
|
38
30
|
items.push({ kind: "entry", entry: { kind: "use", path, mw: fn } });
|
|
39
31
|
},
|
|
40
32
|
get: method("GET"),
|
|
@@ -48,18 +40,18 @@ export function createRouter(prefix) {
|
|
|
48
40
|
on: ((methods, path, a, b) => {
|
|
49
41
|
const list = typeof methods === "string" ? [methods] : methods;
|
|
50
42
|
if (list.length === 0)
|
|
51
|
-
throw
|
|
43
|
+
throw senriError("no_method", `Missing method: ${path}`);
|
|
52
44
|
const handler = routeHandler(path, a, b);
|
|
53
45
|
for (const m of list) {
|
|
54
46
|
if (m.toUpperCase() === "ALL")
|
|
55
|
-
throw
|
|
47
|
+
throw senriError("on_all", "Use all() for every method");
|
|
56
48
|
// 名前の確認(CONNECT などの拒否)は、ここで先にしておく(app.route まで待たない)
|
|
57
49
|
push(normalizeMethod(m), path, handler);
|
|
58
50
|
}
|
|
59
51
|
}),
|
|
60
52
|
route(child) {
|
|
61
53
|
if (!entries.has(child))
|
|
62
|
-
throw new TypeError("
|
|
54
|
+
throw senriError("not_router", new TypeError("Not a router from createRouter()"));
|
|
63
55
|
items.push({ kind: "router", router: child });
|
|
64
56
|
},
|
|
65
57
|
};
|
|
@@ -67,7 +59,7 @@ export function createRouter(prefix) {
|
|
|
67
59
|
entries.set(router, (base) => {
|
|
68
60
|
// ルーターが自分自身を(間接的にでも)含んでいると終わらないので止める
|
|
69
61
|
if (seen.has(router))
|
|
70
|
-
throw
|
|
62
|
+
throw senriError("router_cycle", `Router ${prefix} contains itself`);
|
|
71
63
|
seen.add(router);
|
|
72
64
|
try {
|
|
73
65
|
const here = joinPath(base, prefix);
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { createApp, type App, type AppOptions, type MountableApp } from "./app.ts";
|
|
1
|
+
export { createApp, type App, type AppCore, type AppOptions, type DevHooks, type DevInstalled, type MountableApp, type Resolved, } from "./app.ts";
|
|
2
2
|
export { createRouter, type Router } from "./group.ts";
|
|
3
3
|
export { Context, type BindingsOf, type ExecutionContext, type Handler, type HandlerResult, type Middleware, type Next, type VariablesOf, } from "./context.ts";
|
|
4
4
|
export { HttpError, type ErrorHandler, type NotFoundHandler } from "./errors.ts";
|
|
5
5
|
export { ValidationError, type InferOutput, type RouteSchemas, type StandardIssue, type StandardSchemaV1, type ValidationTarget, } from "./validate.ts";
|
|
6
6
|
export type { CookieOptions } from "./cookie.ts";
|
|
7
7
|
export type { FormBody, FormValue } from "./body.ts";
|
|
8
|
-
export type { Timing } from "./compose.ts";
|
|
8
|
+
export type { Layer, Timing, Trace } from "./compose.ts";
|
|
9
9
|
export { logger, showRoutes } from "./logger.ts";
|
|
10
10
|
export { health, type HealthOptions } from "./health.ts";
|
|
11
11
|
export { cors, type CorsOptions } from "./middleware/cors.ts";
|
package/dist/src/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// @senrijs/senri の入口
|
|
2
|
+
// 開発モード(エラーページ・ダッシュボードなど)は @senrijs/senri/dev、Cookie は @senrijs/senri/cookie、接続元の IP は @senrijs/senri/ip
|
|
3
|
+
export { createApp, } from "./app.js";
|
|
2
4
|
export { createRouter } from "./group.js";
|
|
3
5
|
export { Context, } from "./context.js";
|
|
4
6
|
export { HttpError } from "./errors.js";
|
package/dist/src/ip.d.ts
ADDED
package/dist/src/ip.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// @senrijs/senri/ip の入口
|
|
2
|
+
// Context のメソッドにしないのは、使わないアプリのバンドルに入れないため(0.1 の c.ip)
|
|
3
|
+
import { connectionOf } from "./context.js";
|
|
4
|
+
import { clientIp } from "./connection.js";
|
|
5
|
+
// 接続元の IP アドレス。分からなければ null(app.fetch を直接呼んだときなど)
|
|
6
|
+
// - Bun / Deno / Node(senri/node): ランタイムが渡す接続元
|
|
7
|
+
// - Cloudflare Workers: Cloudflare が付ける CF-Connecting-IP
|
|
8
|
+
// X-Forwarded-For などのヘッダーは偽装できるので読まない。プロキシの後ろでは、プロキシが付けるヘッダーを自分で読む
|
|
9
|
+
export function getIp(c) {
|
|
10
|
+
const { req, info } = connectionOf(c);
|
|
11
|
+
return clientIp(req, info);
|
|
12
|
+
}
|
package/dist/src/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpError } from "./errors.js";
|
|
1
|
+
import { HttpError, senriError } from "./errors.js";
|
|
2
2
|
// ルートのパターン("/users/:id")を分割する。末尾スラッシュや連続スラッシュは無視
|
|
3
3
|
export function splitPattern(pattern) {
|
|
4
4
|
return pattern.split("/").filter(Boolean);
|
|
@@ -110,10 +110,10 @@ export function checkParamNames(path, segments) {
|
|
|
110
110
|
continue;
|
|
111
111
|
const name = s.slice(1);
|
|
112
112
|
if (name === "") {
|
|
113
|
-
throw
|
|
113
|
+
throw senriError("param_empty", `Empty parameter name: ${path}`);
|
|
114
114
|
}
|
|
115
115
|
if (seen.has(name)) {
|
|
116
|
-
throw
|
|
116
|
+
throw senriError("param_twice", `Parameter "${name}" used twice: ${path}`);
|
|
117
117
|
}
|
|
118
118
|
seen.add(name);
|
|
119
119
|
}
|
|
@@ -125,10 +125,10 @@ const METHOD = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
|
125
125
|
// - "ALL" は app.all() の印として使っているので、app.on() からは登録させない
|
|
126
126
|
export function normalizeMethod(method) {
|
|
127
127
|
if (!METHOD.test(method))
|
|
128
|
-
throw
|
|
128
|
+
throw senriError("method_invalid", `Invalid method: ${JSON.stringify(method)}`);
|
|
129
129
|
const upper = method.toUpperCase();
|
|
130
130
|
if (upper === "CONNECT" || upper === "TRACE" || upper === "TRACK") {
|
|
131
|
-
throw
|
|
131
|
+
throw senriError("method_unsupported", `${upper} is not supported`);
|
|
132
132
|
}
|
|
133
133
|
return upper;
|
|
134
134
|
}
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export declare function isWorkers(): boolean;
|
|
|
2
2
|
export declare function cwd(): string | null;
|
|
3
3
|
export declare function env(name: string): string | undefined;
|
|
4
4
|
export declare function canRead(path: string): boolean;
|
|
5
|
+
export declare function canReadEnv(name: string): boolean;
|
package/dist/src/runtime.js
CHANGED
|
@@ -47,3 +47,9 @@ export function canRead(path) {
|
|
|
47
47
|
return denoAllows({ name: "read", path });
|
|
48
48
|
return true;
|
|
49
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
|
+
}
|
package/dist/src/validate.d.ts
CHANGED
|
@@ -40,8 +40,7 @@ export declare class ValidationError extends HttpError {
|
|
|
40
40
|
readonly target: ValidationTarget;
|
|
41
41
|
readonly issues: readonly StandardIssue[];
|
|
42
42
|
constructor(target: ValidationTarget, issues: readonly StandardIssue[]);
|
|
43
|
-
describe(): string;
|
|
44
43
|
}
|
|
45
|
-
export declare function checkSchemas(path: string, schemas: RouteSchemas): void;
|
|
46
44
|
export declare function withValidation(schemas: RouteSchemas, handler: Handler): Handler;
|
|
45
|
+
export declare function routeHandler(path: string, a: Handler | RouteSchemas, b: Handler | undefined): Handler;
|
|
47
46
|
export {};
|
package/dist/src/validate.js
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
// - body: c.body() の結果(Content-Type に合わせて読んだもの)。通れば c.valid("body") で読める
|
|
8
8
|
// どれかが通らなければ、ハンドラーは呼ばずに 400 にする(ValidationError)
|
|
9
9
|
import { setValidated } from "./context.js";
|
|
10
|
-
import { HttpError } from "./errors.js";
|
|
10
|
+
import { HttpError, senriError } from "./errors.js";
|
|
11
11
|
// バリデーションに通らなかった。issues はスキーマのライブラリが返したもの
|
|
12
12
|
// app.onError で受けて、自分の形(JSON など)で返すこともできる
|
|
13
|
+
// (開発モードの「どこが違うか」の一覧は @senrijs/senri/dev が組み立てる)
|
|
13
14
|
export class ValidationError extends HttpError {
|
|
14
15
|
target;
|
|
15
16
|
issues;
|
|
@@ -20,49 +21,6 @@ export class ValidationError extends HttpError {
|
|
|
20
21
|
this.target = target;
|
|
21
22
|
this.issues = issues;
|
|
22
23
|
}
|
|
23
|
-
// 開発モードで返す、どこが違うかの一覧
|
|
24
|
-
describe() {
|
|
25
|
-
const lines = this.issues.map((issue) => {
|
|
26
|
-
const path = issuePath(issue);
|
|
27
|
-
return `- ${path ? `${path}: ` : ""}${issue.message}`;
|
|
28
|
-
});
|
|
29
|
-
return [`400 Bad Request: ${LABEL[this.target]}が、スキーマに合いません`, ...lines].join("\n");
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const LABEL = {
|
|
33
|
-
params: "パスパラメーター",
|
|
34
|
-
query: "クエリ",
|
|
35
|
-
body: "ボディ",
|
|
36
|
-
};
|
|
37
|
-
// ["user", 0, "name"] → "user[0].name"
|
|
38
|
-
function issuePath(issue) {
|
|
39
|
-
let out = "";
|
|
40
|
-
for (const part of issue.path ?? []) {
|
|
41
|
-
const key = typeof part === "object" ? part.key : part;
|
|
42
|
-
if (typeof key === "number")
|
|
43
|
-
out += `[${key}]`;
|
|
44
|
-
else
|
|
45
|
-
out += out ? `.${String(key)}` : String(key);
|
|
46
|
-
}
|
|
47
|
-
return out;
|
|
48
|
-
}
|
|
49
|
-
// ArkType のスキーマは関数なので、オブジェクトでも関数でもよい
|
|
50
|
-
function isSchema(value) {
|
|
51
|
-
return (typeof value === "object" || typeof value === "function") && value !== null && "~standard" in value;
|
|
52
|
-
}
|
|
53
|
-
// スキーマを確かめておく(書き間違いに、登録した時点で気づけるように)
|
|
54
|
-
export function checkSchemas(path, schemas) {
|
|
55
|
-
if (typeof schemas !== "object" || schemas === null) {
|
|
56
|
-
throw new TypeError(`${path}: 2つ目の引数には { params, query, body } のスキーマかハンドラーを渡してください`);
|
|
57
|
-
}
|
|
58
|
-
for (const [key, schema] of Object.entries(schemas)) {
|
|
59
|
-
if (key !== "params" && key !== "query" && key !== "body") {
|
|
60
|
-
throw new TypeError(`${path}: スキーマに渡せるのは params / query / body だけです("${key}" は使えません)`);
|
|
61
|
-
}
|
|
62
|
-
if (schema !== undefined && !isSchema(schema)) {
|
|
63
|
-
throw new TypeError(`${path}: ${key} が Standard Schema のスキーマではありません(Zod・Valibot・ArkType などのスキーマを渡してください)`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
24
|
}
|
|
67
25
|
async function run(schema, target, value) {
|
|
68
26
|
const result = await schema["~standard"].validate(value);
|
|
@@ -85,3 +43,13 @@ export function withValidation(schemas, handler) {
|
|
|
85
43
|
return handler(validParams, c);
|
|
86
44
|
};
|
|
87
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,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@senrijs/senri",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Web 標準の Request / Response で書かれた、小さな Web フレームワーク。Bun・Deno・Node.js・Cloudflare Workers で動く",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ibuki-hum4",
|
|
@@ -35,6 +35,18 @@
|
|
|
35
35
|
"types": "./dist/src/index.d.ts",
|
|
36
36
|
"import": "./dist/src/index.js"
|
|
37
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
|
+
},
|
|
38
50
|
"./node": {
|
|
39
51
|
"types": "./dist/adapters/node/index.d.ts",
|
|
40
52
|
"import": "./dist/adapters/node/index.js"
|
|
@@ -54,23 +66,27 @@
|
|
|
54
66
|
"scripts": {
|
|
55
67
|
"build": "tsc -p tsconfig.build.json",
|
|
56
68
|
"build:pages": "vite build -c ui/vite.config.ts && node scripts/build-pages.ts",
|
|
57
|
-
"typecheck": "bun run build && bun check && bun check -p test && 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",
|
|
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",
|
|
58
70
|
"dev": "bun --watch examples/bun/index.ts",
|
|
59
71
|
"test": "bun test",
|
|
60
72
|
"test:node": "bun run build && node --test --test-timeout=5000 \"test/node/**/*.test.ts\"",
|
|
61
|
-
"smoke": "node scripts/smoke.ts"
|
|
73
|
+
"smoke": "node scripts/smoke.ts",
|
|
74
|
+
"browser": "node scripts/browser.ts",
|
|
75
|
+
"size": "bun scripts/size.ts"
|
|
62
76
|
},
|
|
63
77
|
"devDependencies": {
|
|
64
78
|
"@types/bun": "latest",
|
|
65
79
|
"@types/node": "^26.6.5",
|
|
66
80
|
"@vitejs/plugin-vue": "6.0.10",
|
|
67
81
|
"arktype": "2.2.8",
|
|
82
|
+
"playwright": "1.64.0",
|
|
68
83
|
"typescript": "^7",
|
|
69
84
|
"valibot": "1.5.0",
|
|
70
85
|
"vite": "8.3.5",
|
|
71
86
|
"vite-plugin-singlefile": "2.3.4",
|
|
72
87
|
"vue": "3.5.44",
|
|
73
88
|
"workerd": "1.20261010.1",
|
|
89
|
+
"wrangler": "4.149.0",
|
|
74
90
|
"zod": "4.6.6"
|
|
75
91
|
}
|
|
76
92
|
}
|
package/dist/src/dev/runtime.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// 開発用ページで使う、ランタイムごとに違う処理(カレントディレクトリ、環境変数、ファイルの読み込み権限)
|
|
2
|
-
//
|
|
3
|
-
// Deno は、許可されていない環境変数やファイルを読もうとすると、ターミナルで「許可しますか?」と聞いてきて
|
|
4
|
-
// その間リクエストが止まってしまう。なので先に permissions.querySync() で「許可済みか」だけを確かめ、
|
|
5
|
-
// 許可されていなければ読まずにあきらめる(聞き返しはしない)
|
|
6
|
-
const g = globalThis;
|
|
7
|
-
function denoAllows(desc) {
|
|
8
|
-
try {
|
|
9
|
-
return g.Deno.permissions.querySync(desc).state === "granted";
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
// スラッシュ区切りのカレントディレクトリ。分からなければ null
|
|
16
|
-
export function cwd() {
|
|
17
|
-
try {
|
|
18
|
-
// Deno 2 には process もあるが、権限の確認をしたいので Deno を先に見る
|
|
19
|
-
// 全体({ name: "read" })で聞くと、--allow-read=. のように範囲を絞ったときに "prompt" になってしまう。
|
|
20
|
-
// カレントディレクトリが読めればよいので、"." で聞く
|
|
21
|
-
if (g.Deno)
|
|
22
|
-
return denoAllows({ name: "read", path: "." }) ? g.Deno.cwd().replaceAll("\\", "/") : null;
|
|
23
|
-
if (g.process)
|
|
24
|
-
return g.process.cwd().replaceAll("\\", "/");
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// 読めなければあきらめる
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
export function env(name) {
|
|
32
|
-
try {
|
|
33
|
-
if (g.Deno)
|
|
34
|
-
return denoAllows({ name: "env", variable: name }) ? g.Deno.env.get(name) : undefined;
|
|
35
|
-
return g.process?.env[name];
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export function canRead(path) {
|
|
42
|
-
if (g.Deno)
|
|
43
|
-
return denoAllows({ name: "read", path });
|
|
44
|
-
return true;
|
|
45
|
-
}
|