@suveren/gateway 0.6.4 → 0.6.7
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/bin/suveren-gateway.js +19 -2
- package/content/integrations/deploy-github.json +2 -2
- package/dist/mcp-server/http.mjs +6 -1
- package/lib/autostart-templates.d.mts +2 -0
- package/lib/autostart-templates.mjs +16 -3
- package/node_modules/@hono/node-server/README.md +47 -1
- package/node_modules/@hono/node-server/dist/early-hints.cjs +35 -0
- package/node_modules/@hono/node-server/dist/early-hints.d.cts +16 -0
- package/node_modules/@hono/node-server/dist/early-hints.d.mts +16 -0
- package/node_modules/@hono/node-server/dist/early-hints.mjs +33 -0
- package/node_modules/@hono/node-server/dist/index.cjs +4 -1
- package/node_modules/@hono/node-server/dist/index.mjs +4 -1
- package/node_modules/@hono/node-server/package.json +14 -1
- package/node_modules/express-rate-limit/dist/index.cjs +6 -1
- package/node_modules/express-rate-limit/dist/index.mjs +6 -1
- package/node_modules/express-rate-limit/package.json +6 -6
- package/node_modules/hono/dist/cjs/context.js +28 -13
- package/node_modules/hono/dist/cjs/hono-base.js +3 -2
- package/node_modules/hono/dist/cjs/jsx/base.js +26 -14
- package/node_modules/hono/dist/cjs/jsx/hooks/index.js +2 -2
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
- package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
- package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
- package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
- package/node_modules/hono/dist/cjs/request.js +6 -8
- package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
- package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
- package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
- package/node_modules/hono/dist/cjs/router.js +1 -1
- package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
- package/node_modules/hono/dist/cjs/utils/url.js +7 -7
- package/node_modules/hono/dist/context.js +28 -13
- package/node_modules/hono/dist/hono-base.js +3 -2
- package/node_modules/hono/dist/jsx/base.js +26 -14
- package/node_modules/hono/dist/jsx/hooks/index.js +2 -2
- package/node_modules/hono/dist/middleware/cache/index.js +103 -8
- package/node_modules/hono/dist/middleware/compress/index.js +5 -0
- package/node_modules/hono/dist/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/middleware/etag/index.js +1 -1
- package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
- package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
- package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
- package/node_modules/hono/dist/request.js +7 -9
- package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
- package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
- package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
- package/node_modules/hono/dist/router.js +1 -1
- package/node_modules/hono/dist/types/client/types.d.ts +1 -1
- package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
- package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
- package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
- package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
- package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
- package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
- package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
- package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
- package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
- package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
- package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
- package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
- package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
- package/node_modules/hono/dist/types/router.d.ts +1 -1
- package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
- package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
- package/node_modules/hono/dist/utils/cookie.js +2 -2
- package/node_modules/hono/dist/utils/url.js +5 -6
- package/node_modules/hono/package.json +9 -1
- package/package.json +1 -1
- package/profiles/deploy/0.8.profile.json +170 -0
- package/profiles/index.json +2 -1
package/bin/suveren-gateway.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { spawn, spawnSync } from 'node:child_process';
|
|
14
14
|
import { createConnection } from 'node:net';
|
|
15
15
|
import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync, unlinkSync, statSync } from 'node:fs';
|
|
16
|
-
import { homedir, platform } from 'node:os';
|
|
16
|
+
import { homedir, platform, userInfo } from 'node:os';
|
|
17
17
|
import { dirname, join, resolve } from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
19
|
import { buildLaunchAgentPlist, buildMacLauncher, buildSystemdUnit, buildWindowsTaskXml } from '../lib/autostart-templates.mjs';
|
|
@@ -500,6 +500,22 @@ async function serviceStatusMac() {
|
|
|
500
500
|
|
|
501
501
|
const WIN_TASK_NAME = 'Suveren';
|
|
502
502
|
|
|
503
|
+
/**
|
|
504
|
+
* Who the login task runs as, as `DOMAIN\user` when a domain is known.
|
|
505
|
+
*
|
|
506
|
+
* Without this the task is registered for ANY user's logon, which Windows only
|
|
507
|
+
* lets an administrator do — a standard user just saw "Zugriff verweigert" and
|
|
508
|
+
* had no way to install autostart at all. USERDOMAIN is the machine name on a
|
|
509
|
+
* local account and the AD domain on a joined one; both are correct here.
|
|
510
|
+
* Falling back to the bare username is fine — Task Scheduler resolves it
|
|
511
|
+
* against the local machine.
|
|
512
|
+
*/
|
|
513
|
+
function windowsUserId() {
|
|
514
|
+
const name = process.env.USERNAME || userInfo().username;
|
|
515
|
+
const domain = process.env.USERDOMAIN;
|
|
516
|
+
return domain ? `${domain}\\${name}` : name;
|
|
517
|
+
}
|
|
518
|
+
|
|
503
519
|
async function serviceInstallWindows() {
|
|
504
520
|
ensureDataDir();
|
|
505
521
|
|
|
@@ -513,6 +529,7 @@ async function serviceInstallWindows() {
|
|
|
513
529
|
serverEntry: SERVER_ENTRY,
|
|
514
530
|
author: 'Suveren',
|
|
515
531
|
dataDir: process.env.SUVEREN_DATA_DIR ?? '',
|
|
532
|
+
userId: windowsUserId(),
|
|
516
533
|
});
|
|
517
534
|
const xmlPath = join(DATA_DIR, 'suveren-task.xml');
|
|
518
535
|
writeFileSync(xmlPath, '\ufeff' + xml, { encoding: 'utf16le' });
|
|
@@ -766,7 +783,7 @@ Usage:
|
|
|
766
783
|
suveren-gateway status Show running state + health
|
|
767
784
|
suveren-gateway logs [--tail] Print or tail ~/.suveren/gateway.log
|
|
768
785
|
suveren-gateway service <cmd> Run as a login service that survives reboot
|
|
769
|
-
(install | uninstall | status
|
|
786
|
+
(install | uninstall | status)
|
|
770
787
|
suveren-gateway help Print this help
|
|
771
788
|
|
|
772
789
|
Environment:
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
"staticExecution": {
|
|
80
80
|
"action_type": "release"
|
|
81
81
|
},
|
|
82
|
-
"contentField": "
|
|
82
|
+
"contentField": "commit",
|
|
83
83
|
"actionLabel": "Deployment released",
|
|
84
84
|
"argNormalization": {
|
|
85
|
-
"
|
|
85
|
+
"commit": "sha"
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
}
|
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -1288,8 +1288,13 @@ function normalizeUrl(value) {
|
|
|
1288
1288
|
const port2 = url.port && !defaultPort ? `:${url.port}` : "";
|
|
1289
1289
|
return `${scheme}://${host}${port2}`;
|
|
1290
1290
|
}
|
|
1291
|
+
function normalizeSha(value) {
|
|
1292
|
+
const trimmed = value.trim();
|
|
1293
|
+
return /^[0-9a-fA-F]{7,64}$/.test(trimmed) ? trimmed.toLowerCase() : value;
|
|
1294
|
+
}
|
|
1291
1295
|
var NORMALIZERS = {
|
|
1292
|
-
url: normalizeUrl
|
|
1296
|
+
url: normalizeUrl,
|
|
1297
|
+
sha: normalizeSha
|
|
1293
1298
|
};
|
|
1294
1299
|
function normalizeIncomingArgs(tool, args) {
|
|
1295
1300
|
const declared = tool?.gating?.argNormalization;
|
|
@@ -148,8 +148,15 @@ WantedBy=default.target
|
|
|
148
148
|
* LogonType=InteractiveToken keeps it in the user's own session with no stored
|
|
149
149
|
* password and no admin rights. Hidden + no execution time limit stop it
|
|
150
150
|
* flashing a console window or being killed after the default 72 hours.
|
|
151
|
+
*
|
|
152
|
+
* `userId` is what makes it unprivileged, and leaving it out is not a cosmetic
|
|
153
|
+
* omission: a LogonTrigger with no UserId means "at ANY user's logon", which
|
|
154
|
+
* only an administrator may register. A standard user got `Zugriff verweigert`
|
|
155
|
+
* from schtasks — the install was impossible for exactly the audience the
|
|
156
|
+
* user-level design was for. Naming the user scopes both the trigger and the
|
|
157
|
+
* principal to that account, which needs no elevation.
|
|
151
158
|
*/
|
|
152
|
-
export function buildWindowsTaskXml({ nodePath, serverEntry, author, dataDir }) {
|
|
159
|
+
export function buildWindowsTaskXml({ nodePath, serverEntry, author, dataDir, userId }) {
|
|
153
160
|
// Task Scheduler requires \Command to be the executable and \Arguments the
|
|
154
161
|
// rest; quoting the script path handles spaces (e.g. under "Program Files").
|
|
155
162
|
// Task Scheduler XML carries no environment block, so the marker that this
|
|
@@ -157,6 +164,12 @@ export function buildWindowsTaskXml({ nodePath, serverEntry, author, dataDir })
|
|
|
157
164
|
const args = `"${serverEntry}" --autostart`;
|
|
158
165
|
const envNote = dataDir ? `Suveren data directory: ${dataDir}` : 'Suveren gateway';
|
|
159
166
|
|
|
167
|
+
// Element order is fixed by the Task Scheduler XSD, and schtasks rejects the
|
|
168
|
+
// file outright when it is wrong: inside Principal, UserId precedes LogonType;
|
|
169
|
+
// inside LogonTrigger it follows Enabled.
|
|
170
|
+
const principalUser = userId ? ` <UserId>${escapeXml(userId)}</UserId>\n` : '';
|
|
171
|
+
const triggerUser = userId ? ` <UserId>${escapeXml(userId)}</UserId>\n` : '';
|
|
172
|
+
|
|
160
173
|
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
161
174
|
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
162
175
|
<RegistrationInfo>
|
|
@@ -166,11 +179,11 @@ export function buildWindowsTaskXml({ nodePath, serverEntry, author, dataDir })
|
|
|
166
179
|
<Triggers>
|
|
167
180
|
<LogonTrigger>
|
|
168
181
|
<Enabled>true</Enabled>
|
|
169
|
-
</LogonTrigger>
|
|
182
|
+
${triggerUser} </LogonTrigger>
|
|
170
183
|
</Triggers>
|
|
171
184
|
<Principals>
|
|
172
185
|
<Principal id="Author">
|
|
173
|
-
<LogonType>InteractiveToken</LogonType>
|
|
186
|
+
${principalUser} <LogonType>InteractiveToken</LogonType>
|
|
174
187
|
<RunLevel>LeastPrivilege</RunLevel>
|
|
175
188
|
</Principal>
|
|
176
189
|
</Principals>
|
|
@@ -86,7 +86,7 @@ app.get(
|
|
|
86
86
|
}))
|
|
87
87
|
)
|
|
88
88
|
|
|
89
|
-
const wss = new WebSocketServer({ noServer: true })
|
|
89
|
+
const wss = new WebSocketServer({ noServer: true })
|
|
90
90
|
serve({
|
|
91
91
|
fetch: app.fetch,
|
|
92
92
|
websocket: { server: wss },
|
|
@@ -332,6 +332,52 @@ type Http2Bindings = {
|
|
|
332
332
|
}
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
+
## Early Hints Middleware
|
|
336
|
+
|
|
337
|
+
You can send HTTP 103 Early Hints to instruct browsers to preload or preconnect resources before the final response is prepared. The middleware is supported under Node.js bindings (HTTP/1.1 and HTTP/2).
|
|
338
|
+
|
|
339
|
+
### Usage
|
|
340
|
+
|
|
341
|
+
Import `earlyHints` from `@hono/node-server/early-hints`:
|
|
342
|
+
|
|
343
|
+
#### Static links
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import { serve } from '@hono/node-server'
|
|
347
|
+
import { earlyHints } from '@hono/node-server/early-hints'
|
|
348
|
+
import { Hono } from 'hono'
|
|
349
|
+
|
|
350
|
+
const app = new Hono()
|
|
351
|
+
|
|
352
|
+
app.use(
|
|
353
|
+
earlyHints({
|
|
354
|
+
link: '</styles.css>; rel=preload; as=style',
|
|
355
|
+
})
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
app.get('/', (c) => {
|
|
359
|
+
return c.html('<!DOCTYPE html><html><body><h1>Hello Hono!</h1></body></html>')
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
serve(app)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### Dynamic links
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
app.use(
|
|
369
|
+
earlyHints({
|
|
370
|
+
link: (c) =>
|
|
371
|
+
c.req.query('theme') === 'dark'
|
|
372
|
+
? '</dark.css>; rel=preload; as=style'
|
|
373
|
+
: '</light.css>; rel=preload; as=style',
|
|
374
|
+
})
|
|
375
|
+
)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
> [!NOTE]
|
|
379
|
+
> Early Hints are sent only for requests that look like document navigations. If `Sec-Fetch-Mode` or `Sec-Fetch-Dest` is present with a value other than `navigate` or `document`, for example a `fetch()` or XHR call from a browser, a subresource request, or an iframe navigation, the middleware skips the hints and continues to the handler. Requests without these headers, such as `curl` or `fetch()` from a JavaScript runtime, are treated as navigations and do receive Early Hints.
|
|
380
|
+
|
|
335
381
|
## Direct response from Node.js API
|
|
336
382
|
|
|
337
383
|
You can directly respond to the client from the Node.js API.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
//#region src/early-hints.ts
|
|
4
|
+
/**
|
|
5
|
+
* Early Hints middleware for Node.js
|
|
6
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
7
|
+
*
|
|
8
|
+
* @param options EarlyHintsOptions
|
|
9
|
+
* @returns MiddlewareHandler
|
|
10
|
+
*/
|
|
11
|
+
const earlyHints = (options) => {
|
|
12
|
+
let warned = false;
|
|
13
|
+
return async (c, next) => {
|
|
14
|
+
const mode = c.req.header("Sec-Fetch-Mode");
|
|
15
|
+
const dest = c.req.header("Sec-Fetch-Dest");
|
|
16
|
+
if (mode && mode !== "navigate" || dest && dest !== "document") return next();
|
|
17
|
+
const env = c.env || {};
|
|
18
|
+
const outgoing = (env.server ? env.server : env)?.outgoing;
|
|
19
|
+
if (typeof outgoing?.writeEarlyHints !== "function") {
|
|
20
|
+
if (!warned) {
|
|
21
|
+
console.warn("Early Hints Middleware is not supported because writeEarlyHints is not defined.");
|
|
22
|
+
warned = true;
|
|
23
|
+
}
|
|
24
|
+
return await next();
|
|
25
|
+
}
|
|
26
|
+
if (!outgoing.headersSent) {
|
|
27
|
+
const link = typeof options.link === "function" ? options.link(c) : options.link;
|
|
28
|
+
if (link !== void 0 && (Array.isArray(link) ? link.length > 0 : Boolean(link))) outgoing.writeEarlyHints({ link });
|
|
29
|
+
}
|
|
30
|
+
await next();
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
exports.earlyHints = earlyHints;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context, Env, MiddlewareHandler } from "hono";
|
|
2
|
+
|
|
3
|
+
//#region src/early-hints.d.ts
|
|
4
|
+
type EarlyHintsOptions<E extends Env = Env> = {
|
|
5
|
+
link: string | string[] | ((c: Context<E>) => string | string[] | undefined);
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Early Hints middleware for Node.js
|
|
9
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
10
|
+
*
|
|
11
|
+
* @param options EarlyHintsOptions
|
|
12
|
+
* @returns MiddlewareHandler
|
|
13
|
+
*/
|
|
14
|
+
declare const earlyHints: <E extends Env = any>(options: EarlyHintsOptions<E>) => MiddlewareHandler<E>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { EarlyHintsOptions, earlyHints };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context, Env, MiddlewareHandler } from "hono";
|
|
2
|
+
|
|
3
|
+
//#region src/early-hints.d.ts
|
|
4
|
+
type EarlyHintsOptions<E extends Env = Env> = {
|
|
5
|
+
link: string | string[] | ((c: Context<E>) => string | string[] | undefined);
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Early Hints middleware for Node.js
|
|
9
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
10
|
+
*
|
|
11
|
+
* @param options EarlyHintsOptions
|
|
12
|
+
* @returns MiddlewareHandler
|
|
13
|
+
*/
|
|
14
|
+
declare const earlyHints: <E extends Env = any>(options: EarlyHintsOptions<E>) => MiddlewareHandler<E>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { EarlyHintsOptions, earlyHints };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/early-hints.ts
|
|
2
|
+
/**
|
|
3
|
+
* Early Hints middleware for Node.js
|
|
4
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
5
|
+
*
|
|
6
|
+
* @param options EarlyHintsOptions
|
|
7
|
+
* @returns MiddlewareHandler
|
|
8
|
+
*/
|
|
9
|
+
const earlyHints = (options) => {
|
|
10
|
+
let warned = false;
|
|
11
|
+
return async (c, next) => {
|
|
12
|
+
const mode = c.req.header("Sec-Fetch-Mode");
|
|
13
|
+
const dest = c.req.header("Sec-Fetch-Dest");
|
|
14
|
+
if (mode && mode !== "navigate" || dest && dest !== "document") return next();
|
|
15
|
+
const env = c.env || {};
|
|
16
|
+
const outgoing = (env.server ? env.server : env)?.outgoing;
|
|
17
|
+
if (typeof outgoing?.writeEarlyHints !== "function") {
|
|
18
|
+
if (!warned) {
|
|
19
|
+
console.warn("Early Hints Middleware is not supported because writeEarlyHints is not defined.");
|
|
20
|
+
warned = true;
|
|
21
|
+
}
|
|
22
|
+
return await next();
|
|
23
|
+
}
|
|
24
|
+
if (!outgoing.headersSent) {
|
|
25
|
+
const link = typeof options.link === "function" ? options.link(c) : options.link;
|
|
26
|
+
if (link !== void 0 && (Array.isArray(link) ? link.length > 0 : Boolean(link))) outgoing.writeEarlyHints({ link });
|
|
27
|
+
}
|
|
28
|
+
await next();
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { earlyHints };
|
|
@@ -684,7 +684,10 @@ const drainIncoming = (incoming) => {
|
|
|
684
684
|
const forceClose = () => {
|
|
685
685
|
cleanup();
|
|
686
686
|
const socket = incoming.socket;
|
|
687
|
-
if (socket && !socket.destroyed)
|
|
687
|
+
if (socket && !socket.destroyed) {
|
|
688
|
+
if (typeof socket.destroySoon === "function") socket.destroySoon();
|
|
689
|
+
else if (typeof socket.destroy === "function") socket.destroy();
|
|
690
|
+
}
|
|
688
691
|
};
|
|
689
692
|
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
690
693
|
timer.unref?.();
|
|
@@ -683,7 +683,10 @@ const drainIncoming = (incoming) => {
|
|
|
683
683
|
const forceClose = () => {
|
|
684
684
|
cleanup();
|
|
685
685
|
const socket = incoming.socket;
|
|
686
|
-
if (socket && !socket.destroyed)
|
|
686
|
+
if (socket && !socket.destroyed) {
|
|
687
|
+
if (typeof socket.destroySoon === "function") socket.destroySoon();
|
|
688
|
+
else if (typeof socket.destroy === "function") socket.destroy();
|
|
689
|
+
}
|
|
687
690
|
};
|
|
688
691
|
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
689
692
|
timer.unref?.();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/node-server",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Node.js Adapter for Hono",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
"types": "./dist/conninfo.d.cts",
|
|
49
49
|
"default": "./dist/conninfo.cjs"
|
|
50
50
|
}
|
|
51
|
+
},
|
|
52
|
+
"./early-hints": {
|
|
53
|
+
"import": {
|
|
54
|
+
"types": "./dist/early-hints.d.mts",
|
|
55
|
+
"default": "./dist/early-hints.mjs"
|
|
56
|
+
},
|
|
57
|
+
"require": {
|
|
58
|
+
"types": "./dist/early-hints.d.cts",
|
|
59
|
+
"default": "./dist/early-hints.cjs"
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
},
|
|
53
63
|
"typesVersions": {
|
|
@@ -63,6 +73,9 @@
|
|
|
63
73
|
],
|
|
64
74
|
"conninfo": [
|
|
65
75
|
"./dist/conninfo.d.mts"
|
|
76
|
+
],
|
|
77
|
+
"early-hints": [
|
|
78
|
+
"./dist/early-hints.d.mts"
|
|
66
79
|
]
|
|
67
80
|
}
|
|
68
81
|
},
|
|
@@ -44,10 +44,15 @@ module.exports = __toCommonJS(index_exports);
|
|
|
44
44
|
// source/ip-key-generator.ts
|
|
45
45
|
var import_node_net = require("node:net");
|
|
46
46
|
var import_ip_address = require("ip-address");
|
|
47
|
+
var ipv4CompatibleSubnet = new import_ip_address.Address6("::/96");
|
|
47
48
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
48
49
|
if ((0, import_node_net.isIPv6)(ip)) {
|
|
49
50
|
const address = new import_ip_address.Address6(ip);
|
|
50
|
-
if (address.
|
|
51
|
+
if (address.isMapped4() || // The deprecated IPv4-compatible notation keeps its existing behavior.
|
|
52
|
+
// Its range is shared with addresses that embed no IPv4 address at all
|
|
53
|
+
// (`::`, `::1`), so here the notation is what tells them apart.
|
|
54
|
+
address.is4() && address.isInSubnet(ipv4CompatibleSubnet))
|
|
55
|
+
return address.to4().correctForm();
|
|
51
56
|
if (ipv6Subnet) {
|
|
52
57
|
const subnet = new import_ip_address.Address6(`${ip}/${ipv6Subnet}`);
|
|
53
58
|
return subnet.networkForm();
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
// source/ip-key-generator.ts
|
|
2
2
|
import { isIPv6 } from "node:net";
|
|
3
3
|
import { Address6 } from "ip-address";
|
|
4
|
+
var ipv4CompatibleSubnet = new Address6("::/96");
|
|
4
5
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
5
6
|
if (isIPv6(ip)) {
|
|
6
7
|
const address = new Address6(ip);
|
|
7
|
-
if (address.
|
|
8
|
+
if (address.isMapped4() || // The deprecated IPv4-compatible notation keeps its existing behavior.
|
|
9
|
+
// Its range is shared with addresses that embed no IPv4 address at all
|
|
10
|
+
// (`::`, `::1`), so here the notation is what tells them apart.
|
|
11
|
+
address.is4() && address.isInSubnet(ipv4CompatibleSubnet))
|
|
12
|
+
return address.to4().correctForm();
|
|
8
13
|
if (ipv6Subnet) {
|
|
9
14
|
const subnet = new Address6(`${ip}/${ipv6Subnet}`);
|
|
10
15
|
return subnet.networkForm();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "8.6.
|
|
3
|
+
"version": "8.6.2",
|
|
4
4
|
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nathan Friedly",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"@types/debug": "4.1.13",
|
|
91
91
|
"@types/express": "5.0.6",
|
|
92
92
|
"@types/jest": "30.0.0",
|
|
93
|
-
"@types/node": "26.1.
|
|
93
|
+
"@types/node": "26.1.2",
|
|
94
94
|
"@types/supertest": "7.2.1",
|
|
95
95
|
"del-cli": "7.0.0",
|
|
96
96
|
"dts-bundle-generator": "8.1.2",
|
|
@@ -98,13 +98,13 @@
|
|
|
98
98
|
"express": "5.2.1",
|
|
99
99
|
"husky": "9.1.7",
|
|
100
100
|
"jest": "30.4.2",
|
|
101
|
-
"lint-staged": "17.
|
|
102
|
-
"mintlify": "4.2.
|
|
101
|
+
"lint-staged": "17.2.0",
|
|
102
|
+
"mintlify": "4.2.743",
|
|
103
103
|
"npm-run-all": "4.1.5",
|
|
104
|
-
"prettier": "3.9.
|
|
104
|
+
"prettier": "3.9.6",
|
|
105
105
|
"ratelimit-header-parser": "0.1.0",
|
|
106
106
|
"supertest": "7.2.2",
|
|
107
|
-
"ts-jest": "29.4.
|
|
107
|
+
"ts-jest": "29.4.12",
|
|
108
108
|
"ts-node": "10.9.2",
|
|
109
109
|
"typescript": "5.9.3"
|
|
110
110
|
},
|
|
@@ -295,11 +295,11 @@ class Context {
|
|
|
295
295
|
return Object.fromEntries(this.#var);
|
|
296
296
|
}
|
|
297
297
|
#newResponse(data, arg, headers) {
|
|
298
|
-
|
|
299
|
-
if (typeof arg === "object" &&
|
|
300
|
-
|
|
301
|
-
for (const [key, value] of
|
|
302
|
-
if (key
|
|
298
|
+
let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders;
|
|
299
|
+
if (typeof arg === "object" && arg.headers) {
|
|
300
|
+
responseHeaders ??= new Headers();
|
|
301
|
+
for (const [key, value] of new Headers(arg.headers)) {
|
|
302
|
+
if (key === "set-cookie") {
|
|
303
303
|
responseHeaders.append(key, value);
|
|
304
304
|
} else {
|
|
305
305
|
responseHeaders.set(key, value);
|
|
@@ -307,19 +307,34 @@ class Context {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
if (headers) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
310
|
+
if (!responseHeaders) {
|
|
311
|
+
let count = 0;
|
|
312
|
+
for (const k in headers) {
|
|
313
|
+
if (++count > 1 || typeof headers[k] !== "string") {
|
|
314
|
+
responseHeaders = new Headers();
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (responseHeaders) {
|
|
320
|
+
for (const k in headers) {
|
|
321
|
+
const v = headers[k];
|
|
322
|
+
if (typeof v === "string") {
|
|
323
|
+
responseHeaders.set(k, v);
|
|
324
|
+
} else {
|
|
325
|
+
responseHeaders.delete(k);
|
|
326
|
+
for (const v2 of v) {
|
|
327
|
+
responseHeaders.append(k, v2);
|
|
328
|
+
}
|
|
317
329
|
}
|
|
318
330
|
}
|
|
319
331
|
}
|
|
320
332
|
}
|
|
321
333
|
const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
|
|
322
|
-
return createResponseInstance(data, {
|
|
334
|
+
return createResponseInstance(data, {
|
|
335
|
+
status,
|
|
336
|
+
headers: responseHeaders ?? headers
|
|
337
|
+
});
|
|
323
338
|
}
|
|
324
339
|
newResponse = (...args) => this.#newResponse(...args);
|
|
325
340
|
/**
|
|
@@ -43,6 +43,7 @@ class Hono {
|
|
|
43
43
|
delete;
|
|
44
44
|
options;
|
|
45
45
|
patch;
|
|
46
|
+
query;
|
|
46
47
|
all;
|
|
47
48
|
on;
|
|
48
49
|
use;
|
|
@@ -342,8 +343,8 @@ class Hono {
|
|
|
342
343
|
* @see {@link https://hono.dev/docs/api/hono#fetch}
|
|
343
344
|
*
|
|
344
345
|
* @param {Request} request - request Object of request
|
|
345
|
-
* @param {Env}
|
|
346
|
-
* @param {ExecutionContext} - context of execution
|
|
346
|
+
* @param {Env} env - env Object
|
|
347
|
+
* @param {ExecutionContext} executionCtx - context of execution
|
|
347
348
|
* @returns {Response | Promise<Response>} response of request
|
|
348
349
|
*
|
|
349
350
|
*/
|
|
@@ -100,6 +100,18 @@ const booleanAttributes = [
|
|
|
100
100
|
"reversed",
|
|
101
101
|
"selected"
|
|
102
102
|
];
|
|
103
|
+
const resolveFunctionComponentResult = (result, suspendedContext) => result.then((resolved) => {
|
|
104
|
+
if (!Array.isArray(resolved) && !(resolved instanceof JSXNode)) {
|
|
105
|
+
return resolved;
|
|
106
|
+
}
|
|
107
|
+
const children = Array.isArray(resolved) ? resolved : [resolved];
|
|
108
|
+
const render = () => {
|
|
109
|
+
const buffer = [""];
|
|
110
|
+
childrenToStringToBuffer(children, buffer);
|
|
111
|
+
return buffer.length === 1 ? (0, import_html.raw)(buffer[0], buffer.callbacks) : (0, import_html2.stringBufferToString)(buffer, buffer.callbacks);
|
|
112
|
+
};
|
|
113
|
+
return suspendedContext ? suspendedContext(render) : (0, import_context.runWithRenderContext)(render);
|
|
114
|
+
});
|
|
103
115
|
const childrenToStringToBuffer = (children, buffer) => {
|
|
104
116
|
for (let i = 0, len = children.length; i < len; i++) {
|
|
105
117
|
const child = children[i];
|
|
@@ -109,9 +121,17 @@ const childrenToStringToBuffer = (children, buffer) => {
|
|
|
109
121
|
continue;
|
|
110
122
|
} else if (child instanceof JSXNode) {
|
|
111
123
|
child.toStringToBuffer(buffer);
|
|
112
|
-
} else if (typeof child === "number"
|
|
124
|
+
} else if (typeof child === "number") {
|
|
113
125
|
;
|
|
114
126
|
buffer[0] += child;
|
|
127
|
+
} else if (child.isEscaped) {
|
|
128
|
+
;
|
|
129
|
+
buffer[0] += child;
|
|
130
|
+
const callbacks = child.callbacks;
|
|
131
|
+
if (callbacks) {
|
|
132
|
+
buffer.callbacks ||= [];
|
|
133
|
+
buffer.callbacks.push(...callbacks);
|
|
134
|
+
}
|
|
115
135
|
} else if (child instanceof Promise) {
|
|
116
136
|
buffer.unshift("", child);
|
|
117
137
|
} else {
|
|
@@ -125,7 +145,6 @@ class JSXNode {
|
|
|
125
145
|
key;
|
|
126
146
|
children;
|
|
127
147
|
isEscaped = true;
|
|
128
|
-
suspendedContext;
|
|
129
148
|
constructor(tag, props, children) {
|
|
130
149
|
if (typeof tag !== "function" && !(0, import_utils.isValidTagName)(tag)) {
|
|
131
150
|
throw new Error(`Invalid JSX tag name: ${tag}`);
|
|
@@ -148,7 +167,7 @@ class JSXNode {
|
|
|
148
167
|
this.toStringToBuffer(buffer);
|
|
149
168
|
return buffer.length === 1 ? "callbacks" in buffer ? (0, import_html2.resolveCallbackSync)((0, import_html.raw)(buffer[0], buffer.callbacks)).toString() : buffer[0] : (0, import_html2.stringBufferToString)(buffer, buffer.callbacks);
|
|
150
169
|
};
|
|
151
|
-
return
|
|
170
|
+
return (0, import_context.runWithRenderContext)(render);
|
|
152
171
|
}
|
|
153
172
|
toStringToBuffer(buffer) {
|
|
154
173
|
const tag = this.tag;
|
|
@@ -222,21 +241,14 @@ class JSXFunctionNode extends JSXNode {
|
|
|
222
241
|
return;
|
|
223
242
|
} else if (res instanceof Promise) {
|
|
224
243
|
if (import_context.globalContexts.length === 0) {
|
|
225
|
-
buffer.unshift("", res);
|
|
244
|
+
buffer.unshift("", resolveFunctionComponentResult(res));
|
|
226
245
|
} else {
|
|
227
|
-
|
|
228
|
-
buffer.unshift(
|
|
229
|
-
"",
|
|
230
|
-
res.then((childRes) => {
|
|
231
|
-
if (childRes instanceof JSXNode) {
|
|
232
|
-
childRes.suspendedContext = suspendedContext;
|
|
233
|
-
}
|
|
234
|
-
return childRes;
|
|
235
|
-
})
|
|
236
|
-
);
|
|
246
|
+
buffer.unshift("", resolveFunctionComponentResult(res, (0, import_context.captureRenderContext)()));
|
|
237
247
|
}
|
|
238
248
|
} else if (res instanceof JSXNode) {
|
|
239
249
|
res.toStringToBuffer(buffer);
|
|
250
|
+
} else if (Array.isArray(res)) {
|
|
251
|
+
childrenToStringToBuffer(res, buffer);
|
|
240
252
|
} else if (typeof res === "number" || res.isEscaped) {
|
|
241
253
|
buffer[0] += res;
|
|
242
254
|
if (res.callbacks) {
|
|
@@ -263,7 +263,7 @@ const useCallback = (callback, deps) => {
|
|
|
263
263
|
}
|
|
264
264
|
return callback;
|
|
265
265
|
};
|
|
266
|
-
|
|
266
|
+
function useRef(initialValue) {
|
|
267
267
|
const buildData = import_render.buildDataStack.at(-1);
|
|
268
268
|
if (!buildData) {
|
|
269
269
|
return { current: initialValue };
|
|
@@ -272,7 +272,7 @@ const useRef = (initialValue) => {
|
|
|
272
272
|
const refArray = node[import_constants.DOM_STASH][1][STASH_REF] ||= [];
|
|
273
273
|
const hookIndex = node[import_constants.DOM_STASH][0]++;
|
|
274
274
|
return refArray[hookIndex] ||= { current: initialValue };
|
|
275
|
-
}
|
|
275
|
+
}
|
|
276
276
|
const use = (promise) => {
|
|
277
277
|
const cachedRes = resolvedPromiseValueMap.get(promise);
|
|
278
278
|
if (cachedRes) {
|