@arki/env 0.0.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/README.md +231 -62
  3. package/dist/api.json +597 -0
  4. package/dist/core/define-env.d.ts +29 -4
  5. package/dist/core/define-env.d.ts.map +1 -1
  6. package/dist/core/define-env.js +85 -15
  7. package/dist/core/define-env.js.map +1 -1
  8. package/dist/core/describe.d.ts +29 -0
  9. package/dist/core/describe.d.ts.map +1 -0
  10. package/dist/core/describe.js +25 -0
  11. package/dist/core/describe.js.map +1 -0
  12. package/dist/core/errors.d.ts +10 -0
  13. package/dist/core/errors.d.ts.map +1 -0
  14. package/dist/core/errors.js +13 -0
  15. package/dist/core/errors.js.map +1 -0
  16. package/dist/core/index.d.ts +7 -2
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/index.js +6 -2
  19. package/dist/core/index.js.map +1 -1
  20. package/dist/core/secret.d.ts +56 -0
  21. package/dist/core/secret.d.ts.map +1 -0
  22. package/dist/core/secret.js +85 -0
  23. package/dist/core/secret.js.map +1 -0
  24. package/dist/errors.json +128 -0
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +3 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/node/codec.d.ts +63 -0
  30. package/dist/node/codec.d.ts.map +1 -0
  31. package/dist/node/codec.js +182 -0
  32. package/dist/node/codec.js.map +1 -0
  33. package/dist/node/edit.d.ts +112 -0
  34. package/dist/node/edit.d.ts.map +1 -0
  35. package/dist/node/edit.js +268 -0
  36. package/dist/node/edit.js.map +1 -0
  37. package/dist/node/errors.d.ts +119 -0
  38. package/dist/node/errors.d.ts.map +1 -0
  39. package/dist/node/errors.js +144 -0
  40. package/dist/node/errors.js.map +1 -0
  41. package/dist/node/files.d.ts +9 -0
  42. package/dist/node/files.d.ts.map +1 -0
  43. package/dist/node/files.js +35 -0
  44. package/dist/node/files.js.map +1 -0
  45. package/dist/node/index.d.ts +20 -0
  46. package/dist/node/index.d.ts.map +1 -0
  47. package/dist/node/index.js +13 -0
  48. package/dist/node/index.js.map +1 -0
  49. package/dist/node/key-schemas.d.ts +25 -0
  50. package/dist/node/key-schemas.d.ts.map +1 -0
  51. package/dist/node/key-schemas.js +38 -0
  52. package/dist/node/key-schemas.js.map +1 -0
  53. package/dist/node/keys.d.ts +54 -0
  54. package/dist/node/keys.d.ts.map +1 -0
  55. package/dist/node/keys.js +76 -0
  56. package/dist/node/keys.js.map +1 -0
  57. package/dist/node/load.d.ts +103 -0
  58. package/dist/node/load.d.ts.map +1 -0
  59. package/dist/node/load.js +306 -0
  60. package/dist/node/load.js.map +1 -0
  61. package/dist/node/operation-telemetry.d.ts +3 -0
  62. package/dist/node/operation-telemetry.d.ts.map +1 -0
  63. package/dist/node/operation-telemetry.js +53 -0
  64. package/dist/node/operation-telemetry.js.map +1 -0
  65. package/dist/node/references.d.ts +16 -0
  66. package/dist/node/references.d.ts.map +1 -0
  67. package/dist/node/references.js +72 -0
  68. package/dist/node/references.js.map +1 -0
  69. package/dist/node/register.d.ts +2 -0
  70. package/dist/node/register.d.ts.map +1 -0
  71. package/dist/node/register.js +19 -0
  72. package/dist/node/register.js.map +1 -0
  73. package/dist/{contracts → presets}/common.d.ts +1 -1
  74. package/dist/presets/common.d.ts.map +1 -0
  75. package/dist/{contracts → presets}/common.js +4 -8
  76. package/dist/presets/common.js.map +1 -0
  77. package/dist/{contracts → presets}/index.d.ts +5 -5
  78. package/dist/presets/index.d.ts.map +1 -0
  79. package/dist/presets/index.js +15 -0
  80. package/dist/presets/index.js.map +1 -0
  81. package/dist/{contracts → presets}/parsing.d.ts +2 -2
  82. package/dist/presets/parsing.d.ts.map +1 -0
  83. package/dist/{contracts → presets}/parsing.js +13 -5
  84. package/dist/presets/parsing.js.map +1 -0
  85. package/dist/{contracts → presets}/urls.d.ts +1 -1
  86. package/dist/presets/urls.d.ts.map +1 -0
  87. package/dist/{contracts → presets}/urls.js +1 -1
  88. package/dist/presets/urls.js.map +1 -0
  89. package/docs/agent-guide.md +121 -0
  90. package/docs/upgrades/0.0.11-to-0.1.0.md +62 -0
  91. package/docs/upgrades/0.1.0-to-0.2.0.md +73 -0
  92. package/docs/upgrades/typed-node-outcomes.md +13 -0
  93. package/llms.txt +30 -0
  94. package/package.json +27 -20
  95. package/src/core/define-env.ts +101 -20
  96. package/src/core/describe.ts +52 -0
  97. package/src/core/errors.ts +14 -0
  98. package/src/core/index.ts +7 -2
  99. package/src/core/secret.ts +127 -0
  100. package/src/index.ts +3 -2
  101. package/src/node/codec.ts +220 -0
  102. package/src/node/edit.ts +279 -0
  103. package/src/node/errors.ts +147 -0
  104. package/src/node/files.ts +41 -0
  105. package/src/node/index.ts +48 -0
  106. package/src/node/key-schemas.ts +44 -0
  107. package/src/node/keys.ts +93 -0
  108. package/src/node/load.ts +386 -0
  109. package/src/node/operation-telemetry.ts +56 -0
  110. package/src/node/references.ts +90 -0
  111. package/src/node/register.ts +18 -0
  112. package/src/{contracts → presets}/common.ts +4 -8
  113. package/src/presets/index.ts +18 -0
  114. package/src/{contracts → presets}/parsing.ts +12 -5
  115. package/src/{contracts → presets}/urls.ts +1 -1
  116. package/dist/contracts/common.d.ts.map +0 -1
  117. package/dist/contracts/common.js.map +0 -1
  118. package/dist/contracts/index.d.ts.map +0 -1
  119. package/dist/contracts/index.js +0 -14
  120. package/dist/contracts/index.js.map +0 -1
  121. package/dist/contracts/parsing.d.ts.map +0 -1
  122. package/dist/contracts/parsing.js.map +0 -1
  123. package/dist/contracts/urls.d.ts.map +0 -1
  124. package/dist/contracts/urls.js.map +0 -1
  125. package/dist/dot.d.ts +0 -67
  126. package/dist/dot.d.ts.map +0 -1
  127. package/dist/dot.js +0 -65
  128. package/dist/dot.js.map +0 -1
  129. package/src/contracts/index.ts +0 -17
  130. package/src/dot.ts +0 -94
