@kici-dev/shared 0.0.0 → 0.1.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/LICENSE +202 -0
- package/README.md +1 -6
- package/dist/chunk-gOLHoazu.js +4 -0
- package/dist/cold-store/bucket.d.ts +26 -0
- package/dist/cold-store/bucket.js +40 -0
- package/dist/cold-store/bucket.test.d.ts +2 -0
- package/dist/cold-store/chunk-encoder.d.ts +63 -0
- package/dist/cold-store/chunk-encoder.js +94 -0
- package/dist/cold-store/chunk-encoder.test.d.ts +2 -0
- package/dist/cold-store/chunk-id.d.ts +10 -0
- package/dist/cold-store/chunk-id.js +29 -0
- package/dist/cold-store/chunk-id.test.d.ts +2 -0
- package/dist/cold-store/cold-store.d.ts +345 -0
- package/dist/cold-store/cold-store.js +1078 -0
- package/dist/cold-store/cold-store.test.d.ts +2 -0
- package/dist/cold-store/config.d.ts +62 -0
- package/dist/cold-store/config.js +28 -0
- package/dist/cold-store/index.d.ts +20 -0
- package/dist/cold-store/index.js +11 -0
- package/dist/cold-store/key.d.ts +89 -0
- package/dist/cold-store/key.js +88 -0
- package/dist/cold-store/key.test.d.ts +2 -0
- package/dist/cold-store/lru.d.ts +33 -0
- package/dist/cold-store/lru.js +59 -0
- package/dist/cold-store/lru.test.d.ts +2 -0
- package/dist/cold-store/manifest.d.ts +11 -0
- package/dist/cold-store/manifest.js +57 -0
- package/dist/cold-store/manifest.test.d.ts +2 -0
- package/dist/cold-store/metrics.d.ts +103 -0
- package/dist/cold-store/metrics.js +179 -0
- package/dist/cold-store/table-adapter.d.ts +229 -0
- package/dist/cold-store/table-adapter.js +2 -0
- package/dist/cold-store/types.d.ts +111 -0
- package/dist/cold-store/types.js +2 -0
- package/dist/crypto.d.ts +33 -0
- package/dist/crypto.js +67 -0
- package/dist/db-admin.d.ts +1422 -0
- package/dist/db-admin.js +2525 -0
- package/dist/db-admin.test.d.ts +2 -0
- package/dist/db.d.ts +14 -0
- package/dist/db.js +23 -0
- package/dist/env/allowlist.d.ts +79 -0
- package/dist/env/allowlist.js +86 -0
- package/dist/env/define-env.d.ts +172 -0
- package/dist/env/define-env.js +295 -0
- package/dist/env/define-env.test.d.ts +2 -0
- package/dist/env/env-rule-allowlist.test.d.ts +2 -0
- package/dist/env/index.d.ts +11 -0
- package/dist/env/index.js +4 -0
- package/dist/env/logger-env.d.ts +30 -0
- package/dist/env/logger-env.js +95 -0
- package/dist/error.d.ts +16 -0
- package/dist/error.js +58 -0
- package/dist/error.test.d.ts +2 -0
- package/dist/format-bytes.d.ts +5 -0
- package/dist/format-bytes.js +15 -0
- package/dist/format-bytes.test.d.ts +2 -0
- package/dist/format-duration.d.ts +11 -0
- package/dist/format-duration.js +32 -0
- package/dist/format-duration.test.d.ts +2 -0
- package/dist/graceful-shutdown.d.ts +58 -0
- package/dist/graceful-shutdown.js +71 -0
- package/dist/graceful-shutdown.test.d.ts +2 -0
- package/dist/idempotency-files.d.ts +113 -0
- package/dist/idempotency-files.js +190 -0
- package/dist/idempotency-files.test.d.ts +2 -0
- package/dist/idempotency.d.ts +68 -0
- package/dist/idempotency.js +49 -0
- package/dist/idempotency.test.d.ts +2 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +31 -0
- package/dist/logger.d.ts +57 -0
- package/dist/logger.js +175 -0
- package/dist/logger.test.d.ts +2 -0
- package/dist/reconnect-delay.d.ts +11 -0
- package/dist/reconnect-delay.js +22 -0
- package/dist/request-context.d.ts +42 -0
- package/dist/request-context.js +37 -0
- package/dist/ring-buffer.d.ts +35 -0
- package/dist/ring-buffer.js +62 -0
- package/dist/ring-buffer.test.d.ts +2 -0
- package/dist/routes/health.d.ts +26 -0
- package/dist/routes/health.js +45 -0
- package/dist/routes/health.test.d.ts +2 -0
- package/dist/routes/metrics.d.ts +20 -0
- package/dist/routes/metrics.js +38 -0
- package/dist/s3-client.d.ts +42 -0
- package/dist/s3-client.js +25 -0
- package/dist/telemetry/index.d.ts +3 -0
- package/dist/telemetry/index.js +4 -0
- package/dist/telemetry/init.d.ts +20 -0
- package/dist/telemetry/init.js +42 -0
- package/dist/telemetry/init.test.d.ts +2 -0
- package/dist/telemetry/metrics.d.ts +8 -0
- package/dist/telemetry/metrics.js +16 -0
- package/dist/tool-check.d.ts +33 -0
- package/dist/tool-check.js +60 -0
- package/dist/tool-check.test.d.ts +2 -0
- package/dist/ts-loader-hook.d.ts +26 -0
- package/dist/ts-loader-hook.js +48 -0
- package/dist/zx.d.ts +8 -0
- package/dist/zx.js +78 -0
- package/package.json +69 -5
- package/sbom.spdx.json +8775 -0
- package/index.js +0 -3
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import pg from 'pg';
|
|
2
|
+
import { Kysely } from 'kysely';
|
|
3
|
+
/**
|
|
4
|
+
* Create PostgreSQL connection pool.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createPool(databaseUrl: string): pg.Pool;
|
|
7
|
+
/**
|
|
8
|
+
* Create Kysely database instance (PostgreSQL only).
|
|
9
|
+
*
|
|
10
|
+
* Generic over the database type so each consumer can provide
|
|
11
|
+
* its own schema type (e.g., orchestrator Database vs Platform Database).
|
|
12
|
+
*/
|
|
13
|
+
export declare function createDb<T>(pool: pg.Pool): Kysely<T>;
|
|
14
|
+
//# sourceMappingURL=db.d.ts.map
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "./chunk-gOLHoazu.js";
|
|
2
|
+
import pg from "pg";
|
|
3
|
+
import { Kysely, PostgresDialect } from "kysely";
|
|
4
|
+
//#region src/db.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create PostgreSQL connection pool.
|
|
7
|
+
*/
|
|
8
|
+
function createPool(databaseUrl) {
|
|
9
|
+
return new pg.Pool({ connectionString: databaseUrl });
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create Kysely database instance (PostgreSQL only).
|
|
13
|
+
*
|
|
14
|
+
* Generic over the database type so each consumer can provide
|
|
15
|
+
* its own schema type (e.g., orchestrator Database vs Platform Database).
|
|
16
|
+
*/
|
|
17
|
+
function createDb(pool) {
|
|
18
|
+
return new Kysely({ dialect: new PostgresDialect({ pool }) });
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createDb, createPool };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source of truth for the env-var allowlist.
|
|
3
|
+
*
|
|
4
|
+
* KiCI's naming convention:
|
|
5
|
+
*
|
|
6
|
+
* > Every env var read by KiCI's own code MUST start with `KICI_`,
|
|
7
|
+
* > except for a fixed allowlist of OS / SDK names that KiCI consumes
|
|
8
|
+
* > but does not own (PATH, AWS_*, REDIS_*, OTEL_*, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Two artefacts read this file:
|
|
11
|
+
*
|
|
12
|
+
* 1. `packages/shared/src/env/env-rule-allowlist.test.ts` — a runtime
|
|
13
|
+
* backstop that walks the source tree and matches every
|
|
14
|
+
* `process[.]env[.]NAME` access against `IS_ALLOWED_ENV_NAME(NAME)`.
|
|
15
|
+
* Catches any read that the inline ESLint rule missed. The backstop
|
|
16
|
+
* strips template-literal contents before scanning, so workflow
|
|
17
|
+
* source code embedded in fixture-emitting helpers does NOT
|
|
18
|
+
* contribute to the read set.
|
|
19
|
+
* 2. `eslint.config.js` — an inline `no-restricted-syntax` rule that
|
|
20
|
+
* mirrors the same regex at lint time. The eslint config builds the
|
|
21
|
+
* regex INLINE (it can't import from a TS file at lint init), so any
|
|
22
|
+
* change here MUST also be reflected there. The backstop test fails
|
|
23
|
+
* loudly if the two drift.
|
|
24
|
+
*
|
|
25
|
+
* The phased rename that introduced this rule (P0–P8) is complete: the
|
|
26
|
+
* `MIGRATING_ENV_VARS` ratchet is gone and `KICI_*` is the only accepted
|
|
27
|
+
* project-internal namespace. New env vars MUST use `KICI_*` — see
|
|
28
|
+
* `.claude/rules/env-vars.md` for the convention and how to add one
|
|
29
|
+
* (always `KICI_*`, register in the relevant package's `defineEnv`
|
|
30
|
+
* envMap, regenerate `docs/operator/env-reference.md`, done).
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Anchored regex matching the OS / SDK / external-system env var names
|
|
34
|
+
* that KiCI is the *consumer* of. KiCI does not own these names, so it
|
|
35
|
+
* can't prefix them. Plus the `KICI_*` prefix itself for everything we
|
|
36
|
+
* do own.
|
|
37
|
+
*
|
|
38
|
+
* Categories:
|
|
39
|
+
* - `KICI_*` — every project-internal env var.
|
|
40
|
+
* - OS basics — NODE_ENV, HOME, PATH, TZ, LANG, TMPDIR,
|
|
41
|
+
* USER, USERNAME, SHELL, COMSPEC, PWD, OLDPWD,
|
|
42
|
+
* HOSTNAME.
|
|
43
|
+
* - Terminal / display — COLUMNS, LINES, TERM, COLORTERM, DISPLAY,
|
|
44
|
+
* WAYLAND_DISPLAY, LOCALAPPDATA.
|
|
45
|
+
* - XDG basedir spec — XDG_*.
|
|
46
|
+
* - npm / SSH wrappers — INIT_CWD, npm_*, SSH_*.
|
|
47
|
+
* - CI provider hints — CI, GITHUB_ACTIONS, GITHUB_ENV, GITHUB_OUTPUT,
|
|
48
|
+
* GITHUB_PATH, GITHUB_STEP_SUMMARY, GITLAB_CI.
|
|
49
|
+
* - Cloud / SDK names — AWS_*, REDIS_*, OTEL_*, STRIPE_*, DOCKER_*,
|
|
50
|
+
* CONTAINER_HOST.
|
|
51
|
+
* - Postgres libpq — PGHOST, PGPORT, PGUSER, PGPASSWORD,
|
|
52
|
+
* PGDATABASE, PGSERVICEFILE, PGSSLMODE.
|
|
53
|
+
* - Forgejo dev server — FORGEJO_URL, FORGEJO_CONTAINER (read by the
|
|
54
|
+
* staging Forgejo bootstrap script; conventional
|
|
55
|
+
* names owned by Forgejo, not KiCI).
|
|
56
|
+
* - Zitadel SDK config — ZITADEL_* (canonical names for the staging
|
|
57
|
+
* Zitadel client config: issuer, project ID,
|
|
58
|
+
* CLI client ID, test/admin user SUBs, the
|
|
59
|
+
* staging Terraform PAT, and the management
|
|
60
|
+
* API base URL). NOT aliases of `KICI_OIDC_*` —
|
|
61
|
+
* these are the names Zitadel itself uses.
|
|
62
|
+
* - Vite client config — VITE_* (Vite reserves this prefix for env
|
|
63
|
+
* vars exposed to client bundles; `VITE_BASE`
|
|
64
|
+
* and `VITE_DOCS_BASE_URL` flow through this
|
|
65
|
+
* channel).
|
|
66
|
+
* - Playwright runtime — PLAYWRIGHT (test-runner-set switch the
|
|
67
|
+
* dashboard's vite.config consults to disable
|
|
68
|
+
* the dev proxy), HEADED (Playwright convention
|
|
69
|
+
* for `--headed` runs).
|
|
70
|
+
*/
|
|
71
|
+
export declare const OS_SDK_ALLOWLIST_REGEX: RegExp;
|
|
72
|
+
/**
|
|
73
|
+
* Returns true when `name` is allowed under the KiCI env-var convention:
|
|
74
|
+
* it matches the OS/SDK allowlist regex (which includes the `KICI_*`
|
|
75
|
+
* prefix). The phased migration is complete; there is no longer a
|
|
76
|
+
* separate "migrating" set.
|
|
77
|
+
*/
|
|
78
|
+
export declare function IS_ALLOWED_ENV_NAME(name: string): boolean;
|
|
79
|
+
//# sourceMappingURL=allowlist.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/env/allowlist.ts
|
|
3
|
+
/**
|
|
4
|
+
* Source of truth for the env-var allowlist.
|
|
5
|
+
*
|
|
6
|
+
* KiCI's naming convention:
|
|
7
|
+
*
|
|
8
|
+
* > Every env var read by KiCI's own code MUST start with `KICI_`,
|
|
9
|
+
* > except for a fixed allowlist of OS / SDK names that KiCI consumes
|
|
10
|
+
* > but does not own (PATH, AWS_*, REDIS_*, OTEL_*, etc.).
|
|
11
|
+
*
|
|
12
|
+
* Two artefacts read this file:
|
|
13
|
+
*
|
|
14
|
+
* 1. `packages/shared/src/env/env-rule-allowlist.test.ts` — a runtime
|
|
15
|
+
* backstop that walks the source tree and matches every
|
|
16
|
+
* `process[.]env[.]NAME` access against `IS_ALLOWED_ENV_NAME(NAME)`.
|
|
17
|
+
* Catches any read that the inline ESLint rule missed. The backstop
|
|
18
|
+
* strips template-literal contents before scanning, so workflow
|
|
19
|
+
* source code embedded in fixture-emitting helpers does NOT
|
|
20
|
+
* contribute to the read set.
|
|
21
|
+
* 2. `eslint.config.js` — an inline `no-restricted-syntax` rule that
|
|
22
|
+
* mirrors the same regex at lint time. The eslint config builds the
|
|
23
|
+
* regex INLINE (it can't import from a TS file at lint init), so any
|
|
24
|
+
* change here MUST also be reflected there. The backstop test fails
|
|
25
|
+
* loudly if the two drift.
|
|
26
|
+
*
|
|
27
|
+
* The phased rename that introduced this rule (P0–P8) is complete: the
|
|
28
|
+
* `MIGRATING_ENV_VARS` ratchet is gone and `KICI_*` is the only accepted
|
|
29
|
+
* project-internal namespace. New env vars MUST use `KICI_*` — see
|
|
30
|
+
* `.claude/rules/env-vars.md` for the convention and how to add one
|
|
31
|
+
* (always `KICI_*`, register in the relevant package's `defineEnv`
|
|
32
|
+
* envMap, regenerate `docs/operator/env-reference.md`, done).
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Anchored regex matching the OS / SDK / external-system env var names
|
|
36
|
+
* that KiCI is the *consumer* of. KiCI does not own these names, so it
|
|
37
|
+
* can't prefix them. Plus the `KICI_*` prefix itself for everything we
|
|
38
|
+
* do own.
|
|
39
|
+
*
|
|
40
|
+
* Categories:
|
|
41
|
+
* - `KICI_*` — every project-internal env var.
|
|
42
|
+
* - OS basics — NODE_ENV, HOME, PATH, TZ, LANG, TMPDIR,
|
|
43
|
+
* USER, USERNAME, SHELL, COMSPEC, PWD, OLDPWD,
|
|
44
|
+
* HOSTNAME.
|
|
45
|
+
* - Terminal / display — COLUMNS, LINES, TERM, COLORTERM, DISPLAY,
|
|
46
|
+
* WAYLAND_DISPLAY, LOCALAPPDATA.
|
|
47
|
+
* - XDG basedir spec — XDG_*.
|
|
48
|
+
* - npm / SSH wrappers — INIT_CWD, npm_*, SSH_*.
|
|
49
|
+
* - CI provider hints — CI, GITHUB_ACTIONS, GITHUB_ENV, GITHUB_OUTPUT,
|
|
50
|
+
* GITHUB_PATH, GITHUB_STEP_SUMMARY, GITLAB_CI.
|
|
51
|
+
* - Cloud / SDK names — AWS_*, REDIS_*, OTEL_*, STRIPE_*, DOCKER_*,
|
|
52
|
+
* CONTAINER_HOST.
|
|
53
|
+
* - Postgres libpq — PGHOST, PGPORT, PGUSER, PGPASSWORD,
|
|
54
|
+
* PGDATABASE, PGSERVICEFILE, PGSSLMODE.
|
|
55
|
+
* - Forgejo dev server — FORGEJO_URL, FORGEJO_CONTAINER (read by the
|
|
56
|
+
* staging Forgejo bootstrap script; conventional
|
|
57
|
+
* names owned by Forgejo, not KiCI).
|
|
58
|
+
* - Zitadel SDK config — ZITADEL_* (canonical names for the staging
|
|
59
|
+
* Zitadel client config: issuer, project ID,
|
|
60
|
+
* CLI client ID, test/admin user SUBs, the
|
|
61
|
+
* staging Terraform PAT, and the management
|
|
62
|
+
* API base URL). NOT aliases of `KICI_OIDC_*` —
|
|
63
|
+
* these are the names Zitadel itself uses.
|
|
64
|
+
* - Vite client config — VITE_* (Vite reserves this prefix for env
|
|
65
|
+
* vars exposed to client bundles; `VITE_BASE`
|
|
66
|
+
* and `VITE_DOCS_BASE_URL` flow through this
|
|
67
|
+
* channel).
|
|
68
|
+
* - Playwright runtime — PLAYWRIGHT (test-runner-set switch the
|
|
69
|
+
* dashboard's vite.config consults to disable
|
|
70
|
+
* the dev proxy), HEADED (Playwright convention
|
|
71
|
+
* for `--headed` runs).
|
|
72
|
+
*/
|
|
73
|
+
const OS_SDK_ALLOWLIST_REGEX = /^(KICI_.*|NODE_ENV|HOME|PATH|TZ|LANG|TMPDIR|USER|USERNAME|SHELL|COMSPEC|PWD|OLDPWD|HOSTNAME|COLUMNS|LINES|TERM|COLORTERM|DISPLAY|WAYLAND_DISPLAY|LOCALAPPDATA|XDG_CACHE_HOME|XDG_CONFIG_HOME|XDG_DATA_HOME|XDG_RUNTIME_DIR|XDG_STATE_HOME|INIT_CWD|npm_.*|SSH_.*|CI|GITHUB_ACTIONS|GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH|GITHUB_STEP_SUMMARY|GITLAB_CI|AWS_.*|REDIS_.*|OTEL_.*|STRIPE_.*|DOCKER_.*|CONTAINER_HOST|container|PGHOST|PGPORT|PGUSER|PGPASSWORD|PGDATABASE|PGSERVICEFILE|PGSSLMODE|FORGEJO_URL|FORGEJO_CONTAINER|ZITADEL_.*|VITE_.*|PLAYWRIGHT|HEADED)$/;
|
|
74
|
+
/**
|
|
75
|
+
* Returns true when `name` is allowed under the KiCI env-var convention:
|
|
76
|
+
* it matches the OS/SDK allowlist regex (which includes the `KICI_*`
|
|
77
|
+
* prefix). The phased migration is complete; there is no longer a
|
|
78
|
+
* separate "migrating" set.
|
|
79
|
+
*/
|
|
80
|
+
function IS_ALLOWED_ENV_NAME(name) {
|
|
81
|
+
return OS_SDK_ALLOWLIST_REGEX.test(name);
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { IS_ALLOWED_ENV_NAME, OS_SDK_ALLOWLIST_REGEX };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=allowlist.js.map
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineEnv — small helper that ties a Zod schema to its env-var mapping so
|
|
3
|
+
* services can:
|
|
4
|
+
* 1. parse process.env into a typed config (the existing pattern, just
|
|
5
|
+
* centralised),
|
|
6
|
+
* 2. reject unknown KICI_* env vars (typo catcher),
|
|
7
|
+
* 3. emit machine-readable field metadata for docs/operator/env-reference.md.
|
|
8
|
+
*
|
|
9
|
+
* The helper deliberately stays small. Each service still owns its schema
|
|
10
|
+
* (defaults, refinements, cross-field rules); we only standardise the boring
|
|
11
|
+
* env-name <-> field-name plumbing and the "did you mean ...?" UX.
|
|
12
|
+
*
|
|
13
|
+
* `envMap` accepts:
|
|
14
|
+
* - a string: the single env var that backs this top-level field, OR
|
|
15
|
+
* - a string[]: multiple aliases, first non-undefined wins (legacy
|
|
16
|
+
* pattern; the only historical user was the agent's
|
|
17
|
+
* KICI_JOB_HEARTBEAT_INTERVAL_MS alias, collapsed in P3 of the
|
|
18
|
+
* env-var standardization plan). OR
|
|
19
|
+
* - an object: nested map for fields that are themselves z.object(...)
|
|
20
|
+
* (orchestrator's `cluster: { instanceId: '...', ... }`). Nested objects
|
|
21
|
+
* can recurse arbitrarily, but in practice we only need one level.
|
|
22
|
+
*
|
|
23
|
+
* The schema may be a plain z.object(...) OR a `.superRefine`'d composition
|
|
24
|
+
* built on top of one (orchestrator does this for cross-field rules). We keep
|
|
25
|
+
* the input type as `z.ZodType` so both shapes are accepted; we reach into
|
|
26
|
+
* `.shape` only when the caller asks for `describe()` output.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
/** Recursive env-var map. Leaf is the env var name (or array of aliases). */
|
|
30
|
+
export type EnvMapValue = string | string[] | EnvMap;
|
|
31
|
+
export interface EnvMap {
|
|
32
|
+
[field: string]: EnvMapValue;
|
|
33
|
+
}
|
|
34
|
+
/** Description of a single env var, suitable for docs generation. */
|
|
35
|
+
export interface EnvFieldSpec {
|
|
36
|
+
/** Env var name (the canonical one when there are aliases). */
|
|
37
|
+
envVar: string;
|
|
38
|
+
/** Aliases (other env vars that map to the same field). */
|
|
39
|
+
aliases: string[];
|
|
40
|
+
/** Dotted JS path inside the parsed config (e.g. `cluster.instanceId`). */
|
|
41
|
+
fieldPath: string;
|
|
42
|
+
/** Whether the field is required (no default, not optional). */
|
|
43
|
+
required: boolean;
|
|
44
|
+
/** Default value, if present, formatted as a string. */
|
|
45
|
+
defaultValue?: string;
|
|
46
|
+
/** Type label (e.g. `string`, `number`, `enum:a|b`, `boolean`). */
|
|
47
|
+
type: string;
|
|
48
|
+
/** Description from the schema's `.describe()`, or an explicit override. */
|
|
49
|
+
description?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface DefineEnvOptions<TShape extends z.ZodRawShape> {
|
|
52
|
+
/** Service identifier (free-form, used for docs grouping + error messages). */
|
|
53
|
+
service: string;
|
|
54
|
+
/**
|
|
55
|
+
* The Zod object schema. Pass the underlying ZodObject before any
|
|
56
|
+
* `.superRefine(...)` chain so we can walk `.shape` for docs metadata. The
|
|
57
|
+
* returned `parse()` runs the full schema (with refinements) — pass that as
|
|
58
|
+
* the `parser`.
|
|
59
|
+
*/
|
|
60
|
+
schema: z.ZodObject<TShape>;
|
|
61
|
+
/** Optional outer schema (e.g., `schema.superRefine(...)` for cross-field). */
|
|
62
|
+
parser?: z.ZodType;
|
|
63
|
+
/** Mapping from field name (or nested path) to env var(s). */
|
|
64
|
+
envMap: EnvMap;
|
|
65
|
+
/** Optional per-field description override (keyed by dotted path). */
|
|
66
|
+
descriptions?: Record<string, string>;
|
|
67
|
+
}
|
|
68
|
+
export interface DefineEnvResult<T> {
|
|
69
|
+
/** Parse `env` (defaults to `process.env`) into a typed config. */
|
|
70
|
+
parse(env?: NodeJS.ProcessEnv): T;
|
|
71
|
+
/** Machine-readable field specs for docs generation. */
|
|
72
|
+
describe(): EnvFieldSpec[];
|
|
73
|
+
/** Flat list of every env var the schema reads (for the unknown-var scanner). */
|
|
74
|
+
listKnownEnvVars(): string[];
|
|
75
|
+
/** Subset of `listKnownEnvVars()` starting with `KICI_`. */
|
|
76
|
+
listKnownKiciVars(): string[];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Build a `defineEnv` helper for a service.
|
|
80
|
+
*
|
|
81
|
+
* Example:
|
|
82
|
+
* const envDef = defineEnv({
|
|
83
|
+
* service: 'agent',
|
|
84
|
+
* schema: configSchema,
|
|
85
|
+
* envMap: {
|
|
86
|
+
* orchestratorUrl: 'KICI_ORCHESTRATOR_URL',
|
|
87
|
+
* cluster: { instanceId: 'KICI_CLUSTER_INSTANCE_ID' },
|
|
88
|
+
* },
|
|
89
|
+
* });
|
|
90
|
+
* const config = envDef.parse();
|
|
91
|
+
*/
|
|
92
|
+
export declare function defineEnv<TShape extends z.ZodRawShape>(opts: DefineEnvOptions<TShape>): DefineEnvResult<z.infer<z.ZodObject<TShape>>>;
|
|
93
|
+
/**
|
|
94
|
+
* `KICI_*` env vars that are NOT part of any service's config schema but are
|
|
95
|
+
* legitimately set by packaging / tooling / dev shims. The scanner always
|
|
96
|
+
* allowlists these so the typo-catcher doesn't false-positive on them.
|
|
97
|
+
*
|
|
98
|
+
* - `KICI_CACHE`: set by the packaged CLI shim (packages/… .cmd and the
|
|
99
|
+
* POSIX equivalent emitted by scripts/package.mjs) to point at the cached
|
|
100
|
+
* Node.js binary. Inherited by the orchestrator/agent process on Windows
|
|
101
|
+
* (on POSIX it's a shell-local var that doesn't leak).
|
|
102
|
+
* - `KICI_DEV`: the dev-mode toggle itself — read by the scanner to flip
|
|
103
|
+
* to warn-only, so it must not trip the scanner.
|
|
104
|
+
*
|
|
105
|
+
* Keep this list small and well-justified. Every addition is a typo we can
|
|
106
|
+
* no longer catch, so only list things that are (a) actually set in the
|
|
107
|
+
* wild by our own tooling and (b) could never be a config typo.
|
|
108
|
+
*/
|
|
109
|
+
export declare const RESERVED_NON_SCHEMA_KICI_VARS: readonly string[];
|
|
110
|
+
/**
|
|
111
|
+
* `KICI_*` prefixes that are entirely outside the service-config namespace —
|
|
112
|
+
* usually set by our own test / dev tooling and inherited into a child
|
|
113
|
+
* orchestrator/agent/platform process by mistake of inheritance rather than
|
|
114
|
+
* design. Any env var starting with one of these prefixes is treated as
|
|
115
|
+
* known, regardless of the specific suffix.
|
|
116
|
+
*
|
|
117
|
+
* - `KICI_E2E_`: the E2E framework's namespace (`KICI_E2E_PROVIDER`,
|
|
118
|
+
* `KICI_E2E_MODE`, future E2E toggles). Set by `e2e/vitest.*.config.ts`
|
|
119
|
+
* files. The native orchestrator spawn in `e2e/helpers/deploy.ts`
|
|
120
|
+
* inherits the test runner's process.env, so these leak in.
|
|
121
|
+
* - `KICI_AGENT_ENV_`: the agent-env forwarding namespace (see
|
|
122
|
+
* `@kici-dev/engine` → `KICI_AGENT_ENV_PREFIX`). Any env var set on the
|
|
123
|
+
* orchestrator process with this prefix is stripped and forwarded into
|
|
124
|
+
* the spawned agent (bare-metal, container, or Firecracker MMDS). The
|
|
125
|
+
* suffix is by design arbitrary and user-controlled, so it cannot be
|
|
126
|
+
* enumerated in the schema.
|
|
127
|
+
*
|
|
128
|
+
* As with RESERVED_NON_SCHEMA_KICI_VARS, keep this short — each prefix
|
|
129
|
+
* widens the set of names we can no longer catch as typos.
|
|
130
|
+
*/
|
|
131
|
+
export declare const RESERVED_NON_SCHEMA_KICI_PREFIXES: readonly string[];
|
|
132
|
+
/**
|
|
133
|
+
* `KICI_*` suffix patterns that mark vars outside the service-config namespace.
|
|
134
|
+
* A var is treated as known when its name ends with any listed suffix
|
|
135
|
+
* (or the suffix appears as a boundary segment — matching
|
|
136
|
+
* `${suffix}$|${suffix}_`, same rule as the agent's probe collector).
|
|
137
|
+
*
|
|
138
|
+
* - `_ENV_PROBE`: diagnostic probe vars (see `packages/agent/src/server.ts` —
|
|
139
|
+
* the agent collects every `KICI_*_ENV_PROBE` var and logs its value in
|
|
140
|
+
* "Agent startup env probes (diagnostic)"). The E2E firecracker-pipeline
|
|
141
|
+
* test uses `KICI_AGENT_ENV_KICI_FC_ENV_PROBE=fc-probe-value` which flows
|
|
142
|
+
* through the scaler's env-forwarding path and lands in the agent's
|
|
143
|
+
* process.env as `KICI_FC_ENV_PROBE`. Without this suffix allowlist the
|
|
144
|
+
* validator rejects it as an unknown KICI_* var and the agent refuses to
|
|
145
|
+
* start — defeating the test's entire purpose.
|
|
146
|
+
*
|
|
147
|
+
* As with RESERVED_NON_SCHEMA_KICI_VARS / _PREFIXES, keep this short. Every
|
|
148
|
+
* suffix widens the set of names we can no longer catch as typos.
|
|
149
|
+
*/
|
|
150
|
+
export declare const RESERVED_NON_SCHEMA_KICI_SUFFIXES: readonly string[];
|
|
151
|
+
export interface ValidateUnknownKiciVarsOptions {
|
|
152
|
+
/** Extra env-var names to treat as known (not all consumers can be migrated in one go). */
|
|
153
|
+
extraKnown?: string[];
|
|
154
|
+
/**
|
|
155
|
+
* When `true` (default in dev mode), unknown KICI_* vars only log a warning
|
|
156
|
+
* via `onWarn`. When `false`, throw — the production behaviour.
|
|
157
|
+
*/
|
|
158
|
+
warnOnly?: boolean;
|
|
159
|
+
/** Logger callback for warn-mode (defaults to `console.warn`). */
|
|
160
|
+
onWarn?: (msg: string) => void;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Inspect `env` for KICI_* keys that are not in `known`. Throws (or warns,
|
|
164
|
+
* see options) with a single combined message listing every unknown var and
|
|
165
|
+
* its closest legitimate match (when the Levenshtein distance is small).
|
|
166
|
+
*
|
|
167
|
+
* Production mode (the default): throws — staging deploys should never reach
|
|
168
|
+
* runtime with a typo'd KICI_* variable. Set `KICI_DEV=true` (or pass
|
|
169
|
+
* `warnOnly: true`) to downgrade to a warning during local development.
|
|
170
|
+
*/
|
|
171
|
+
export declare function validateUnknownKiciVars(known: string[], options?: ValidateUnknownKiciVarsOptions, env?: NodeJS.ProcessEnv): void;
|
|
172
|
+
//# sourceMappingURL=define-env.d.ts.map
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/env/define-env.ts
|
|
3
|
+
/** Read an env value following the alias precedence (first non-undefined wins). */
|
|
4
|
+
function readEnv(envMap, env) {
|
|
5
|
+
if (typeof envMap === "string") return env[envMap];
|
|
6
|
+
if (Array.isArray(envMap)) {
|
|
7
|
+
for (const name of envMap) {
|
|
8
|
+
const v = env[name];
|
|
9
|
+
if (v !== void 0) return v;
|
|
10
|
+
}
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const result = {};
|
|
14
|
+
for (const [k, v] of Object.entries(envMap)) result[k] = readEnv(v, env);
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
/** Walk an EnvMap and yield every leaf env var name. */
|
|
18
|
+
function* walkEnvNames(envMap) {
|
|
19
|
+
for (const value of Object.values(envMap)) if (typeof value === "string") yield value;
|
|
20
|
+
else if (Array.isArray(value)) for (const name of value) yield name;
|
|
21
|
+
else yield* walkEnvNames(value);
|
|
22
|
+
}
|
|
23
|
+
/** Best-effort type label for docs. Walks through default / optional / pipe / transform wrappers. */
|
|
24
|
+
function describeType(field) {
|
|
25
|
+
let t = field;
|
|
26
|
+
for (let i = 0; i < 8; i++) {
|
|
27
|
+
const def = t.def;
|
|
28
|
+
if (!def) return "unknown";
|
|
29
|
+
switch (def.type) {
|
|
30
|
+
case "string": return "string";
|
|
31
|
+
case "number": return "number";
|
|
32
|
+
case "boolean": return "boolean";
|
|
33
|
+
case "enum": return `enum:${(def.entries ? Object.keys(def.entries) : def.values ?? []).join("|")}`;
|
|
34
|
+
case "object": return "object";
|
|
35
|
+
case "optional":
|
|
36
|
+
case "default":
|
|
37
|
+
case "nullable":
|
|
38
|
+
if (def.innerType) {
|
|
39
|
+
t = def.innerType;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
return def.type ?? "unknown";
|
|
43
|
+
case "pipe":
|
|
44
|
+
if (def.in) {
|
|
45
|
+
t = def.in;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
return "string";
|
|
49
|
+
case "transform": return "string";
|
|
50
|
+
case "union": return "union";
|
|
51
|
+
default: return def.type ?? "unknown";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return "unknown";
|
|
55
|
+
}
|
|
56
|
+
/** Best-effort default value extraction for docs. Recurses into pipe/transform wrappers. */
|
|
57
|
+
function extractDefault(field) {
|
|
58
|
+
let t = field;
|
|
59
|
+
for (let i = 0; i < 6; i++) {
|
|
60
|
+
const def = t.def;
|
|
61
|
+
if (!def) return void 0;
|
|
62
|
+
if (def.type === "default" && def.defaultValue !== void 0) {
|
|
63
|
+
const v = typeof def.defaultValue === "function" ? "<computed>" : def.defaultValue;
|
|
64
|
+
if (v === "" || v === void 0) return void 0;
|
|
65
|
+
return JSON.stringify(v);
|
|
66
|
+
}
|
|
67
|
+
if (def.in) {
|
|
68
|
+
t = def.in;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (def.innerType) {
|
|
72
|
+
t = def.innerType;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function isOptional(field) {
|
|
79
|
+
let t = field;
|
|
80
|
+
for (let i = 0; i < 6; i++) {
|
|
81
|
+
const def = t.def;
|
|
82
|
+
if (!def) return false;
|
|
83
|
+
if (def.type === "optional" || def.type === "default") return true;
|
|
84
|
+
if (def.in) {
|
|
85
|
+
t = def.in;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (def.innerType) {
|
|
89
|
+
t = def.innerType;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
function describeFieldRecursive(shape, envMap, fieldPath, descriptions, out) {
|
|
97
|
+
for (const [name, field] of Object.entries(shape)) {
|
|
98
|
+
const path = fieldPath ? `${fieldPath}.${name}` : name;
|
|
99
|
+
const mapping = envMap[name];
|
|
100
|
+
if (mapping === void 0) continue;
|
|
101
|
+
if (typeof mapping === "string" || Array.isArray(mapping)) {
|
|
102
|
+
const aliases = Array.isArray(mapping) ? mapping : [mapping];
|
|
103
|
+
const explicitDesc = descriptions?.[path];
|
|
104
|
+
const zodDesc = field.description;
|
|
105
|
+
const def2 = field.def;
|
|
106
|
+
out.push({
|
|
107
|
+
envVar: aliases[0],
|
|
108
|
+
aliases: aliases.slice(1),
|
|
109
|
+
fieldPath: path,
|
|
110
|
+
required: !isOptional(field) && extractDefault(field) === void 0,
|
|
111
|
+
defaultValue: extractDefault(field),
|
|
112
|
+
type: describeType(field),
|
|
113
|
+
description: explicitDesc ?? zodDesc ?? def2?.description
|
|
114
|
+
});
|
|
115
|
+
} else {
|
|
116
|
+
const nested = field;
|
|
117
|
+
const innerShape = nested.shape ?? nested.def?.shape;
|
|
118
|
+
if (innerShape) describeFieldRecursive(innerShape, mapping, path, descriptions, out);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Build a `defineEnv` helper for a service.
|
|
124
|
+
*
|
|
125
|
+
* Example:
|
|
126
|
+
* const envDef = defineEnv({
|
|
127
|
+
* service: 'agent',
|
|
128
|
+
* schema: configSchema,
|
|
129
|
+
* envMap: {
|
|
130
|
+
* orchestratorUrl: 'KICI_ORCHESTRATOR_URL',
|
|
131
|
+
* cluster: { instanceId: 'KICI_CLUSTER_INSTANCE_ID' },
|
|
132
|
+
* },
|
|
133
|
+
* });
|
|
134
|
+
* const config = envDef.parse();
|
|
135
|
+
*/
|
|
136
|
+
function defineEnv(opts) {
|
|
137
|
+
const parserSchema = opts.parser ?? opts.schema;
|
|
138
|
+
function parse(env = process.env) {
|
|
139
|
+
const raw = readEnv(opts.envMap, env);
|
|
140
|
+
const result = parserSchema.safeParse(raw);
|
|
141
|
+
if (!result.success) {
|
|
142
|
+
const errors = result.error.issues.map((err) => ` - ${err.path.join(".")}: ${err.message}`).join("\n");
|
|
143
|
+
throw new Error(`Configuration validation failed:\n${errors}`);
|
|
144
|
+
}
|
|
145
|
+
return result.data;
|
|
146
|
+
}
|
|
147
|
+
function describe() {
|
|
148
|
+
const out = [];
|
|
149
|
+
describeFieldRecursive(opts.schema.shape, opts.envMap, "", opts.descriptions, out);
|
|
150
|
+
return out.sort((a, b) => a.envVar.localeCompare(b.envVar));
|
|
151
|
+
}
|
|
152
|
+
function listKnownEnvVars() {
|
|
153
|
+
return [...new Set(walkEnvNames(opts.envMap))].sort();
|
|
154
|
+
}
|
|
155
|
+
function listKnownKiciVars() {
|
|
156
|
+
return listKnownEnvVars().filter((n) => n.startsWith("KICI_"));
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
parse,
|
|
160
|
+
describe,
|
|
161
|
+
listKnownEnvVars,
|
|
162
|
+
listKnownKiciVars
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Levenshtein distance between two strings. Small + dependency-free; we only
|
|
167
|
+
* use it to suggest alternates for unknown KICI_* env vars at boot. O(n*m) is
|
|
168
|
+
* fine because both strings are short env var names.
|
|
169
|
+
*/
|
|
170
|
+
function levenshtein(a, b) {
|
|
171
|
+
if (a === b) return 0;
|
|
172
|
+
if (!a.length) return b.length;
|
|
173
|
+
if (!b.length) return a.length;
|
|
174
|
+
const prev = new Array(b.length + 1);
|
|
175
|
+
const curr = new Array(b.length + 1);
|
|
176
|
+
for (let j = 0; j <= b.length; j++) prev[j] = j;
|
|
177
|
+
for (let i = 1; i <= a.length; i++) {
|
|
178
|
+
curr[0] = i;
|
|
179
|
+
for (let j = 1; j <= b.length; j++) {
|
|
180
|
+
const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
|
|
181
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
182
|
+
}
|
|
183
|
+
for (let j = 0; j <= b.length; j++) prev[j] = curr[j];
|
|
184
|
+
}
|
|
185
|
+
return prev[b.length];
|
|
186
|
+
}
|
|
187
|
+
function suggestClosest(name, candidates) {
|
|
188
|
+
let best;
|
|
189
|
+
for (const c of candidates) {
|
|
190
|
+
const d = levenshtein(name, c);
|
|
191
|
+
if (best === void 0 || d < best.dist) best = {
|
|
192
|
+
name: c,
|
|
193
|
+
dist: d
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (best && best.dist <= Math.max(2, Math.floor(name.length / 4))) return best.name;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* `KICI_*` env vars that are NOT part of any service's config schema but are
|
|
200
|
+
* legitimately set by packaging / tooling / dev shims. The scanner always
|
|
201
|
+
* allowlists these so the typo-catcher doesn't false-positive on them.
|
|
202
|
+
*
|
|
203
|
+
* - `KICI_CACHE`: set by the packaged CLI shim (packages/… .cmd and the
|
|
204
|
+
* POSIX equivalent emitted by scripts/package.mjs) to point at the cached
|
|
205
|
+
* Node.js binary. Inherited by the orchestrator/agent process on Windows
|
|
206
|
+
* (on POSIX it's a shell-local var that doesn't leak).
|
|
207
|
+
* - `KICI_DEV`: the dev-mode toggle itself — read by the scanner to flip
|
|
208
|
+
* to warn-only, so it must not trip the scanner.
|
|
209
|
+
*
|
|
210
|
+
* Keep this list small and well-justified. Every addition is a typo we can
|
|
211
|
+
* no longer catch, so only list things that are (a) actually set in the
|
|
212
|
+
* wild by our own tooling and (b) could never be a config typo.
|
|
213
|
+
*/
|
|
214
|
+
const RESERVED_NON_SCHEMA_KICI_VARS = ["KICI_CACHE", "KICI_DEV"];
|
|
215
|
+
/**
|
|
216
|
+
* `KICI_*` prefixes that are entirely outside the service-config namespace —
|
|
217
|
+
* usually set by our own test / dev tooling and inherited into a child
|
|
218
|
+
* orchestrator/agent/platform process by mistake of inheritance rather than
|
|
219
|
+
* design. Any env var starting with one of these prefixes is treated as
|
|
220
|
+
* known, regardless of the specific suffix.
|
|
221
|
+
*
|
|
222
|
+
* - `KICI_E2E_`: the E2E framework's namespace (`KICI_E2E_PROVIDER`,
|
|
223
|
+
* `KICI_E2E_MODE`, future E2E toggles). Set by `e2e/vitest.*.config.ts`
|
|
224
|
+
* files. The native orchestrator spawn in `e2e/helpers/deploy.ts`
|
|
225
|
+
* inherits the test runner's process.env, so these leak in.
|
|
226
|
+
* - `KICI_AGENT_ENV_`: the agent-env forwarding namespace (see
|
|
227
|
+
* `@kici-dev/engine` → `KICI_AGENT_ENV_PREFIX`). Any env var set on the
|
|
228
|
+
* orchestrator process with this prefix is stripped and forwarded into
|
|
229
|
+
* the spawned agent (bare-metal, container, or Firecracker MMDS). The
|
|
230
|
+
* suffix is by design arbitrary and user-controlled, so it cannot be
|
|
231
|
+
* enumerated in the schema.
|
|
232
|
+
*
|
|
233
|
+
* As with RESERVED_NON_SCHEMA_KICI_VARS, keep this short — each prefix
|
|
234
|
+
* widens the set of names we can no longer catch as typos.
|
|
235
|
+
*/
|
|
236
|
+
const RESERVED_NON_SCHEMA_KICI_PREFIXES = ["KICI_E2E_", "KICI_AGENT_ENV_"];
|
|
237
|
+
/**
|
|
238
|
+
* `KICI_*` suffix patterns that mark vars outside the service-config namespace.
|
|
239
|
+
* A var is treated as known when its name ends with any listed suffix
|
|
240
|
+
* (or the suffix appears as a boundary segment — matching
|
|
241
|
+
* `${suffix}$|${suffix}_`, same rule as the agent's probe collector).
|
|
242
|
+
*
|
|
243
|
+
* - `_ENV_PROBE`: diagnostic probe vars (see `packages/agent/src/server.ts` —
|
|
244
|
+
* the agent collects every `KICI_*_ENV_PROBE` var and logs its value in
|
|
245
|
+
* "Agent startup env probes (diagnostic)"). The E2E firecracker-pipeline
|
|
246
|
+
* test uses `KICI_AGENT_ENV_KICI_FC_ENV_PROBE=fc-probe-value` which flows
|
|
247
|
+
* through the scaler's env-forwarding path and lands in the agent's
|
|
248
|
+
* process.env as `KICI_FC_ENV_PROBE`. Without this suffix allowlist the
|
|
249
|
+
* validator rejects it as an unknown KICI_* var and the agent refuses to
|
|
250
|
+
* start — defeating the test's entire purpose.
|
|
251
|
+
*
|
|
252
|
+
* As with RESERVED_NON_SCHEMA_KICI_VARS / _PREFIXES, keep this short. Every
|
|
253
|
+
* suffix widens the set of names we can no longer catch as typos.
|
|
254
|
+
*/
|
|
255
|
+
const RESERVED_NON_SCHEMA_KICI_SUFFIXES = ["_ENV_PROBE"];
|
|
256
|
+
/**
|
|
257
|
+
* Inspect `env` for KICI_* keys that are not in `known`. Throws (or warns,
|
|
258
|
+
* see options) with a single combined message listing every unknown var and
|
|
259
|
+
* its closest legitimate match (when the Levenshtein distance is small).
|
|
260
|
+
*
|
|
261
|
+
* Production mode (the default): throws — staging deploys should never reach
|
|
262
|
+
* runtime with a typo'd KICI_* variable. Set `KICI_DEV=true` (or pass
|
|
263
|
+
* `warnOnly: true`) to downgrade to a warning during local development.
|
|
264
|
+
*/
|
|
265
|
+
function validateUnknownKiciVars(known, options = {}, env = process.env) {
|
|
266
|
+
const knownSet = new Set([
|
|
267
|
+
...known,
|
|
268
|
+
...options.extraKnown ?? [],
|
|
269
|
+
...RESERVED_NON_SCHEMA_KICI_VARS
|
|
270
|
+
]);
|
|
271
|
+
const unknown = [];
|
|
272
|
+
for (const key of Object.keys(env)) {
|
|
273
|
+
if (!key.startsWith("KICI_")) continue;
|
|
274
|
+
if (knownSet.has(key)) continue;
|
|
275
|
+
if (RESERVED_NON_SCHEMA_KICI_PREFIXES.some((p) => key.startsWith(p))) continue;
|
|
276
|
+
if (RESERVED_NON_SCHEMA_KICI_SUFFIXES.some((s) => key.endsWith(s) || key.includes(`${s}_`))) continue;
|
|
277
|
+
unknown.push({
|
|
278
|
+
name: key,
|
|
279
|
+
suggestion: suggestClosest(key, [...knownSet])
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (unknown.length === 0) return;
|
|
283
|
+
const header = `Unknown KICI_* env var(s) detected — refusing to start.
|
|
284
|
+
Set KICI_DEV=true to downgrade this check to a warning.
|
|
285
|
+
Unknown vars:\n${unknown.map(({ name, suggestion }) => suggestion ? ` - ${name} (did you mean ${suggestion}?)` : ` - ${name} (no close match in the schema)`).join("\n")}`;
|
|
286
|
+
if (options.warnOnly ?? env.KICI_DEV === "true") {
|
|
287
|
+
(options.onWarn ?? console.warn)(header);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
throw new Error(header);
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
export { RESERVED_NON_SCHEMA_KICI_PREFIXES, RESERVED_NON_SCHEMA_KICI_SUFFIXES, RESERVED_NON_SCHEMA_KICI_VARS, defineEnv, validateUnknownKiciVars };
|
|
294
|
+
|
|
295
|
+
//# sourceMappingURL=define-env.js.map
|