@selvajs/server 1.0.4 → 1.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/dist/compute/index.d.ts +1 -1
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +1 -1
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/safe-url.d.ts +16 -0
- package/dist/compute/safe-url.d.ts.map +1 -1
- package/dist/compute/safe-url.js +36 -0
- package/dist/compute/safe-url.js.map +1 -1
- package/dist/http/security-headers.d.ts +14 -4
- package/dist/http/security-headers.d.ts.map +1 -1
- package/dist/http/security-headers.js +10 -4
- package/dist/http/security-headers.js.map +1 -1
- package/dist/notifications/index.d.ts +3 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +3 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/smtp-config.d.ts +37 -0
- package/dist/notifications/smtp-config.d.ts.map +1 -0
- package/dist/notifications/smtp-config.js +41 -0
- package/dist/notifications/smtp-config.js.map +1 -0
- package/dist/notifications/smtp-provider.d.ts +18 -0
- package/dist/notifications/smtp-provider.d.ts.map +1 -0
- package/dist/notifications/smtp-provider.js +71 -0
- package/dist/notifications/smtp-provider.js.map +1 -0
- package/dist/ops/deploymentConfig.d.ts +73 -0
- package/dist/ops/deploymentConfig.d.ts.map +1 -0
- package/dist/ops/deploymentConfig.js +154 -0
- package/dist/ops/deploymentConfig.js.map +1 -0
- package/dist/ops/index.d.ts +1 -0
- package/dist/ops/index.d.ts.map +1 -1
- package/dist/ops/index.js +3 -2
- package/dist/ops/index.js.map +1 -1
- package/package.json +17 -4
package/dist/compute/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool, type ComputeLimits, type EnvRecord } from './limits.js';
|
|
2
2
|
export { createComputeRateLimiter, DEFAULT_MAX_KEYS, type ComputeRateLimiter, type ComputeRateLimiterConfig, type RateLimitResult } from './rate-limit.js';
|
|
3
3
|
export { createIdempotencyStore, DEFAULT_IDEMPOTENCY_MAX_KEYS, type IdempotencyStore, type IdempotencyStoreConfig, type IdempotencyOutcome } from './idempotency.js';
|
|
4
|
-
export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
|
|
4
|
+
export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl, isLinkLocalUrl } from './safe-url.js';
|
|
5
5
|
export { createRemoteDefinitionFetcher, readBodyWithCap, type RemoteDefinitionFetcher, type RemoteDefinitionConfig } from './remote-definition.js';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,yBAAyB,EACzB,6BAA6B,EAC7B,cAAc,EACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC"}
|
package/dist/compute/index.js
CHANGED
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool } from './limits.js';
|
|
12
12
|
export { createComputeRateLimiter, DEFAULT_MAX_KEYS } from './rate-limit.js';
|
|
13
13
|
export { createIdempotencyStore, DEFAULT_IDEMPOTENCY_MAX_KEYS } from './idempotency.js';
|
|
14
|
-
export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
|
|
14
|
+
export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl, isLinkLocalUrl } from './safe-url.js';
|
|
15
15
|
export { createRemoteDefinitionFetcher, readBodyWithCap } from './remote-definition.js';
|
|
16
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,qCAAqC;AAErC,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAI5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,qCAAqC;AAErC,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAI5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,yBAAyB,EACzB,6BAA6B,EAC7B,cAAc,EACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC"}
|
|
@@ -6,6 +6,22 @@ import { type ILogger } from '@selvajs/platform';
|
|
|
6
6
|
* caught only by `assertSafeRemoteDefinitionUrl`.
|
|
7
7
|
*/
|
|
8
8
|
export declare function isSafeRemoteDefinitionUrl(raw: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* True when `raw` targets the link-local range (`169.254.0.0/16`), which
|
|
11
|
+
* carries the cloud metadata service at `169.254.169.254` — the endpoint that
|
|
12
|
+
* hands out a host's own IAM credentials to anything on the box.
|
|
13
|
+
*
|
|
14
|
+
* A far narrower judgement than {@link isSafeRemoteDefinitionUrl}, for the one
|
|
15
|
+
* caller that must tolerate private addresses: an operator's own compute server
|
|
16
|
+
* legitimately lives on `localhost` or a LAN, so blocking RFC1918 there would
|
|
17
|
+
* reject the ordinary self-hosted layout. Nothing legitimate is ever reached
|
|
18
|
+
* over link-local, so this stays blocked with no opt-out.
|
|
19
|
+
*
|
|
20
|
+
* Shares `canonicalizeNumericIpv4`, so the encodings that bypass a naive string
|
|
21
|
+
* comparison — `http://2852039166/`, `http://0251.0376.0251.0376/`,
|
|
22
|
+
* `http://0xa9fea9fe/` — all resolve to the same verdict.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isLinkLocalUrl(raw: string): boolean;
|
|
9
25
|
/**
|
|
10
26
|
* Full SSRF check: literal pre-filter, then resolve every A/AAAA record and
|
|
11
27
|
* reject if *any* resolved IP is blocked. Returns the original URL on success
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-url.d.ts","sourceRoot":"","sources":["../../src/compute/safe-url.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AA0H7D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA2B9D;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CAClD,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAA0B,GAChC,OAAO,CAAC,MAAM,CAAC,CAqCjB"}
|
|
1
|
+
{"version":3,"file":"safe-url.d.ts","sourceRoot":"","sources":["../../src/compute/safe-url.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AA0H7D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA2B9D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAiBnD;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CAClD,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAA0B,GAChC,OAAO,CAAC,MAAM,CAAC,CAqCjB"}
|
package/dist/compute/safe-url.js
CHANGED
|
@@ -166,6 +166,42 @@ export function isSafeRemoteDefinitionUrl(raw) {
|
|
|
166
166
|
// A real hostname — passes the literal filter; DNS resolution is the gate.
|
|
167
167
|
return true;
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* True when `raw` targets the link-local range (`169.254.0.0/16`), which
|
|
171
|
+
* carries the cloud metadata service at `169.254.169.254` — the endpoint that
|
|
172
|
+
* hands out a host's own IAM credentials to anything on the box.
|
|
173
|
+
*
|
|
174
|
+
* A far narrower judgement than {@link isSafeRemoteDefinitionUrl}, for the one
|
|
175
|
+
* caller that must tolerate private addresses: an operator's own compute server
|
|
176
|
+
* legitimately lives on `localhost` or a LAN, so blocking RFC1918 there would
|
|
177
|
+
* reject the ordinary self-hosted layout. Nothing legitimate is ever reached
|
|
178
|
+
* over link-local, so this stays blocked with no opt-out.
|
|
179
|
+
*
|
|
180
|
+
* Shares `canonicalizeNumericIpv4`, so the encodings that bypass a naive string
|
|
181
|
+
* comparison — `http://2852039166/`, `http://0251.0376.0251.0376/`,
|
|
182
|
+
* `http://0xa9fea9fe/` — all resolve to the same verdict.
|
|
183
|
+
*/
|
|
184
|
+
export function isLinkLocalUrl(raw) {
|
|
185
|
+
let host;
|
|
186
|
+
try {
|
|
187
|
+
host = new URL(raw).hostname.toLowerCase();
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (!host)
|
|
193
|
+
return false;
|
|
194
|
+
if (host.startsWith('[') && host.endsWith(']'))
|
|
195
|
+
host = host.slice(1, -1);
|
|
196
|
+
// IPv6 link-local (fe80::/10), the v6 counterpart of the range below.
|
|
197
|
+
if (isIP(host) === 6)
|
|
198
|
+
return /^fe[89ab][0-9a-f]:/.test(host);
|
|
199
|
+
const canonical = canonicalizeNumericIpv4(host);
|
|
200
|
+
if (!canonical)
|
|
201
|
+
return false; // a real hostname — DNS is not this check's job
|
|
202
|
+
const [a, b] = canonical.split('.').map(Number);
|
|
203
|
+
return a === 169 && b === 254;
|
|
204
|
+
}
|
|
169
205
|
/**
|
|
170
206
|
* Full SSRF check: literal pre-filter, then resolve every A/AAAA record and
|
|
171
207
|
* reject if *any* resolved IP is blocked. Returns the original URL on success
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-url.js","sourceRoot":"","sources":["../../src/compute/safe-url.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"safe-url.js","sourceRoot":"","sources":["../../src/compute/safe-url.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,14 @@ export interface SecurityHeaderOptions {
|
|
|
5
5
|
* what "production" means (e.g. `NODE_ENV`).
|
|
6
6
|
*/
|
|
7
7
|
hsts: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Forbid framing this response. Off by default — Selva-engine app routes are
|
|
10
|
+
* built to be embedded. Turn it on for operator surfaces (`/admin/*`,
|
|
11
|
+
* `/setup`), where an authenticated session plus a framable page is a
|
|
12
|
+
* UI-redress attack. `SameSite=Lax` blunts cross-site POST, not same-origin
|
|
13
|
+
* redress, so the cookie flag is not a substitute.
|
|
14
|
+
*/
|
|
15
|
+
denyFraming?: boolean;
|
|
8
16
|
}
|
|
9
17
|
/**
|
|
10
18
|
* Apply baseline browser-hardening headers to a response and return it for
|
|
@@ -17,12 +25,14 @@ export interface SecurityHeaderOptions {
|
|
|
17
25
|
* - Permissions-Policy: opts out of browser APIs Selva-engine apps don't use,
|
|
18
26
|
* so XSS can't enable them.
|
|
19
27
|
* - HSTS when `opts.hsts`.
|
|
28
|
+
* - X-Frame-Options + `frame-ancestors 'none'` when `opts.denyFraming` — the
|
|
29
|
+
* caller opts in per route, since embeddable app routes must stay framable.
|
|
20
30
|
*
|
|
21
31
|
* **Intentionally NOT set here:**
|
|
22
|
-
* - Content-Security-Policy
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
32
|
+
* - A full Content-Security-Policy. Selva-engine apps are built for iframe
|
|
33
|
+
* embedding, so a strict CSP needs validating against real consumer sites
|
|
34
|
+
* before it can ship. `denyFraming` sets only the `frame-ancestors`
|
|
35
|
+
* directive, which constrains nothing else the page loads.
|
|
26
36
|
* - Cache-Control. Asset-path layout is app policy; set it in the caller.
|
|
27
37
|
*/
|
|
28
38
|
export declare function applySecurityHeaders(response: Response, opts: SecurityHeaderOptions): Response;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACrC;;;;OAIG;IACH,IAAI,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACrC;;;;OAIG;IACH,IAAI,EAAE,OAAO,CAAC;IACd;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,GAAG,QAAQ,CAe9F"}
|
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
* - Permissions-Policy: opts out of browser APIs Selva-engine apps don't use,
|
|
10
10
|
* so XSS can't enable them.
|
|
11
11
|
* - HSTS when `opts.hsts`.
|
|
12
|
+
* - X-Frame-Options + `frame-ancestors 'none'` when `opts.denyFraming` — the
|
|
13
|
+
* caller opts in per route, since embeddable app routes must stay framable.
|
|
12
14
|
*
|
|
13
15
|
* **Intentionally NOT set here:**
|
|
14
|
-
* - Content-Security-Policy
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* - A full Content-Security-Policy. Selva-engine apps are built for iframe
|
|
17
|
+
* embedding, so a strict CSP needs validating against real consumer sites
|
|
18
|
+
* before it can ship. `denyFraming` sets only the `frame-ancestors`
|
|
19
|
+
* directive, which constrains nothing else the page loads.
|
|
18
20
|
* - Cache-Control. Asset-path layout is app policy; set it in the caller.
|
|
19
21
|
*/
|
|
20
22
|
export function applySecurityHeaders(response, opts) {
|
|
@@ -24,6 +26,10 @@ export function applySecurityHeaders(response, opts) {
|
|
|
24
26
|
if (opts.hsts) {
|
|
25
27
|
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
26
28
|
}
|
|
29
|
+
if (opts.denyFraming) {
|
|
30
|
+
response.headers.set('X-Frame-Options', 'DENY');
|
|
31
|
+
response.headers.set('Content-Security-Policy', "frame-ancestors 'none'");
|
|
32
|
+
}
|
|
27
33
|
return response;
|
|
28
34
|
}
|
|
29
35
|
//# sourceMappingURL=security-headers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAkB,EAAE,IAA2B;IACnF,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;IAC1D,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,iCAAiC,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,CAAC,GAAG,CACnB,oBAAoB,EACpB,+GAA+G,CAC/G,CAAC;IACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,qCAAqC,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACtB,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAChD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,wBAAwB,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/notifications/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/notifications/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAiC,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface SmtpConfig {
|
|
2
|
+
host: string;
|
|
3
|
+
port: number;
|
|
4
|
+
secure: boolean;
|
|
5
|
+
auth?: {
|
|
6
|
+
user: string;
|
|
7
|
+
pass: string;
|
|
8
|
+
};
|
|
9
|
+
from: string;
|
|
10
|
+
}
|
|
11
|
+
/** The env vars this provider reads. The app passes `process.env` or its framework equivalent. */
|
|
12
|
+
export interface SmtpEnv {
|
|
13
|
+
SMTP_HOST?: string;
|
|
14
|
+
SMTP_PORT?: string;
|
|
15
|
+
SMTP_USER?: string;
|
|
16
|
+
SMTP_PASS?: string;
|
|
17
|
+
SMTP_FROM?: string;
|
|
18
|
+
SMTP_SECURE?: string;
|
|
19
|
+
[key: string]: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read SMTP settings from an env bag. Taken as a parameter rather than read
|
|
23
|
+
* from `process.env` directly: under `vite dev` the framework does not mirror
|
|
24
|
+
* `.env` into `process.env`, so the app must hand over its own resolved env
|
|
25
|
+
* (`$env/dynamic/private` in SvelteKit) or overrides silently fall back to
|
|
26
|
+
* defaults.
|
|
27
|
+
*
|
|
28
|
+
* Returns null when mail is not configured — the caller treats that as "no
|
|
29
|
+
* mail", not as an error.
|
|
30
|
+
*
|
|
31
|
+
* Throws only when `SMTP_HOST` is set but `SMTP_FROM` is missing: that
|
|
32
|
+
* combination is a half-finished config, and failing loudly beats silently
|
|
33
|
+
* never sending. Every message needs an envelope sender, and guessing one
|
|
34
|
+
* produces mail that fails SPF at the recipient.
|
|
35
|
+
*/
|
|
36
|
+
export declare function readSmtpConfig(env: SmtpEnv): SmtpConfig | null;
|
|
37
|
+
//# sourceMappingURL=smtp-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp-config.d.ts","sourceRoot":"","sources":["../../src/notifications/smtp-config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,kGAAkG;AAClG,MAAM,WAAW,OAAO;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IAKrB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAClC;AAOD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAsB9D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
function parsePort(raw) {
|
|
2
|
+
const n = Number(raw ?? 587);
|
|
3
|
+
return Number.isInteger(n) && n > 0 && n < 65536 ? n : 587;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Read SMTP settings from an env bag. Taken as a parameter rather than read
|
|
7
|
+
* from `process.env` directly: under `vite dev` the framework does not mirror
|
|
8
|
+
* `.env` into `process.env`, so the app must hand over its own resolved env
|
|
9
|
+
* (`$env/dynamic/private` in SvelteKit) or overrides silently fall back to
|
|
10
|
+
* defaults.
|
|
11
|
+
*
|
|
12
|
+
* Returns null when mail is not configured — the caller treats that as "no
|
|
13
|
+
* mail", not as an error.
|
|
14
|
+
*
|
|
15
|
+
* Throws only when `SMTP_HOST` is set but `SMTP_FROM` is missing: that
|
|
16
|
+
* combination is a half-finished config, and failing loudly beats silently
|
|
17
|
+
* never sending. Every message needs an envelope sender, and guessing one
|
|
18
|
+
* produces mail that fails SPF at the recipient.
|
|
19
|
+
*/
|
|
20
|
+
export function readSmtpConfig(env) {
|
|
21
|
+
const host = env.SMTP_HOST?.trim();
|
|
22
|
+
if (!host)
|
|
23
|
+
return null;
|
|
24
|
+
const from = env.SMTP_FROM?.trim();
|
|
25
|
+
if (!from) {
|
|
26
|
+
throw new Error('SMTP_HOST is set but SMTP_FROM is missing — mail needs a sender address.');
|
|
27
|
+
}
|
|
28
|
+
const port = parsePort(env.SMTP_PORT);
|
|
29
|
+
const user = env.SMTP_USER?.trim();
|
|
30
|
+
const pass = env.SMTP_PASS;
|
|
31
|
+
return {
|
|
32
|
+
host,
|
|
33
|
+
port,
|
|
34
|
+
// Port 465 is implicit TLS; 587 and 25 start plaintext and upgrade via
|
|
35
|
+
// STARTTLS, which nodemailer does on its own when `secure` is false.
|
|
36
|
+
secure: env.SMTP_SECURE ? env.SMTP_SECURE === 'true' : port === 465,
|
|
37
|
+
auth: user && pass ? { user, pass } : undefined,
|
|
38
|
+
from
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=smtp-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp-config.js","sourceRoot":"","sources":["../../src/notifications/smtp-config.ts"],"names":[],"mappings":"AAuBA,SAAS,SAAS,CAAC,GAAuB;IACzC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC;IAE3B,OAAO;QACN,IAAI;QACJ,IAAI;QACJ,uEAAuE;QACvE,qEAAqE;QACrE,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG;QACnE,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;QAC/C,IAAI;KACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ILogger, type INotificationProvider, type OutboundMessage, type SendResult } from '@selvajs/platform';
|
|
2
|
+
import { type SmtpEnv } from './smtp-config.js';
|
|
3
|
+
/**
|
|
4
|
+
* SMTP transport. Any server works — a transactional provider, an internal
|
|
5
|
+
* relay, or a mailbox with an app password.
|
|
6
|
+
*
|
|
7
|
+
* Settings are read per send rather than cached, so a restart is the only
|
|
8
|
+
* thing needed to reconfigure and a rotated password takes effect without
|
|
9
|
+
* anyone remembering there is a cache.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SmtpNotificationProvider implements INotificationProvider {
|
|
12
|
+
private readonly env;
|
|
13
|
+
readonly name = "SMTP";
|
|
14
|
+
constructor(env: SmtpEnv);
|
|
15
|
+
send(message: OutboundMessage, log?: ILogger): Promise<SendResult>;
|
|
16
|
+
isConfigured(): boolean;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=smtp-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp-provider.d.ts","sourceRoot":"","sources":["../../src/notifications/smtp-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;GAOG;AACH,qBAAa,wBAAyB,YAAW,qBAAqB;IAGzD,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,QAAQ,CAAC,IAAI,UAAU;gBAEM,GAAG,EAAE,OAAO;IAEnC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,GAAE,OAA0B,GAAG,OAAO,CAAC,UAAU,CAAC;IA6C1F,YAAY,IAAI,OAAO;CAOvB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { NoopLogger } from '@selvajs/platform';
|
|
2
|
+
import { readSmtpConfig } from './smtp-config.js';
|
|
3
|
+
/**
|
|
4
|
+
* SMTP transport. Any server works — a transactional provider, an internal
|
|
5
|
+
* relay, or a mailbox with an app password.
|
|
6
|
+
*
|
|
7
|
+
* Settings are read per send rather than cached, so a restart is the only
|
|
8
|
+
* thing needed to reconfigure and a rotated password takes effect without
|
|
9
|
+
* anyone remembering there is a cache.
|
|
10
|
+
*/
|
|
11
|
+
export class SmtpNotificationProvider {
|
|
12
|
+
env;
|
|
13
|
+
name = 'SMTP';
|
|
14
|
+
constructor(env) {
|
|
15
|
+
this.env = env;
|
|
16
|
+
}
|
|
17
|
+
async send(message, log = new NoopLogger()) {
|
|
18
|
+
let config;
|
|
19
|
+
try {
|
|
20
|
+
config = readSmtpConfig(this.env);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
log.error('SMTP is misconfigured', {
|
|
24
|
+
component: 'SmtpNotificationProvider',
|
|
25
|
+
error: err instanceof Error ? err.message : String(err)
|
|
26
|
+
});
|
|
27
|
+
return { status: 'failed', reason: 'misconfigured' };
|
|
28
|
+
}
|
|
29
|
+
if (!config)
|
|
30
|
+
return { status: 'not-configured' };
|
|
31
|
+
try {
|
|
32
|
+
// Lazy: an instance with no SMTP_HOST never pays to load nodemailer.
|
|
33
|
+
const { createTransport } = await import('nodemailer');
|
|
34
|
+
const transport = createTransport({
|
|
35
|
+
host: config.host,
|
|
36
|
+
port: config.port,
|
|
37
|
+
secure: config.secure,
|
|
38
|
+
auth: config.auth
|
|
39
|
+
});
|
|
40
|
+
await transport.sendMail({
|
|
41
|
+
from: config.from,
|
|
42
|
+
to: message.to,
|
|
43
|
+
subject: message.subject,
|
|
44
|
+
text: message.text,
|
|
45
|
+
html: message.html
|
|
46
|
+
});
|
|
47
|
+
return { status: 'sent' };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
51
|
+
// Recipient is personal data — logged at warn only, as the minimum
|
|
52
|
+
// needed to diagnose a bounce. Never the subject or body.
|
|
53
|
+
log.warn('Failed to send mail', {
|
|
54
|
+
component: 'SmtpNotificationProvider',
|
|
55
|
+
to: message.to,
|
|
56
|
+
kind: message.kind,
|
|
57
|
+
reason
|
|
58
|
+
});
|
|
59
|
+
return { status: 'failed', reason };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
isConfigured() {
|
|
63
|
+
try {
|
|
64
|
+
return readSmtpConfig(this.env) !== null;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=smtp-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp-provider.js","sourceRoot":"","sources":["../../src/notifications/smtp-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EAKV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,OAAO,wBAAwB;IAGP;IAFpB,IAAI,GAAG,MAAM,CAAC;IAEvB,YAA6B,GAAY;QAAZ,QAAG,GAAH,GAAG,CAAS;IAAG,CAAC;IAE7C,KAAK,CAAC,IAAI,CAAC,OAAwB,EAAE,MAAe,IAAI,UAAU,EAAE;QACnE,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACJ,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE;gBAClC,SAAS,EAAE,0BAA0B;gBACrC,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACvD,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAEjD,IAAI,CAAC;YACJ,qEAAqE;YACrE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,SAAS,GAAG,eAAe,CAAC;gBACjC,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;aACjB,CAAC,CAAC;YAEH,MAAM,SAAS,CAAC,QAAQ,CAAC;gBACxB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,mEAAmE;YACnE,0DAA0D;YAC1D,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE;gBAC/B,SAAS,EAAE,0BAA0B;gBACrC,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,MAAM;aACN,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACrC,CAAC;IACF,CAAC;IAED,YAAY;QACX,IAAI,CAAC;YACJ,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;CACD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse-proxy and body-limit configuration rules, evaluated against a plain
|
|
3
|
+
* env bag. Pure predicates — no filesystem, no network, no process.
|
|
4
|
+
*
|
|
5
|
+
* These settings are read by adapter-node, not by Selva, so getting them wrong
|
|
6
|
+
* is invisible: nothing throws, nothing logs, and the deployment serves traffic
|
|
7
|
+
* while a whole class of requests fails. `selva doctor` reports them on the
|
|
8
|
+
* host; the admin health panel runs the same rules so an operator who never
|
|
9
|
+
* opens a shell still sees them.
|
|
10
|
+
*
|
|
11
|
+
* `packages/cli/src/checks/config.js` carries a duplicate of every rule here.
|
|
12
|
+
* Duplicated because the CLI is dependency-free by design — it scaffolds the
|
|
13
|
+
* deployment that installs this runtime, so it cannot import from it. A shared
|
|
14
|
+
* fixture table (`ops/__tests__/deployment-config-fixtures.js`) is asserted by
|
|
15
|
+
* both test suites, so a rule changed on one side fails CI on the other.
|
|
16
|
+
*/
|
|
17
|
+
/** Neutral severity. Callers map it onto their own presentation. */
|
|
18
|
+
export type ConfigVerdict = 'ok' | 'warn' | 'fail';
|
|
19
|
+
export interface ConfigFinding {
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
verdict: ConfigVerdict;
|
|
23
|
+
summary: string;
|
|
24
|
+
/** Concrete next step. Absent when the verdict is `ok`. */
|
|
25
|
+
remediation?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Just the vars these rules read. Values are raw strings, as they come from `.env`. */
|
|
28
|
+
export interface DeploymentEnv {
|
|
29
|
+
ORIGIN?: string;
|
|
30
|
+
ADDRESS_HEADER?: string;
|
|
31
|
+
XFF_DEPTH?: string;
|
|
32
|
+
BODY_SIZE_LIMIT?: string;
|
|
33
|
+
COMPUTE_REQUEST_MAX_BYTES?: string;
|
|
34
|
+
[key: string]: string | undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a `BODY_SIZE_LIMIT` the way adapter-node does, so this agrees with the
|
|
38
|
+
* thing that actually rejects the request.
|
|
39
|
+
*
|
|
40
|
+
* The quirk is load-bearing: adapter-node reads only the LAST character as the
|
|
41
|
+
* suffix, so `60mb` parses as the digits `60m` and happens to mean 60 MB, while
|
|
42
|
+
* `60xy` silently becomes NaN. Returns bytes, or null when the value is one
|
|
43
|
+
* adapter-node can't use.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseBodySizeLimit(raw: string | undefined | null): number | null;
|
|
46
|
+
/**
|
|
47
|
+
* BODY_SIZE_LIMIT has to clear the compute request cap, or adapter-node's global
|
|
48
|
+
* backstop rejects the upload first — with a non-JSON body the app never sees
|
|
49
|
+
* and cannot log. Unset is the dangerous case, not a neutral one: adapter-node
|
|
50
|
+
* falls back to 512 KB, so every upload 413s on a deployment that looks fine.
|
|
51
|
+
*/
|
|
52
|
+
export declare function checkBodySizeLimit(env: DeploymentEnv): ConfigFinding;
|
|
53
|
+
/**
|
|
54
|
+
* `ADDRESS_HEADER` / `XFF_DEPTH` are read by adapter-node, not by Selva, and
|
|
55
|
+
* unset they are invisible: nothing fails, nothing logs, and the deployment
|
|
56
|
+
* looks healthy.
|
|
57
|
+
*
|
|
58
|
+
* What actually happens is that `getClientAddress()` returns the socket peer,
|
|
59
|
+
* which behind a reverse proxy is the proxy — `127.0.0.1` for every request
|
|
60
|
+
* from every user. Login rate limiting keys on that, so the whole instance
|
|
61
|
+
* shares one bucket: five failed logins from anywhere return 429 to everyone,
|
|
62
|
+
* and only a *successful* login clears a bucket, which nobody can then reach.
|
|
63
|
+
*
|
|
64
|
+
* `ORIGIN` is the proxy tell. It is only needed when the app is served under a
|
|
65
|
+
* public URL it can't derive from its own socket, so its presence means a proxy
|
|
66
|
+
* is terminating requests. Without it we say nothing: on a directly-reachable
|
|
67
|
+
* app these settings are a footgun, since `X-Forwarded-For` is client-supplied
|
|
68
|
+
* and anyone could then pick their own rate-limit bucket.
|
|
69
|
+
*/
|
|
70
|
+
export declare function checkClientAddress(env: DeploymentEnv): ConfigFinding;
|
|
71
|
+
/** Every deployment-config rule, in report order. */
|
|
72
|
+
export declare function checkDeploymentConfig(env: DeploymentEnv): ConfigFinding[];
|
|
73
|
+
//# sourceMappingURL=deploymentConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploymentConfig.d.ts","sourceRoot":"","sources":["../../src/ops/deploymentConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,oEAAoE;AACpE,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAKnC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAUhF;AAOD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,CA0CpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,CAwDpE;AAED,qDAAqD;AACrD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,EAAE,CAEzE"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse-proxy and body-limit configuration rules, evaluated against a plain
|
|
3
|
+
* env bag. Pure predicates — no filesystem, no network, no process.
|
|
4
|
+
*
|
|
5
|
+
* These settings are read by adapter-node, not by Selva, so getting them wrong
|
|
6
|
+
* is invisible: nothing throws, nothing logs, and the deployment serves traffic
|
|
7
|
+
* while a whole class of requests fails. `selva doctor` reports them on the
|
|
8
|
+
* host; the admin health panel runs the same rules so an operator who never
|
|
9
|
+
* opens a shell still sees them.
|
|
10
|
+
*
|
|
11
|
+
* `packages/cli/src/checks/config.js` carries a duplicate of every rule here.
|
|
12
|
+
* Duplicated because the CLI is dependency-free by design — it scaffolds the
|
|
13
|
+
* deployment that installs this runtime, so it cannot import from it. A shared
|
|
14
|
+
* fixture table (`ops/__tests__/deployment-config-fixtures.js`) is asserted by
|
|
15
|
+
* both test suites, so a rule changed on one side fails CI on the other.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Parse a `BODY_SIZE_LIMIT` the way adapter-node does, so this agrees with the
|
|
19
|
+
* thing that actually rejects the request.
|
|
20
|
+
*
|
|
21
|
+
* The quirk is load-bearing: adapter-node reads only the LAST character as the
|
|
22
|
+
* suffix, so `60mb` parses as the digits `60m` and happens to mean 60 MB, while
|
|
23
|
+
* `60xy` silently becomes NaN. Returns bytes, or null when the value is one
|
|
24
|
+
* adapter-node can't use.
|
|
25
|
+
*/
|
|
26
|
+
export function parseBodySizeLimit(raw) {
|
|
27
|
+
if (raw == null || raw === '')
|
|
28
|
+
return null;
|
|
29
|
+
const value = String(raw).trim();
|
|
30
|
+
if (value === '')
|
|
31
|
+
return null;
|
|
32
|
+
const units = { K: 1024, M: 1024 * 1024, G: 1024 * 1024 * 1024 };
|
|
33
|
+
const multiplier = units[value.at(-1).toUpperCase()] ?? 1;
|
|
34
|
+
const bytes = Number(multiplier !== 1 ? value.slice(0, -1) : value) * multiplier;
|
|
35
|
+
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
36
|
+
return null;
|
|
37
|
+
return bytes;
|
|
38
|
+
}
|
|
39
|
+
/** The app's own default for COMPUTE_REQUEST_MAX_BYTES (compute/limits.ts). */
|
|
40
|
+
const COMPUTE_REQUEST_DEFAULT_BYTES = 256 * 1024 * 1024;
|
|
41
|
+
const asMb = (bytes) => `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
42
|
+
/**
|
|
43
|
+
* BODY_SIZE_LIMIT has to clear the compute request cap, or adapter-node's global
|
|
44
|
+
* backstop rejects the upload first — with a non-JSON body the app never sees
|
|
45
|
+
* and cannot log. Unset is the dangerous case, not a neutral one: adapter-node
|
|
46
|
+
* falls back to 512 KB, so every upload 413s on a deployment that looks fine.
|
|
47
|
+
*/
|
|
48
|
+
export function checkBodySizeLimit(env) {
|
|
49
|
+
const base = { id: 'body-size-limit', label: 'Upload size limit' };
|
|
50
|
+
const raw = env.BODY_SIZE_LIMIT;
|
|
51
|
+
const requestCap = Number(env.COMPUTE_REQUEST_MAX_BYTES) || COMPUTE_REQUEST_DEFAULT_BYTES;
|
|
52
|
+
if (raw == null || raw === '') {
|
|
53
|
+
return {
|
|
54
|
+
...base,
|
|
55
|
+
verdict: 'fail',
|
|
56
|
+
summary: 'BODY_SIZE_LIMIT is unset — adapter-node falls back to 512 KB, so uploads over ' +
|
|
57
|
+
'that fail with an opaque 413 no app log records.',
|
|
58
|
+
remediation: `Set BODY_SIZE_LIMIT to at least ${asMb(requestCap)} (the compute request cap) and restart.`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const bytes = parseBodySizeLimit(raw);
|
|
62
|
+
if (bytes === null) {
|
|
63
|
+
return {
|
|
64
|
+
...base,
|
|
65
|
+
verdict: 'fail',
|
|
66
|
+
summary: `BODY_SIZE_LIMIT="${raw}" — adapter-node cannot parse this and throws on boot.`,
|
|
67
|
+
remediation: 'Use a byte count or a single K/M/G suffix, e.g. "256M".'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (bytes < requestCap) {
|
|
71
|
+
return {
|
|
72
|
+
...base,
|
|
73
|
+
verdict: 'warn',
|
|
74
|
+
summary: `BODY_SIZE_LIMIT=${raw} (${asMb(bytes)}) is below COMPUTE_REQUEST_MAX_BYTES ` +
|
|
75
|
+
`(${asMb(requestCap)}) — the global cap rejects first, making the compute cap dead config.`,
|
|
76
|
+
remediation: `Raise BODY_SIZE_LIMIT to at least ${asMb(requestCap)} and restart.`
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
...base,
|
|
81
|
+
verdict: 'ok',
|
|
82
|
+
summary: `BODY_SIZE_LIMIT=${raw} clears the compute request cap.`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `ADDRESS_HEADER` / `XFF_DEPTH` are read by adapter-node, not by Selva, and
|
|
87
|
+
* unset they are invisible: nothing fails, nothing logs, and the deployment
|
|
88
|
+
* looks healthy.
|
|
89
|
+
*
|
|
90
|
+
* What actually happens is that `getClientAddress()` returns the socket peer,
|
|
91
|
+
* which behind a reverse proxy is the proxy — `127.0.0.1` for every request
|
|
92
|
+
* from every user. Login rate limiting keys on that, so the whole instance
|
|
93
|
+
* shares one bucket: five failed logins from anywhere return 429 to everyone,
|
|
94
|
+
* and only a *successful* login clears a bucket, which nobody can then reach.
|
|
95
|
+
*
|
|
96
|
+
* `ORIGIN` is the proxy tell. It is only needed when the app is served under a
|
|
97
|
+
* public URL it can't derive from its own socket, so its presence means a proxy
|
|
98
|
+
* is terminating requests. Without it we say nothing: on a directly-reachable
|
|
99
|
+
* app these settings are a footgun, since `X-Forwarded-For` is client-supplied
|
|
100
|
+
* and anyone could then pick their own rate-limit bucket.
|
|
101
|
+
*/
|
|
102
|
+
export function checkClientAddress(env) {
|
|
103
|
+
const base = { id: 'client-address', label: 'Client IP behind proxy' };
|
|
104
|
+
const header = env.ADDRESS_HEADER;
|
|
105
|
+
const depth = env.XFF_DEPTH;
|
|
106
|
+
if (!env.ORIGIN) {
|
|
107
|
+
return {
|
|
108
|
+
...base,
|
|
109
|
+
verdict: 'ok',
|
|
110
|
+
summary: 'ADDRESS_HEADER is not needed — ORIGIN is unset, so no reverse proxy is in play.'
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (!header) {
|
|
114
|
+
return {
|
|
115
|
+
...base,
|
|
116
|
+
verdict: 'fail',
|
|
117
|
+
summary: 'ADDRESS_HEADER is unset while ORIGIN is set — every request looks like it came from ' +
|
|
118
|
+
'the proxy, so all users share ONE login rate-limit bucket. Five failed logins from ' +
|
|
119
|
+
'anyone locks out the whole instance for 15 minutes.',
|
|
120
|
+
remediation: 'Set ADDRESS_HEADER=X-Forwarded-For and XFF_DEPTH=<number of proxies>, then restart. ' +
|
|
121
|
+
'To count your proxies, see docs/self-hosting/deployment/reverse-proxy.md.'
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (header.toLowerCase() === 'x-forwarded-for' && !depth) {
|
|
125
|
+
return {
|
|
126
|
+
...base,
|
|
127
|
+
verdict: 'warn',
|
|
128
|
+
summary: `ADDRESS_HEADER=${header} but XFF_DEPTH is unset. X-Forwarded-For is a ` +
|
|
129
|
+
'client-appendable list, so a caller can prepend fake entries — adapter-node needs ' +
|
|
130
|
+
'the proxy count to know which entry is real.',
|
|
131
|
+
remediation: 'Set XFF_DEPTH=1 for a single reverse proxy, 2 if a CDN or load balancer sits in ' +
|
|
132
|
+
'front of it. Unsure? docs/self-hosting/deployment/reverse-proxy.md shows how to ' +
|
|
133
|
+
'read the header the app actually receives.'
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (depth !== undefined && !/^[1-9][0-9]*$/.test(depth)) {
|
|
137
|
+
return {
|
|
138
|
+
...base,
|
|
139
|
+
verdict: 'fail',
|
|
140
|
+
summary: `XFF_DEPTH="${depth}" is not a positive integer.`,
|
|
141
|
+
remediation: 'It counts proxies from the outside in — use 1 for a single reverse proxy.'
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
...base,
|
|
146
|
+
verdict: 'ok',
|
|
147
|
+
summary: `ADDRESS_HEADER=${header}${depth ? ` (XFF_DEPTH=${depth})` : ''}.`
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Every deployment-config rule, in report order. */
|
|
151
|
+
export function checkDeploymentConfig(env) {
|
|
152
|
+
return [checkClientAddress(env), checkBodySizeLimit(env)];
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=deploymentConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploymentConfig.js","sourceRoot":"","sources":["../../src/ops/deploymentConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA4BH;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA8B;IAChE,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE9B,MAAM,KAAK,GAA2B,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;IACzF,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC;IACjF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,MAAM,6BAA6B,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAkB;IACpD,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IACnE,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC;IAChC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC,IAAI,6BAA6B,CAAC;IAE1F,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QAC/B,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,MAAM;YACf,OAAO,EACN,gFAAgF;gBAChF,kDAAkD;YACnD,WAAW,EAAE,mCAAmC,IAAI,CAAC,UAAU,CAAC,yCAAyC;SACzG,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,oBAAoB,GAAG,wDAAwD;YACxF,WAAW,EAAE,yDAAyD;SACtE,CAAC;IACH,CAAC;IAED,IAAI,KAAK,GAAG,UAAU,EAAE,CAAC;QACxB,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,MAAM;YACf,OAAO,EACN,mBAAmB,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,uCAAuC;gBAC7E,IAAI,IAAI,CAAC,UAAU,CAAC,uEAAuE;YAC5F,WAAW,EAAE,qCAAqC,IAAI,CAAC,UAAU,CAAC,eAAe;SACjF,CAAC;IACH,CAAC;IAED,OAAO;QACN,GAAG,IAAI;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,mBAAmB,GAAG,kCAAkC;KACjE,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAkB;IACpD,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;IACvE,MAAM,MAAM,GAAG,GAAG,CAAC,cAAc,CAAC;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC;IAE5B,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,iFAAiF;SAC1F,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,MAAM;YACf,OAAO,EACN,sFAAsF;gBACtF,qFAAqF;gBACrF,qDAAqD;YACtD,WAAW,EACV,sFAAsF;gBACtF,2EAA2E;SAC5E,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,iBAAiB,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1D,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,MAAM;YACf,OAAO,EACN,kBAAkB,MAAM,gDAAgD;gBACxE,oFAAoF;gBACpF,8CAA8C;YAC/C,WAAW,EACV,kFAAkF;gBAClF,kFAAkF;gBAClF,4CAA4C;SAC7C,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO;YACN,GAAG,IAAI;YACP,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,cAAc,KAAK,8BAA8B;YAC1D,WAAW,EAAE,2EAA2E;SACxF,CAAC;IACH,CAAC;IAED,OAAO;QACN,GAAG,IAAI;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,kBAAkB,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG;KAC3E,CAAC;AACH,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,qBAAqB,CAAC,GAAkB;IACvD,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
package/dist/ops/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { parseSemver, isNewer, compareCore, satisfiesRange, type ReleaseChannel } from './semver.js';
|
|
2
|
+
export { checkClientAddress, checkBodySizeLimit, checkDeploymentConfig, parseBodySizeLimit, type ConfigFinding, type ConfigVerdict, type DeploymentEnv } from './deploymentConfig.js';
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/ops/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ops/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,WAAW,EACX,OAAO,EACP,WAAW,EACX,cAAc,EACd,KAAK,cAAc,EACnB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ops/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,WAAW,EACX,OAAO,EACP,WAAW,EACX,cAAc,EACd,KAAK,cAAc,EACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,uBAAuB,CAAC"}
|
package/dist/ops/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// Deployment/ops helpers — channel-aware semver comparison
|
|
2
|
-
//
|
|
1
|
+
// Deployment/ops helpers — channel-aware semver comparison, engines.node range
|
|
2
|
+
// checks, and the reverse-proxy/body-limit configuration rules.
|
|
3
3
|
export { parseSemver, isNewer, compareCore, satisfiesRange } from './semver.js';
|
|
4
|
+
export { checkClientAddress, checkBodySizeLimit, checkDeploymentConfig, parseBodySizeLimit } from './deploymentConfig.js';
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
package/dist/ops/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ops/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ops/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gEAAgE;AAEhE,OAAO,EACN,WAAW,EACX,OAAO,EACP,WAAW,EACX,cAAc,EAEd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAIlB,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/server",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Transport-agnostic server building blocks for apps built on the Selva engine (limits, rate limiting, SSRF guard, remote-definition fetch, definition service + render loader); the solve core lives in @selvajs/solve/server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
@@ -70,6 +70,11 @@
|
|
|
70
70
|
"selva-source": "./src/ops/index.ts",
|
|
71
71
|
"types": "./dist/ops/index.d.ts",
|
|
72
72
|
"import": "./dist/ops/index.js"
|
|
73
|
+
},
|
|
74
|
+
"./notifications": {
|
|
75
|
+
"selva-source": "./src/notifications/index.ts",
|
|
76
|
+
"types": "./dist/notifications/index.d.ts",
|
|
77
|
+
"import": "./dist/notifications/index.js"
|
|
73
78
|
}
|
|
74
79
|
},
|
|
75
80
|
"files": [
|
|
@@ -80,23 +85,29 @@
|
|
|
80
85
|
],
|
|
81
86
|
"dependencies": {
|
|
82
87
|
"@selvajs/compute": "^4.0.2",
|
|
83
|
-
"@selvajs/platform": "^0.
|
|
84
|
-
"@selvajs/schemas": "^5.0.
|
|
88
|
+
"@selvajs/platform": "^0.20.0",
|
|
89
|
+
"@selvajs/schemas": "^5.0.1"
|
|
85
90
|
},
|
|
86
91
|
"peerDependencies": {
|
|
87
92
|
"@sentry/node": ">=8",
|
|
93
|
+
"nodemailer": ">=7",
|
|
88
94
|
"pino": ">=9"
|
|
89
95
|
},
|
|
90
96
|
"peerDependenciesMeta": {
|
|
91
97
|
"@sentry/node": {
|
|
92
98
|
"optional": true
|
|
93
99
|
},
|
|
100
|
+
"nodemailer": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
94
103
|
"pino": {
|
|
95
104
|
"optional": true
|
|
96
105
|
}
|
|
97
106
|
},
|
|
98
107
|
"devDependencies": {
|
|
99
108
|
"@types/node": "^26.2.0",
|
|
109
|
+
"@types/nodemailer": "^8.0.1",
|
|
110
|
+
"nodemailer": "^9.0.5",
|
|
100
111
|
"typescript": "~6.0.3",
|
|
101
112
|
"vitest": "^4.1.10",
|
|
102
113
|
"@selvajs/config": "0.0.4"
|
|
@@ -106,6 +117,8 @@
|
|
|
106
117
|
"dev": "tsc --watch",
|
|
107
118
|
"type-check": "tsc --noEmit",
|
|
108
119
|
"test": "vitest run",
|
|
109
|
-
"test:watch": "vitest"
|
|
120
|
+
"test:watch": "vitest",
|
|
121
|
+
"lint": "eslint .",
|
|
122
|
+
"lint:types": "eslint . --config eslint.typed.config.js"
|
|
110
123
|
}
|
|
111
124
|
}
|