@ubean/shared 0.6.8 → 0.7.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/README.md +15 -8
- package/README.zh-CN.md +15 -8
- package/dist/index.d.ts +33 -25
- package/dist/index.js +83 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,18 +73,21 @@ isNodeRuntime(); // false on Cloudflare Workers
|
|
|
73
73
|
|
|
74
74
|
## Environment Variables
|
|
75
75
|
|
|
76
|
-
`defineEnv(config)` validates a
|
|
76
|
+
`defineEnv(config)` validates a schema and returns `{ env, validate }`. Every field declares its own `context` (`client` | `server`) and `access` (`public` | `secret`) via `envField.*()` — the same shape `ubean.config.ts` accepts under `env.schema`.
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
|
-
import { defineEnv } from '@ubean/shared';
|
|
79
|
+
import { defineEnv, envField } from '@ubean/shared';
|
|
80
80
|
|
|
81
81
|
export const { env, validate } = defineEnv({
|
|
82
|
-
|
|
83
|
-
DATABASE_URL: {
|
|
84
|
-
PORT: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
schema: {
|
|
83
|
+
DATABASE_URL: envField.string({ context: 'server', access: 'secret', optional: true }),
|
|
84
|
+
PORT: envField.number({ context: 'server', access: 'public', default: 9527 }),
|
|
85
|
+
APP_NAME: envField.string({
|
|
86
|
+
context: 'client',
|
|
87
|
+
access: 'public',
|
|
88
|
+
default: 'ubean-app',
|
|
89
|
+
description: 'Shown in the header'
|
|
90
|
+
})
|
|
88
91
|
},
|
|
89
92
|
mode: 'warn' // 'warn' logs; 'throw' rethrows on validation failure
|
|
90
93
|
});
|
|
@@ -93,6 +96,10 @@ env.PORT; // number
|
|
|
93
96
|
validate(); // { success, errors, data }
|
|
94
97
|
```
|
|
95
98
|
|
|
99
|
+
Every builder also accepts `description`, emitted as JSDoc in the generated `.ubean/env.d.ts`. Grouped `server:` / `public:` schemas are gone: dimensions live on the field, so nothing silently changes meaning when an entry moves between groups. `client` + `secret` throws at declaration time. The constructor shape (`{ type: String }`) still parses under `schema`, but it has no dimension to read from, so it is treated as `server + secret` (the conservative choice).
|
|
100
|
+
|
|
101
|
+
For zod / valibot-grade validation, `defineEnv()` also accepts Standard Schema and `safeParse` shapes — it shares a process with the validator, unlike the generated env modules. Validation must be synchronous.
|
|
102
|
+
|
|
96
103
|
| API | Description |
|
|
97
104
|
| ------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
98
105
|
| `defineEnv(config)` | Validate a schema and return `{ env, validate }` (`DefineEnvResult<S>`) |
|
package/README.zh-CN.md
CHANGED
|
@@ -73,18 +73,21 @@ isNodeRuntime(); // Cloudflare Workers 上为 false
|
|
|
73
73
|
|
|
74
74
|
## 环境变量
|
|
75
75
|
|
|
76
|
-
`defineEnv(config)`
|
|
76
|
+
`defineEnv(config)` 校验 schema 并返回 `{ env, validate }`。每个字段用 `envField.*()` 自带 `context`(`client` | `server`)与 `access`(`public` | `secret`)—— 与 `ubean.config.ts` 的 `env.schema` 完全同形。
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
|
-
import { defineEnv } from '@ubean/shared';
|
|
79
|
+
import { defineEnv, envField } from '@ubean/shared';
|
|
80
80
|
|
|
81
81
|
export const { env, validate } = defineEnv({
|
|
82
|
-
|
|
83
|
-
DATABASE_URL: {
|
|
84
|
-
PORT: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
schema: {
|
|
83
|
+
DATABASE_URL: envField.string({ context: 'server', access: 'secret', optional: true }),
|
|
84
|
+
PORT: envField.number({ context: 'server', access: 'public', default: 9527 }),
|
|
85
|
+
APP_NAME: envField.string({
|
|
86
|
+
context: 'client',
|
|
87
|
+
access: 'public',
|
|
88
|
+
default: 'ubean-app',
|
|
89
|
+
description: '页头展示的名称'
|
|
90
|
+
})
|
|
88
91
|
},
|
|
89
92
|
mode: 'warn' // 'warn' 记录日志;'throw' 在校验失败时抛出
|
|
90
93
|
});
|
|
@@ -93,6 +96,10 @@ env.PORT; // number
|
|
|
93
96
|
validate(); // { success, errors, data }
|
|
94
97
|
```
|
|
95
98
|
|
|
99
|
+
每个 builder 还接受 `description`,它会以 JSDoc 形式写进生成的 `.ubean/env.d.ts`。分组糖(`server:` / `public:`)已删除:维度写在字段上,同一个字段挪位置不会静默换语义。`client` + `secret` 在声明时直接抛错。构造函数形态(`{ type: String }`)仍可在 `schema` 下解析,但它没有维度可读,按 `server + secret` 处理(最保守的取舍)。
|
|
100
|
+
|
|
101
|
+
需要 zod / valibot 级校验时,`defineEnv()` 也接受 Standard Schema 与 `safeParse` 形态——它与校验器同进程,而生成的 env 模块装不下函数。校验必须同步。
|
|
102
|
+
|
|
96
103
|
| API | 说明 |
|
|
97
104
|
| ------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
98
105
|
| `defineEnv(config)` | 校验 schema 并返回 `{ env, validate }`(`DefineEnvResult<S>`) |
|
package/dist/index.d.ts
CHANGED
|
@@ -396,7 +396,7 @@ export type EnvSchema = Record<string, EnvFieldDef>;
|
|
|
396
396
|
export type EnvContext = 'client' | 'server';
|
|
397
397
|
export type EnvAccess = 'public' | 'secret';
|
|
398
398
|
export interface EnvFieldMeta<T = unknown> {
|
|
399
|
-
/** 判别标记:把 `envField.*()`
|
|
399
|
+
/** 判别标记:把 `envField.*()` 的产物与构造函数形态区分开。 */
|
|
400
400
|
readonly __ubeanEnvField: true;
|
|
401
401
|
readonly type: 'string' | 'number' | 'boolean' | 'enum';
|
|
402
402
|
readonly context: EnvContext;
|
|
@@ -405,14 +405,18 @@ export interface EnvFieldMeta<T = unknown> {
|
|
|
405
405
|
readonly values?: readonly string[];
|
|
406
406
|
readonly optional?: boolean;
|
|
407
407
|
readonly default?: T;
|
|
408
|
+
/**
|
|
409
|
+
* 字段说明。写进 `.ubean/env.d.ts` 的 JSDoc,编辑器里 hover 具名导入时直接可见 ——
|
|
410
|
+
* 这是唯一能跟着变量走到消费端的地方(`.env.example` 里的注释不会)。
|
|
411
|
+
*/
|
|
412
|
+
readonly description?: string;
|
|
408
413
|
/** 纯类型标记(运行时不写),让 `InferEnvOutput` 能推出字面量联合。 */
|
|
409
414
|
readonly _output?: T;
|
|
410
415
|
}
|
|
411
416
|
/**
|
|
412
|
-
* 一个 schema
|
|
417
|
+
* 一个 schema 字段:`envField.*()` 产物、运行期 schema,或构造函数形态(`{ type: String }`)。
|
|
413
418
|
*
|
|
414
|
-
*
|
|
415
|
-
* 与示例都在用,`0.6.x` 内不能变语义。
|
|
419
|
+
* 构造函数形态没有维度信息,按 `server + secret` 处理(见 `DEFAULT_FIELD_DIMENSIONS`)。
|
|
416
420
|
*/
|
|
417
421
|
export type EnvFieldDef = StandardSchemaV1 | GenericSchema | EnvFieldMeta | {
|
|
418
422
|
type: StringConstructor | NumberConstructor | BooleanConstructor;
|
|
@@ -423,6 +427,13 @@ interface EnvFieldBase {
|
|
|
423
427
|
context: EnvContext;
|
|
424
428
|
access: EnvAccess;
|
|
425
429
|
optional?: boolean;
|
|
430
|
+
/**
|
|
431
|
+
* 字段说明,会作为 JSDoc 出现在生成的 `.ubean/env.d.ts` 里。
|
|
432
|
+
*
|
|
433
|
+
* 与 `.env.example` 的注释分工:那边是给运维看「这个值填什么」,这里是给写代码的人
|
|
434
|
+
* 看「这个变量是什么」—— 且会跟着类型走到每一处 import。
|
|
435
|
+
*/
|
|
436
|
+
description?: string;
|
|
426
437
|
}
|
|
427
438
|
export interface EnvStringFieldOptions extends EnvFieldBase {
|
|
428
439
|
default?: string;
|
|
@@ -469,8 +480,8 @@ export type InferEnvOutput<S extends EnvSchema> = { [K in keyof S]: S[K] extends
|
|
|
469
480
|
type: BooleanConstructor;
|
|
470
481
|
} ? boolean : string; };
|
|
471
482
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
483
|
+
* 规范化后的字段:每个都带着确定的 `context` / `access`。codegen 与虚拟模块都只
|
|
484
|
+
* 消费这个形状。
|
|
474
485
|
*/
|
|
475
486
|
export interface NormalizedEnvField {
|
|
476
487
|
key: string;
|
|
@@ -480,22 +491,14 @@ export interface NormalizedEnvField {
|
|
|
480
491
|
}
|
|
481
492
|
export interface EnvConfig<S extends EnvSchema = EnvSchema> {
|
|
482
493
|
/**
|
|
483
|
-
* 扁平 schema —— 每个字段自带 `context` / `access
|
|
484
|
-
*
|
|
485
|
-
*/
|
|
486
|
-
schema?: S;
|
|
487
|
-
/**
|
|
488
|
-
* @deprecated 用 `schema` + `envField.*()` 代替。
|
|
489
|
-
*
|
|
490
|
-
* 分组糖:组内字段默认 `context: 'server', access: 'secret'`。
|
|
491
|
-
*/
|
|
492
|
-
server?: S;
|
|
493
|
-
/**
|
|
494
|
-
* @deprecated 用 `schema` + `envField.*()` 代替。
|
|
494
|
+
* 扁平 schema —— 每个字段自带 `context` / `access`(用 `envField.*()` 声明)。
|
|
495
|
+
* `ubean.config.ts` 的 `env.schema` 与 `defineEnv({ schema })` 都是这个形状。
|
|
495
496
|
*
|
|
496
|
-
*
|
|
497
|
+
* 分组糖(`server:` / `public:`)已删除:维度**必须**写在字段上。旧写法靠组名
|
|
498
|
+
* 隐式决定 `context`/`access`,同一个字段换个位置就换了语义,且 `client+secret`
|
|
499
|
+
* 这类非法组合只能靠组合规则兜。
|
|
497
500
|
*/
|
|
498
|
-
|
|
501
|
+
schema?: S;
|
|
499
502
|
/** Validation mode - 'warn' logs errors, 'throw' throws on validation failure */
|
|
500
503
|
mode?: 'warn' | 'throw';
|
|
501
504
|
}
|
|
@@ -513,17 +516,22 @@ export interface EnvValidationError {
|
|
|
513
516
|
value: unknown;
|
|
514
517
|
}
|
|
515
518
|
/**
|
|
516
|
-
* 把用户 schema
|
|
519
|
+
* 把用户 schema 摊平成 `NormalizedEnvField[]`。
|
|
520
|
+
*
|
|
521
|
+
* 字段自带 `context`/`access`(`envField.*()`)时用它;构造函数形态(`{ type: String }`)
|
|
522
|
+
* 没有维度信息,按「最保守」处理 —— 当 `server + secret`,绝不进客户端产物。要跳侧可见
|
|
523
|
+
* 就显式用 `envField.*()`。
|
|
517
524
|
*
|
|
518
|
-
*
|
|
519
|
-
* `
|
|
525
|
+
* 分组糖(`server:` / `public:`)已删除:维度必须写在字段上。旧写法靠组名隐式决定
|
|
526
|
+
* `context`/`access`,同一字段换个位置就换了语义,而混用(分组里再放 envField)只能靠
|
|
527
|
+
* 优先级规则解释。现在传入分组语法会直接抛错。
|
|
520
528
|
*/
|
|
521
529
|
export declare function normalizeEnvSchema(schema: EnvSchema | EnvConfig): NormalizedEnvField[];
|
|
522
530
|
/**
|
|
523
531
|
* 服务端运行时校验:按规范化字段读 `source`(默认 `process.env`),产出强类型对象。
|
|
524
532
|
*
|
|
525
|
-
* 与 `defineEnv()`
|
|
526
|
-
*
|
|
533
|
+
* 与 `defineEnv()` 的区别:这里只做「已归好维度」的字段到运行时值的解析 —— 虚拟模块
|
|
534
|
+
* `ubean:env/server` 的运行时校验走它。
|
|
527
535
|
*/
|
|
528
536
|
export declare function resolveEnvSchema(schema: EnvSchema | EnvConfig, source?: Record<string, string | undefined>): {
|
|
529
537
|
success: boolean;
|
package/dist/index.js
CHANGED
|
@@ -171,32 +171,69 @@ function isEnvFieldMeta(value) {
|
|
|
171
171
|
return typeof value === "object" && value !== null && value.__ubeanEnvField === true;
|
|
172
172
|
}
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
174
|
+
* 构造函数形态(无维度信息)的默认维度:按「最保守」处理 —— 当服务端 secret,
|
|
175
|
+
* 绝不进客户端产物。要跳侧可见就显式用 `envField.*()`。
|
|
176
|
+
*/
|
|
177
|
+
const DEFAULT_FIELD_DIMENSIONS = {
|
|
178
|
+
context: "server",
|
|
179
|
+
access: "secret"
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* 判断一个值是否符合 Standard Schema 接口(zod / valibot / arktype 等)。
|
|
175
183
|
*
|
|
176
|
-
* `
|
|
177
|
-
*
|
|
178
|
-
* 同样可读,所以 examples 里在 API handler 中读 `env.API_BASE_URL` 的行为不变)。
|
|
184
|
+
* 只看 `~standard.validate` 是不是函数,不看 `vendor` / `version` —— 各家实现这几个字段
|
|
185
|
+
* 的值不一致(valibot 是 `vendor: 'valibot'`),但**调用面**统一。
|
|
179
186
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
function isStandardSchema(value) {
|
|
188
|
+
if (typeof value !== "object" || value === null) return false;
|
|
189
|
+
const standard = value["~standard"];
|
|
190
|
+
return typeof standard === "object" && standard !== null && typeof standard.validate === "function";
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 用 Standard Schema 校验一个环境变量。
|
|
194
|
+
*
|
|
195
|
+
* **`~standard.validate()` 必须同步返回。** 大多数 schema 都是同步的(zod 的 `.min()`
|
|
196
|
+
* 一类、valibot 的 `v.pipe()`),但 zod 的 `.refine(async …)` 会返回 Promise:那种 schema
|
|
197
|
+
* 在环境变量上无法支持 —— 生成的 server 模块在模块顶层同步执行,而
|
|
198
|
+
* `loadUbeanConfigSync()` 又把配置编译成 CJS(顶层 await 在那里是语法错误),
|
|
199
|
+
* 两边都没有 await 的位置。
|
|
200
|
+
*
|
|
201
|
+
* 早先这里写的是「看到 `~standard` 就判失败」并提示「请用 validate()」—— 可它从未
|
|
202
|
+
* 调用过 `validate()`,于是**任何**标准 schema 都必定校验失败,而且报错指向一个同样会
|
|
203
|
+
* 失败的方向。现在真的调用它了。
|
|
204
|
+
*/
|
|
205
|
+
function parseStandardSchemaValue(schema, rawValue, key) {
|
|
206
|
+
const input = rawValue === "" ? void 0 : rawValue;
|
|
207
|
+
let result;
|
|
208
|
+
try {
|
|
209
|
+
result = schema["~standard"].validate(input);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
error: error instanceof Error ? error.message : String(error)
|
|
214
|
+
};
|
|
188
215
|
}
|
|
189
|
-
|
|
216
|
+
if (typeof result?.then === "function") return {
|
|
217
|
+
ok: false,
|
|
218
|
+
error: `Env ${key}: async Standard Schema validation is not supported (env validation must be synchronous)`
|
|
219
|
+
};
|
|
220
|
+
const settled = result;
|
|
221
|
+
if (settled.issues && settled.issues.length > 0) return {
|
|
222
|
+
ok: false,
|
|
223
|
+
error: settled.issues[0]?.message ?? `Env ${key} is invalid`
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
ok: true,
|
|
227
|
+
value: settled.value
|
|
228
|
+
};
|
|
229
|
+
}
|
|
190
230
|
function parseSchemaValue(schema, rawValue, key) {
|
|
191
231
|
if (!schema) return {
|
|
192
232
|
ok: true,
|
|
193
233
|
value: rawValue
|
|
194
234
|
};
|
|
195
235
|
if (isEnvFieldMeta(schema)) return parseEnvFieldValue(schema, rawValue, key);
|
|
196
|
-
if (
|
|
197
|
-
ok: false,
|
|
198
|
-
error: "Standard schema validation requires async validate, use validate() instead"
|
|
199
|
-
};
|
|
236
|
+
if (isStandardSchema(schema)) return parseStandardSchemaValue(schema, rawValue, key);
|
|
200
237
|
if (typeof schema.safeParse === "function") {
|
|
201
238
|
const result = schema.safeParse(rawValue);
|
|
202
239
|
if (!result.success) return {
|
|
@@ -283,7 +320,7 @@ function parseSchemaValue(schema, rawValue, key) {
|
|
|
283
320
|
/**
|
|
284
321
|
* 解析 `envField.*()` 声明的字段。
|
|
285
322
|
*
|
|
286
|
-
*
|
|
323
|
+
* 三种类型的行为与构造函数形态一致(缺失 → `default` → `optional` 放行 → 报错),
|
|
287
324
|
* 多出来的是 `enum` 的成员校验:值不在 `values` 里直接失败,不做静默回落 ——
|
|
288
325
|
* 枚举变量的拼写错误在启动期暴露比在运行期暴露便宜得多。
|
|
289
326
|
*/
|
|
@@ -332,52 +369,47 @@ function parseEnvFieldValue(meta, rawValue, key) {
|
|
|
332
369
|
};
|
|
333
370
|
}
|
|
334
371
|
/**
|
|
335
|
-
* 把用户 schema
|
|
372
|
+
* 把用户 schema 摊平成 `NormalizedEnvField[]`。
|
|
373
|
+
*
|
|
374
|
+
* 字段自带 `context`/`access`(`envField.*()`)时用它;构造函数形态(`{ type: String }`)
|
|
375
|
+
* 没有维度信息,按「最保守」处理 —— 当 `server + secret`,绝不进客户端产物。要跳侧可见
|
|
376
|
+
* 就显式用 `envField.*()`。
|
|
336
377
|
*
|
|
337
|
-
*
|
|
338
|
-
* `
|
|
378
|
+
* 分组糖(`server:` / `public:`)已删除:维度必须写在字段上。旧写法靠组名隐式决定
|
|
379
|
+
* `context`/`access`,同一字段换个位置就换了语义,而混用(分组里再放 envField)只能靠
|
|
380
|
+
* 优先级规则解释。现在传入分组语法会直接抛错。
|
|
339
381
|
*/
|
|
340
382
|
function normalizeEnvSchema(schema) {
|
|
341
383
|
const fields = [];
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (
|
|
384
|
+
const config = schema;
|
|
385
|
+
for (const groupKey of ["server", "public"]) if (groupKey in schema) throw new Error(`Invalid env config: grouped \`${groupKey}:\` schemas were removed.\nDeclare every field under \`schema:\` with explicit dimensions, e.g. \`${groupKey.toUpperCase()}_KEY: envField.string({ context: '${groupKey}', access: 'public' })\`.`);
|
|
386
|
+
const raw = config.schema ?? schema;
|
|
387
|
+
if (!raw || typeof raw !== "object") return fields;
|
|
388
|
+
for (const [key, def] of Object.entries(raw)) {
|
|
389
|
+
if (def === void 0 || def === null) continue;
|
|
390
|
+
if (!isEnvFieldMeta(def)) {
|
|
391
|
+
fields.push({
|
|
392
|
+
key,
|
|
393
|
+
...DEFAULT_FIELD_DIMENSIONS,
|
|
394
|
+
def
|
|
395
|
+
});
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (def.context === "client" && def.access === "secret") throw new Error(`Invalid env field "${key}": context 'client' cannot be combined with access 'secret'.`);
|
|
348
399
|
fields.push({
|
|
349
400
|
key,
|
|
350
|
-
context:
|
|
351
|
-
access:
|
|
401
|
+
context: def.context,
|
|
402
|
+
access: def.access,
|
|
352
403
|
def
|
|
353
404
|
});
|
|
354
|
-
};
|
|
355
|
-
const record = schema;
|
|
356
|
-
if (!("server" in record) && !("public" in record)) {
|
|
357
|
-
const flat = "schema" in record ? record.schema : record;
|
|
358
|
-
if (!flat || typeof flat !== "object") return fields;
|
|
359
|
-
for (const [key, def] of Object.entries(flat)) {
|
|
360
|
-
if (!isEnvFieldMeta(def)) {
|
|
361
|
-
push(key, def, LEGACY_GROUP_DIMENSIONS.server);
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
push(key, def, {
|
|
365
|
-
context: def.context,
|
|
366
|
-
access: def.access
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
return fields;
|
|
370
405
|
}
|
|
371
|
-
const grouped = schema;
|
|
372
|
-
for (const [key, def] of Object.entries(grouped.server ?? {})) push(key, def, LEGACY_GROUP_DIMENSIONS.server);
|
|
373
|
-
for (const [key, def] of Object.entries(grouped.public ?? {})) push(key, def, LEGACY_GROUP_DIMENSIONS.public);
|
|
374
406
|
return fields;
|
|
375
407
|
}
|
|
376
408
|
/**
|
|
377
409
|
* 服务端运行时校验:按规范化字段读 `source`(默认 `process.env`),产出强类型对象。
|
|
378
410
|
*
|
|
379
|
-
* 与 `defineEnv()`
|
|
380
|
-
*
|
|
411
|
+
* 与 `defineEnv()` 的区别:这里只做「已归好维度」的字段到运行时值的解析 —— 虚拟模块
|
|
412
|
+
* `ubean:env/server` 的运行时校验走它。
|
|
381
413
|
*/
|
|
382
414
|
function resolveEnvSchema(schema, source) {
|
|
383
415
|
const fields = normalizeEnvSchema(schema);
|
|
@@ -428,11 +460,7 @@ function validateSchemaSync(schema, source) {
|
|
|
428
460
|
}
|
|
429
461
|
function defineEnv(config) {
|
|
430
462
|
const serverSource = readProcessEnv();
|
|
431
|
-
const mergedSchema = {
|
|
432
|
-
...config.public,
|
|
433
|
-
...config.server,
|
|
434
|
-
...config.schema
|
|
435
|
-
};
|
|
463
|
+
const mergedSchema = config.schema ?? {};
|
|
436
464
|
const initial = validateSchemaSync(mergedSchema, serverSource);
|
|
437
465
|
if (!initial.success && config.mode === "throw") {
|
|
438
466
|
const messages = initial.errors.map((e) => ` - ${e.key}: ${e.message}`).join("\n");
|