@@ -1,19 +1,38 @@
1
1
  import type { CreateEnv, DefaultCombinedSchema } from '@t3-oss/env-core';
2
- import type { ZodType } from 'zod';
3
2
  import { createEnv } from '@t3-oss/env-core';
3
+ import type { StandardSchemaV1 } from '@arki/schema';
4
+ /**
5
+ * The process environment, or `undefined` on engines with no `process` (Hermes, browsers, Deno without
6
+ * Node compat). Reads through `globalThis` so a missing global is not a ReferenceError. Called only when
7
+ * the env is first read, never at import.
8
+ */
9
+ export declare function processEnv(): Record<string, string | undefined> | undefined;
10
+ /** The prefix of a value encrypted in a dotenvx-format env file (`@arki/env/node`). */
11
+ export declare const ENCRYPTED_PREFIX = "encrypted:";
4
12
  /** The issue shape t3-oss hands to `onValidationError`, derived so it stays in sync with the lib. */
5
13
  type ValidationIssues = Parameters<NonNullable<Parameters<typeof createEnv>[0]['onValidationError']>>[0];
14
+ /**
15
+ * Fold validation issues into a human-legible, log-pipeline-safe string. The t3-oss default handler
16
+ * logs `issues` as a raw object, which platforms like Coolify collapse to `[Object]` — so the var
17
+ * that actually failed never reaches the logs. A flat ` path: message` list survives transport.
18
+ */
19
+ export declare function formatIssues(name: string | undefined, issues: ValidationIssues): string;
6
20
  /**
7
21
  * Thin, type-preserving wrapper over `@t3-oss/env-core`'s `createEnv`. Generic over the server,
8
22
  * client, shared and composed schemas so the parsed env keeps its exact inferred types — the wrapper
9
- * adds the ARKI defaults (auto `runtimeEnv`, prod-safe `skipValidation`, a legible labelled error
10
- * handler) without widening any value to `unknown`.
23
+ * adds the ARKI defaults (auto `runtimeEnv`, validation on in every environment, a legible labelled
24
+ * error handler) without widening any value to `unknown`.
25
+ *
26
+ * Every schema is a Standard Schema (Zod, Valibot, ArkType, or a hand-written `~standard` object); each
27
+ * value's type is the schema's OUTPUT, so defaults and coercions in the caller's schemas still apply.
28
+ * Validation is synchronous: a schema whose `validate` returns a Promise or throws makes the first read
29
+ * throw `UnexpectedFailure` with the canonical schema failure, never a validation error.
11
30
  *
12
31
  * `TPrefix` is kept as a string-literal generic on purpose: `createEnv` uses it to enforce that
13
32
  * client keys (and only client keys) carry the prefix. Widening it to `string` would make every
14
33
  * server key collapse to a type-level `ErrorMessage`, so the prefix must stay a literal.
15
34
  */
