@c9up/ream 0.1.25 → 0.1.27
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/README.md +65 -0
- package/dist/ConfigLoader.d.ts +46 -5
- package/dist/ConfigLoader.d.ts.map +1 -1
- package/dist/ConfigLoader.js +60 -9
- package/dist/ConfigLoader.js.map +1 -1
- package/dist/HttpKernel.d.ts.map +1 -1
- package/dist/HttpKernel.js +46 -6
- package/dist/HttpKernel.js.map +1 -1
- package/dist/Ignitor.d.ts +115 -7
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +254 -40
- package/dist/Ignitor.js.map +1 -1
- package/dist/console/BaseCommand.d.ts +217 -0
- package/dist/console/BaseCommand.d.ts.map +1 -0
- package/dist/console/BaseCommand.js +340 -0
- package/dist/console/BaseCommand.js.map +1 -0
- package/dist/console/Console.d.ts +86 -0
- package/dist/console/Console.d.ts.map +1 -0
- package/dist/console/Console.js +134 -0
- package/dist/console/Console.js.map +1 -0
- package/dist/console/ExceptionHandler.d.ts +28 -0
- package/dist/console/ExceptionHandler.d.ts.map +1 -0
- package/dist/console/ExceptionHandler.js +76 -0
- package/dist/console/ExceptionHandler.js.map +1 -0
- package/dist/console/HelpCommand.d.ts +16 -0
- package/dist/console/HelpCommand.d.ts.map +1 -0
- package/dist/console/HelpCommand.js +40 -0
- package/dist/console/HelpCommand.js.map +1 -0
- package/dist/console/IndexGenerator.d.ts +24 -0
- package/dist/console/IndexGenerator.d.ts.map +1 -0
- package/dist/console/IndexGenerator.js +65 -0
- package/dist/console/IndexGenerator.js.map +1 -0
- package/dist/console/Kernel.d.ts +298 -0
- package/dist/console/Kernel.d.ts.map +1 -0
- package/dist/console/Kernel.js +1120 -0
- package/dist/console/Kernel.js.map +1 -0
- package/dist/console/ListCommand.d.ts +18 -0
- package/dist/console/ListCommand.d.ts.map +1 -0
- package/dist/console/ListCommand.js +60 -0
- package/dist/console/ListCommand.js.map +1 -0
- package/dist/console/assertions.d.ts +20 -0
- package/dist/console/assertions.d.ts.map +1 -0
- package/dist/console/assertions.js +84 -0
- package/dist/console/assertions.js.map +1 -0
- package/dist/console/cliui.d.ts +248 -0
- package/dist/console/cliui.d.ts.map +1 -0
- package/dist/console/cliui.js +587 -0
- package/dist/console/cliui.js.map +1 -0
- package/dist/console/decorators.d.ts +90 -0
- package/dist/console/decorators.d.ts.map +1 -0
- package/dist/console/decorators.js +142 -0
- package/dist/console/decorators.js.map +1 -0
- package/dist/console/index.d.ts +32 -0
- package/dist/console/index.d.ts.map +1 -0
- package/dist/console/index.js +24 -0
- package/dist/console/index.js.map +1 -0
- package/dist/console/loaders.d.ts +52 -0
- package/dist/console/loaders.d.ts.map +1 -0
- package/dist/console/loaders.js +131 -0
- package/dist/console/loaders.js.map +1 -0
- package/dist/console/parser.d.ts +86 -0
- package/dist/console/parser.d.ts.map +1 -0
- package/dist/console/parser.js +440 -0
- package/dist/console/parser.js.map +1 -0
- package/dist/console/prompts.d.ts +124 -0
- package/dist/console/prompts.d.ts.map +1 -0
- package/dist/console/prompts.js +356 -0
- package/dist/console/prompts.js.map +1 -0
- package/dist/console/tracing.d.ts +17 -0
- package/dist/console/tracing.d.ts.map +1 -0
- package/dist/console/tracing.js +11 -0
- package/dist/console/tracing.js.map +1 -0
- package/dist/console/types.d.ts +255 -0
- package/dist/console/types.d.ts.map +1 -0
- package/dist/console/types.js +25 -0
- package/dist/console/types.js.map +1 -0
- package/dist/console/ui.d.ts +20 -0
- package/dist/console/ui.d.ts.map +1 -0
- package/dist/console/ui.js +31 -0
- package/dist/console/ui.js.map +1 -0
- package/dist/console/utils.d.ts +27 -0
- package/dist/console/utils.d.ts.map +1 -0
- package/dist/console/utils.js +56 -0
- package/dist/console/utils.js.map +1 -0
- package/dist/container/Container.d.ts +23 -3
- package/dist/container/Container.d.ts.map +1 -1
- package/dist/container/Container.js +177 -70
- package/dist/container/Container.js.map +1 -1
- package/dist/container/types.d.ts +14 -2
- package/dist/container/types.d.ts.map +1 -1
- package/dist/decorators/Service.d.ts +7 -0
- package/dist/decorators/Service.d.ts.map +1 -1
- package/dist/decorators/Service.js +23 -3
- package/dist/decorators/Service.js.map +1 -1
- package/dist/env/Env.d.ts +29 -4
- package/dist/env/Env.d.ts.map +1 -1
- package/dist/env/Env.js +27 -4
- package/dist/env/Env.js.map +1 -1
- package/dist/env/Secret.d.ts +11 -0
- package/dist/env/Secret.d.ts.map +1 -0
- package/dist/env/Secret.js +36 -0
- package/dist/env/Secret.js.map +1 -0
- package/dist/env/interpolate.d.ts +26 -0
- package/dist/env/interpolate.d.ts.map +1 -0
- package/dist/env/interpolate.js +48 -0
- package/dist/env/interpolate.js.map +1 -0
- package/dist/env/loadEnvFiles.d.ts.map +1 -1
- package/dist/env/loadEnvFiles.js +14 -4
- package/dist/env/loadEnvFiles.js.map +1 -1
- package/dist/env/schema.d.ts +23 -6
- package/dist/env/schema.d.ts.map +1 -1
- package/dist/env/schema.js +67 -33
- package/dist/env/schema.js.map +1 -1
- package/dist/errors/ReamError.d.ts +25 -0
- package/dist/errors/ReamError.d.ts.map +1 -1
- package/dist/errors/ReamError.js +5 -0
- package/dist/errors/ReamError.js.map +1 -1
- package/dist/errors/prettyPrintError.d.ts +9 -0
- package/dist/errors/prettyPrintError.d.ts.map +1 -0
- package/dist/errors/prettyPrintError.js +22 -0
- package/dist/errors/prettyPrintError.js.map +1 -0
- package/dist/events/Emitter.d.ts +1 -1
- package/dist/events/Emitter.d.ts.map +1 -1
- package/dist/events/Emitter.js +1 -1
- package/dist/events/Emitter.js.map +1 -1
- package/dist/events/EventsProvider.d.ts +2 -2
- package/dist/events/EventsProvider.d.ts.map +1 -1
- package/dist/events/EventsProvider.js +3 -3
- package/dist/events/EventsProvider.js.map +1 -1
- package/dist/graphql/GraphQLEngine.d.ts +1 -1
- package/dist/graphql/GraphQLEngine.d.ts.map +1 -1
- package/dist/graphql/GraphQLEngine.js +2 -2
- package/dist/graphql/GraphQLEngine.js.map +1 -1
- package/dist/graphql/GraphQLProvider.d.ts.map +1 -1
- package/dist/graphql/GraphQLProvider.js +7 -3
- package/dist/graphql/GraphQLProvider.js.map +1 -1
- package/dist/http/Exception.d.ts +63 -1
- package/dist/http/Exception.d.ts.map +1 -1
- package/dist/http/Exception.js +185 -48
- package/dist/http/Exception.js.map +1 -1
- package/dist/http/HttpContext.d.ts +53 -7
- package/dist/http/HttpContext.d.ts.map +1 -1
- package/dist/http/HttpContext.js +56 -15
- package/dist/http/HttpContext.js.map +1 -1
- package/dist/http/RedirectBuilder.d.ts.map +1 -1
- package/dist/http/RedirectBuilder.js +4 -1
- package/dist/http/RedirectBuilder.js.map +1 -1
- package/dist/http/Request.d.ts +141 -10
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +244 -34
- package/dist/http/Request.js.map +1 -1
- package/dist/http/Response.d.ts +176 -9
- package/dist/http/Response.d.ts.map +1 -1
- package/dist/http/Response.js +404 -16
- package/dist/http/Response.js.map +1 -1
- package/dist/index.d.ts +19 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/middleware/Pipeline.d.ts +29 -8
- package/dist/middleware/Pipeline.d.ts.map +1 -1
- package/dist/middleware/Pipeline.js +40 -2
- package/dist/middleware/Pipeline.js.map +1 -1
- package/dist/openapi/OpenApiProvider.js +4 -4
- package/dist/openapi/OpenApiProvider.js.map +1 -1
- package/dist/router/Router.d.ts +223 -31
- package/dist/router/Router.d.ts.map +1 -1
- package/dist/router/Router.js +520 -102
- package/dist/router/Router.js.map +1 -1
- package/dist/rpc/RpcProvider.d.ts +1 -0
- package/dist/rpc/RpcProvider.d.ts.map +1 -1
- package/dist/rpc/RpcProvider.js +7 -3
- package/dist/rpc/RpcProvider.js.map +1 -1
- package/dist/rpc/RpcRouter.d.ts +2 -2
- package/dist/rpc/RpcRouter.d.ts.map +1 -1
- package/dist/rpc/RpcRouter.js +17 -5
- package/dist/rpc/RpcRouter.js.map +1 -1
- package/dist/scheduler/ScheduleProvider.d.ts.map +1 -1
- package/dist/scheduler/ScheduleProvider.js +7 -15
- package/dist/scheduler/ScheduleProvider.js.map +1 -1
- package/dist/server/Server.d.ts +17 -4
- package/dist/server/Server.d.ts.map +1 -1
- package/dist/server/Server.js +41 -7
- package/dist/server/Server.js.map +1 -1
- package/dist/services/app.d.ts +1 -1
- package/dist/services/app.js +1 -1
- package/dist/services/console.d.ts +21 -0
- package/dist/services/console.d.ts.map +1 -0
- package/dist/services/console.js +35 -0
- package/dist/services/console.js.map +1 -0
- package/dist/session/SessionMiddleware.js +3 -3
- package/dist/session/SessionMiddleware.js.map +1 -1
- package/dist/storage/StaticProvider.js +1 -1
- package/dist/storage/StaticProvider.js.map +1 -1
- package/dist/testing/ApiResponse.d.ts +218 -0
- package/dist/testing/ApiResponse.d.ts.map +1 -0
- package/dist/testing/ApiResponse.js +576 -0
- package/dist/testing/ApiResponse.js.map +1 -0
- package/dist/testing/RequestBuilder.d.ts +155 -35
- package/dist/testing/RequestBuilder.d.ts.map +1 -1
- package/dist/testing/RequestBuilder.js +455 -109
- package/dist/testing/RequestBuilder.js.map +1 -1
- package/dist/testing/TestClient.d.ts +53 -37
- package/dist/testing/TestClient.d.ts.map +1 -1
- package/dist/testing/TestClient.js +84 -52
- package/dist/testing/TestClient.js.map +1 -1
- package/dist/utils/Macroable.d.ts +30 -0
- package/dist/utils/Macroable.d.ts.map +1 -0
- package/dist/utils/Macroable.js +43 -0
- package/dist/utils/Macroable.js.map +1 -0
- package/dist/utils/inflect.d.ts +17 -0
- package/dist/utils/inflect.d.ts.map +1 -0
- package/dist/utils/inflect.js +32 -0
- package/dist/utils/inflect.js.map +1 -0
- package/dist/utils/objectPath.d.ts +57 -0
- package/dist/utils/objectPath.d.ts.map +1 -0
- package/dist/utils/objectPath.js +141 -0
- package/dist/utils/objectPath.js.map +1 -0
- package/events.darwin-arm64.node +0 -0
- package/events.darwin-x64.node +0 -0
- package/events.linux-arm64-gnu.node +0 -0
- package/events.linux-x64-gnu.node +0 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +20 -2
- package/scheduler.darwin-arm64.node +0 -0
- package/scheduler.darwin-x64.node +0 -0
- package/scheduler.linux-arm64-gnu.node +0 -0
- package/scheduler.linux-x64-gnu.node +0 -0
- package/scheduler.win32-x64-msvc.node +0 -0
- package/src/ConfigLoader.ts +72 -9
- package/src/HttpKernel.ts +53 -15
- package/src/Ignitor.ts +364 -45
- package/src/console/BaseCommand.ts +467 -0
- package/src/console/Console.ts +174 -0
- package/src/console/ExceptionHandler.ts +91 -0
- package/src/console/HelpCommand.ts +37 -0
- package/src/console/IndexGenerator.ts +79 -0
- package/src/console/Kernel.ts +1382 -0
- package/src/console/ListCommand.ts +55 -0
- package/src/console/assertions.ts +114 -0
- package/src/console/cliui.ts +756 -0
- package/src/console/decorators.ts +199 -0
- package/src/console/index.ts +67 -0
- package/src/console/loaders.ts +145 -0
- package/src/console/parser.ts +598 -0
- package/src/console/prompts.ts +548 -0
- package/src/console/tracing.ts +22 -0
- package/src/console/types.ts +280 -0
- package/src/console/ui.ts +37 -0
- package/src/console/utils.ts +69 -0
- package/src/container/Container.ts +198 -72
- package/src/container/types.ts +15 -2
- package/src/decorators/Service.ts +28 -3
- package/src/env/Env.ts +49 -5
- package/src/env/Secret.ts +41 -0
- package/src/env/interpolate.ts +54 -0
- package/src/env/loadEnvFiles.ts +12 -4
- package/src/env/schema.ts +109 -35
- package/src/errors/ReamError.ts +31 -0
- package/src/errors/prettyPrintError.ts +20 -0
- package/src/events/Emitter.ts +2 -2
- package/src/events/EventsProvider.ts +5 -5
- package/src/graphql/GraphQLEngine.ts +4 -4
- package/src/graphql/GraphQLProvider.ts +7 -3
- package/src/http/Exception.ts +214 -37
- package/src/http/HttpContext.ts +87 -18
- package/src/http/RedirectBuilder.ts +4 -1
- package/src/http/Request.ts +264 -34
- package/src/http/Response.ts +478 -27
- package/src/index.ts +41 -6
- package/src/middleware/Pipeline.ts +77 -6
- package/src/openapi/OpenApiProvider.ts +4 -4
- package/src/router/Router.ts +631 -113
- package/src/rpc/RpcProvider.ts +6 -2
- package/src/rpc/RpcRouter.ts +22 -7
- package/src/scheduler/ScheduleProvider.ts +7 -15
- package/src/server/Server.ts +49 -10
- package/src/services/app.ts +1 -1
- package/src/services/console.ts +42 -0
- package/src/session/SessionMiddleware.ts +4 -4
- package/src/storage/StaticProvider.ts +1 -1
- package/src/testing/ApiResponse.ts +691 -0
- package/src/testing/RequestBuilder.ts +577 -137
- package/src/testing/TestClient.ts +110 -77
- package/src/utils/Macroable.ts +54 -0
- package/src/utils/inflect.ts +29 -0
- package/src/utils/objectPath.ts +142 -0
- package/dist/console/CommandRunner.d.ts +0 -18
- package/dist/console/CommandRunner.d.ts.map +0 -1
- package/dist/console/CommandRunner.js +0 -65
- package/dist/console/CommandRunner.js.map +0 -1
- package/dist/decorators/Lazy.d.ts +0 -19
- package/dist/decorators/Lazy.d.ts.map +0 -1
- package/dist/decorators/Lazy.js +0 -53
- package/dist/decorators/Lazy.js.map +0 -1
- package/src/console/CommandRunner.ts +0 -82
- package/src/decorators/Lazy.ts +0 -57
package/src/env/loadEnvFiles.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { parseEnv } from 'node:util'
|
|
4
|
+
import { interpolate } from './interpolate.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Load `.env` files into `process.env` — the shared primitive behind both the
|
|
@@ -31,10 +32,17 @@ export function loadEnvFiles(appRoot: URL, options: { skipEnvLocal?: boolean } =
|
|
|
31
32
|
} catch {
|
|
32
33
|
continue // file absent — nothing to load
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
35
|
+
const parsed = parseEnv(contents)
|
|
36
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
37
|
+
if (typeof value !== 'string' || process.env[key] !== undefined) continue
|
|
38
|
+
// Interpolate `$VAR`/`${VAR}` against process.env then this file's own
|
|
39
|
+
// (already-loaded) values, and apply any `identifier:` resolver.
|
|
40
|
+
process.env[key] = interpolate(value, (name) => {
|
|
41
|
+
const fromProcess = process.env[name]
|
|
42
|
+
if (fromProcess !== undefined) return fromProcess
|
|
43
|
+
const fromFile = parsed[name]
|
|
44
|
+
return typeof fromFile === 'string' ? fromFile : undefined
|
|
45
|
+
})
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
}
|
package/src/env/schema.ts
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* value as `undefined`.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { Secret } from './Secret.js'
|
|
17
|
+
|
|
16
18
|
/** A single failed-variable validation — collected by `Env.create`. */
|
|
17
19
|
export class EnvVarError extends Error {
|
|
18
20
|
constructor(message: string) {
|
|
@@ -26,14 +28,27 @@ export interface SchemaNode<T> {
|
|
|
26
28
|
validate(name: string, value: string | undefined): T
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
/**
|
|
31
|
+
/** Condition deciding whether a variable is optional (AdonisJS `optionalWhen`). */
|
|
32
|
+
export type OptionalCondition = boolean | ((name: string, value: string | undefined) => boolean)
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A required node that can be turned optional — `.optional()` widens `T` to
|
|
36
|
+
* `T | undefined`; `.optionalWhen(cond)` does so only when `cond` holds.
|
|
37
|
+
*/
|
|
30
38
|
export interface RequiredNode<T> extends SchemaNode<T> {
|
|
31
39
|
optional(): SchemaNode<T | undefined>
|
|
40
|
+
optionalWhen(condition: OptionalCondition): SchemaNode<T | undefined>
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
/** Options common to every schema function (AdonisJS `SchemaFnOptions`). */
|
|
44
|
+
export interface SchemaFnOptions {
|
|
45
|
+
/** Custom error message, replacing the default when validation fails. */
|
|
46
|
+
message?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface StringOptions extends SchemaFnOptions {
|
|
35
50
|
/** Validate the string against a known format. */
|
|
36
|
-
format?: 'host' | 'url' | 'email'
|
|
51
|
+
format?: 'host' | 'url' | 'email' | 'uuid'
|
|
37
52
|
/** For `format: 'url'` — require a TLD (default true). */
|
|
38
53
|
tld?: boolean
|
|
39
54
|
/** For `format: 'url'` — require a protocol (default true). */
|
|
@@ -45,24 +60,30 @@ const FALSE_VALUES = new Set(['false', '0'])
|
|
|
45
60
|
|
|
46
61
|
/**
|
|
47
62
|
* Build a node from a `parse` that only ever sees a present, non-empty string.
|
|
48
|
-
* Required `validate` throws on absent/empty
|
|
63
|
+
* Required `validate` throws on absent/empty (using `message` when given);
|
|
64
|
+
* `.optional()` returns `undefined`; `.optionalWhen(cond)` is optional only
|
|
65
|
+
* when the condition holds.
|
|
49
66
|
*/
|
|
50
|
-
function makeNode<T>(parse: (name: string, raw: string) => T): RequiredNode<T> {
|
|
67
|
+
function makeNode<T>(parse: (name: string, raw: string) => T, message?: string): RequiredNode<T> {
|
|
68
|
+
const required = (name: string, value: string | undefined): T => {
|
|
69
|
+
if (value === undefined || value === '') {
|
|
70
|
+
throw new EnvVarError(message ?? `Missing required environment variable "${name}"`)
|
|
71
|
+
}
|
|
72
|
+
return parse(name, value)
|
|
73
|
+
}
|
|
74
|
+
const optional = (name: string, value: string | undefined): T | undefined => {
|
|
75
|
+
if (value === undefined || value === '') return undefined
|
|
76
|
+
return parse(name, value)
|
|
77
|
+
}
|
|
51
78
|
return {
|
|
52
|
-
validate
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
validate(name, value) {
|
|
61
|
-
if (value === undefined || value === '') return undefined
|
|
62
|
-
return parse(name, value)
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
},
|
|
79
|
+
validate: required,
|
|
80
|
+
optional: () => ({ validate: optional }),
|
|
81
|
+
optionalWhen: (condition) => ({
|
|
82
|
+
validate(name, value) {
|
|
83
|
+
const skip = typeof condition === 'function' ? condition(name, value) : condition
|
|
84
|
+
return skip ? optional(name, value) : required(name, value)
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
66
87
|
}
|
|
67
88
|
}
|
|
68
89
|
|
|
@@ -93,6 +114,10 @@ function isEmail(value: string): boolean {
|
|
|
93
114
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
|
|
94
115
|
}
|
|
95
116
|
|
|
117
|
+
function isUuid(value: string): boolean {
|
|
118
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)
|
|
119
|
+
}
|
|
120
|
+
|
|
96
121
|
function checkFormat(name: string, value: string, options?: StringOptions): void {
|
|
97
122
|
if (!options?.format) return
|
|
98
123
|
const ok =
|
|
@@ -100,10 +125,13 @@ function checkFormat(name: string, value: string, options?: StringOptions): void
|
|
|
100
125
|
? isHost(value)
|
|
101
126
|
: options.format === 'url'
|
|
102
127
|
? isUrl(value, options)
|
|
103
|
-
:
|
|
128
|
+
: options.format === 'uuid'
|
|
129
|
+
? isUuid(value)
|
|
130
|
+
: isEmail(value)
|
|
104
131
|
if (!ok) {
|
|
105
132
|
throw new EnvVarError(
|
|
106
|
-
|
|
133
|
+
options.message ??
|
|
134
|
+
`Environment variable "${name}" must be a valid ${options.format}, got "${value}"`,
|
|
107
135
|
)
|
|
108
136
|
}
|
|
109
137
|
}
|
|
@@ -115,49 +143,95 @@ function stringParse(options?: StringOptions): (name: string, raw: string) => st
|
|
|
115
143
|
}
|
|
116
144
|
}
|
|
117
145
|
|
|
118
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* `string(opts?)` is callable AND exposes `.optional(opts?)` / `.optionalWhen()`
|
|
148
|
+
* shortcuts — all AdonisJS forms.
|
|
149
|
+
*/
|
|
119
150
|
const string: ((options?: StringOptions) => RequiredNode<string>) & {
|
|
120
151
|
optional(options?: StringOptions): SchemaNode<string | undefined>
|
|
121
|
-
|
|
122
|
-
|
|
152
|
+
optionalWhen(
|
|
153
|
+
condition: OptionalCondition,
|
|
154
|
+
options?: StringOptions,
|
|
155
|
+
): SchemaNode<string | undefined>
|
|
156
|
+
} = Object.assign((options?: StringOptions) => makeNode(stringParse(options), options?.message), {
|
|
157
|
+
optional: (options?: StringOptions) =>
|
|
158
|
+
makeNode(stringParse(options), options?.message).optional(),
|
|
159
|
+
optionalWhen: (condition: OptionalCondition, options?: StringOptions) =>
|
|
160
|
+
makeNode(stringParse(options), options?.message).optionalWhen(condition),
|
|
123
161
|
})
|
|
124
162
|
|
|
125
|
-
function
|
|
126
|
-
return
|
|
163
|
+
function numberParse(options?: SchemaFnOptions): (name: string, raw: string) => number {
|
|
164
|
+
return (name, raw) => {
|
|
127
165
|
const parsed = Number(raw)
|
|
128
166
|
if (Number.isNaN(parsed)) {
|
|
129
|
-
throw new EnvVarError(
|
|
167
|
+
throw new EnvVarError(
|
|
168
|
+
options?.message ?? `Environment variable "${name}" must be a number, got "${raw}"`,
|
|
169
|
+
)
|
|
130
170
|
}
|
|
131
171
|
return parsed
|
|
132
|
-
}
|
|
172
|
+
}
|
|
133
173
|
}
|
|
134
174
|
|
|
135
|
-
function
|
|
136
|
-
return makeNode((
|
|
175
|
+
function number(options?: SchemaFnOptions): RequiredNode<number> {
|
|
176
|
+
return makeNode(numberParse(options), options?.message)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function booleanParse(options?: SchemaFnOptions): (name: string, raw: string) => boolean {
|
|
180
|
+
return (name, raw) => {
|
|
137
181
|
if (TRUE_VALUES.has(raw)) return true
|
|
138
182
|
if (FALSE_VALUES.has(raw)) return false
|
|
139
183
|
throw new EnvVarError(
|
|
140
|
-
|
|
184
|
+
options?.message ??
|
|
185
|
+
`Environment variable "${name}" must be a boolean (true/false/1/0), got "${raw}"`,
|
|
141
186
|
)
|
|
142
|
-
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function boolean(options?: SchemaFnOptions): RequiredNode<boolean> {
|
|
191
|
+
return makeNode(booleanParse(options), options?.message)
|
|
143
192
|
}
|
|
144
193
|
|
|
145
|
-
function enumNode<V extends readonly string[]>(
|
|
194
|
+
function enumNode<V extends readonly string[]>(
|
|
195
|
+
values: V,
|
|
196
|
+
options?: SchemaFnOptions,
|
|
197
|
+
): RequiredNode<V[number]> {
|
|
146
198
|
return makeNode((name, raw) => {
|
|
147
199
|
const match = values.find((candidate) => candidate === raw)
|
|
148
200
|
if (match === undefined) {
|
|
149
201
|
throw new EnvVarError(
|
|
150
|
-
|
|
202
|
+
options?.message ??
|
|
203
|
+
`Environment variable "${name}" must be one of [${values.join(', ')}], got "${raw}"`,
|
|
151
204
|
)
|
|
152
205
|
}
|
|
153
206
|
return match
|
|
154
|
-
})
|
|
207
|
+
}, options?.message)
|
|
155
208
|
}
|
|
156
209
|
|
|
210
|
+
/**
|
|
211
|
+
* `secret(opts?)` wraps the value in a {@link Secret} so it never leaks through
|
|
212
|
+
* logs/JSON (AdonisJS `Env.schema.secret`). Read it with `.release()`.
|
|
213
|
+
*/
|
|
214
|
+
const secret: ((options?: SchemaFnOptions) => RequiredNode<Secret<string>>) & {
|
|
215
|
+
optional(options?: SchemaFnOptions): SchemaNode<Secret<string> | undefined>
|
|
216
|
+
optionalWhen(
|
|
217
|
+
condition: OptionalCondition,
|
|
218
|
+
options?: SchemaFnOptions,
|
|
219
|
+
): SchemaNode<Secret<string> | undefined>
|
|
220
|
+
} = Object.assign(
|
|
221
|
+
(options?: SchemaFnOptions) => makeNode((_name, raw) => new Secret(raw), options?.message),
|
|
222
|
+
{
|
|
223
|
+
optional: (options?: SchemaFnOptions) =>
|
|
224
|
+
makeNode((_name, raw) => new Secret(raw), options?.message).optional(),
|
|
225
|
+
optionalWhen: (condition: OptionalCondition, options?: SchemaFnOptions) =>
|
|
226
|
+
makeNode((_name, raw) => new Secret(raw), options?.message).optionalWhen(condition),
|
|
227
|
+
},
|
|
228
|
+
)
|
|
229
|
+
|
|
157
230
|
/** The `Env.schema` surface. */
|
|
158
231
|
export const schema = {
|
|
159
232
|
string,
|
|
160
233
|
number,
|
|
161
234
|
boolean,
|
|
162
235
|
enum: enumNode,
|
|
236
|
+
secret,
|
|
163
237
|
}
|
package/src/errors/ReamError.ts
CHANGED
|
@@ -7,6 +7,37 @@
|
|
|
7
7
|
* @implements FR71, FR72, FR73, FR77
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Is this a Ream error, and one of these codes?
|
|
12
|
+
*
|
|
13
|
+
* The framework carries ONE error type with a `code`, because an error can be
|
|
14
|
+
* raised in Rust and rebuilt in TypeScript across the NAPI boundary — see
|
|
15
|
+
* {@link ReamError.fromNapi}. A class per code cannot cross that line, so the
|
|
16
|
+
* check that replaces `instanceof MyError` is this one.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* try {
|
|
21
|
+
* await consoleApp.exec('provision')
|
|
22
|
+
* } catch (error) {
|
|
23
|
+
* if (isReamError(error, 'E_CONSOLE_MISSING_FLAG', 'E_CONSOLE_MISSING_ARGUMENT')) {
|
|
24
|
+
* console.error(error.hint) // narrowed: `error` IS a ReamError here
|
|
25
|
+
* return
|
|
26
|
+
* }
|
|
27
|
+
* throw error
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function isReamError(error: unknown): error is ReamError
|
|
32
|
+
export function isReamError<const Code extends string>(
|
|
33
|
+
error: unknown,
|
|
34
|
+
...codes: [Code, ...Code[]]
|
|
35
|
+
): error is ReamError & { code: Code }
|
|
36
|
+
export function isReamError(error: unknown, ...codes: string[]): boolean {
|
|
37
|
+
if (!(error instanceof ReamError)) return false
|
|
38
|
+
return codes.length === 0 || codes.includes(error.code)
|
|
39
|
+
}
|
|
40
|
+
|
|
10
41
|
export class ReamError extends Error {
|
|
11
42
|
/** Error code (e.g., "ATLAS_QUERY_ERROR", "CONTAINER_NOT_FOUND") */
|
|
12
43
|
readonly code: string
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render an error for a human reading a terminal (AdonisJS `prettyPrintError`).
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a helper inside `Ignitor.ts`: the console kernel
|
|
5
|
+
* renders the failures of the command it ran, and reaching into the Ignitor for
|
|
6
|
+
* that would make the two import each other.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ReamError } from './ReamError.js'
|
|
10
|
+
|
|
11
|
+
export function prettyPrintError(error: unknown): void {
|
|
12
|
+
if (error instanceof ReamError) {
|
|
13
|
+
console.error(error.toDevString())
|
|
14
|
+
} else if (error instanceof Error) {
|
|
15
|
+
console.error(`\n ${error.message}\n`)
|
|
16
|
+
if (error.stack) console.error(error.stack)
|
|
17
|
+
} else {
|
|
18
|
+
console.error(error)
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/events/Emitter.ts
CHANGED
|
@@ -35,7 +35,7 @@ type Listener<T = unknown> = ListenerFn<T> | ListenerConstructor<T>
|
|
|
35
35
|
|
|
36
36
|
/** Resolver for instantiating listener classes with DI. */
|
|
37
37
|
export interface ContainerResolver {
|
|
38
|
-
make<T>(target: new (...args: never[]) => T): T
|
|
38
|
+
make<T>(target: new (...args: never[]) => T): Promise<T>
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export class Emitter {
|
|
@@ -135,7 +135,7 @@ export class Emitter {
|
|
|
135
135
|
if (isListenerClass(listener)) {
|
|
136
136
|
// Listener class — resolve via container for @inject() support
|
|
137
137
|
const instance = this.resolver
|
|
138
|
-
? this.resolver.make(listener as ListenerConstructor<T>)
|
|
138
|
+
? await this.resolver.make(listener as ListenerConstructor<T>)
|
|
139
139
|
: new (listener as ListenerConstructor<T>)()
|
|
140
140
|
await instance.handle(event)
|
|
141
141
|
} else {
|
|
@@ -15,8 +15,8 @@ import { setEmitter } from './services/main.js'
|
|
|
15
15
|
*/
|
|
16
16
|
interface EventsContainer {
|
|
17
17
|
singleton(token: unknown, factory: () => unknown): void
|
|
18
|
-
resolve<T = unknown>(token: unknown): T
|
|
19
|
-
make?<T>(target: new (...args: never[]) => T): T
|
|
18
|
+
resolve<T = unknown>(token: unknown): Promise<T>
|
|
19
|
+
make?<T>(target: new (...args: never[]) => T): Promise<T>
|
|
20
20
|
}
|
|
21
21
|
export interface EventsAppContext {
|
|
22
22
|
container: EventsContainer
|
|
@@ -31,8 +31,8 @@ export default class EventsProvider {
|
|
|
31
31
|
this.app.container.singleton(EventBus, () => new EventBus())
|
|
32
32
|
this.app.container.singleton('bus', () => this.app.container.resolve<EventBus>(EventBus))
|
|
33
33
|
|
|
34
|
-
this.app.container.singleton(Emitter, () => {
|
|
35
|
-
const bus = this.app.container.resolve<EventBus>(EventBus)
|
|
34
|
+
this.app.container.singleton(Emitter, async () => {
|
|
35
|
+
const bus = await this.app.container.resolve<EventBus>(EventBus)
|
|
36
36
|
// Forward `make` only when the host actually exposes it.
|
|
37
37
|
// Emitter falls back to direct `new Listener()` instantiation
|
|
38
38
|
// otherwise, which keeps zero-arg listener classes working
|
|
@@ -49,7 +49,7 @@ export default class EventsProvider {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async boot() {
|
|
52
|
-
this.#emitter = this.app.container.resolve<Emitter>(Emitter)
|
|
52
|
+
this.#emitter = await this.app.container.resolve<Emitter>(Emitter)
|
|
53
53
|
BaseEvent.useEmitter(this.#emitter)
|
|
54
54
|
setEmitter(this.#emitter)
|
|
55
55
|
}
|
|
@@ -203,7 +203,7 @@ export interface SelectionField {
|
|
|
203
203
|
export class GraphQLEngine {
|
|
204
204
|
#schemaSource: string
|
|
205
205
|
#resolvers: Map<string, ResolverEntry> = new Map()
|
|
206
|
-
#container?: { make<T>(target: new (...args: unknown[]) => T): T }
|
|
206
|
+
#container?: { make<T>(target: new (...args: unknown[]) => T): Promise<T> }
|
|
207
207
|
readonly path: string
|
|
208
208
|
#playground: boolean
|
|
209
209
|
#maxQueryBytes: number
|
|
@@ -238,7 +238,7 @@ export class GraphQLEngine {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/** Set IoC container for resolver instantiation. */
|
|
241
|
-
useContainer(container: { make<T>(target: new (...args: unknown[]) => T): T }): void {
|
|
241
|
+
useContainer(container: { make<T>(target: new (...args: unknown[]) => T): Promise<T> }): void {
|
|
242
242
|
this.#container = container
|
|
243
243
|
}
|
|
244
244
|
|
|
@@ -369,7 +369,7 @@ export class GraphQLEngine {
|
|
|
369
369
|
try {
|
|
370
370
|
// Resolve handler via IoC or direct instantiation.
|
|
371
371
|
const instance = this.#container
|
|
372
|
-
? this.#container.make(entry.handlerClass)
|
|
372
|
+
? await this.#container.make(entry.handlerClass)
|
|
373
373
|
: new entry.handlerClass()
|
|
374
374
|
|
|
375
375
|
const handler = instance[entry.methodName]
|
|
@@ -436,7 +436,7 @@ export class GraphQLEngine {
|
|
|
436
436
|
const permissions = options.permissions ?? []
|
|
437
437
|
|
|
438
438
|
const needsAuth = guards.length > 0 || roles.length > 0 || permissions.length > 0
|
|
439
|
-
if (needsAuth && !ctx.auth?.
|
|
439
|
+
if (needsAuth && !ctx.auth?.isAuthenticated) {
|
|
440
440
|
return 'Unauthorized'
|
|
441
441
|
}
|
|
442
442
|
|
|
@@ -28,6 +28,7 @@ export interface GraphQLProviderOptions {
|
|
|
28
28
|
|
|
29
29
|
export class GraphQLProvider extends Provider {
|
|
30
30
|
#engine?: GraphQLEngine
|
|
31
|
+
#registered = false
|
|
31
32
|
|
|
32
33
|
constructor(app: AppContext, options: GraphQLProviderOptions = {}) {
|
|
33
34
|
super(app)
|
|
@@ -45,9 +46,11 @@ export class GraphQLProvider extends Provider {
|
|
|
45
46
|
if (!engine) return // GraphQL not configured — opt-out.
|
|
46
47
|
const container = this.app.container
|
|
47
48
|
if (container.has('graphql')) {
|
|
48
|
-
|
|
49
|
+
// Idempotent re-register: tracked with a flag rather than
|
|
50
|
+
// `container.resolve('graphql')` because resolution is now async.
|
|
51
|
+
if (this.#registered) {
|
|
49
52
|
this.#engine = engine
|
|
50
|
-
return
|
|
53
|
+
return
|
|
51
54
|
}
|
|
52
55
|
throw new ReamError(
|
|
53
56
|
'GRAPHQL_PROVIDER_ALREADY_REGISTERED',
|
|
@@ -60,12 +63,13 @@ export class GraphQLProvider extends Provider {
|
|
|
60
63
|
engine.useContainer(container)
|
|
61
64
|
this.#engine = engine
|
|
62
65
|
container.singleton('graphql', () => engine)
|
|
66
|
+
this.#registered = true
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
override async boot(): Promise<void> {
|
|
66
70
|
const engine = this.#engine
|
|
67
71
|
if (!engine) return
|
|
68
|
-
const router = this.app.container.make<Router>('router')
|
|
72
|
+
const router = await this.app.container.make<Router>('router')
|
|
69
73
|
// GraphQLEngine.handle() serves the playground on GET and executes queries
|
|
70
74
|
// on POST, so mount the same path for both verbs.
|
|
71
75
|
router.get(engine.path, (ctx) => engine.handle(ctx))
|