@zerotal/core 1.9.0 → 1.10.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/CHANGELOG.md +117 -0
- package/api-surface.md +148 -81
- package/package.json +2 -1
- package/src/application/Application.ts +4 -4
- package/src/build/codemod.ts +1 -1
- package/src/command/CommandRunner.ts +2 -0
- package/src/command/builtin/AssetsPruneCommand.ts +138 -0
- package/src/command/builtin/DeployCommand.ts +19 -0
- package/src/command/builtin/index.ts +1 -0
- package/src/command/startZerotal.ts +36 -9
- package/src/config/AppConfig.ts +10 -10
- package/src/conventions/ConventionLoader.ts +39 -2
- package/src/dev/BuildOutput.ts +58 -6
- package/src/dev/CssPlugins.ts +2 -2
- package/src/dev/DevDeck.ts +3 -3
- package/src/dev/DevProcess.ts +2 -2
- package/src/dev/DevSupervisor.ts +5 -5
- package/src/dev/index.ts +6 -1
- package/src/dev/startDevMode.ts +1 -1
- package/src/doctor/throttleIdentity.ts +6 -2
- package/src/health/Health.ts +5 -5
- package/src/helpers/index.ts +2 -1
- package/src/helpers/markdown.ts +7 -0
- package/src/http/UploadedFile.ts +2 -2
- package/src/index.ts +2 -1
- package/src/lock/LockManager.ts +4 -4
- package/src/logger/LoggerMiddleware.ts +1 -1
- package/src/logger/types.ts +4 -4
- package/src/middleware/CorsMiddleware.ts +6 -6
- package/src/middleware/RateLimiter.ts +105 -22
- package/src/middleware/SecureHeadersMiddleware.ts +8 -8
- package/src/middleware/ThrottleMiddleware.ts +57 -4
- package/src/middleware/WebhookMiddleware.ts +7 -7
- package/src/router/RouteHandler.ts +2 -2
- package/src/router/Router.ts +5 -5
- package/src/security/redactGraph.ts +1 -1
- package/src/shared/format.ts +8 -8
- package/src/storage/StorageFilesMiddleware.ts +2 -2
- package/src/storage/types.ts +10 -10
- package/src/support/cookie.ts +6 -6
- package/src/support/deepMerge.ts +49 -0
- package/src/support/runtime.ts +91 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,123 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [1.10.0] — 2026-08-30
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **Optional properties in public option shapes are declared `?: T | undefined`.**
|
|
16
|
+
The generated `tsconfig.json` turns on `exactOptionalPropertyTypes`, under which
|
|
17
|
+
`image?: string` and `image?: string | undefined` are different types: only the
|
|
18
|
+
second accepts a key that is present and holds `undefined`. So an app with the
|
|
19
|
+
strictness the framework shipped it could not write the most ordinary thing there
|
|
20
|
+
is — `{ image: candidate ?? undefined }` — and had to spell every conditional field
|
|
21
|
+
`...(x ? { x } : {})` instead. An options bag exists to be spread into; one built by
|
|
22
|
+
spreading has conditionally-absent keys.
|
|
23
|
+
|
|
24
|
+
438 properties across 115 files, applied by `scripts/exact-optional.ts` and checked
|
|
25
|
+
by `bun run exact:optional:check` so the convention holds for shapes written later.
|
|
26
|
+
Scoped to exported `*Options` / `*Config` shapes — what an app constructs — rather
|
|
27
|
+
than every optional property in the codebase. Nothing changes for a reader: an
|
|
28
|
+
absent optional property already read as `undefined`.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **`zt assets:prune`** — remove the chunks an earlier release left behind, on the
|
|
33
|
+
machine that never ran a build. `assets:build --clean` cleans the directory it
|
|
34
|
+
_builds into_, which does nothing for the common release shape: build here, tar the
|
|
35
|
+
output, extract it over `public/` there. Extracting merges — nothing removes a file
|
|
36
|
+
the new release does not contain — so every deploy adds another set of
|
|
37
|
+
content-hashed chunks. One app reached 225 chunk files for the 49 its entry point
|
|
38
|
+
references. Ship `.zerotal/` with the release and this removes what the build record
|
|
39
|
+
does not claim; without a record it says so and removes nothing rather than guessing.
|
|
40
|
+
|
|
41
|
+
- **`definedOnly` (`@zerotal/core/helpers`) and the `Resolved<T>` type.** Widening the
|
|
42
|
+
option shapes moves a problem to the merge, and both of these are that problem's
|
|
43
|
+
answer. Object spread copies own properties even when their value is `undefined`,
|
|
44
|
+
so `{ ...DEFAULTS, ...options }` now lets an explicit `undefined` overwrite a
|
|
45
|
+
default; `definedOnly` drops those keys, turning "supplied as undefined" back into
|
|
46
|
+
"not supplied". (`deepMerge` already did this at every depth.)
|
|
47
|
+
|
|
48
|
+
`Resolved<T>` is what a defaults-applied object is. **`Required<T>` is not**, which
|
|
49
|
+
is the trap: `-?` removes the optionality a `?` introduced but not an `undefined`
|
|
50
|
+
written into the type, so `Required<Options>` quietly stopped meaning "the defaults
|
|
51
|
+
have been applied".
|
|
52
|
+
|
|
53
|
+
- **`zt deploy:<env> --check`** — the preflight gate on its own. A release script has a
|
|
54
|
+
moment where the new code is on disk and the service has not restarted yet, and that
|
|
55
|
+
is where a gate belongs: exit 0 and restart, exit non-zero and keep serving the
|
|
56
|
+
previous release. Everything that can refuse already runs by the end of preflight and
|
|
57
|
+
none of it mutates, so stopping there is a complete answer rather than half a deploy.
|
|
58
|
+
A workspace that has lost its banking details, or had its mail driver knocked back to
|
|
59
|
+
`log`, never goes live broken.
|
|
60
|
+
|
|
61
|
+
- **`engines.bun` is enforced.** Every generated app writes a floor and nothing had
|
|
62
|
+
ever read it. `runtimeMismatch` did not cover this: it compares the running Bun
|
|
63
|
+
against an _installed_ one, and most projects do not install Bun as a package, so
|
|
64
|
+
it correctly says nothing about most of them. `startZerotal()` now refuses to run
|
|
65
|
+
below the declared floor, so every `zt` command is covered.
|
|
66
|
+
|
|
67
|
+
The failure it catches is narrow and expensive. `Intl` output moves between Bun
|
|
68
|
+
releases, so a suite with currency or date assertions goes red on a runtime that is
|
|
69
|
+
otherwise fine, the failures name the code they touch, and the version is the last
|
|
70
|
+
thing anyone checks. `ZT_ALLOW_RUNTIME_MISMATCH=1` downgrades it to a warning.
|
|
71
|
+
|
|
72
|
+
- **`@zerotal/core/runtime`** (`zerotal/runtime`) — a subpath exporting the checks
|
|
73
|
+
themselves: `runtimeBelowFloor`, `declaredBunFloor`, `runtimeMismatch`, `bunBinary`
|
|
74
|
+
and the messages that go with them, so a script or a test can make the same
|
|
75
|
+
assertion. A subpath rather than the main barrel, which is deliberately lean.
|
|
76
|
+
|
|
77
|
+
- **A boot line when a convention is skipped in this environment.** An env-restricted
|
|
78
|
+
concern is skipped by _not looking_, which is correct and completely silent: from a
|
|
79
|
+
web process's point of view nothing is wrong, because from a web process's point of
|
|
80
|
+
view nothing exists. An app ran for weeks in production with `app/schedules` full
|
|
81
|
+
and no worker process — no inventory hold released, no payment reminder sent, and
|
|
82
|
+
nothing logged, because there was nothing to log. It was found by going looking.
|
|
83
|
+
|
|
84
|
+
`runConventions` now says so once, at boot, and only when the directory actually
|
|
85
|
+
holds files:
|
|
86
|
+
|
|
87
|
+
Skipping 3 file(s) in app/schedules — the "schedules" convention does not run in env=web (it runs in: worker, console).
|
|
88
|
+
|
|
89
|
+
"Skipping 0 schedules" on every boot of every app would be noise, and noise is how
|
|
90
|
+
the line that matters gets scrolled past.
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- **Named rate limiters honour `trustedProxies`.** `ThrottleMiddleware` resolves the
|
|
95
|
+
client address from the socket unless told how many proxies sit in front — that was
|
|
96
|
+
fixed, documented and covered by a doctor check. `RateLimiter`'s `.byIp()`,
|
|
97
|
+
`.byUser()` and `.byApiKey()` did not: they used a private resolver that read the
|
|
98
|
+
socket address and fell back to the **leftmost** `X-Forwarded-For` entry, with no
|
|
99
|
+
proxy count anywhere in it. A `keyResolver` replaces the middleware's own
|
|
100
|
+
proxy-aware path entirely, so the sibling API kept the bug the middleware had shed.
|
|
101
|
+
|
|
102
|
+
Two consequences, in opposite directions. Where there was no socket address a client
|
|
103
|
+
picked its own bucket by writing the header, which is the part of it the client
|
|
104
|
+
controls. Behind a real reverse proxy — where there _is_ a socket address — it was
|
|
105
|
+
the proxy's, so **every visitor shared one bucket**: the documented
|
|
106
|
+
`RateLimiter.for("login").limit(5).every(60)` was five attempts a minute across the
|
|
107
|
+
entire user base, and one attacker locked everybody out.
|
|
108
|
+
|
|
109
|
+
All three now resolve through the same proxy-aware function, and
|
|
110
|
+
`.trustedProxies(n)` is on the builder. The strategy is recorded and the resolver
|
|
111
|
+
built at the end, so `.byIp().trustedProxies(1)` and `.trustedProxies(1).byIp()`
|
|
112
|
+
mean the same thing.
|
|
113
|
+
|
|
114
|
+
- **`zt doctor` audits named limiters.** Its `trustedProxies` check exempted any
|
|
115
|
+
throttle with a custom `keyResolver` — right for one keyed on a user id, where no
|
|
116
|
+
proxy can affect the answer, and exactly wrong for `RateLimiter`'s built-ins, which
|
|
117
|
+
_are_ custom resolvers and do key on an address. The check that exists to catch this
|
|
118
|
+
skipped the whole API that still had it. The built-in resolvers mark themselves now;
|
|
119
|
+
an app's own `.by(fn)` stays exempt, which is the point of the exemption.
|
|
120
|
+
|
|
121
|
+
- **The build record is portable.** Its filename was hashed from the output
|
|
122
|
+
directory's _absolute_ path, so the same project built at `/home/me/app` and
|
|
123
|
+
unpacked at `/opt/app` wrote and looked for two different files. A record shipped
|
|
124
|
+
with a release matched nothing at the other end — which is why a server could not
|
|
125
|
+
say which files belonged to the current release — and moving a checkout silently
|
|
126
|
+
orphaned it. It is keyed on the path relative to the project now.
|
|
127
|
+
|
|
11
128
|
## [1.9.0] — 2026-08-29
|
|
12
129
|
|
|
13
130
|
### Added
|
package/api-surface.md
CHANGED
|
@@ -394,6 +394,7 @@ class LimiterDefinition = {
|
|
|
394
394
|
register: () => LimiterDefinition
|
|
395
395
|
toMiddleware: () => ThrottleMiddleware
|
|
396
396
|
toMiddlewareClass: () => new () => ThrottleMiddleware
|
|
397
|
+
trustedProxies: (count: number) => LimiterDefinition
|
|
397
398
|
}
|
|
398
399
|
|
|
399
400
|
class MarkdownBuilder = {
|
|
@@ -808,23 +809,23 @@ interface ContainerBindings = {
|
|
|
808
809
|
}
|
|
809
810
|
|
|
810
811
|
interface CookieOptions = {
|
|
811
|
-
domain?: string
|
|
812
|
-
httpOnly?: boolean
|
|
813
|
-
maxAge?: number
|
|
812
|
+
domain?: string | undefined
|
|
813
|
+
httpOnly?: boolean | undefined
|
|
814
|
+
maxAge?: number | undefined
|
|
814
815
|
name: string
|
|
815
|
-
path?: string
|
|
816
|
-
sameSite?: SameSite
|
|
817
|
-
secure?: boolean
|
|
816
|
+
path?: string | undefined
|
|
817
|
+
sameSite?: SameSite | undefined
|
|
818
|
+
secure?: boolean | undefined
|
|
818
819
|
value: string
|
|
819
820
|
}
|
|
820
821
|
|
|
821
822
|
interface CorsOptions = {
|
|
822
|
-
allowedHeaders?: string[]
|
|
823
|
-
credentials?: boolean
|
|
824
|
-
exposedHeaders?: string[]
|
|
825
|
-
maxAge?: number
|
|
826
|
-
methods?: string[]
|
|
827
|
-
origin?: string | string[] | ((origin: string) => boolean)
|
|
823
|
+
allowedHeaders?: string[] | undefined
|
|
824
|
+
credentials?: boolean | undefined
|
|
825
|
+
exposedHeaders?: string[] | undefined
|
|
826
|
+
maxAge?: number | undefined
|
|
827
|
+
methods?: string[] | undefined
|
|
828
|
+
origin?: string | string[] | ((origin: string) => boolean) | undefined
|
|
828
829
|
}
|
|
829
830
|
|
|
830
831
|
interface DevProcessDefinition = {
|
|
@@ -870,9 +871,9 @@ interface ErrorDiagnosis = {
|
|
|
870
871
|
}
|
|
871
872
|
|
|
872
873
|
interface GroupOptions = {
|
|
873
|
-
domain?: string
|
|
874
|
-
middleware?: string | string[] | MiddlewareClass | MiddlewareClass[]
|
|
875
|
-
prefix?: string
|
|
874
|
+
domain?: string | undefined
|
|
875
|
+
middleware?: string | string[] | MiddlewareClass | MiddlewareClass[] | undefined
|
|
876
|
+
prefix?: string | undefined
|
|
876
877
|
}
|
|
877
878
|
|
|
878
879
|
interface ModelClass = {
|
|
@@ -925,25 +926,25 @@ interface RouterMacros = {
|
|
|
925
926
|
}
|
|
926
927
|
|
|
927
928
|
interface SecureHeadersOptions = {
|
|
928
|
-
contentSecurityPolicy?: string
|
|
929
|
-
frameOptions?: false | 'DENY' | 'SAMEORIGIN'
|
|
930
|
-
hstsIncludeSubDomains?: boolean
|
|
931
|
-
hstsMaxAge?: number
|
|
932
|
-
hstsPreload?: boolean
|
|
933
|
-
permissionsPolicy?: string | false
|
|
934
|
-
referrerPolicy?: string
|
|
935
|
-
secure?: boolean
|
|
929
|
+
contentSecurityPolicy?: string | undefined
|
|
930
|
+
frameOptions?: false | 'DENY' | 'SAMEORIGIN' | undefined
|
|
931
|
+
hstsIncludeSubDomains?: boolean | undefined
|
|
932
|
+
hstsMaxAge?: number | undefined
|
|
933
|
+
hstsPreload?: boolean | undefined
|
|
934
|
+
permissionsPolicy?: string | false | undefined
|
|
935
|
+
referrerPolicy?: string | undefined
|
|
936
|
+
secure?: boolean | undefined
|
|
936
937
|
}
|
|
937
938
|
|
|
938
939
|
interface StartZerotalOptions = {
|
|
939
|
-
configDir?: string
|
|
940
|
+
configDir?: string | undefined
|
|
940
941
|
}
|
|
941
942
|
|
|
942
943
|
interface ThrottleOptions = {
|
|
943
|
-
keyResolver?: (ctx: HttpContext) => string
|
|
944
|
+
keyResolver?: ((ctx: HttpContext) => string) | undefined
|
|
944
945
|
maxAttempts: number
|
|
945
|
-
trustedProxies?: number
|
|
946
|
-
windowSeconds?: number
|
|
946
|
+
trustedProxies?: number | undefined
|
|
947
|
+
windowSeconds?: number | undefined
|
|
947
948
|
}
|
|
948
949
|
|
|
949
950
|
interface ViewRegistration = {
|
|
@@ -952,14 +953,14 @@ interface ViewRegistration = {
|
|
|
952
953
|
}
|
|
953
954
|
|
|
954
955
|
interface WebhookOptions = {
|
|
955
|
-
algorithm?: 'sha256' | 'sha1' | 'sha512'
|
|
956
|
-
format?: 'raw' | 'stripe'
|
|
957
|
-
header?: string
|
|
958
|
-
prefix?: string
|
|
956
|
+
algorithm?: 'sha256' | 'sha1' | 'sha512' | undefined
|
|
957
|
+
format?: 'raw' | 'stripe' | undefined
|
|
958
|
+
header?: string | undefined
|
|
959
|
+
prefix?: string | undefined
|
|
959
960
|
secret: string
|
|
960
|
-
timestampHeader?: string
|
|
961
|
-
timestampSeparator?: string
|
|
962
|
-
tolerance?: number
|
|
961
|
+
timestampHeader?: string | undefined
|
|
962
|
+
timestampSeparator?: string | undefined
|
|
963
|
+
tolerance?: number | undefined
|
|
963
964
|
}
|
|
964
965
|
|
|
965
966
|
interface WebSocketHandlers = {
|
|
@@ -998,6 +999,8 @@ type NextFn = () => Promise<Response | void>
|
|
|
998
999
|
|
|
999
1000
|
type ParamsOf = P extends `${string}:${infer Name}/${infer Rest}` ? { [K in Name]: RouteParamValue; } & ParamsOf<`/${Rest}`> : P extends `${string}:${infer Name}` ? { [K in Name]: RouteParamValue; } : P extends `${string}*${string}` ? { "*": RouteParamValue | readonly RouteParamValue[];} : Record<never, never>
|
|
1000
1001
|
|
|
1002
|
+
type Resolved = { [K in keyof T]-?: Exclude<T[K], undefined>; }
|
|
1003
|
+
|
|
1001
1004
|
type RouteArgs = [params?: RouteParamValues, query?: RouteQuery]
|
|
1002
1005
|
|
|
1003
1006
|
type RouteMiddleware = MiddlewareClass[] | RouteMethodMiddleware
|
|
@@ -1037,7 +1040,7 @@ function addToDefaultArrayExport = (source: string, identifier: string) => strin
|
|
|
1037
1040
|
function registerProvider = (options: RegisterProviderOptions) => Promise<RegisterResult>
|
|
1038
1041
|
|
|
1039
1042
|
interface RegisterProviderOptions = {
|
|
1040
|
-
bootstrapPath?: string
|
|
1043
|
+
bootstrapPath?: string | undefined
|
|
1041
1044
|
className: string
|
|
1042
1045
|
importPath: string
|
|
1043
1046
|
}
|
|
@@ -1304,6 +1307,34 @@ class AssetsBuildCommand = {
|
|
|
1304
1307
|
write: (msg: string) => void
|
|
1305
1308
|
}
|
|
1306
1309
|
|
|
1310
|
+
class AssetsPruneCommand = {
|
|
1311
|
+
new (): AssetsPruneCommand
|
|
1312
|
+
static args: ArgDef[]
|
|
1313
|
+
static commandName: string
|
|
1314
|
+
static description: string
|
|
1315
|
+
static flags: ({ name: string; type: 'string'; description: string; default?: never;} | { name: string; type: 'boolean'; description: string; default: boolean;})[]
|
|
1316
|
+
static needsApp: boolean
|
|
1317
|
+
_readLine: () => Promise<string>
|
|
1318
|
+
_writer: OutputWriter
|
|
1319
|
+
app: unknown
|
|
1320
|
+
args: Record<string, string>
|
|
1321
|
+
ask: (question: string, defaultValue?: string) => Promise<string>
|
|
1322
|
+
choice: (question: string, options: string[]) => Promise<string>
|
|
1323
|
+
confirm: (question: string, defaultValue?: boolean) => Promise<boolean>
|
|
1324
|
+
dim: (msg: string) => void
|
|
1325
|
+
error: (msg: string) => void
|
|
1326
|
+
flags: Record<string, string | number | boolean>
|
|
1327
|
+
info: (msg: string) => void
|
|
1328
|
+
line: (msg: string) => void
|
|
1329
|
+
newLine: () => void
|
|
1330
|
+
run: () => Promise<void>
|
|
1331
|
+
secret: (question: string) => Promise<string>
|
|
1332
|
+
section: (title: string) => void
|
|
1333
|
+
table: (rows: [string, string][], indent?: number) => void
|
|
1334
|
+
warn: (msg: string) => void
|
|
1335
|
+
write: (msg: string) => void
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1307
1338
|
class CompileCommand = {
|
|
1308
1339
|
new (): CompileCommand
|
|
1309
1340
|
static args: ArgDef[]
|
|
@@ -2191,7 +2222,7 @@ function DeployConfig = (options?: Partial<DeployConfigShape>) => DeployConfigSh
|
|
|
2191
2222
|
|
|
2192
2223
|
interface AppAssetsConfig = {
|
|
2193
2224
|
entrypoint: string | string[]
|
|
2194
|
-
loader?: Record<string, AssetLoaderKind>
|
|
2225
|
+
loader?: Record<string, AssetLoaderKind> | undefined
|
|
2195
2226
|
minify: boolean
|
|
2196
2227
|
outDir: string
|
|
2197
2228
|
prefix: string
|
|
@@ -2199,11 +2230,11 @@ interface AppAssetsConfig = {
|
|
|
2199
2230
|
|
|
2200
2231
|
interface AppConfigShape = {
|
|
2201
2232
|
allowedOrigins: string[]
|
|
2202
|
-
assets?: AppAssetsConfig
|
|
2233
|
+
assets?: AppAssetsConfig | undefined
|
|
2203
2234
|
conventions: ConventionsConfig
|
|
2204
2235
|
cors: AppCorsConfig
|
|
2205
2236
|
debug: boolean
|
|
2206
|
-
dev?: DevConfigShape
|
|
2237
|
+
dev?: DevConfigShape | undefined
|
|
2207
2238
|
env: string
|
|
2208
2239
|
health: boolean | HealthConfigShape
|
|
2209
2240
|
http3: boolean
|
|
@@ -2215,7 +2246,7 @@ interface AppConfigShape = {
|
|
|
2215
2246
|
secureHeaders: AppSecureHeadersConfig
|
|
2216
2247
|
throttle: AppThrottleConfig
|
|
2217
2248
|
timezone: string
|
|
2218
|
-
tls?: AppTlsConfig
|
|
2249
|
+
tls?: AppTlsConfig | undefined
|
|
2219
2250
|
url: string
|
|
2220
2251
|
}
|
|
2221
2252
|
|
|
@@ -2579,10 +2610,10 @@ interface HealthCheckReport = {
|
|
|
2579
2610
|
}
|
|
2580
2611
|
|
|
2581
2612
|
interface HealthConfigShape = {
|
|
2582
|
-
enabled?: boolean
|
|
2583
|
-
path?: string
|
|
2584
|
-
secret?: string
|
|
2585
|
-
showDetails?: boolean
|
|
2613
|
+
enabled?: boolean | undefined
|
|
2614
|
+
path?: string | undefined
|
|
2615
|
+
secret?: string | undefined
|
|
2616
|
+
showDetails?: boolean | undefined
|
|
2586
2617
|
}
|
|
2587
2618
|
|
|
2588
2619
|
interface HealthReport = {
|
|
@@ -2602,7 +2633,7 @@ interface HealthResult = {
|
|
|
2602
2633
|
interface ResolvedHealthConfig = {
|
|
2603
2634
|
enabled: boolean
|
|
2604
2635
|
path: string
|
|
2605
|
-
secret?: string
|
|
2636
|
+
secret?: string | undefined
|
|
2606
2637
|
showDetails: boolean
|
|
2607
2638
|
}
|
|
2608
2639
|
|
|
@@ -2620,6 +2651,8 @@ function data_get = (target: unknown, key: string, defaultValue?: unknown) => un
|
|
|
2620
2651
|
|
|
2621
2652
|
function deepMerge = <T extends object>(base: T, override: DeepPartial<T>) => T
|
|
2622
2653
|
|
|
2654
|
+
function definedOnly = <T extends object>(value: T) => { [K in keyof T]?: Exclude<T[K], undefined>; }
|
|
2655
|
+
|
|
2623
2656
|
function env = { (key: string): string | undefined; (key: string, fallback: string): string; (key: string, fallback: boolean): boolean; (key: string, fallback: number): number;}
|
|
2624
2657
|
|
|
2625
2658
|
function pipe = <T, R>(value: T, fn: (val: T) => R) => R
|
|
@@ -2644,6 +2677,8 @@ function tap = <T>(value: T, callback: (val: T) => void) => T
|
|
|
2644
2677
|
|
|
2645
2678
|
function tapAsync = <T>(value: T, callback: (val: T) => Promise<void>) => Promise<T>
|
|
2646
2679
|
|
|
2680
|
+
type Resolved = { [K in keyof T]-?: Exclude<T[K], undefined>; }
|
|
2681
|
+
|
|
2647
2682
|
## ./http `(./src/http/index.ts)`
|
|
2648
2683
|
|
|
2649
2684
|
class Http = {
|
|
@@ -2802,8 +2837,8 @@ interface CliContext = {
|
|
|
2802
2837
|
}
|
|
2803
2838
|
|
|
2804
2839
|
interface FileValidationOptions = {
|
|
2805
|
-
maxSize?: number
|
|
2806
|
-
mimes?: string[]
|
|
2840
|
+
maxSize?: number | undefined
|
|
2841
|
+
mimes?: string[] | undefined
|
|
2807
2842
|
}
|
|
2808
2843
|
|
|
2809
2844
|
interface NegotiateMap = {
|
|
@@ -2955,10 +2990,10 @@ class SqliteLockDriver = {
|
|
|
2955
2990
|
function LockConfig = (options?: Partial<LockConfigShape>) => LockConfigShape
|
|
2956
2991
|
|
|
2957
2992
|
interface BlockOptions = {
|
|
2958
|
-
refresh?: boolean
|
|
2959
|
-
refreshEvery?: number
|
|
2960
|
-
retryDelay?: number
|
|
2961
|
-
timeout?: number
|
|
2993
|
+
refresh?: boolean | undefined
|
|
2994
|
+
refreshEvery?: number | undefined
|
|
2995
|
+
retryDelay?: number | undefined
|
|
2996
|
+
timeout?: number | undefined
|
|
2962
2997
|
}
|
|
2963
2998
|
|
|
2964
2999
|
interface LockConfigShape = {
|
|
@@ -2977,8 +3012,8 @@ interface LockDriver = {
|
|
|
2977
3012
|
}
|
|
2978
3013
|
|
|
2979
3014
|
interface RefreshOptions = {
|
|
2980
|
-
refresh?: boolean
|
|
2981
|
-
refreshEvery?: number
|
|
3015
|
+
refresh?: boolean | undefined
|
|
3016
|
+
refreshEvery?: number | undefined
|
|
2982
3017
|
}
|
|
2983
3018
|
|
|
2984
3019
|
type LockedCallback = (lock: ManagedLock, signal: AbortSignal) => Promise<T> | T
|
|
@@ -3093,16 +3128,16 @@ interface LogEntry = {
|
|
|
3093
3128
|
}
|
|
3094
3129
|
|
|
3095
3130
|
interface LoggerOptions = {
|
|
3096
|
-
format?: 'text' | 'json'
|
|
3131
|
+
format?: 'text' | 'json' | undefined
|
|
3097
3132
|
}
|
|
3098
3133
|
|
|
3099
3134
|
interface LoggingConfigShape = {
|
|
3100
3135
|
channels: Record<string, ChannelConfig>
|
|
3101
|
-
console?: ConsoleSinkConfig
|
|
3136
|
+
console?: ConsoleSinkConfig | undefined
|
|
3102
3137
|
default: string
|
|
3103
|
-
file?: FileSinkConfig
|
|
3104
|
-
requests?: boolean
|
|
3105
|
-
slowQueryMs?: number
|
|
3138
|
+
file?: FileSinkConfig | undefined
|
|
3139
|
+
requests?: boolean | undefined
|
|
3140
|
+
slowQueryMs?: number | undefined
|
|
3106
3141
|
}
|
|
3107
3142
|
|
|
3108
3143
|
type ChannelConfig = { driver: 'console'; level?: LogLevel; format?: 'json' | 'pretty' } | { driver: 'single'; level?: LogLevel; path: string } | { driver: 'daily'; level?: LogLevel; path: string; days?: number } | { driver: 'stack'; level?: LogLevel; channels: string[] } | { driver: 'null' }
|
|
@@ -3164,6 +3199,38 @@ type RouteTable = Readonly<Record<string, string>> | ReadonlyMap<string, string>
|
|
|
3164
3199
|
|
|
3165
3200
|
type RouteTarget = string
|
|
3166
3201
|
|
|
3202
|
+
## ./runtime `(./src/support/runtime.ts)`
|
|
3203
|
+
|
|
3204
|
+
const RUNTIME_MISMATCH_ESCAPE = 'ZT_ALLOW_RUNTIME_MISMATCH'
|
|
3205
|
+
|
|
3206
|
+
function bunBinary = () => string
|
|
3207
|
+
|
|
3208
|
+
function declaredBunFloor = (cwd: string) => { range: string; manifest: string;} | null
|
|
3209
|
+
|
|
3210
|
+
function installedBunVersion = (cwd: string) => { version: string; manifest: string;} | null
|
|
3211
|
+
|
|
3212
|
+
function runtimeBelowFloor = (cwd?: string) => RuntimeFloor | null
|
|
3213
|
+
|
|
3214
|
+
function runtimeBelowFloorMessage = (floor: RuntimeFloor) => string
|
|
3215
|
+
|
|
3216
|
+
function runtimeMismatch = (cwd?: string) => RuntimeMismatch | null
|
|
3217
|
+
|
|
3218
|
+
function runtimeMismatchAllowed = () => boolean
|
|
3219
|
+
|
|
3220
|
+
function runtimeMismatchMessage = (mismatch: RuntimeMismatch) => string
|
|
3221
|
+
|
|
3222
|
+
interface RuntimeFloor = {
|
|
3223
|
+
manifest: string
|
|
3224
|
+
required: string
|
|
3225
|
+
running: string
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
interface RuntimeMismatch = {
|
|
3229
|
+
installed: string
|
|
3230
|
+
manifest: string
|
|
3231
|
+
running: string
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3167
3234
|
## ./security `(./src/security/index.ts)`
|
|
3168
3235
|
|
|
3169
3236
|
class CryptKeyMissingError = {
|
|
@@ -3194,7 +3261,7 @@ function redactGraph = (value: unknown, options: RedactGraphOptions) => unknown
|
|
|
3194
3261
|
|
|
3195
3262
|
interface RedactGraphOptions = {
|
|
3196
3263
|
circular: string
|
|
3197
|
-
flatten?: (value: unknown) => string | undefined
|
|
3264
|
+
flatten?: ((value: unknown) => string | undefined) | undefined
|
|
3198
3265
|
mask: string
|
|
3199
3266
|
maxDepth: number
|
|
3200
3267
|
sensitive: (key: string) => boolean
|
|
@@ -3224,27 +3291,27 @@ function snakeCase = (value: string) => string
|
|
|
3224
3291
|
function tableNameFor = (className: string) => string
|
|
3225
3292
|
|
|
3226
3293
|
interface DateOptions = {
|
|
3227
|
-
dateStyle?: 'medium' | 'short' | 'full' | 'long'
|
|
3228
|
-
locale?: string
|
|
3229
|
-
timeStyle?: 'medium' | 'short' | 'full' | 'long'
|
|
3230
|
-
timeZone?: string
|
|
3294
|
+
dateStyle?: 'medium' | 'short' | 'full' | 'long' | undefined
|
|
3295
|
+
locale?: string | undefined
|
|
3296
|
+
timeStyle?: 'medium' | 'short' | 'full' | 'long' | undefined
|
|
3297
|
+
timeZone?: string | undefined
|
|
3231
3298
|
}
|
|
3232
3299
|
|
|
3233
3300
|
interface FormatOptions = {
|
|
3234
|
-
locale?: string
|
|
3301
|
+
locale?: string | undefined
|
|
3235
3302
|
}
|
|
3236
3303
|
|
|
3237
3304
|
interface MoneyOptions = {
|
|
3238
3305
|
currency: string
|
|
3239
|
-
fractionDigits?: number
|
|
3240
|
-
locale?: string
|
|
3241
|
-
minorUnits?: boolean
|
|
3306
|
+
fractionDigits?: number | undefined
|
|
3307
|
+
locale?: string | undefined
|
|
3308
|
+
minorUnits?: boolean | undefined
|
|
3242
3309
|
}
|
|
3243
3310
|
|
|
3244
3311
|
interface NumberOptions = {
|
|
3245
|
-
locale?: string
|
|
3246
|
-
maximumFractionDigits?: number
|
|
3247
|
-
minimumFractionDigits?: number
|
|
3312
|
+
locale?: string | undefined
|
|
3313
|
+
maximumFractionDigits?: number | undefined
|
|
3314
|
+
minimumFractionDigits?: number | undefined
|
|
3248
3315
|
}
|
|
3249
3316
|
|
|
3250
3317
|
## ./storage `(./src/storage/index.ts)`
|
|
@@ -3411,10 +3478,10 @@ const Storage = StorageManager
|
|
|
3411
3478
|
function StorageConfig = (options?: Partial<StorageConfigShape>) => StorageConfigShape
|
|
3412
3479
|
|
|
3413
3480
|
interface DiskServeConfig = {
|
|
3414
|
-
expiresIn?: number
|
|
3415
|
-
headers?: Record<string, string>
|
|
3481
|
+
expiresIn?: number | undefined
|
|
3482
|
+
headers?: Record<string, string> | undefined
|
|
3416
3483
|
path: string
|
|
3417
|
-
signed?: boolean
|
|
3484
|
+
signed?: boolean | undefined
|
|
3418
3485
|
}
|
|
3419
3486
|
|
|
3420
3487
|
interface FakeStoredFile = {
|
|
@@ -3428,24 +3495,24 @@ interface FakeStoredFile = {
|
|
|
3428
3495
|
interface LocalDiskConfig = {
|
|
3429
3496
|
driver: 'local'
|
|
3430
3497
|
root: string
|
|
3431
|
-
serve?: DiskServeConfig
|
|
3432
|
-
url?: string
|
|
3498
|
+
serve?: DiskServeConfig | undefined
|
|
3499
|
+
url?: string | undefined
|
|
3433
3500
|
}
|
|
3434
3501
|
|
|
3435
3502
|
interface PutOptions = {
|
|
3436
|
-
contentType?: string
|
|
3437
|
-
visibility?: 'public' | 'private'
|
|
3503
|
+
contentType?: string | undefined
|
|
3504
|
+
visibility?: 'public' | 'private' | undefined
|
|
3438
3505
|
}
|
|
3439
3506
|
|
|
3440
3507
|
interface S3DiskConfig = {
|
|
3441
3508
|
bucket: string
|
|
3442
3509
|
driver: 's3'
|
|
3443
|
-
endpoint?: string
|
|
3510
|
+
endpoint?: string | undefined
|
|
3444
3511
|
key: string
|
|
3445
3512
|
region: string
|
|
3446
3513
|
secret: string
|
|
3447
|
-
serve?: DiskServeConfig
|
|
3448
|
-
url?: string
|
|
3514
|
+
serve?: DiskServeConfig | undefined
|
|
3515
|
+
url?: string | undefined
|
|
3449
3516
|
}
|
|
3450
3517
|
|
|
3451
3518
|
interface StorageConfigShape = {
|
|
@@ -3470,8 +3537,8 @@ interface StorageDriver = {
|
|
|
3470
3537
|
}
|
|
3471
3538
|
|
|
3472
3539
|
interface StorageFilesOptions = {
|
|
3473
|
-
mounts?: Mount[]
|
|
3474
|
-
storage?: StorageManager
|
|
3540
|
+
mounts?: Mount[] | undefined
|
|
3541
|
+
storage?: StorageManager | undefined
|
|
3475
3542
|
}
|
|
3476
3543
|
|
|
3477
3544
|
type DiskConfig = LocalDiskConfig | S3DiskConfig
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerotal/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"maturity": "stable",
|
|
6
6
|
"private": false,
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"./logger": "./src/logger/index.ts",
|
|
17
17
|
"./commands": "./src/command/builtin/index.ts",
|
|
18
18
|
"./helpers": "./src/helpers/index.ts",
|
|
19
|
+
"./runtime": "./src/support/runtime.ts",
|
|
19
20
|
"./shared": "./src/shared/index.ts",
|
|
20
21
|
"./facades": "./src/facade/facades/index.ts",
|
|
21
22
|
"./carbon": "./src/datetime/index.ts",
|
|
@@ -182,9 +182,9 @@ type PipeClass = new (...args: any[]) => Pipe<HttpContext>;
|
|
|
182
182
|
|
|
183
183
|
/** Options form for `Application.create({ ... })`. */
|
|
184
184
|
export interface CreateOptions {
|
|
185
|
-
providers?: ProviderClass[];
|
|
186
|
-
env?: Environment;
|
|
187
|
-
config?: ConfigLoader | ConfigMap;
|
|
185
|
+
providers?: ProviderClass[] | undefined;
|
|
186
|
+
env?: Environment | undefined;
|
|
187
|
+
config?: ConfigLoader | ConfigMap | undefined;
|
|
188
188
|
/**
|
|
189
189
|
* Drop {@link SecureHeadersMiddleware} from the front of the pipeline.
|
|
190
190
|
*
|
|
@@ -194,7 +194,7 @@ export interface CreateOptions {
|
|
|
194
194
|
* `config('app.secureHeaders')`; turn it off only when a proxy in front of the app is
|
|
195
195
|
* already emitting the same headers.
|
|
196
196
|
*/
|
|
197
|
-
secureHeaders?: false;
|
|
197
|
+
secureHeaders?: false | undefined;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
function _normaliseEnv(raw: string): Environment {
|
package/src/build/codemod.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface RegisterProviderOptions {
|
|
|
40
40
|
/** Import specifier, e.g. '../app/providers/FooProvider.ts' or '@zerotal/foo'. */
|
|
41
41
|
importPath: string;
|
|
42
42
|
/** Bootstrap file. Default: 'bootstrap/providers.ts'. */
|
|
43
|
-
bootstrapPath?: string;
|
|
43
|
+
bootstrapPath?: string | undefined;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -363,6 +363,7 @@ export class CommandRunner {
|
|
|
363
363
|
MakeProviderCommand,
|
|
364
364
|
CssBuildCommand,
|
|
365
365
|
AssetsBuildCommand,
|
|
366
|
+
AssetsPruneCommand,
|
|
366
367
|
LintPackagesCommand,
|
|
367
368
|
MakePackageCommand,
|
|
368
369
|
} = await import("./builtin/index.ts");
|
|
@@ -414,6 +415,7 @@ export class CommandRunner {
|
|
|
414
415
|
TestCommand,
|
|
415
416
|
CssBuildCommand,
|
|
416
417
|
AssetsBuildCommand,
|
|
418
|
+
AssetsPruneCommand,
|
|
417
419
|
LintPackagesCommand,
|
|
418
420
|
MakePackageCommand,
|
|
419
421
|
]);
|