16
- export declare function defineEnv<TServer extends Record<string, ZodType>, TClient extends Record<string, ZodType> = NonNullable<unknown>, TShared extends Record<string, ZodType> = NonNullable<unknown>, const TPrefix extends string = '', const TExtends extends Record<string, unknown>[] = []>(config: {
35
+ export declare function defineEnv<TServer extends Record<string, StandardSchemaV1>, TClient extends Record<string, StandardSchemaV1> = NonNullable<unknown>, TShared extends Record<string, StandardSchemaV1> = NonNullable<unknown>, const TPrefix extends string = '', const TExtends extends Record<string, unknown>[] = []>(config: {
17
36
  /**
18
37
  * Label shown in validation-error logs, e.g. `'@acme/backend'`. Without it a misconfigured
19
38
  * deploy logs a bare "Invalid environment variables" with no hint which env failed — exactly the
@@ -40,6 +59,12 @@ export declare function defineEnv<TServer extends Record<string, ZodType>, TClie
40
59
  */
41
60
  runtimeEnv?: Record<string, string | boolean | number | undefined>;
42
61
  options?: {
62
+ /**
63
+ * `true` skips schema validation: values pass through raw. Default: validate on the first read,
64
+ * in every environment, unless the process sets `SKIP_ENV_VALIDATION=1` (exactly `1`). That
65
+ * variable is the only implicit opt-out, meant for Docker build stages that prerender without
66
+ * secrets. `CI`, lint and `NODE_ENV` never skip.
67
+ */
43
68
  skipValidation?: boolean;
44
69
  /**
45
70
  * Treat empty-string env values as `undefined` so defaults apply (e.g. `PORT=` in `.env`, or a
@@ -1 +1 @@
1
- {"version":3,"file":"define-env.d.ts","sourceRoot":"","sources":["../../src/core/define-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,qGAAqG;AACrG,KAAK,gBAAgB,GAAG,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAkBzG;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,EAC9D,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,EAC9D,KAAK,CAAC,OAAO,SAAS,MAAM,GAAG,EAAE,EACjC,KAAK,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,EACrD,MAAM,EAAE;IACR;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB;;;;WAIG;QACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,KAAK,CAAC;KACzD,CAAC;CACH,GAAG,SAAS,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAsExE"}
1
+ {"version":3,"file":"define-env.d.ts","sourceRoot":"","sources":["../../src/core/define-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAOrD;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAI3E;AAED,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAK7C,qGAAqG;AACrG,KAAK,gBAAgB,GAAG,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzG;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAOvF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CACvB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAChD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,EACvE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,EACvE,KAAK,CAAC,OAAO,SAAS,MAAM,GAAG,EAAE,EACjC,KAAK,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,EACrD,MAAM,EAAE;IACR;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE;QACR;;;;;WAKG;QACH,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB;;;;WAIG;QACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,KAAK,CAAC;KACzD,CAAC;CACH,GAAG,SAAS,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CA6FxE"}
@@ -1,15 +1,30 @@
1
1
  import { createEnv } from '@t3-oss/env-core';
2
+ import { InvalidInput, orThrow, UnexpectedFailure } from '@arki/failure';
3
+ import { parse, pathOf } from '@arki/schema';
4
+ import { registerEnvDescription } from './describe.js';
5
+ import { InvalidEnvironment } from './errors.js';
6
+ /**
7
+ * The process environment, or `undefined` on engines with no `process` (Hermes, browsers, Deno without
8
+ * Node compat). Reads through `globalThis` so a missing global is not a ReferenceError. Called only when
9
+ * the env is first read, never at import.
10
+ */
11
+ export function processEnv() {
12
+ // Typed as optional on purpose: the Node typings say `process` always exists, Hermes says otherwise.
13
+ const scope = globalThis;
14
+ return scope.process?.env;
15
+ }
16
+ /** The prefix of a value encrypted in a dotenvx-format env file (`@arki/env/node`). */
17
+ export const ENCRYPTED_PREFIX = 'encrypted:';
18
+ const STILL_ENCRYPTED_MESSAGE = 'still encrypted: start the process with `dot env run -- <cmd>`, or import `@arki/env/node/register` first';
2
19
  /**
3
20
  * Fold validation issues into a human-legible, log-pipeline-safe string. The t3-oss default handler
4
21
  * logs `issues` as a raw object, which platforms like Coolify collapse to `[Object]` — so the var
5
22
  * that actually failed never reaches the logs. A flat ` path: message` list survives transport.
6
23
  */
7
- function formatIssues(name, issues) {
24
+ export function formatIssues(name, issues) {
8
25
  const label = name ? `[${name}] ` : '';
9
26
  const lines = issues.map(issue => {
10
- const path = (issue.path ?? [])
11
- .map(seg => (typeof seg === 'object' && seg !== null && 'key' in seg ? String(seg.key) : String(seg)))
12
- .join('.');
27
+ const path = pathOf(issue).map(String).join('.');
13
28
  return ` ${path || '(root)'}: ${issue.message}`;
14
29
  });
15
30
  return `❌ ${label}Invalid environment variables:\n${lines.join('\n')}`;
@@ -17,8 +32,13 @@ function formatIssues(name, issues) {
17
32
  /**
18
33
  * Thin, type-preserving wrapper over `@t3-oss/env-core`'s `createEnv`. Generic over the server,
19
34
  * client, shared and composed schemas so the parsed env keeps its exact inferred types — the wrapper
20
- * adds the ARKI defaults (auto `runtimeEnv`, prod-safe `skipValidation`, a legible labelled error
21
- * handler) without widening any value to `unknown`.
35
+ * adds the ARKI defaults (auto `runtimeEnv`, validation on in every environment, a legible labelled
36
+ * error handler) without widening any value to `unknown`.
37
+ *
38
+ * Every schema is a Standard Schema (Zod, Valibot, ArkType, or a hand-written `~standard` object); each
39
+ * value's type is the schema's OUTPUT, so defaults and coercions in the caller's schemas still apply.
40
+ * Validation is synchronous: a schema whose `validate` returns a Promise or throws makes the first read
41
+ * throw `UnexpectedFailure` with the canonical schema failure, never a validation error.
22
42
  *
23
43
  * `TPrefix` is kept as a string-literal generic on purpose: `createEnv` uses it to enforce that
24
44
  * client keys (and only client keys) carry the prefix. Widening it to `string` would make every
@@ -33,7 +53,11 @@ export function defineEnv(config) {
33
53
  // consumed (boot, a request, a job). This is the same import/observe split
34
54
  // drizzle tables have: define everywhere, observe only at use.
35
55
  const build = () => {
36
- const skipValidation = config.options?.skipValidation ?? (!!process.env.CI || process.env.NODE_ENV !== 'development');
56
+ // Validate everywhere the app runs, production included. The only implicit opt-out is the
57
+ // purpose-named SKIP_ENV_VALIDATION=1 (Docker build stages without secrets). Ambient variables
58
+ // like CI or the npm script name must never turn validation off: that would boot production on
59
+ // an unchecked config.
60
+ const skipValidation = config.options?.skipValidation ?? processEnv()?.SKIP_ENV_VALIDATION === '1';
37
61
  // Use the caller's explicit runtimeEnv when supplied (Vite client vars via import.meta.env, or
38
62
  // fallback aliases). Otherwise auto-build from every declared key (server + client + shared);
39
63
  // createEnv tolerates keys absent from process.env (they parse as undefined → optional/default).
@@ -43,22 +67,34 @@ export function defineEnv(config) {
43
67
  }
44
68
  else {
45
69
  runtimeEnv = {};
70
+ const source = processEnv();
46
71
  for (const key of Object.keys(config.server)) {
47
- runtimeEnv[key] = process.env[key];
72
+ runtimeEnv[key] = source?.[key];
48
73
  }
49
74
  for (const key of Object.keys(config.client ?? {})) {
50
- runtimeEnv[key] = process.env[key];
75
+ runtimeEnv[key] = source?.[key];
51
76
  }
52
77
  for (const key of Object.keys(config.shared ?? {})) {
53
- runtimeEnv[key] = process.env[key];
78
+ runtimeEnv[key] = source?.[key];
54
79
  }
55
80
  }
56
81
  const onValidationError = config.options?.onValidationError ??
57
82
  ((issues) => {
58
83
  const message = formatIssues(config.name, issues);
59
84
  console.error(message);
60
- throw new Error(message);
85
+ // A configuration fault at boot: a typed failure, so a catcher can match on the code.
86
+ throw new UnexpectedFailure(InvalidEnvironment({ report: message }));
61
87
  });
88
+ // A value that is still `encrypted:` means the env files were not loaded (Bun auto-loads them
89
+ // as they are, ciphertext included). A string schema would accept it, so refuse it here, by name.
90
+ if (!skipValidation) {
91
+ const stillEncrypted = Object.keys(runtimeEnv).filter(key => {
92
+ const value = runtimeEnv[key];
93
+ return typeof value === 'string' && value.startsWith(ENCRYPTED_PREFIX);
94
+ });
95
+ if (stillEncrypted.length > 0)
96
+ onValidationError(stillEncrypted.map(key => ({ message: STILL_ENCRYPTED_MESSAGE, path: [key] })));
97
+ }
62
98
  // `createEnv`'s `server`/`client` params are conditional on `clientPrefix` being a *concrete*
63
99
  // string literal; inside a generic wrapper `TPrefix` is abstract, so the body cannot be verified
64
100
  // for all prefixes and the call argument must be asserted once. The PUBLIC contract stays exact:
@@ -66,10 +102,10 @@ export function defineEnv(config) {
66
102
  // caller's `server`/`client`/`extends` are fully type-checked against `TServer`/`TClient`/`TExtends`
67
103
  // at the call site. This is the only seam where types are asserted — value types are never widened.
68
104
  const options = {
69
- shared: config.shared ?? {},
105
+ shared: syncSchemas(config.shared),
70
106
  extends: config.extends,
71
- server: config.server,
72
- client: config.client ?? {},
107
+ server: syncSchemas(config.server),
108
+ client: syncSchemas(config.client),
73
109
  clientPrefix: config.clientPrefix ?? '',
74
110
  runtimeEnv,
75
111
  skipValidation,
@@ -85,7 +121,41 @@ export function defineEnv(config) {
85
121
  const isPrivate = skipValidation ? config.runtimeEnv === undefined : isServer();
86
122
  return { env, isPrivate };
87
123
  };
88
- return lazyEnv(build);
124
+ const env = lazyEnv(build);
125
+ registerEnvDescription(env, {
126
+ ...(config.name === undefined ? {} : { name: config.name }),
127
+ server: config.server,
128
+ ...(config.client === undefined ? {} : { client: config.client }),
129
+ ...(config.shared === undefined ? {} : { shared: config.shared }),
130
+ ...(config.extends === undefined ? {} : { extends: config.extends }),
131
+ });
132
+ return env;
133
+ }
134
+ /**
135
+ * The caller's schemas, each run through `parse` before t3 sees it. t3 would let a throwing validator's
136
+ * raw error escape and report an async one with its own error (leaving a rejection unhandled). Here both
137
+ * fail as `UnexpectedFailure` (canonical validator defect with its private cause, or async mismatch), and bad values still reach t3 as issues.
138
+ */
139
+ function syncSchemas(schemas) {
140
+ const wrapped = {};
141
+ for (const [key, schema] of Object.entries(schemas ?? {}))
142
+ wrapped[key] = syncSchema(schema);
143
+ return wrapped;
144
+ }
145
+ /** Same `version`, `vendor` and `types` as `schema`; only `validate` changes. */
146
+ function syncSchema(schema) {
147
+ return {
148
+ '~standard': {
149
+ ...schema['~standard'],
150
+ validate: value => {
151
+ const result = parse(schema, value);
152
+ if (InvalidInput.is(result))
153
+ return { issues: result.data.issues };
154
+ // env-core expects validator defects to throw; bridge that foreign contract explicitly.
155
+ return { value: orThrow(result) };
156
+ },
157
+ },
158
+ };
89
159
  }
90
160
  /**
91
161
  * Reads an env takes through the lazy view before it copies the values for fast reads. A copy costs
@@ -1 +1 @@
1
- {"version":3,"file":"define-env.js","sourceRoot":"","sources":["../../src/core/define-env.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"define-env.js","sourceRoot":"","sources":["../../src/core/define-env.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,UAAU;IACxB,qGAAqG;IACrG,MAAM,KAAK,GAA8D,UAAU,CAAC;IACpF,OAAO,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC;AAC5B,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE7C,MAAM,uBAAuB,GAC3B,2GAA2G,CAAC;AAK9G;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAwB,EAAE,MAAwB;IAC7E,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,OAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,KAAK,mCAAmC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzE,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAMvB,MA0CD;IAGC,4EAA4E;IAC5E,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,KAAK,GAAG,GAAkB,EAAE;QAChC,0FAA0F;QAC1F,+FAA+F;QAC/F,+FAA+F;QAC/F,uBAAuB;QACvB,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,EAAE,cAAc,IAAI,UAAU,EAAE,EAAE,mBAAmB,KAAK,GAAG,CAAC;QAEnG,+FAA+F;QAC/F,8FAA8F;QAC9F,iGAAiG;QACjG,IAAI,UAAiE,CAAC;QACtE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7C,UAAU,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;gBACnD,UAAU,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;gBACnD,UAAU,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED,MAAM,iBAAiB,GACrB,MAAM,CAAC,OAAO,EAAE,iBAAiB;YACjC,CAAC,CAAC,MAAwB,EAAS,EAAE;gBACnC,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAClD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACvB,sFAAsF;gBACtF,MAAM,IAAI,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QAEL,8FAA8F;QAC9F,kGAAkG;QAClG,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;gBAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;gBAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;YACzE,CAAC,CAAC,CAAC;YACH,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC;gBAAE,iBAAiB,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnI,CAAC;QAED,8FAA8F;QAC9F,iGAAiG;QACjG,iGAAiG;QACjG,kGAAkG;QAClG,qGAAqG;QACrG,oGAAoG;QACpG,MAAM,OAAO,GAAG;YACd,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;YAClC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;YAClC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;YAClC,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;YACvC,UAAU;YACV,cAAc;YACd,6FAA6F;YAC7F,gGAAgG;YAChG,wFAAwF;YACxF,sBAAsB,EAAE,MAAM,CAAC,OAAO,EAAE,sBAAsB,IAAI,IAAI;YACtE,iBAAiB;SACgB,CAAC;QAEpC,MAAM,GAAG,GAAG,SAAS,CAAC,OAA0C,CAAW,CAAC;QAC5E,0FAA0F;QAC1F,kGAAkG;QAClG,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAChF,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,sBAAsB,CAAC,GAAG,EAAE;QAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3D,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QACjE,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QACjE,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;KACrE,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,OAAqD;IACxE,MAAM,OAAO,GAAqC,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7F,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iFAAiF;AACjF,SAAS,UAAU,CAAgB,MAAuC;IACxE,OAAO;QACL,WAAW,EAAE;YACX,GAAG,MAAM,CAAC,WAAW,CAAC;YACtB,QAAQ,EAAE,KAAK,CAAC,EAAE;gBAChB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACpC,IAAI,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC;oBAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACnE,wFAAwF;gBACxF,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,CAAC;SACF;KACF,CAAC;AACJ,CAAC;AAKD;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B;;;;;;;;;;;;;;GAcG;AACH,SAAS,OAAO,CAAmB,KAAqB;IACtD,IAAI,KAAoB,CAAC;IACzB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,MAAM,GAAiC,EAAE,CAAC;IAEhD,MAAM,OAAO,GAAG,GAAM,EAAE;QACtB,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACtC,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC;QACvB,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC;QACnB,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,QAAW,EAAQ,EAAE;QACjC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5C,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,GAAG,IAAI,CAAC;QACd,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC;IAEF,MAAM,OAAO,GAA+C;QAC1D,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YACzB,MAAM,QAAQ,GAAG,OAAO,EAAE,CAAC;YAC3B,IAAI,OAAO,IAAI,EAAE,KAAK,IAAI,iBAAiB,EAAE,CAAC;gBAC5C,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACf,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC1B,CAAC;YACD,OAAO,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACvC,CAAC;QACD,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;YAChC,MAAM,QAAQ,GAAG,OAAO,EAAE,CAAC;YAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YAClD,IAAI,EAAE,IAAI,MAAM;gBAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC5E,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,cAAc,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YACpC,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;YACvD,IAAI,EAAE,IAAI,MAAM;gBAAE,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC3D,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,cAAc,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;QACvD,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC;QAC5D,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACzC,wBAAwB,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC;KACtG,CAAC;IAEF,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,CAAM,CAAC;AACzC,CAAC;AAED,yFAAyF;AACzF,SAAS,QAAQ;IACf,MAAM,EAAE,MAAM,EAAE,GAAG,UAAiC,CAAC;IACrD,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,MAAM,CAAC;AAClD,CAAC;AAED,2FAA2F;AAC3F,SAAS,SAAS,CAAC,QAAgB,EAAE,QAAqB;IACxD,MAAM,KAAK,GAAY,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjE,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAE,KAAyC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACzG,CAAC;AAED,sGAAsG;AACtG,SAAS,QAAQ,CAAC,MAAoC,EAAE,GAAgB,EAAE,KAAc;IACtF,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;QACxB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IACtG,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `describeEnv(env)`: the keys an env built by `defineEnv` declares, which of them are `secret()`,
3
+ * and where they live. It reads the declaration only, never a value, so tools (`dot env check`) can
4
+ * use it on a bare checkout.
5
+ */
6
+ import type { StandardSchemaV1 } from '@arki/schema';
7
+ /** One declared key. */
8
+ export type EnvKeyDescription = {
9
+ readonly key: string;
10
+ /** Declared with `secret()`: must never be plaintext in a tracked file. */
11
+ readonly secret: boolean;
12
+ readonly scope: 'server' | 'client' | 'shared';
13
+ };
14
+ /** What an env declares, its `extends` included. */
15
+ export type EnvDescription = {
16
+ readonly name?: string;
17
+ readonly keys: readonly EnvKeyDescription[];
18
+ };
19
+ /** The description of an env built by `defineEnv`, or `undefined` for any other object. */
20
+ export declare function describeEnv(env: object): EnvDescription | undefined;
21
+ /** Called by `defineEnv` once per env. Later scopes and the env's own keys win over `extends`. */
22
+ export declare function registerEnvDescription(env: object, declaration: {
23
+ readonly name?: string;
24
+ readonly server: Record<string, StandardSchemaV1>;
25
+ readonly client?: Record<string, StandardSchemaV1>;
26
+ readonly shared?: Record<string, StandardSchemaV1>;
27
+ readonly extends?: readonly object[];
28
+ }): void;
29
+ //# sourceMappingURL=describe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../src/core/describe.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIrD,wBAAwB;AACxB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAChD,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,CAAC;AAIrG,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEnE;AAED,kGAAkG;AAClG,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE;IACX,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,GACA,IAAI,CAcN"}
@@ -0,0 +1,25 @@
1
+ import { isSecretSchema } from './secret.js';
2
+ const descriptions = new WeakMap();
3
+ /** The description of an env built by `defineEnv`, or `undefined` for any other object. */
4
+ export function describeEnv(env) {
5
+ return descriptions.get(env);
6
+ }
7
+ /** Called by `defineEnv` once per env. Later scopes and the env's own keys win over `extends`. */
8
+ export function registerEnvDescription(env, declaration) {
9
+ const keys = new Map();
10
+ for (const base of declaration.extends ?? []) {
11
+ for (const key of describeEnv(base)?.keys ?? [])
12
+ keys.set(key.key, key);
13
+ }
14
+ const scopes = [
15
+ ['shared', declaration.shared],
16
+ ['client', declaration.client],
17
+ ['server', declaration.server],
18
+ ];
19
+ for (const [scope, schemas] of scopes) {
20
+ for (const [key, schema] of Object.entries(schemas ?? {}))
21
+ keys.set(key, { key, secret: isSecretSchema(schema), scope });
22
+ }
23
+ descriptions.set(env, declaration.name === undefined ? { keys: [...keys.values()] } : { name: declaration.name, keys: [...keys.values()] });
24
+ }
25
+ //# sourceMappingURL=describe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.js","sourceRoot":"","sources":["../../src/core/describe.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAa7C,MAAM,YAAY,GAAG,IAAI,OAAO,EAA0B,CAAC;AAE3D,2FAA2F;AAC3F,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,sBAAsB,CACpC,GAAW,EACX,WAMC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAA6B,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC7C,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,MAAM,GAAG;QACb,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC;QAC9B,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC;QAC9B,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC;KACtB,CAAC;IACX,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,EAAE,CAAC;QACtC,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3H,CAAC;IACD,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAC9I,CAAC"}
@@ -0,0 +1,10 @@
1
+ /** The process environment does not match the schema, so the app must not boot.
2
+ * @fix Set or correct the variables listed in the report, then start the app again.
3
+ * @verify bun run test -- src/core/define-env.test.ts
4
+ */
5
+ export declare const InvalidEnvironment: import("@arki/failure").FailureDefinition<"env.invalid_environment", import("@standard-schema/spec").StandardSchemaV1<{
6
+ report: string;
7
+ }, {
8
+ report: string;
9
+ }>>;
10
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;GAK7B,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ import { defineFailure } from '@arki/failure';
3
+ /** The process environment does not match the schema, so the app must not boot.
4
+ * @fix Set or correct the variables listed in the report, then start the app again.
5
+ * @verify bun run test -- src/core/define-env.test.ts
6
+ */
7
+ export const InvalidEnvironment = /* @__PURE__ */ defineFailure('env.invalid_environment', {
8
+ status: 500,
9
+ data: z.object({ report: z.string() }),
10
+ message: data => data.report,
11
+ remediation: 'Set or correct the variables listed in the report, then start the app again.',
12
+ });
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC,aAAa,CAAC,yBAAyB,EAAE;IACzF,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACtC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM;IAC5B,WAAW,EAAE,8EAA8E;CAC5F,CAAC,CAAC"}
@@ -1,3 +1,8 @@
1
- export { defineEnv } from './define-env.js';
2
- export { contracts } from '../contracts/index.js';
1
+ /** `defineEnv()` plus `secret()`, `reveal()` and `Redacted` for typed, lazily validated environments. */
2
+ export { defineEnv, formatIssues } from './define-env.js';
3
+ export { InvalidEnvironment } from './errors.js';
4
+ export { isSecretSchema, Redacted, reveal, secret, SECRET_SCHEMA } from './secret.js';
5
+ export { describeEnv } from './describe.js';
6
+ export type { EnvDescription, EnvKeyDescription } from './describe.js';
7
+ export { presets } from '../presets/index.js';
3
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1,3 +1,7 @@
1
- export { defineEnv } from './define-env.js';
2
- export { contracts } from '../contracts/index.js';
1
+ /** `defineEnv()` plus `secret()`, `reveal()` and `Redacted` for typed, lazily validated environments. */
2
+ export { defineEnv, formatIssues } from './define-env.js';
3
+ export { InvalidEnvironment } from './errors.js';
4
+ export { isSecretSchema, Redacted, reveal, secret, SECRET_SCHEMA } from './secret.js';
5
+ export { describeEnv } from './describe.js';
6
+ export { presets } from '../presets/index.js';
3
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Secrets that cannot leak by accident.
3
+ *
4
+ * `secret(schema)` parses a variable as usual and wraps the result in a
5
+ * {@link Redacted}. Printing it, logging it, serialising it or putting it in a
6
+ * template string all show `[redacted]`. Reading the value takes a deliberate,
7
+ * greppable `reveal(env.KEY)` call at the one place that needs it.
8
+ *
9
+ * Redaction guards against accidents (a logged config object, an error
10
+ * message with the env in it). It is not encryption: code that calls
11
+ * `reveal` gets the plain value.
12
+ */
13
+ import type { SchemaInput, SchemaOutput, StandardSchemaV1 } from '@arki/schema';
14
+ /** Node's `util.inspect` hook, so `console.log(env)` shows `[redacted]` too. */
15
+ declare const inspect: unique symbol;
16
+ /** A value that prints as `[redacted]`. Read it with {@link reveal}. */
17
+ export declare class Redacted<Value = string> {
18
+ #private;
19
+ constructor(value: Value);
20
+ toString(): string;
21
+ toJSON(): string;
22
+ [inspect](): string;
23
+ }
24
+ /** What {@link secret} returns: a Standard Schema whose output is redacted, with an `.optional()` variant. */
25
+ export type SecretSchema<Schema extends StandardSchemaV1> = StandardSchemaV1<SchemaInput<Schema>, Redacted<SchemaOutput<Schema>>> & {
26
+ readonly [SECRET_SCHEMA]: true;
27
+ /** Accepts a missing variable. `undefined` passes through as `undefined`, never redacted. */
28
+ optional(): StandardSchemaV1<SchemaInput<Schema> | undefined, Redacted<SchemaOutput<Schema>> | undefined> & {
29
+ readonly [SECRET_SCHEMA]: true;
30
+ };
31
+ };
32
+ /** Marks a schema built by {@link secret}, so `describeEnv` can tell secrets apart. */
33
+ export declare const SECRET_SCHEMA: unique symbol;
34
+ /** True for a schema built by {@link secret} (or its `.optional()`). */
35
+ export declare function isSecretSchema(schema: StandardSchemaV1): boolean;
36
+ /**
37
+ * Validate with `schema` (any Standard Schema), then wrap the value in a {@link Redacted}. For an
38
+ * optional secret, call `.optional()`: `secret(z.string()).optional()`. The inner result passes
39
+ * straight through: an async inner schema yields a promise, so `defineEnv`'s synchronous check
40
+ * fails with the same `UnexpectedFailure` it would give without `secret`, and a throwing validator
41
+ * still throws.
42
+ *
43
+ * @example
44
+ * const env = defineEnv({ server: { STRIPE_KEY: secret(z.string().min(1)) } });
45
+ * console.log(env.STRIPE_KEY); // [redacted]
46
+ * stripe(reveal(env.STRIPE_KEY)); // the key
47
+ */
48
+ export declare function secret<Schema extends StandardSchemaV1>(schema: Schema): SecretSchema<Schema>;
49
+ /**
50
+ * The plain value of a secret. Also takes the raw value, because when
51
+ * validation is skipped (`skipValidation`, or `CI` builds) schemas do not run
52
+ * and the variable is still a plain string.
53
+ */
54
+ export declare function reveal<Value>(value: Redacted<Value> | Value): Value;
55
+ export {};
56
+ //# sourceMappingURL=secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/core/secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKhF,gFAAgF;AAChF,QAAA,MAAM,OAAO,EAAE,OAAO,MAAiD,CAAC;AAKxE,wEAAwE;AACxE,qBAAa,QAAQ,CAAC,KAAK,GAAG,MAAM;;IAQlC,YAAY,KAAK,EAAE,KAAK,EAEvB;IAED,QAAQ,IAAI,MAAM,CAEjB;IAED,MAAM,IAAI,MAAM,CAEf;IAED,CAAC,OAAO,CAAC,IAAI,MAAM,CAElB;CACF;AAED,8GAA8G;AAC9G,MAAM,MAAM,YAAY,CAAC,MAAM,SAAS,gBAAgB,IAAI,gBAAgB,CAC1E,WAAW,CAAC,MAAM,CAAC,EACnB,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAC/B,GAAG;IACF,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,IAAI,gBAAgB,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG;QAC1G,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;KAChC,CAAC;CACH,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,aAAa,EAAE,OAAO,MAAsC,CAAC;AAE1E,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAEhE;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,MAAM,SAAS,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CA6B5F;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK,CAEnE"}
@@ -0,0 +1,85 @@
1
+ /** What a redacted value prints as, everywhere. */
2
+ const REDACTED_TEXT = '[redacted]';
3
+ /** Node's `util.inspect` hook, so `console.log(env)` shows `[redacted]` too. */
4
+ const inspect = Symbol.for('nodejs.util.inspect.custom');
5
+ /** Reads the private value. Assigned in the class's static block, so only this module can read it. */
6
+ let read;
7
+ /** A value that prints as `[redacted]`. Read it with {@link reveal}. */
8
+ export class Redacted {
9
+ // A private field: invisible to JSON, `Object.keys`, spreading and `util.inspect`.
10
+ #value;
11
+ static {
12
+ read = redacted => redacted.#value;
13
+ }
14
+ constructor(value) {
15
+ this.#value = value;
16
+ }
17
+ toString() {
18
+ return REDACTED_TEXT;
19
+ }
20
+ toJSON() {
21
+ return REDACTED_TEXT;
22
+ }
23
+ [inspect]() {
24
+ return REDACTED_TEXT;
25
+ }
26
+ }
27
+ /** Marks a schema built by {@link secret}, so `describeEnv` can tell secrets apart. */
28
+ export const SECRET_SCHEMA = Symbol.for('arki.env.secret');
29
+ /** True for a schema built by {@link secret} (or its `.optional()`). */
30
+ export function isSecretSchema(schema) {
31
+ return SECRET_SCHEMA in schema;
32
+ }
33
+ /**
34
+ * What every issue of a rejected secret says. Vendors such as valibot put the received value in
35
+ * their messages (`Received "sk_live_…"`), and `defineEnv` logs and throws issues, so a secret's
36
+ * issues keep only their path.
37
+ */
38
+ const HIDDEN_ISSUE_MESSAGE = 'Invalid secret (details hidden so the value cannot leak)';
39
+ /**
40
+ * Validate with `schema` (any Standard Schema), then wrap the value in a {@link Redacted}. For an
41
+ * optional secret, call `.optional()`: `secret(z.string()).optional()`. The inner result passes
42
+ * straight through: an async inner schema yields a promise, so `defineEnv`'s synchronous check
43
+ * fails with the same `UnexpectedFailure` it would give without `secret`, and a throwing validator
44
+ * still throws.
45
+ *
46
+ * @example
47
+ * const env = defineEnv({ server: { STRIPE_KEY: secret(z.string().min(1)) } });
48
+ * console.log(env.STRIPE_KEY); // [redacted]
49
+ * stripe(reveal(env.STRIPE_KEY)); // the key
50
+ */
51
+ export function secret(schema) {
52
+ const { version, vendor } = schema['~standard'];
53
+ const seal = (result) => result.issues === undefined
54
+ ? { value: new Redacted(result.value) }
55
+ : {
56
+ issues: result.issues.map(issue => issue.path === undefined
57
+ ? { message: HIDDEN_ISSUE_MESSAGE }
58
+ : { message: HIDDEN_ISSUE_MESSAGE, path: issue.path }),
59
+ };
60
+ const redact = (value) => {
61
+ const result = schema['~standard'].validate(value);
62
+ return result instanceof Promise ? result.then(seal) : seal(result);
63
+ };
64
+ return {
65
+ [SECRET_SCHEMA]: true,
66
+ '~standard': { version, vendor, validate: redact },
67
+ optional: () => ({
68
+ [SECRET_SCHEMA]: true,
69
+ '~standard': {
70
+ version,
71
+ vendor,
72
+ validate: value => (value === undefined ? { value: undefined } : redact(value)),
73
+ },
74
+ }),
75
+ };
76
+ }
77
+ /**
78
+ * The plain value of a secret. Also takes the raw value, because when
79
+ * validation is skipped (`skipValidation`, or `CI` builds) schemas do not run
80
+ * and the variable is still a plain string.
81
+ */
82
+ export function reveal(value) {
83
+ return value instanceof Redacted ? read(value) : value;
84
+ }
85
+ //# sourceMappingURL=secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.js","sourceRoot":"","sources":["../../src/core/secret.ts"],"names":[],"mappings":"AAcA,mDAAmD;AACnD,MAAM,aAAa,GAAG,YAAY,CAAC;AAEnC,gFAAgF;AAChF,MAAM,OAAO,GAAkB,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAExE,sGAAsG;AACtG,IAAI,IAAiD,CAAC;AAEtD,wEAAwE;AACxE,MAAM,OAAO,QAAQ;IACnB,mFAAmF;IAC1E,MAAM,CAAQ;IAEvB;QACE,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;IACrC,CAAC;IAED,YAAY,KAAY;QACtB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,QAAQ;QACN,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,MAAM;QACJ,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,CAAC,OAAO,CAAC;QACP,OAAO,aAAa,CAAC;IACvB,CAAC;CACF;AAcD,uFAAuF;AACvF,MAAM,CAAC,MAAM,aAAa,GAAkB,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAE1E,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,OAAO,aAAa,IAAI,MAAM,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,0DAA0D,CAAC;AAExF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,MAAM,CAAkC,MAAc;IACpE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAEhD,MAAM,IAAI,GAAG,CAAC,MAAqD,EAAU,EAAE,CAC7E,MAAM,CAAC,MAAM,KAAK,SAAS;QACzB,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;QACvC,CAAC,CAAC;YACE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAChC,KAAK,CAAC,IAAI,KAAK,SAAS;gBACtB,CAAC,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE;gBACnC,CAAC,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CACxD;SACF,CAAC;IACR,MAAM,MAAM,GAAG,CAAC,KAAc,EAA4B,EAAE;QAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnD,OAAO,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtE,CAAC,CAAC;IACF,OAAO;QACL,CAAC,aAAa,CAAC,EAAE,IAAI;QACrB,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;QAClD,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;YACf,CAAC,aAAa,CAAC,EAAE,IAAI;YACrB,WAAW,EAAE;gBACX,OAAO;gBACP,MAAM;gBACN,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aAChF;SACF,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAQ,KAA8B;IAC1D,OAAO,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC"}