@beignet/core 0.0.1
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 +5 -0
- package/README.md +288 -0
- package/dist/application/index.d.ts +260 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +324 -0
- package/dist/application/index.js.map +1 -0
- package/dist/client/client.d.ts +241 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +531 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +10 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +139 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config/index.d.ts +122 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +216 -0
- package/dist/config/index.js.map +1 -0
- package/dist/contracts/contract-builder.d.ts +121 -0
- package/dist/contracts/contract-builder.d.ts.map +1 -0
- package/dist/contracts/contract-builder.js +346 -0
- package/dist/contracts/contract-builder.js.map +1 -0
- package/dist/contracts/contract-group.d.ts +106 -0
- package/dist/contracts/contract-group.d.ts.map +1 -0
- package/dist/contracts/contract-group.js +240 -0
- package/dist/contracts/contract-group.js.map +1 -0
- package/dist/contracts/contract-like.d.ts +21 -0
- package/dist/contracts/contract-like.d.ts.map +1 -0
- package/dist/contracts/contract-like.js +9 -0
- package/dist/contracts/contract-like.js.map +1 -0
- package/dist/contracts/index.d.ts +15 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +11 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/openapi-meta.d.ts +23 -0
- package/dist/contracts/openapi-meta.d.ts.map +1 -0
- package/dist/contracts/openapi-meta.js +2 -0
- package/dist/contracts/openapi-meta.js.map +1 -0
- package/dist/contracts/path-template.d.ts +17 -0
- package/dist/contracts/path-template.d.ts.map +1 -0
- package/dist/contracts/path-template.js +50 -0
- package/dist/contracts/path-template.js.map +1 -0
- package/dist/contracts/rate-limit.d.ts +50 -0
- package/dist/contracts/rate-limit.d.ts.map +1 -0
- package/dist/contracts/rate-limit.js +2 -0
- package/dist/contracts/rate-limit.js.map +1 -0
- package/dist/contracts/types.d.ts +97 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +54 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/contracts/utils.d.ts +3 -0
- package/dist/contracts/utils.d.ts.map +1 -0
- package/dist/contracts/utils.js +44 -0
- package/dist/contracts/utils.js.map +1 -0
- package/dist/domain/entity.d.ts +87 -0
- package/dist/domain/entity.d.ts.map +1 -0
- package/dist/domain/entity.js +155 -0
- package/dist/domain/entity.js.map +1 -0
- package/dist/domain/events.d.ts +41 -0
- package/dist/domain/events.d.ts.map +1 -0
- package/dist/domain/events.js +21 -0
- package/dist/domain/events.js.map +1 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +14 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/value-object.d.ts +60 -0
- package/dist/domain/value-object.d.ts.map +1 -0
- package/dist/domain/value-object.js +87 -0
- package/dist/domain/value-object.js.map +1 -0
- package/dist/errors/catalog.d.ts +71 -0
- package/dist/errors/catalog.d.ts.map +1 -0
- package/dist/errors/catalog.js +71 -0
- package/dist/errors/catalog.js.map +1 -0
- package/dist/errors/http.d.ts +77 -0
- package/dist/errors/http.d.ts.map +1 -0
- package/dist/errors/http.js +74 -0
- package/dist/errors/http.js.map +1 -0
- package/dist/errors/index.d.ts +10 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +14 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/response.d.ts +26 -0
- package/dist/errors/response.d.ts.map +1 -0
- package/dist/errors/response.js +34 -0
- package/dist/errors/response.js.map +1 -0
- package/dist/errors/validation.d.ts +18 -0
- package/dist/errors/validation.d.ts.map +1 -0
- package/dist/errors/validation.js +21 -0
- package/dist/errors/validation.js.map +1 -0
- package/dist/events/index.d.ts +58 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +102 -0
- package/dist/events/index.js.map +1 -0
- package/dist/jobs/index.d.ts +56 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +89 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/mail/index.d.ts +75 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +84 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/openapi/index.d.ts +207 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +449 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/schema-introspector.d.ts +38 -0
- package/dist/openapi/schema-introspector.d.ts.map +1 -0
- package/dist/openapi/schema-introspector.js +67 -0
- package/dist/openapi/schema-introspector.js.map +1 -0
- package/dist/ports/audit.d.ts +58 -0
- package/dist/ports/audit.d.ts.map +1 -0
- package/dist/ports/audit.js +74 -0
- package/dist/ports/audit.js.map +1 -0
- package/dist/ports/auth.d.ts +23 -0
- package/dist/ports/auth.d.ts.map +1 -0
- package/dist/ports/auth.js +31 -0
- package/dist/ports/auth.js.map +1 -0
- package/dist/ports/builder.d.ts +61 -0
- package/dist/ports/builder.d.ts.map +1 -0
- package/dist/ports/builder.js +48 -0
- package/dist/ports/builder.js.map +1 -0
- package/dist/ports/cache.d.ts +15 -0
- package/dist/ports/cache.d.ts.map +1 -0
- package/dist/ports/cache.js +57 -0
- package/dist/ports/cache.js.map +1 -0
- package/dist/ports/clock.d.ts +10 -0
- package/dist/ports/clock.d.ts.map +1 -0
- package/dist/ports/clock.js +21 -0
- package/dist/ports/clock.js.map +1 -0
- package/dist/ports/events.d.ts +71 -0
- package/dist/ports/events.d.ts.map +1 -0
- package/dist/ports/events.js +2 -0
- package/dist/ports/events.js.map +1 -0
- package/dist/ports/id-generator.d.ts +12 -0
- package/dist/ports/id-generator.d.ts.map +1 -0
- package/dist/ports/id-generator.js +22 -0
- package/dist/ports/id-generator.js.map +1 -0
- package/dist/ports/index.d.ts +98 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +67 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/ports/logger.d.ts +22 -0
- package/dist/ports/logger.d.ts.map +1 -0
- package/dist/ports/logger.js +34 -0
- package/dist/ports/logger.js.map +1 -0
- package/dist/ports/mailer.d.ts +6 -0
- package/dist/ports/mailer.d.ts.map +1 -0
- package/dist/ports/mailer.js +2 -0
- package/dist/ports/mailer.js.map +1 -0
- package/dist/ports/policy.d.ts +53 -0
- package/dist/ports/policy.d.ts.map +1 -0
- package/dist/ports/policy.js +81 -0
- package/dist/ports/policy.js.map +1 -0
- package/dist/ports/rate-limit.d.ts +41 -0
- package/dist/ports/rate-limit.d.ts.map +1 -0
- package/dist/ports/rate-limit.js +37 -0
- package/dist/ports/rate-limit.js.map +1 -0
- package/dist/ports/redaction.d.ts +26 -0
- package/dist/ports/redaction.d.ts.map +1 -0
- package/dist/ports/redaction.js +126 -0
- package/dist/ports/redaction.js.map +1 -0
- package/dist/ports/schedules.d.ts +9 -0
- package/dist/ports/schedules.d.ts.map +1 -0
- package/dist/ports/schedules.js +2 -0
- package/dist/ports/schedules.js.map +1 -0
- package/dist/ports/storage.d.ts +47 -0
- package/dist/ports/storage.d.ts.map +1 -0
- package/dist/ports/storage.js +185 -0
- package/dist/ports/storage.js.map +1 -0
- package/dist/ports/testing.d.ts +73 -0
- package/dist/ports/testing.d.ts.map +1 -0
- package/dist/ports/testing.js +105 -0
- package/dist/ports/testing.js.map +1 -0
- package/dist/ports/unit-of-work.d.ts +56 -0
- package/dist/ports/unit-of-work.d.ts.map +1 -0
- package/dist/ports/unit-of-work.js +64 -0
- package/dist/ports/unit-of-work.js.map +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/instrumentation.d.ts +91 -0
- package/dist/providers/instrumentation.d.ts.map +1 -0
- package/dist/providers/instrumentation.js +93 -0
- package/dist/providers/instrumentation.js.map +1 -0
- package/dist/providers/provider.d.ts +146 -0
- package/dist/providers/provider.d.ts.map +1 -0
- package/dist/providers/provider.js +31 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/schedules/index.d.ts +105 -0
- package/dist/schedules/index.d.ts.map +1 -0
- package/dist/schedules/index.js +178 -0
- package/dist/schedules/index.js.map +1 -0
- package/dist/server/contract-like.d.ts +5 -0
- package/dist/server/contract-like.d.ts.map +1 -0
- package/dist/server/contract-like.js +5 -0
- package/dist/server/contract-like.js.map +1 -0
- package/dist/server/health.d.ts +41 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +46 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/hooks/auth.d.ts +42 -0
- package/dist/server/hooks/auth.d.ts.map +1 -0
- package/dist/server/hooks/auth.js +61 -0
- package/dist/server/hooks/auth.js.map +1 -0
- package/dist/server/hooks/cors.d.ts +13 -0
- package/dist/server/hooks/cors.d.ts.map +1 -0
- package/dist/server/hooks/cors.js +70 -0
- package/dist/server/hooks/cors.js.map +1 -0
- package/dist/server/hooks/errors.d.ts +66 -0
- package/dist/server/hooks/errors.d.ts.map +1 -0
- package/dist/server/hooks/errors.js +83 -0
- package/dist/server/hooks/errors.js.map +1 -0
- package/dist/server/hooks/index.d.ts +12 -0
- package/dist/server/hooks/index.d.ts.map +1 -0
- package/dist/server/hooks/index.js +12 -0
- package/dist/server/hooks/index.js.map +1 -0
- package/dist/server/hooks/logging.d.ts +33 -0
- package/dist/server/hooks/logging.d.ts.map +1 -0
- package/dist/server/hooks/logging.js +90 -0
- package/dist/server/hooks/logging.js.map +1 -0
- package/dist/server/hooks/rate-limit.d.ts +29 -0
- package/dist/server/hooks/rate-limit.d.ts.map +1 -0
- package/dist/server/hooks/rate-limit.js +93 -0
- package/dist/server/hooks/rate-limit.js.map +1 -0
- package/dist/server/hooks/utils.d.ts +9 -0
- package/dist/server/hooks/utils.d.ts.map +1 -0
- package/dist/server/hooks/utils.js +16 -0
- package/dist/server/hooks/utils.js.map +1 -0
- package/dist/server/hooks.d.ts +2 -0
- package/dist/server/hooks.d.ts.map +1 -0
- package/dist/server/hooks.js +2 -0
- package/dist/server/hooks.js.map +1 -0
- package/dist/server/http.d.ts +124 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +2 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +19 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/openapi.d.ts +32 -0
- package/dist/server/openapi.d.ts.map +1 -0
- package/dist/server/openapi.js +43 -0
- package/dist/server/openapi.js.map +1 -0
- package/dist/server/providers/index.d.ts +4 -0
- package/dist/server/providers/index.d.ts.map +1 -0
- package/dist/server/providers/index.js +4 -0
- package/dist/server/providers/index.js.map +1 -0
- package/dist/server/providers/loadProviderConfig.d.ts +7 -0
- package/dist/server/providers/loadProviderConfig.d.ts.map +1 -0
- package/dist/server/providers/loadProviderConfig.js +42 -0
- package/dist/server/providers/loadProviderConfig.js.map +1 -0
- package/dist/server/server.d.ts +86 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +1031 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/types.d.ts +3 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +3 -0
- package/dist/server/types.js.map +1 -0
- package/package.json +129 -0
- package/src/application/index.ts +747 -0
- package/src/client/client.ts +1105 -0
- package/src/client/index.ts +45 -0
- package/src/client/types.ts +305 -0
- package/src/config/index.ts +497 -0
- package/src/contracts/contract-builder.ts +583 -0
- package/src/contracts/contract-group.ts +502 -0
- package/src/contracts/contract-like.ts +29 -0
- package/src/contracts/index.ts +53 -0
- package/src/contracts/openapi-meta.ts +22 -0
- package/src/contracts/path-template.ts +91 -0
- package/src/contracts/rate-limit.ts +50 -0
- package/src/contracts/types.ts +207 -0
- package/src/contracts/utils.ts +56 -0
- package/src/domain/entity.ts +256 -0
- package/src/domain/events.ts +52 -0
- package/src/domain/index.ts +18 -0
- package/src/domain/value-object.ts +135 -0
- package/src/errors/catalog.ts +149 -0
- package/src/errors/http.ts +80 -0
- package/src/errors/index.ts +28 -0
- package/src/errors/response.ts +54 -0
- package/src/errors/validation.ts +35 -0
- package/src/events/index.ts +246 -0
- package/src/jobs/index.ts +211 -0
- package/src/mail/index.ts +177 -0
- package/src/openapi/index.ts +865 -0
- package/src/openapi/schema-introspector.ts +107 -0
- package/src/ports/audit.ts +176 -0
- package/src/ports/auth.ts +76 -0
- package/src/ports/builder.ts +97 -0
- package/src/ports/cache.ts +94 -0
- package/src/ports/clock.ts +34 -0
- package/src/ports/events.ts +100 -0
- package/src/ports/id-generator.ts +36 -0
- package/src/ports/index.ts +221 -0
- package/src/ports/logger.ts +67 -0
- package/src/ports/policy.ts +242 -0
- package/src/ports/rate-limit.ts +91 -0
- package/src/ports/redaction.ts +199 -0
- package/src/ports/storage.ts +282 -0
- package/src/ports/testing.ts +234 -0
- package/src/ports/unit-of-work.ts +134 -0
- package/src/providers/index.ts +40 -0
- package/src/providers/instrumentation.ts +248 -0
- package/src/providers/provider.ts +191 -0
- package/src/schedules/index.ts +442 -0
- package/src/server/contract-like.ts +8 -0
- package/src/server/health.ts +82 -0
- package/src/server/hooks/auth.ts +147 -0
- package/src/server/hooks/cors.ts +87 -0
- package/src/server/hooks/errors.ts +126 -0
- package/src/server/hooks/index.ts +43 -0
- package/src/server/hooks/logging.ts +121 -0
- package/src/server/hooks/rate-limit.ts +171 -0
- package/src/server/hooks/utils.ts +16 -0
- package/src/server/hooks.ts +1 -0
- package/src/server/http.ts +189 -0
- package/src/server/index.ts +35 -0
- package/src/server/openapi.ts +72 -0
- package/src/server/providers/index.ts +3 -0
- package/src/server/providers/loadProviderConfig.ts +72 -0
- package/src/server/server.ts +1521 -0
- package/src/server/types.ts +2 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/core/config
|
|
3
|
+
*
|
|
4
|
+
* Environment-first configuration layer using Standard Schema (Zod, Valibot,
|
|
5
|
+
* ArkType, etc.) for Beignet applications and providers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
9
|
+
|
|
10
|
+
export type StandardSchema = StandardSchemaV1<unknown, unknown>;
|
|
11
|
+
export type RuntimeEnv = Record<string, string | undefined>;
|
|
12
|
+
export type EnvSchemaShape = Record<string, StandardSchema>;
|
|
13
|
+
type EmptyEnvSchemaShape = Record<keyof never, never>;
|
|
14
|
+
type NoInferType<T> = [T][T extends unknown ? 0 : never];
|
|
15
|
+
|
|
16
|
+
export type InferOutput<T extends StandardSchemaV1> =
|
|
17
|
+
StandardSchemaV1.InferOutput<T>;
|
|
18
|
+
|
|
19
|
+
export type InferEnvShape<Shape extends EnvSchemaShape> = {
|
|
20
|
+
[Key in keyof Shape]: InferOutput<Shape[Key]>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type ClientEnvSchemaShape<ClientPrefix extends string> =
|
|
24
|
+
ClientPrefix extends ""
|
|
25
|
+
? EnvSchemaShape
|
|
26
|
+
: Record<`${ClientPrefix}${string}`, StandardSchema>;
|
|
27
|
+
|
|
28
|
+
type ValidateClientEnvShape<
|
|
29
|
+
ClientPrefix extends string,
|
|
30
|
+
Client extends EnvSchemaShape,
|
|
31
|
+
> = ClientPrefix extends ""
|
|
32
|
+
? Client
|
|
33
|
+
: {
|
|
34
|
+
[Key in keyof Client]: Key extends `${ClientPrefix}${string}`
|
|
35
|
+
? Client[Key]
|
|
36
|
+
: never;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type EnvValidationIssue = StandardSchemaV1.Issue;
|
|
40
|
+
|
|
41
|
+
export class ConfigValidationError extends Error {
|
|
42
|
+
readonly issues: readonly EnvValidationIssue[];
|
|
43
|
+
|
|
44
|
+
constructor(issues: readonly EnvValidationIssue[], message?: string) {
|
|
45
|
+
super(message ?? formatStandardSchemaIssues(issues));
|
|
46
|
+
this.name = "ConfigValidationError";
|
|
47
|
+
this.issues = issues;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ReadEnvOptions {
|
|
52
|
+
env?: RuntimeEnv;
|
|
53
|
+
prefix?: string;
|
|
54
|
+
emptyStringAsUndefined?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DefineEnvOptions<Schema extends StandardSchemaV1> {
|
|
58
|
+
/**
|
|
59
|
+
* Standard Schema for validating the full environment object.
|
|
60
|
+
*/
|
|
61
|
+
schema: Schema;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Optional prefix to filter env vars. Matching keys are stripped before
|
|
65
|
+
* validation, so `APP_DATABASE_URL` becomes `DATABASE_URL`.
|
|
66
|
+
*/
|
|
67
|
+
prefix?: string;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Runtime environment object. Defaults to `process.env` when available.
|
|
71
|
+
*/
|
|
72
|
+
runtimeEnv?: RuntimeEnv;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Treat empty strings as missing values before validation.
|
|
76
|
+
*/
|
|
77
|
+
emptyStringAsUndefined?: boolean;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Skip validation and return the raw env object. This is intended for build
|
|
81
|
+
* phases where real secrets are unavailable.
|
|
82
|
+
*/
|
|
83
|
+
skipValidation?: boolean;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Called when validation fails. Throw from this hook to customize the error.
|
|
87
|
+
*/
|
|
88
|
+
onValidationError?: (issues: readonly EnvValidationIssue[]) => never;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface EnvInstance<Out> {
|
|
92
|
+
load(options?: { env?: RuntimeEnv }): Out;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface CreateEnvOptions<
|
|
96
|
+
Server extends EnvSchemaShape,
|
|
97
|
+
ClientPrefix extends string,
|
|
98
|
+
Client extends EnvSchemaShape,
|
|
99
|
+
> {
|
|
100
|
+
/**
|
|
101
|
+
* Server-only environment variables. These throw if accessed from a client
|
|
102
|
+
* runtime through the returned env object.
|
|
103
|
+
*/
|
|
104
|
+
server?: Server;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Client-safe environment variables. Keys must use `clientPrefix` when a
|
|
108
|
+
* prefix is provided.
|
|
109
|
+
*/
|
|
110
|
+
client?: Client & ValidateClientEnvShape<NoInferType<ClientPrefix>, Client>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Prefix required for client variables, e.g. `NEXT_PUBLIC_`.
|
|
114
|
+
*/
|
|
115
|
+
clientPrefix?: ClientPrefix;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Runtime environment object. Defaults to `process.env` when available.
|
|
119
|
+
*/
|
|
120
|
+
runtimeEnv?: RuntimeEnv;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Strict runtime environment object. Every declared key must be present on the
|
|
124
|
+
* object, even if the value is `undefined`. This catches framework bundling
|
|
125
|
+
* mistakes where an env var was not explicitly accessed.
|
|
126
|
+
*/
|
|
127
|
+
runtimeEnvStrict?: RuntimeEnv;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Treat empty strings as missing values before validation.
|
|
131
|
+
*
|
|
132
|
+
* Defaults to `true` for `createEnv` because it keeps defaults ergonomic in
|
|
133
|
+
* framework starters.
|
|
134
|
+
*/
|
|
135
|
+
emptyStringAsUndefined?: boolean;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Skip validation and return raw values. Use sparingly for build phases where
|
|
139
|
+
* deployment secrets are unavailable.
|
|
140
|
+
*/
|
|
141
|
+
skipValidation?: boolean;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Override server detection. Defaults to checking for `window` on globalThis.
|
|
145
|
+
*/
|
|
146
|
+
isServer?: boolean;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Called when validation fails. Throw from this hook to customize the error.
|
|
150
|
+
*/
|
|
151
|
+
onValidationError?: (issues: readonly EnvValidationIssue[]) => never;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Called when a server-only variable is read from a client runtime.
|
|
155
|
+
*/
|
|
156
|
+
onInvalidAccess?: (key: string) => never;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type CreateEnvResult<
|
|
160
|
+
Server extends EnvSchemaShape,
|
|
161
|
+
Client extends EnvSchemaShape,
|
|
162
|
+
> = Readonly<InferEnvShape<Server> & InferEnvShape<Client>>;
|
|
163
|
+
|
|
164
|
+
function defaultRuntimeEnv(): RuntimeEnv {
|
|
165
|
+
return typeof process !== "undefined" && process.env ? process.env : {};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
169
|
+
return (
|
|
170
|
+
value !== null &&
|
|
171
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
172
|
+
"then" in value &&
|
|
173
|
+
typeof (value as { then?: unknown }).then === "function"
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizeEnvValue(
|
|
178
|
+
value: string | undefined,
|
|
179
|
+
emptyStringAsUndefined: boolean,
|
|
180
|
+
): string | undefined {
|
|
181
|
+
if (emptyStringAsUndefined && value === "") return undefined;
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function issuePath(issue: StandardSchemaV1.Issue): string | undefined {
|
|
186
|
+
if (!issue.path?.length) return undefined;
|
|
187
|
+
|
|
188
|
+
return issue.path
|
|
189
|
+
.map((segment) => {
|
|
190
|
+
if (segment && typeof segment === "object" && "key" in segment) {
|
|
191
|
+
return String(segment.key);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (
|
|
195
|
+
typeof segment === "string" ||
|
|
196
|
+
typeof segment === "number" ||
|
|
197
|
+
typeof segment === "symbol"
|
|
198
|
+
) {
|
|
199
|
+
return String(segment);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return undefined;
|
|
203
|
+
})
|
|
204
|
+
.filter((segment): segment is string => Boolean(segment))
|
|
205
|
+
.join(".");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function prependIssuePath(
|
|
209
|
+
key: string,
|
|
210
|
+
issue: StandardSchemaV1.Issue,
|
|
211
|
+
): StandardSchemaV1.Issue {
|
|
212
|
+
return {
|
|
213
|
+
...issue,
|
|
214
|
+
path: [{ key }, ...(issue.path ?? [])],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function formatStandardSchemaIssues(
|
|
219
|
+
issues: readonly StandardSchemaV1.Issue[],
|
|
220
|
+
): string {
|
|
221
|
+
return issues
|
|
222
|
+
.map((issue) => {
|
|
223
|
+
const path = issuePath(issue);
|
|
224
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
225
|
+
})
|
|
226
|
+
.join("; ");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function readEnv({
|
|
230
|
+
env = defaultRuntimeEnv(),
|
|
231
|
+
prefix,
|
|
232
|
+
emptyStringAsUndefined = false,
|
|
233
|
+
}: ReadEnvOptions = {}): Record<string, string | undefined> {
|
|
234
|
+
const raw: Record<string, string | undefined> = {};
|
|
235
|
+
|
|
236
|
+
for (const [key, value] of Object.entries(env)) {
|
|
237
|
+
if (value == null) continue;
|
|
238
|
+
|
|
239
|
+
if (prefix && prefix.length > 0) {
|
|
240
|
+
if (!key.startsWith(prefix)) continue;
|
|
241
|
+
raw[key.slice(prefix.length)] = normalizeEnvValue(
|
|
242
|
+
value,
|
|
243
|
+
emptyStringAsUndefined,
|
|
244
|
+
);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
raw[key] = normalizeEnvValue(value, emptyStringAsUndefined);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return raw;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function validationResultValue<Schema extends StandardSchemaV1>(
|
|
255
|
+
result: StandardSchemaV1.Result<InferOutput<Schema>>,
|
|
256
|
+
): InferOutput<Schema> {
|
|
257
|
+
if (result.issues?.length) {
|
|
258
|
+
throw new ConfigValidationError(result.issues);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if ("value" in result) {
|
|
262
|
+
return result.value as InferOutput<Schema>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
throw new Error("Invalid Standard Schema result: missing value");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function parseStandardSchemaSync<Schema extends StandardSchemaV1>(
|
|
269
|
+
schema: Schema,
|
|
270
|
+
input: unknown,
|
|
271
|
+
): InferOutput<Schema> {
|
|
272
|
+
const validate = schema?.["~standard"]?.validate;
|
|
273
|
+
|
|
274
|
+
if (typeof validate !== "function") {
|
|
275
|
+
throw new Error("Invalid Standard Schema: missing ~standard.validate()");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const result = validate(input);
|
|
279
|
+
|
|
280
|
+
if (isPromiseLike(result)) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
"[Beignet env] Schema uses async validation, which is not supported by load().",
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return validationResultValue<Schema>(result);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export async function parseStandardSchemaAsync<Schema extends StandardSchemaV1>(
|
|
290
|
+
schema: Schema,
|
|
291
|
+
input: unknown,
|
|
292
|
+
): Promise<InferOutput<Schema>> {
|
|
293
|
+
const validate = schema?.["~standard"]?.validate;
|
|
294
|
+
|
|
295
|
+
if (typeof validate !== "function") {
|
|
296
|
+
throw new Error("Invalid Standard Schema: missing ~standard.validate()");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return validationResultValue<Schema>(await validate(input));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function wrapEnvError(err: unknown, prefix?: string): Error {
|
|
303
|
+
if (err instanceof ConfigValidationError) {
|
|
304
|
+
return new Error(
|
|
305
|
+
`[Beignet env] Invalid environment${
|
|
306
|
+
prefix ? ` for prefix "${prefix}"` : ""
|
|
307
|
+
}: ${err.message}`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (err instanceof Error) {
|
|
312
|
+
return new Error(
|
|
313
|
+
`[Beignet env] Invalid environment${
|
|
314
|
+
prefix ? ` for prefix "${prefix}"` : ""
|
|
315
|
+
}: ${err.message}`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return new Error(
|
|
320
|
+
`[Beignet env] Invalid environment${
|
|
321
|
+
prefix ? ` for prefix "${prefix}"` : ""
|
|
322
|
+
}: ${String(err)}`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function defineEnv<Schema extends StandardSchemaV1>(
|
|
327
|
+
options: DefineEnvOptions<Schema>,
|
|
328
|
+
): EnvInstance<InferOutput<Schema>> {
|
|
329
|
+
const {
|
|
330
|
+
schema,
|
|
331
|
+
prefix,
|
|
332
|
+
runtimeEnv,
|
|
333
|
+
emptyStringAsUndefined = false,
|
|
334
|
+
skipValidation = false,
|
|
335
|
+
onValidationError,
|
|
336
|
+
} = options;
|
|
337
|
+
|
|
338
|
+
function load({
|
|
339
|
+
env = runtimeEnv ?? defaultRuntimeEnv(),
|
|
340
|
+
} = {}): InferOutput<Schema> {
|
|
341
|
+
const raw = readEnv({ env, prefix, emptyStringAsUndefined });
|
|
342
|
+
|
|
343
|
+
if (skipValidation) {
|
|
344
|
+
return raw as InferOutput<Schema>;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
return parseStandardSchemaSync(schema, raw);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
if (err instanceof ConfigValidationError) {
|
|
351
|
+
onValidationError?.(err.issues);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
throw wrapEnvError(err, prefix);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return { load };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function validateEnvShape<Shape extends EnvSchemaShape>(
|
|
362
|
+
shape: Shape,
|
|
363
|
+
runtimeEnv: RuntimeEnv,
|
|
364
|
+
emptyStringAsUndefined: boolean,
|
|
365
|
+
): InferEnvShape<Shape> {
|
|
366
|
+
const output: Record<string, unknown> = {};
|
|
367
|
+
const issues: StandardSchemaV1.Issue[] = [];
|
|
368
|
+
|
|
369
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
370
|
+
const value = normalizeEnvValue(runtimeEnv[key], emptyStringAsUndefined);
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
output[key] = parseStandardSchemaSync(schema, value);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof ConfigValidationError) {
|
|
376
|
+
issues.push(
|
|
377
|
+
...error.issues.map((issue) => prependIssuePath(key, issue)),
|
|
378
|
+
);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (issues.length) {
|
|
387
|
+
throw new ConfigValidationError(issues);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return output as InferEnvShape<Shape>;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function validateClientPrefix(
|
|
394
|
+
client: EnvSchemaShape,
|
|
395
|
+
clientPrefix: string | undefined,
|
|
396
|
+
) {
|
|
397
|
+
if (!clientPrefix) return;
|
|
398
|
+
|
|
399
|
+
for (const key of Object.keys(client)) {
|
|
400
|
+
if (!key.startsWith(clientPrefix)) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`[Beignet env] Client environment variable "${key}" must start with "${clientPrefix}".`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function validateRuntimeEnvStrict(
|
|
409
|
+
runtimeEnv: RuntimeEnv,
|
|
410
|
+
keys: readonly string[],
|
|
411
|
+
) {
|
|
412
|
+
const missing = keys.filter((key) => !Object.hasOwn(runtimeEnv, key));
|
|
413
|
+
if (missing.length === 0) return;
|
|
414
|
+
|
|
415
|
+
throw new Error(
|
|
416
|
+
`[Beignet env] runtimeEnvStrict is missing declared keys: ${missing.join(
|
|
417
|
+
", ",
|
|
418
|
+
)}`,
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export function createEnv<
|
|
423
|
+
const ClientPrefix extends string = "",
|
|
424
|
+
Server extends EnvSchemaShape = EmptyEnvSchemaShape,
|
|
425
|
+
Client extends EnvSchemaShape = EmptyEnvSchemaShape,
|
|
426
|
+
>(
|
|
427
|
+
options: CreateEnvOptions<Server, ClientPrefix, Client>,
|
|
428
|
+
): CreateEnvResult<Server, Client> {
|
|
429
|
+
const server = (options.server ?? {}) as Server;
|
|
430
|
+
const client = (options.client ?? {}) as Client;
|
|
431
|
+
const runtimeEnv =
|
|
432
|
+
options.runtimeEnvStrict ?? options.runtimeEnv ?? defaultRuntimeEnv();
|
|
433
|
+
const emptyStringAsUndefined = options.emptyStringAsUndefined ?? true;
|
|
434
|
+
const isServer = options.isServer ?? !("window" in globalThis);
|
|
435
|
+
|
|
436
|
+
if (options.runtimeEnv && options.runtimeEnvStrict) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
"[Beignet env] Specify runtimeEnv or runtimeEnvStrict, not both.",
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
validateClientPrefix(client, options.clientPrefix);
|
|
443
|
+
|
|
444
|
+
const declaredKeys = [...Object.keys(server), ...Object.keys(client)];
|
|
445
|
+
const validationShape = isServer ? { ...server, ...client } : client;
|
|
446
|
+
const validationKeys = isServer ? declaredKeys : Object.keys(client);
|
|
447
|
+
|
|
448
|
+
if (options.runtimeEnvStrict) {
|
|
449
|
+
validateRuntimeEnvStrict(runtimeEnv, validationKeys);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
let parsed: Record<string, unknown>;
|
|
453
|
+
|
|
454
|
+
if (options.skipValidation) {
|
|
455
|
+
parsed = Object.fromEntries(
|
|
456
|
+
validationKeys.map((key) => [
|
|
457
|
+
key,
|
|
458
|
+
normalizeEnvValue(runtimeEnv[key], emptyStringAsUndefined),
|
|
459
|
+
]),
|
|
460
|
+
);
|
|
461
|
+
} else {
|
|
462
|
+
try {
|
|
463
|
+
parsed = validateEnvShape(
|
|
464
|
+
validationShape,
|
|
465
|
+
runtimeEnv,
|
|
466
|
+
emptyStringAsUndefined,
|
|
467
|
+
);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
if (error instanceof ConfigValidationError) {
|
|
470
|
+
options.onValidationError?.(error.issues);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
throw wrapEnvError(error);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const serverKeys = new Set(Object.keys(server));
|
|
478
|
+
const clientKeys = new Set(Object.keys(client));
|
|
479
|
+
|
|
480
|
+
return new Proxy(parsed, {
|
|
481
|
+
get(target, property, receiver) {
|
|
482
|
+
if (
|
|
483
|
+
typeof property === "string" &&
|
|
484
|
+
!isServer &&
|
|
485
|
+
serverKeys.has(property) &&
|
|
486
|
+
!clientKeys.has(property)
|
|
487
|
+
) {
|
|
488
|
+
options.onInvalidAccess?.(property);
|
|
489
|
+
throw new Error(
|
|
490
|
+
`[Beignet env] Attempted to access server-only environment variable "${property}" from a client runtime.`,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return Reflect.get(target, property, receiver);
|
|
495
|
+
},
|
|
496
|
+
}) as CreateEnvResult<Server, Client>;
|
|
497
|
+
}
|