rpc4next 0.1.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 +130 -0
- package/dist/cli/cache.d.ts +4 -0
- package/dist/cli/cache.js +24 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +81 -0
- package/dist/cli/constants.d.ts +12 -0
- package/dist/cli/constants.js +20 -0
- package/dist/cli/debounce.d.ts +1 -0
- package/dist/cli/debounce.js +14 -0
- package/dist/cli/generate-path-structure.d.ts +7 -0
- package/dist/cli/generate-path-structure.js +37 -0
- package/dist/cli/path-utils.d.ts +1 -0
- package/dist/cli/path-utils.js +19 -0
- package/dist/cli/route-scanner.d.ts +21 -0
- package/dist/cli/route-scanner.js +167 -0
- package/dist/cli/scan-utils.d.ts +20 -0
- package/dist/cli/scan-utils.js +52 -0
- package/dist/cli/type-utils.d.ts +6 -0
- package/dist/cli/type-utils.js +26 -0
- package/dist/cli/types.d.ts +2 -0
- package/dist/cli/types.js +2 -0
- package/dist/helper/client/http-method.d.ts +2 -0
- package/dist/helper/client/http-method.js +68 -0
- package/dist/helper/client/index.d.ts +2 -0
- package/dist/helper/client/index.js +6 -0
- package/dist/helper/client/match.d.ts +1 -0
- package/dist/helper/client/match.js +33 -0
- package/dist/helper/client/rpc.d.ts +55 -0
- package/dist/helper/client/rpc.js +100 -0
- package/dist/helper/client/types.d.ts +119 -0
- package/dist/helper/client/types.js +2 -0
- package/dist/helper/client/url.d.ts +8 -0
- package/dist/helper/client/url.js +57 -0
- package/dist/helper/client/utils.d.ts +5 -0
- package/dist/helper/client/utils.js +31 -0
- package/dist/helper/server/create-handler.d.ts +2 -0
- package/dist/helper/server/create-handler.js +10 -0
- package/dist/helper/server/create-route-context.d.ts +5 -0
- package/dist/helper/server/create-route-context.js +27 -0
- package/dist/helper/server/index.d.ts +2 -0
- package/dist/helper/server/index.js +5 -0
- package/dist/helper/server/route-handler-factory.d.ts +53 -0
- package/dist/helper/server/route-handler-factory.js +67 -0
- package/dist/helper/server/search-params-to-object.d.ts +1 -0
- package/dist/helper/server/search-params-to-object.js +18 -0
- package/dist/helper/server/types.d.ts +250 -0
- package/dist/helper/server/types.js +4 -0
- package/dist/helper/server/validators/zod/index.d.ts +1 -0
- package/dist/helper/server/validators/zod/index.js +5 -0
- package/dist/helper/server/validators/zod/zod-validator.d.ts +6 -0
- package/dist/helper/server/validators/zod/zod-validator.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +9 -0
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.js +2 -0
- package/package.json +81 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createImport = exports.createObjectType = exports.createRecodeType = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const createRecodeType = (key, value) => {
|
|
6
|
+
if (!key || !value)
|
|
7
|
+
return "";
|
|
8
|
+
return `Record<${key}, ${value}>`;
|
|
9
|
+
};
|
|
10
|
+
exports.createRecodeType = createRecodeType;
|
|
11
|
+
const createObjectType = (fields) => {
|
|
12
|
+
if (fields.length === 0 || fields.some(({ name, type }) => !name || !type))
|
|
13
|
+
return "";
|
|
14
|
+
return `{ ${fields
|
|
15
|
+
.map(({ name, type }) => `"${name}": ${type}`)
|
|
16
|
+
.join(`${constants_1.TYPE_SEPARATOR} `)}${fields.length > 1 ? constants_1.TYPE_SEPARATOR : ""} }`;
|
|
17
|
+
};
|
|
18
|
+
exports.createObjectType = createObjectType;
|
|
19
|
+
const createImport = (type, path, importAlias) => {
|
|
20
|
+
if (!type || !path)
|
|
21
|
+
return "";
|
|
22
|
+
return importAlias
|
|
23
|
+
? `import type { ${type} as ${importAlias} } from "${path}"${constants_1.STATEMENT_TERMINATOR}`
|
|
24
|
+
: `import type { ${type} } from "${path}"${constants_1.STATEMENT_TERMINATOR}`;
|
|
25
|
+
};
|
|
26
|
+
exports.createImport = createImport;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { FuncParams, UrlOptions, ClientOptions } from "./types";
|
|
2
|
+
export declare const httpMethod: (key: string, paths: string[], params: FuncParams, dynamicKeys: string[], defaultOptions: ClientOptions) => (url?: UrlOptions, options?: ClientOptions) => Promise<Response>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.httpMethod = void 0;
|
|
24
|
+
const url_1 = require("./url");
|
|
25
|
+
const utils_1 = require("./utils");
|
|
26
|
+
const normalizeHeaders = (headers) => {
|
|
27
|
+
const result = {};
|
|
28
|
+
if (!headers)
|
|
29
|
+
return result;
|
|
30
|
+
if (headers instanceof Headers) {
|
|
31
|
+
headers.forEach((value, key) => {
|
|
32
|
+
result[key.toLowerCase()] = value;
|
|
33
|
+
});
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(headers)) {
|
|
37
|
+
headers.forEach(([key, value]) => {
|
|
38
|
+
result[key.toLowerCase()] = value;
|
|
39
|
+
});
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
43
|
+
result[key.toLowerCase()] = value;
|
|
44
|
+
});
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
const httpMethod = (key, paths, params, dynamicKeys, defaultOptions) => {
|
|
48
|
+
return (url, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
const urlObj = (0, url_1.createUrl)([...paths], params, dynamicKeys)(url);
|
|
50
|
+
const method = key.replace(/^\$/, "").toUpperCase();
|
|
51
|
+
const customFetch = (options === null || options === void 0 ? void 0 : options.fetch) || defaultOptions.fetch || fetch;
|
|
52
|
+
const defaultInit = defaultOptions.init || {};
|
|
53
|
+
const innerInit = (options === null || options === void 0 ? void 0 : options.init) || {};
|
|
54
|
+
const defaultHeaders = normalizeHeaders(defaultInit.headers);
|
|
55
|
+
const innerHeaders = normalizeHeaders(innerInit.headers);
|
|
56
|
+
const mergedHeaders = Object.assign(Object.assign({}, defaultHeaders), innerHeaders);
|
|
57
|
+
const { headers: _defaultHeaders } = defaultInit, defaultInitWithoutHeaders = __rest(defaultInit, ["headers"]);
|
|
58
|
+
const { headers: _innerHeaders } = innerInit, innerInitWithoutHeaders = __rest(innerInit, ["headers"]);
|
|
59
|
+
const mergedInit = (0, utils_1.deepMerge)(defaultInitWithoutHeaders, innerInitWithoutHeaders);
|
|
60
|
+
mergedInit.method = method;
|
|
61
|
+
if (Object.keys(mergedHeaders).length > 0) {
|
|
62
|
+
mergedInit.headers = mergedHeaders;
|
|
63
|
+
}
|
|
64
|
+
const response = yield customFetch(urlObj.path, mergedInit);
|
|
65
|
+
return response;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
exports.httpMethod = httpMethod;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRpcHelper = exports.createRpcClient = void 0;
|
|
4
|
+
var rpc_1 = require("./rpc");
|
|
5
|
+
Object.defineProperty(exports, "createRpcClient", { enumerable: true, get: function () { return rpc_1.createRpcClient; } });
|
|
6
|
+
Object.defineProperty(exports, "createRpcHelper", { enumerable: true, get: function () { return rpc_1.createRpcHelper; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const matchPath: (paths: string[], dynamicKeys: string[]) => (path: string) => {} | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchPath = void 0;
|
|
4
|
+
const url_1 = require("./url");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
const matchPath = (paths, dynamicKeys) => {
|
|
7
|
+
return (path) => {
|
|
8
|
+
const basePath = `/${paths.slice(1).join("/")}`;
|
|
9
|
+
const regexPattern = (0, url_1.replaceDynamicSegments)(basePath, {
|
|
10
|
+
optionalCatchAll: "(?:/(.*))?",
|
|
11
|
+
catchAll: "/([^/]+(?:/[^/]+)*)",
|
|
12
|
+
dynamic: "/([^/]+)",
|
|
13
|
+
});
|
|
14
|
+
// Append /? to match with or without a trailing slash.
|
|
15
|
+
const match = new RegExp(`^${regexPattern}/?$`).exec(path);
|
|
16
|
+
if (!match)
|
|
17
|
+
return null;
|
|
18
|
+
return dynamicKeys.reduce((acc, key, index) => {
|
|
19
|
+
const paramKey = key.replace(/^_+/, "");
|
|
20
|
+
const matchValue = match[index + 1];
|
|
21
|
+
const paramValue = (0, utils_1.isCatchAllOrOptional)(key)
|
|
22
|
+
? matchValue === undefined || matchValue === ""
|
|
23
|
+
? undefined
|
|
24
|
+
: matchValue
|
|
25
|
+
.split("/")
|
|
26
|
+
.filter((segment) => segment.length > 0)
|
|
27
|
+
.map((segment) => decodeURIComponent(segment))
|
|
28
|
+
: decodeURIComponent(matchValue);
|
|
29
|
+
return Object.assign(Object.assign({}, acc), { [paramKey]: paramValue });
|
|
30
|
+
}, {});
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
exports.matchPath = matchPath;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { FuncParams, DynamicPathProxyAsFunction, ClientOptions, DynamicPathProxyAsProperty } from "./types";
|
|
2
|
+
export declare const createRpcProxy: <T extends object>(options: ClientOptions, paths?: string[], params?: FuncParams, dynamicKeys?: string[]) => T;
|
|
3
|
+
/**
|
|
4
|
+
* Creates an RPC client proxy for making HTTP requests with a strongly typed API.
|
|
5
|
+
*
|
|
6
|
+
* @template T - The type defining the RPC endpoint structure.
|
|
7
|
+
* @param baseUrl - The base URL for the RPC client. This URL will be used as the root for all generated endpoint URLs.
|
|
8
|
+
* @param options - (Optional) Client options to customize the behavior of the RPC client. These options may include a custom fetch function and default request initialization options.
|
|
9
|
+
* @returns An object that enables you to dynamically build endpoint URLs and execute HTTP requests (such as GET, POST, DELETE, etc.) with full type support.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createRpcClient } from "./rpc";
|
|
14
|
+
* import type { PathStructure } from "./types";
|
|
15
|
+
*
|
|
16
|
+
* // Create an RPC client with a base URL
|
|
17
|
+
* const client = createRpcClient<PathStructure>("http://localhost:3000");
|
|
18
|
+
*
|
|
19
|
+
* // Generate a URL for a dynamic endpoint
|
|
20
|
+
* const urlResult = client.fuga._foo("test").$url();
|
|
21
|
+
* console.log(urlResult.path); // "http://localhost:3000/fuga/test"
|
|
22
|
+
* console.log(urlResult.relativePath); // "/fuga/test"
|
|
23
|
+
* console.log(urlResult.pathname); // "/fuga/[foo]"
|
|
24
|
+
* console.log(urlResult.params); // { foo: "test" }
|
|
25
|
+
*
|
|
26
|
+
* // Execute a GET request on an endpoint
|
|
27
|
+
* const response = await client.api.hoge._foo("test").$get();
|
|
28
|
+
* console.log(await response.json()); // Expected response: { method: "get" }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* The above example demonstrates how to generate URLs with dynamic segments and how to execute HTTP requests.
|
|
32
|
+
*/
|
|
33
|
+
export declare const createRpcClient: <T extends object>(baseUrl: string, options?: ClientOptions) => DynamicPathProxyAsFunction<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Creates an RPC helper proxy for dynamic path matching based on a given endpoint structure.
|
|
36
|
+
*
|
|
37
|
+
* @template T - The type defining the RPC endpoint structure.
|
|
38
|
+
*
|
|
39
|
+
* @returns An object that provides dynamic RPC helper methods for the defined endpoints.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* // Create the RPC helper
|
|
44
|
+
* const rpcHelper = createRpcHelper<PathStructure>();
|
|
45
|
+
*
|
|
46
|
+
* // Use the $match method to extract dynamic parameters from a URL path
|
|
47
|
+
* const match = rpcHelper.fuga._foo.$match("/fuga/test");
|
|
48
|
+
* // Expected output: { foo: "test" }
|
|
49
|
+
*
|
|
50
|
+
* // If the path does not match, it returns null
|
|
51
|
+
* const noMatch = rpcHelper.fuga._foo.$match("/hoge/test");
|
|
52
|
+
* // Expected output: null
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare const createRpcHelper: <T extends object>() => DynamicPathProxyAsProperty<T>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Inspired by Hono (https://github.com/honojs/hono)
|
|
3
|
+
// Some parts of this code are based on or adapted from the Hono project
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.createRpcHelper = exports.createRpcClient = exports.createRpcProxy = void 0;
|
|
6
|
+
const http_method_1 = require("./http-method");
|
|
7
|
+
const match_1 = require("./match");
|
|
8
|
+
const url_1 = require("./url");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
const createRpcProxy = (options, paths = [], params = {}, dynamicKeys = []) => {
|
|
11
|
+
const proxy = new Proxy((value) => {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
if (value === undefined) {
|
|
14
|
+
return (0, exports.createRpcProxy)(options, [...paths], params, dynamicKeys);
|
|
15
|
+
}
|
|
16
|
+
const newKey = (_a = paths.at(-1)) !== null && _a !== void 0 ? _a : "";
|
|
17
|
+
if ((0, utils_1.isDynamic)(newKey)) {
|
|
18
|
+
// Treat as a dynamic parameter
|
|
19
|
+
return (0, exports.createRpcProxy)(options, [...paths], Object.assign(Object.assign({}, params), { [(_b = dynamicKeys.at(-1)) !== null && _b !== void 0 ? _b : ""]: value }), dynamicKeys);
|
|
20
|
+
}
|
|
21
|
+
return (0, exports.createRpcProxy)(options, [...paths], params, dynamicKeys);
|
|
22
|
+
}, {
|
|
23
|
+
get: (_, key) => {
|
|
24
|
+
if (key === "$url") {
|
|
25
|
+
return (0, url_1.createUrl)([...paths], params, dynamicKeys);
|
|
26
|
+
}
|
|
27
|
+
if (key === "$match") {
|
|
28
|
+
return (0, match_1.matchPath)([...paths], dynamicKeys);
|
|
29
|
+
}
|
|
30
|
+
if ((0, utils_1.isHttpMethod)(key)) {
|
|
31
|
+
return (0, http_method_1.httpMethod)(key, [...paths], params, dynamicKeys, Object.assign({}, options));
|
|
32
|
+
}
|
|
33
|
+
if ((0, utils_1.isDynamic)(key)) {
|
|
34
|
+
// Treat as a dynamic parameter
|
|
35
|
+
return (0, exports.createRpcProxy)(options, [...paths, key], params, [
|
|
36
|
+
...dynamicKeys,
|
|
37
|
+
key,
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
return (0, exports.createRpcProxy)(options, [...paths, key], params, dynamicKeys);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
return proxy;
|
|
44
|
+
};
|
|
45
|
+
exports.createRpcProxy = createRpcProxy;
|
|
46
|
+
/**
|
|
47
|
+
* Creates an RPC client proxy for making HTTP requests with a strongly typed API.
|
|
48
|
+
*
|
|
49
|
+
* @template T - The type defining the RPC endpoint structure.
|
|
50
|
+
* @param baseUrl - The base URL for the RPC client. This URL will be used as the root for all generated endpoint URLs.
|
|
51
|
+
* @param options - (Optional) Client options to customize the behavior of the RPC client. These options may include a custom fetch function and default request initialization options.
|
|
52
|
+
* @returns An object that enables you to dynamically build endpoint URLs and execute HTTP requests (such as GET, POST, DELETE, etc.) with full type support.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { createRpcClient } from "./rpc";
|
|
57
|
+
* import type { PathStructure } from "./types";
|
|
58
|
+
*
|
|
59
|
+
* // Create an RPC client with a base URL
|
|
60
|
+
* const client = createRpcClient<PathStructure>("http://localhost:3000");
|
|
61
|
+
*
|
|
62
|
+
* // Generate a URL for a dynamic endpoint
|
|
63
|
+
* const urlResult = client.fuga._foo("test").$url();
|
|
64
|
+
* console.log(urlResult.path); // "http://localhost:3000/fuga/test"
|
|
65
|
+
* console.log(urlResult.relativePath); // "/fuga/test"
|
|
66
|
+
* console.log(urlResult.pathname); // "/fuga/[foo]"
|
|
67
|
+
* console.log(urlResult.params); // { foo: "test" }
|
|
68
|
+
*
|
|
69
|
+
* // Execute a GET request on an endpoint
|
|
70
|
+
* const response = await client.api.hoge._foo("test").$get();
|
|
71
|
+
* console.log(await response.json()); // Expected response: { method: "get" }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* The above example demonstrates how to generate URLs with dynamic segments and how to execute HTTP requests.
|
|
75
|
+
*/
|
|
76
|
+
const createRpcClient = (baseUrl, options = {}) => (0, exports.createRpcProxy)(options, [baseUrl]);
|
|
77
|
+
exports.createRpcClient = createRpcClient;
|
|
78
|
+
/**
|
|
79
|
+
* Creates an RPC helper proxy for dynamic path matching based on a given endpoint structure.
|
|
80
|
+
*
|
|
81
|
+
* @template T - The type defining the RPC endpoint structure.
|
|
82
|
+
*
|
|
83
|
+
* @returns An object that provides dynamic RPC helper methods for the defined endpoints.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* // Create the RPC helper
|
|
88
|
+
* const rpcHelper = createRpcHelper<PathStructure>();
|
|
89
|
+
*
|
|
90
|
+
* // Use the $match method to extract dynamic parameters from a URL path
|
|
91
|
+
* const match = rpcHelper.fuga._foo.$match("/fuga/test");
|
|
92
|
+
* // Expected output: { foo: "test" }
|
|
93
|
+
*
|
|
94
|
+
* // If the path does not match, it returns null
|
|
95
|
+
* const noMatch = rpcHelper.fuga._foo.$match("/hoge/test");
|
|
96
|
+
* // Expected output: null
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
const createRpcHelper = () => (0, exports.createRpcProxy)({}, ["/"]);
|
|
100
|
+
exports.createRpcHelper = createRpcHelper;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { OPTIONAL_CATCH_ALL_PREFIX, CATCH_ALL_PREFIX, DYNAMIC_PREFIX, HTTP_METHOD_FUNC_KEYS } from "../../lib/constants";
|
|
2
|
+
import type { TypedNextResponse, HttpStatusCode, ContentType } from "../server/types";
|
|
3
|
+
import type { NextResponse } from "next/server";
|
|
4
|
+
/**
|
|
5
|
+
* Represents HTTP request headers with optional fields.
|
|
6
|
+
* This type includes general request headers, CORS/security-related headers, and client-specific headers.
|
|
7
|
+
*/
|
|
8
|
+
type HttpRequestHeaders = Partial<{
|
|
9
|
+
Accept: string;
|
|
10
|
+
"Accept-Charset": string;
|
|
11
|
+
"Accept-Encoding": string;
|
|
12
|
+
"Accept-Language": string;
|
|
13
|
+
Authorization: string;
|
|
14
|
+
"Cache-Control": string;
|
|
15
|
+
Connection: string;
|
|
16
|
+
"Content-Length": string;
|
|
17
|
+
"Content-Type": string;
|
|
18
|
+
Cookie: string;
|
|
19
|
+
Date: string;
|
|
20
|
+
Expect: string;
|
|
21
|
+
Forwarded: string;
|
|
22
|
+
From: string;
|
|
23
|
+
Host: string;
|
|
24
|
+
"If-Match": string;
|
|
25
|
+
"If-Modified-Since": string;
|
|
26
|
+
"If-None-Match": string;
|
|
27
|
+
"If-Range": string;
|
|
28
|
+
"If-Unmodified-Since": string;
|
|
29
|
+
"Max-Forwards": string;
|
|
30
|
+
Origin: string;
|
|
31
|
+
Pragma: string;
|
|
32
|
+
Range: string;
|
|
33
|
+
Referer: string;
|
|
34
|
+
TE: string;
|
|
35
|
+
Trailer: string;
|
|
36
|
+
"Transfer-Encoding": string;
|
|
37
|
+
Upgrade: string;
|
|
38
|
+
"User-Agent": string;
|
|
39
|
+
Via: string;
|
|
40
|
+
Warning: string;
|
|
41
|
+
"Access-Control-Request-Method": string;
|
|
42
|
+
"Access-Control-Request-Headers": string;
|
|
43
|
+
DNT: string;
|
|
44
|
+
"Sec-Fetch-Dest": string;
|
|
45
|
+
"Sec-Fetch-Mode": string;
|
|
46
|
+
"Sec-Fetch-Site": string;
|
|
47
|
+
"Sec-Fetch-User": string;
|
|
48
|
+
"Sec-CH-UA": string;
|
|
49
|
+
"Sec-CH-UA-Platform": string;
|
|
50
|
+
"Sec-CH-UA-Mobile": string;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Extension of the standard `RequestInit` interface with strongly typed headers.
|
|
54
|
+
*/
|
|
55
|
+
export interface TypedRequestInit extends RequestInit {
|
|
56
|
+
headers?: HttpRequestHeaders | HeadersInit;
|
|
57
|
+
}
|
|
58
|
+
export type ClientOptions = {
|
|
59
|
+
fetch?: typeof fetch;
|
|
60
|
+
init?: TypedRequestInit;
|
|
61
|
+
};
|
|
62
|
+
declare const __proxy: unique symbol;
|
|
63
|
+
export type Endpoint = {
|
|
64
|
+
[__proxy]?: true;
|
|
65
|
+
};
|
|
66
|
+
export type ParamsKey = "__params";
|
|
67
|
+
type IsParams = Record<ParamsKey, unknown>;
|
|
68
|
+
export type QueryKey = "__query";
|
|
69
|
+
type IsQuery = Record<QueryKey, unknown>;
|
|
70
|
+
export type OptionalQueryKey = "__op_query";
|
|
71
|
+
type IsOptionalQuery = Record<OptionalQueryKey, unknown>;
|
|
72
|
+
export type HttpMethodFuncKey = (typeof HTTP_METHOD_FUNC_KEYS)[number];
|
|
73
|
+
type IsHttpMethod = {
|
|
74
|
+
[K in HttpMethodFuncKey]?: unknown;
|
|
75
|
+
};
|
|
76
|
+
type IsOptionalCatchAll = `${typeof OPTIONAL_CATCH_ALL_PREFIX}${string}`;
|
|
77
|
+
type IsCatchAll = `${typeof CATCH_ALL_PREFIX}${string}`;
|
|
78
|
+
type IsDynamic = `${typeof DYNAMIC_PREFIX}${string}`;
|
|
79
|
+
export type FuncParams<T = Record<string, string | number | string[] | undefined>> = T;
|
|
80
|
+
type QueryParams<T = Record<string, string | number>> = T;
|
|
81
|
+
type Params<T = unknown> = T extends IsParams ? T[ParamsKey] : Record<string, string>;
|
|
82
|
+
export type UrlOptions<T = unknown> = T extends IsQuery ? {
|
|
83
|
+
query: T[QueryKey];
|
|
84
|
+
hash?: string;
|
|
85
|
+
} : T extends IsOptionalQuery ? {
|
|
86
|
+
query?: T[OptionalQueryKey];
|
|
87
|
+
hash?: string;
|
|
88
|
+
} : {
|
|
89
|
+
query?: QueryParams;
|
|
90
|
+
hash?: string;
|
|
91
|
+
};
|
|
92
|
+
export type UrlResult<T = unknown> = {
|
|
93
|
+
pathname: string;
|
|
94
|
+
path: string;
|
|
95
|
+
relativePath: string;
|
|
96
|
+
params: Params<T>;
|
|
97
|
+
};
|
|
98
|
+
type UrlArg<T> = T extends IsQuery ? [url: UrlOptions<T>] : [url?: UrlOptions<T>];
|
|
99
|
+
type HttpMethodsArg<T> = [...UrlArg<T>, option?: ClientOptions];
|
|
100
|
+
type InferHttpMethods<T extends IsHttpMethod> = {
|
|
101
|
+
[K in keyof T as K extends HttpMethodFuncKey ? K : never]: (...args: HttpMethodsArg<T>) => Promise<InferTypedNextResponseType<T[K]>>;
|
|
102
|
+
};
|
|
103
|
+
type InferNextResponseType<T> = T extends (...args: any[]) => Promise<NextResponse<infer U>> ? U : never;
|
|
104
|
+
type InferTypedNextResponseType<T> = T extends (...args: any[]) => Promise<TypedNextResponse> ? Awaited<ReturnType<T>> : TypedNextResponse<InferNextResponseType<T>, HttpStatusCode, ContentType>;
|
|
105
|
+
type PathProxyAsProperty<T> = {
|
|
106
|
+
$match: (path: string) => Params<T> | null;
|
|
107
|
+
};
|
|
108
|
+
type PathProxyAsFunction<T> = {
|
|
109
|
+
$url: (...args: UrlArg<T>) => UrlResult<T>;
|
|
110
|
+
} & (T extends IsHttpMethod ? InferHttpMethods<T> : unknown);
|
|
111
|
+
type ParamFunction<T, TParamArgs extends unknown[]> = (...args: [...TParamArgs]) => DynamicPathProxyAsFunction<T>;
|
|
112
|
+
type NonEmptyArray<T> = [T, ...T[]];
|
|
113
|
+
export type DynamicPathProxyAsFunction<T> = Omit<(T extends Endpoint ? PathProxyAsFunction<T> : unknown) & {
|
|
114
|
+
[K in keyof T]: K extends IsOptionalCatchAll ? ParamFunction<T[K], [value?: string[]]> : K extends IsCatchAll ? ParamFunction<T[K], [value: NonEmptyArray<string>]> : K extends IsDynamic ? ParamFunction<T[K], [value: string | number]> : DynamicPathProxyAsFunction<T[K]>;
|
|
115
|
+
}, QueryKey | OptionalQueryKey | ParamsKey>;
|
|
116
|
+
export type DynamicPathProxyAsProperty<T> = Omit<(T extends Endpoint ? PathProxyAsProperty<T> : unknown) & {
|
|
117
|
+
[K in keyof T]: K extends unknown ? DynamicPathProxyAsProperty<T[K]> : DynamicPathProxyAsProperty<T[K]>;
|
|
118
|
+
}, QueryKey | OptionalQueryKey | ParamsKey>;
|
|
119
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { UrlOptions, UrlResult, FuncParams } from "./types";
|
|
2
|
+
export declare const buildUrlSuffix: (url?: UrlOptions) => string;
|
|
3
|
+
export declare const replaceDynamicSegments: (basePath: string, replacements: {
|
|
4
|
+
optionalCatchAll: string;
|
|
5
|
+
catchAll: string;
|
|
6
|
+
dynamic: string;
|
|
7
|
+
}) => string;
|
|
8
|
+
export declare const createUrl: (paths: string[], params: FuncParams, dynamicKeys: string[]) => (url?: UrlOptions) => UrlResult;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createUrl = exports.replaceDynamicSegments = exports.buildUrlSuffix = void 0;
|
|
4
|
+
const buildUrlSuffix = (url) => {
|
|
5
|
+
if (!url)
|
|
6
|
+
return "";
|
|
7
|
+
const query = url.query
|
|
8
|
+
? "?" + new URLSearchParams(url.query).toString()
|
|
9
|
+
: "";
|
|
10
|
+
const hash = url.hash ? `#${url.hash}` : "";
|
|
11
|
+
return query + hash;
|
|
12
|
+
};
|
|
13
|
+
exports.buildUrlSuffix = buildUrlSuffix;
|
|
14
|
+
const replaceDynamicSegments = (basePath, replacements) => basePath
|
|
15
|
+
// optionalCatchAll
|
|
16
|
+
.replace(/\/_{5}(\w+)/g, replacements.optionalCatchAll)
|
|
17
|
+
// catchAll
|
|
18
|
+
.replace(/\/_{3}(\w+)/g, replacements.catchAll)
|
|
19
|
+
// dynamic
|
|
20
|
+
.replace(/\/_(\w+)/g, replacements.dynamic);
|
|
21
|
+
exports.replaceDynamicSegments = replaceDynamicSegments;
|
|
22
|
+
const createUrl = (paths, params, dynamicKeys) => {
|
|
23
|
+
const baseUrl = paths.shift();
|
|
24
|
+
const basePath = `/${paths.join("/")}`;
|
|
25
|
+
const dynamicPath = dynamicKeys.reduce((acc, key) => {
|
|
26
|
+
const param = params[key];
|
|
27
|
+
if (Array.isArray(param)) {
|
|
28
|
+
return acc.replace(`/${key}`, `/${param.map(encodeURIComponent).join("/")}`);
|
|
29
|
+
}
|
|
30
|
+
if (param === undefined) {
|
|
31
|
+
return acc.replace(`/${key}`, "");
|
|
32
|
+
}
|
|
33
|
+
return acc.replace(`/${key}`, `/${encodeURIComponent(param)}`);
|
|
34
|
+
}, basePath);
|
|
35
|
+
return (url) => {
|
|
36
|
+
const relativePath = `${dynamicPath}${(0, exports.buildUrlSuffix)(url)}`;
|
|
37
|
+
const pathname = (0, exports.replaceDynamicSegments)(basePath, {
|
|
38
|
+
optionalCatchAll: "/[[...$1]]",
|
|
39
|
+
catchAll: "/[...$1]",
|
|
40
|
+
dynamic: "/[$1]",
|
|
41
|
+
});
|
|
42
|
+
const cleanedParams = {};
|
|
43
|
+
for (const key in params) {
|
|
44
|
+
const cleanedKey = key.replace(/^_+/, "");
|
|
45
|
+
cleanedParams[cleanedKey] = params[key];
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
pathname,
|
|
49
|
+
params: cleanedParams,
|
|
50
|
+
path: baseUrl
|
|
51
|
+
? `${baseUrl.replace(/\/$/, "")}${relativePath}`
|
|
52
|
+
: relativePath,
|
|
53
|
+
relativePath,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
exports.createUrl = createUrl;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HttpMethodFuncKey } from "./types";
|
|
2
|
+
export declare const isDynamic: (key: string) => boolean;
|
|
3
|
+
export declare const isCatchAllOrOptional: (key: string) => boolean;
|
|
4
|
+
export declare const isHttpMethod: (value: string) => value is HttpMethodFuncKey;
|
|
5
|
+
export declare const deepMerge: <T extends object, U extends object>(target: T, source: U) => T;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepMerge = exports.isHttpMethod = exports.isCatchAllOrOptional = exports.isDynamic = void 0;
|
|
4
|
+
const constants_1 = require("../../lib/constants");
|
|
5
|
+
const isDynamic = (key) => key.startsWith(constants_1.DYNAMIC_PREFIX);
|
|
6
|
+
exports.isDynamic = isDynamic;
|
|
7
|
+
const isCatchAllOrOptional = (key) => key.startsWith(constants_1.CATCH_ALL_PREFIX) || key.startsWith(constants_1.OPTIONAL_CATCH_ALL_PREFIX);
|
|
8
|
+
exports.isCatchAllOrOptional = isCatchAllOrOptional;
|
|
9
|
+
const httpMethods = new Set(constants_1.HTTP_METHOD_FUNC_KEYS);
|
|
10
|
+
const isHttpMethod = (value) => httpMethods.has(value);
|
|
11
|
+
exports.isHttpMethod = isHttpMethod;
|
|
12
|
+
const isPlainObject = (value) => {
|
|
13
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
};
|
|
15
|
+
const deepMerge = (target, source) => {
|
|
16
|
+
const result = Object.assign({}, target);
|
|
17
|
+
for (const key in source) {
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
19
|
+
const targetValue = target[key];
|
|
20
|
+
const sourceValue = source[key];
|
|
21
|
+
if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {
|
|
22
|
+
result[key] = (0, exports.deepMerge)(targetValue, sourceValue);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
result[key] = sourceValue;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
exports.deepMerge = deepMerge;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { Handler, Params, Query, RouteResponse, ValidationSchema } from "./types";
|
|
2
|
+
export declare const createHandler: <TParams extends Params, TQuery extends Query, TValidationSchema extends ValidationSchema>() => <TRouteResponse extends RouteResponse>(handler: Handler<TParams, TQuery, TValidationSchema, TRouteResponse>) => Handler<TParams, TQuery, TValidationSchema, TRouteResponse>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createHandler = void 0;
|
|
4
|
+
// I want to use currying so that the return value can be inferred.
|
|
5
|
+
const createHandler = () => {
|
|
6
|
+
return (handler) => {
|
|
7
|
+
return handler;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
exports.createHandler = createHandler;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RouteContext, Query, ValidationSchema, Params } from "./types";
|
|
2
|
+
import type { NextRequest } from "next/server";
|
|
3
|
+
export declare const createRouteContext: <TParams extends Params, TQuery extends Query, TValidationSchema extends ValidationSchema>(req: NextRequest, segmentData: {
|
|
4
|
+
params: Promise<TParams>;
|
|
5
|
+
}) => RouteContext<TParams, TQuery, TValidationSchema>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Inspired by Hono (https://github.com/honojs/hono)
|
|
3
|
+
// Some parts of this code are based on or adapted from the Hono project
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.createRouteContext = void 0;
|
|
6
|
+
const server_1 = require("next/server");
|
|
7
|
+
const search_params_to_object_1 = require("./search-params-to-object");
|
|
8
|
+
const createRouteContext = (req, segmentData) => {
|
|
9
|
+
const validationResults = {};
|
|
10
|
+
return {
|
|
11
|
+
req: Object.assign(req, {
|
|
12
|
+
query: () => (0, search_params_to_object_1.searchParamsToObject)(req.nextUrl.searchParams),
|
|
13
|
+
params: () => segmentData.params,
|
|
14
|
+
valid: (target) => {
|
|
15
|
+
return validationResults[target];
|
|
16
|
+
},
|
|
17
|
+
addValidatedData: (target, value) => {
|
|
18
|
+
validationResults[target] = value;
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
body: (data, init) => new server_1.NextResponse(data, init),
|
|
22
|
+
json: (data, init) => server_1.NextResponse.json(data, init),
|
|
23
|
+
text: (data, init) => new server_1.NextResponse(data, Object.assign(Object.assign({}, init), { headers: Object.assign({ "Content-Type": "text/plain" }, init === null || init === void 0 ? void 0 : init.headers) })),
|
|
24
|
+
redirect: (url, init) => server_1.NextResponse.redirect(url, init),
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.createRouteContext = createRouteContext;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.routeHandlerFactory = void 0;
|
|
4
|
+
var route_handler_factory_1 = require("./route-handler-factory");
|
|
5
|
+
Object.defineProperty(exports, "routeHandlerFactory", { enumerable: true, get: function () { return route_handler_factory_1.routeHandlerFactory; } });
|