@alphatique/hono-fetch 1.0.4 → 1.0.5

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.
Files changed (2) hide show
  1. package/dist/index.mjs +49 -13
  2. package/package.json +3 -2
package/dist/index.mjs CHANGED
@@ -1,10 +1,51 @@
1
- //#region src/utils.ts
2
- function replaceUrlParam(urlString, params) {
3
- for (const [k, v] of Object.entries(params)) {
4
- const reg = new RegExp("/:" + k + "(?:{[^/]+})?\\??");
5
- urlString = urlString.replace(reg, v ? `/${v}` : "");
1
+ //#region src/url.ts
2
+ const PARAM_SEGMENT = /\/:([^/{}?]+)(\{[^/]*\})?(\?)?(?=\/|$)/g;
3
+ /**
4
+ * baseUrl、hono の route(schema の key)、param、query から送信先の URL を作る。
5
+ * package の entry からは export しない。
6
+ *
7
+ * 不変条件:
8
+ * - 戻り値の origin は、常に new URL(baseUrl).origin と一致する。
9
+ * - 1 つの param は、encodeURIComponent した 1 つの path segment にだけ入る。
10
+ * - `:name` は名前が完全に一致した場合にだけ置換する。`:id` は `:idx` に一致しない。
11
+ * - `:name?` の値が undefined か空文字なら、その segment を取り除く。
12
+ * - query は、undefined の値と配列内の undefined を送らない。配列は同じ key で append する。
13
+ * - baseUrl の path は、これまでどおり使わない(route は baseUrl の origin からの絶対 path として解決する)。
14
+ *
15
+ * エラー(同期の TypeError):
16
+ * - baseUrl が URL として不正
17
+ * - 必須の param が無い、または空文字
18
+ * - param の値が "." または ".."(encode しても dot segment として正規化されるため)
19
+ * - param の値が対になっていない surrogate を含む(encodeURIComponent が URIError を投げるため)
20
+ * - 組み立てた URL の origin が baseUrl と異なる(多重の防御。上の規則が正しければ起きない)
21
+ *
22
+ * 対象外: route 中の `*` ワイルドカード
23
+ */
24
+ function buildUrl(baseUrl, route, input = {}) {
25
+ const base = new URL(baseUrl);
26
+ const param = input.param ?? {};
27
+ const path = route.replace(PARAM_SEGMENT, (_match, name, _pattern, optional) => {
28
+ const value = Object.hasOwn(param, name) ? param[name] : void 0;
29
+ if (value === void 0 || value === "") {
30
+ if (optional) return "";
31
+ throw new TypeError(`hono-fetch: path param "${name}" is required for ${route}`);
32
+ }
33
+ if (value === "." || value === "..") throw new TypeError(`hono-fetch: path param "${name}" must not be "." or ".."`);
34
+ try {
35
+ return "/" + encodeURIComponent(value);
36
+ } catch (err) {
37
+ throw new TypeError(`hono-fetch: path param "${name}" is not a well-formed string`, { cause: err });
38
+ }
39
+ });
40
+ const url = new URL(path === "" ? "/" : path, base);
41
+ for (const [k, v] of Object.entries(input.query ?? {})) {
42
+ if (v === void 0) continue;
43
+ if (Array.isArray(v)) {
44
+ for (const v2 of v) if (v2 !== void 0) url.searchParams.append(k, v2);
45
+ } else url.searchParams.set(k, v);
6
46
  }
7
- return urlString;
47
+ if (url.origin !== base.origin) throw new TypeError(`hono-fetch: built URL left the origin of baseUrl: ${url.href}`);
48
+ return url;
8
49
  }
9
50
  //#endregion
10
51
  //#region src/index.ts
@@ -14,12 +55,7 @@ var HonoFetch = class {
14
55
  this.config = config;
15
56
  }
16
57
  async fetch(method, path, args) {
17
- const url = new URL(args.param ? replaceUrlParam(path, args.param) : path, this.config.baseUrl);
18
- if (args.query) for (const [k, v] of Object.entries(args.query)) {
19
- if (v === void 0) continue;
20
- if (Array.isArray(v)) for (const v2 of v) url.searchParams.append(k, v2);
21
- else url.searchParams.set(k, v);
22
- }
58
+ const url = buildUrl(this.config.baseUrl, path, args);
23
59
  let contentType = null;
24
60
  let body = null;
25
61
  if (args.json) {
@@ -38,7 +74,7 @@ var HonoFetch = class {
38
74
  const headers = new Headers(args.header);
39
75
  if (contentType) headers.set("Content-Type", contentType);
40
76
  try {
41
- return (await fetch(url, {
77
+ return await (await fetch(url, {
42
78
  method: method.toUpperCase(),
43
79
  headers,
44
80
  body
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alphatique/hono-fetch",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "license": "ISC",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,8 @@
21
21
  },
22
22
  "scripts": {
23
23
  "prepare": "vp config --no-agent",
24
- "build": "vp pack"
24
+ "build": "vp pack",
25
+ "test": "vp test"
25
26
  },
26
27
  "devDependencies": {
27
28
  "hono": "^4.12.10",