typed-settings 0.1.4 → 0.2.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/README.md +14 -7
- package/bin/typed-settings.mjs +5 -0
- package/dist/adapters/joi.cjs +1 -26
- package/dist/adapters/joi.d.cts +2 -1
- package/dist/adapters/joi.d.ts +2 -1
- package/dist/adapters/joi.js +1 -1
- package/dist/adapters/superstruct.cjs +1 -26
- package/dist/adapters/superstruct.js +1 -1
- package/dist/adapters/yup.cjs +1 -25
- package/dist/adapters/yup.js +1 -1
- package/dist/adapters/zod.cjs +3 -36
- package/dist/adapters/zod.js +1 -33
- package/dist/check-XVFZEVNV.js +1 -0
- package/dist/chunk-4EAMKDNK.js +3 -0
- package/dist/chunk-5D2AQWDG.js +1 -0
- package/dist/chunk-C6CG2D4B.js +2 -0
- package/dist/chunk-FQYD5ZKE.js +1 -0
- package/dist/chunk-NQDE3WLR.js +3 -0
- package/dist/chunk-RURPDXIJ.js +1 -0
- package/dist/cli/main.cjs +36 -2225
- package/dist/cli/main.js +32 -806
- package/dist/errors-JkkOM0u-.d.cts +25 -0
- package/dist/errors-JkkOM0u-.d.ts +25 -0
- package/dist/framework/next.cjs +5 -1158
- package/dist/framework/next.d.cts +3 -3
- package/dist/framework/next.d.ts +3 -3
- package/dist/framework/next.js +1 -15
- package/dist/framework/vite.cjs +5 -1156
- package/dist/framework/vite.d.cts +8 -3
- package/dist/framework/vite.d.ts +8 -3
- package/dist/framework/vite.js +1 -14
- package/dist/index.cjs +5 -1181
- package/dist/index.d.cts +38 -7
- package/dist/index.d.ts +38 -7
- package/dist/index.js +1 -6
- package/dist/node.cjs +6 -1241
- package/dist/node.d.cts +23 -9
- package/dist/node.d.ts +23 -9
- package/dist/node.js +1 -7
- package/dist/{types-B_Dc2L4T.d.cts → types-CcsYDAdO.d.cts} +23 -1
- package/dist/{types-B_Dc2L4T.d.ts → types-CcsYDAdO.d.ts} +23 -1
- package/dist/vault/aws.cjs +4 -217
- package/dist/vault/aws.d.cts +1 -1
- package/dist/vault/aws.d.ts +1 -1
- package/dist/vault/aws.js +2 -167
- package/dist/vault/hashicorp.cjs +4 -183
- package/dist/vault/hashicorp.d.cts +1 -1
- package/dist/vault/hashicorp.d.ts +1 -1
- package/dist/vault/hashicorp.js +2 -77
- package/dist/vault/http.cjs +4 -185
- package/dist/vault/http.d.cts +1 -1
- package/dist/vault/http.d.ts +1 -1
- package/dist/vault/http.js +2 -79
- package/package.json +2 -3
- package/dist/chunk-33M5GIKQ.js +0 -13
- package/dist/chunk-A5TCQJ7L.js +0 -65
- package/dist/chunk-DEP5SD4J.js +0 -233
- package/dist/chunk-EE6LXNJV.js +0 -34
- package/dist/chunk-F6DX3HXZ.js +0 -101
- package/dist/chunk-OVFMKC2O.js +0 -15
- package/dist/chunk-RQC6OMVV.js +0 -86
- package/dist/chunk-UFUHHR2J.js +0 -38
- package/dist/chunk-YVJEZPPZ.js +0 -989
- package/dist/errors-itGaU4Q8.d.cts +0 -17
- package/dist/errors-itGaU4Q8.d.ts +0 -17
- package/dist/formats/env.cjs +0 -103
- package/dist/formats/env.d.cts +0 -8
- package/dist/formats/env.d.ts +0 -8
- package/dist/formats/env.js +0 -1
- package/dist/formats/json.cjs +0 -15
- package/dist/formats/json.d.cts +0 -7
- package/dist/formats/json.d.ts +0 -7
- package/dist/formats/json.js +0 -1
- package/dist/formats/toml.cjs +0 -17
- package/dist/formats/toml.d.cts +0 -7
- package/dist/formats/toml.d.ts +0 -7
- package/dist/formats/toml.js +0 -1
- package/dist/formats/yaml.cjs +0 -40
- package/dist/formats/yaml.d.cts +0 -8
- package/dist/formats/yaml.d.ts +0 -8
- package/dist/formats/yaml.js +0 -1
package/dist/cli/main.js
CHANGED
|
@@ -1,831 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { settings } from '../chunk-YVJEZPPZ.js';
|
|
4
|
-
import '../chunk-33M5GIKQ.js';
|
|
5
|
-
import '../chunk-OVFMKC2O.js';
|
|
6
|
-
import '../chunk-UFUHHR2J.js';
|
|
7
|
-
import { ConfigError } from '../chunk-EE6LXNJV.js';
|
|
8
|
-
import '../chunk-F6DX3HXZ.js';
|
|
9
|
-
import { parseArgs } from 'util';
|
|
10
|
-
import { resolve, join, dirname } from 'path';
|
|
11
|
-
import { existsSync, mkdirSync, writeFileSync, chmodSync, readFileSync } from 'fs';
|
|
12
|
-
import { spawn } from 'child_process';
|
|
13
|
-
|
|
14
|
-
async function loadSchema(schemaPath, exportName) {
|
|
15
|
-
const abs = resolve(schemaPath);
|
|
16
|
-
let mod;
|
|
17
|
-
try {
|
|
18
|
-
const { createJiti } = await import('jiti');
|
|
19
|
-
const jiti = createJiti(import.meta.url, { interopDefault: false, fsCache: false, moduleCache: false });
|
|
20
|
-
mod = await jiti.import(abs);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
const err2 = e;
|
|
23
|
-
const wrapped = new Error(`SCHEMA_LOAD_ERROR: ${abs}: ${err2.message}`);
|
|
24
|
-
wrapped.code = "SCHEMA_LOAD_ERROR";
|
|
25
|
-
throw wrapped;
|
|
26
|
-
}
|
|
27
|
-
const pick = (name) => mod[name];
|
|
28
|
-
const isStandard = (v) => {
|
|
29
|
-
if (!v) return false;
|
|
30
|
-
if (typeof v !== "object" && typeof v !== "function") return false;
|
|
31
|
-
return "~standard" in v;
|
|
32
|
-
};
|
|
33
|
-
if (exportName) {
|
|
34
|
-
const picked = pick(exportName);
|
|
35
|
-
if (!isStandard(picked)) {
|
|
36
|
-
const err2 = new Error(
|
|
37
|
-
`SCHEMA_LOAD_ERROR: --schema-export ${JSON.stringify(exportName)} did not resolve to a Standard Schema in ${abs}`
|
|
38
|
-
);
|
|
39
|
-
err2.code = "SCHEMA_LOAD_ERROR";
|
|
40
|
-
throw err2;
|
|
41
|
-
}
|
|
42
|
-
return picked;
|
|
43
|
-
}
|
|
44
|
-
for (const cand of ["schema", "settingsSchema", "default"]) {
|
|
45
|
-
if (isStandard(pick(cand))) return pick(cand);
|
|
46
|
-
}
|
|
47
|
-
const first = Object.values(mod).find((v) => isStandard(v));
|
|
48
|
-
if (first) return first;
|
|
49
|
-
const err = new Error(`SCHEMA_LOAD_ERROR: no Standard Schema export in ${abs} (tried default/schema/settingsSchema)`);
|
|
50
|
-
err.code = "SCHEMA_LOAD_ERROR";
|
|
51
|
-
throw err;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// src/cli/check.ts
|
|
55
|
-
async function runCheck(args) {
|
|
56
|
-
let schema;
|
|
57
|
-
try {
|
|
58
|
-
schema = await loadSchema(args.schema, args.schemaExport);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
const err = e;
|
|
61
|
-
if (args.format === "json") console.log(JSON.stringify({ ok: false, error: err.message }));
|
|
62
|
-
else console.error(err.message);
|
|
63
|
-
return 2;
|
|
64
|
-
}
|
|
65
|
-
const sources = (args.config ?? ".env,env").split(",").map((s) => s.trim()).filter(Boolean);
|
|
66
|
-
try {
|
|
67
|
-
const cfg = settings({
|
|
68
|
-
schema,
|
|
69
|
-
sources: sources.length ? sources : [".env", "env"],
|
|
70
|
-
strict: void 0,
|
|
71
|
-
unknownKeys: args.strict ? "reject" : "strip",
|
|
72
|
-
expand: args.expand ?? true,
|
|
73
|
-
arrayStrategy: args.array ?? "replace",
|
|
74
|
-
prefix: args.prefix
|
|
75
|
-
});
|
|
76
|
-
if (args.format === "json") console.log(JSON.stringify({ ok: true, config: cfg }));
|
|
77
|
-
else console.log("OK: config valid");
|
|
78
|
-
return 0;
|
|
79
|
-
} catch (e) {
|
|
80
|
-
if (e instanceof ConfigError) {
|
|
81
|
-
if (args.format === "json") console.log(JSON.stringify({ ok: false, issues: e.issues }));
|
|
82
|
-
else {
|
|
83
|
-
console.error(e.message);
|
|
84
|
-
}
|
|
85
|
-
return 1;
|
|
86
|
-
}
|
|
87
|
-
const err = e;
|
|
88
|
-
if (args.format === "json") console.log(JSON.stringify({ ok: false, error: err.message }));
|
|
89
|
-
else console.error(`E_LOAD: ${err.message}`);
|
|
90
|
-
return 2;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
var SETTINGS_TPL = {
|
|
94
|
-
zod: `import { z } from 'zod';
|
|
2
|
+
import {b as b$1,a}from'../chunk-FQYD5ZKE.js';import {d}from'../chunk-C6CG2D4B.js';import'../chunk-4EAMKDNK.js';import'../chunk-NQDE3WLR.js';import {parseArgs}from'util';import {existsSync,mkdirSync,writeFileSync,chmodSync,readFileSync,lstatSync}from'fs';import {resolve,join,dirname}from'path';import {spawn}from'child_process';var w={zod:`import { z } from 'zod';
|
|
95
3
|
import { settings } from 'typed-settings';
|
|
4
|
+
import 'typed-settings/node';
|
|
96
5
|
|
|
97
|
-
const schema = z.object({
|
|
6
|
+
export const schema = z.object({
|
|
98
7
|
port: z.coerce.number().default(3000),
|
|
99
8
|
});
|
|
100
9
|
|
|
101
10
|
export const cfg = settings({ schema, sources: ['.env', 'env'], prefix: 'APP_' });
|
|
102
|
-
`,
|
|
103
|
-
valibot: `import * as v from 'valibot';
|
|
11
|
+
`,valibot:`import * as v from 'valibot';
|
|
104
12
|
import { settings } from 'typed-settings';
|
|
13
|
+
import 'typed-settings/node';
|
|
105
14
|
|
|
106
|
-
const schema = v.object({ port: v.optional(v.pipe(v.unknown(), v.transform(Number)), 3000) });
|
|
15
|
+
export const schema = v.object({ port: v.optional(v.pipe(v.unknown(), v.transform(Number), v.number()), 3000) });
|
|
107
16
|
|
|
108
17
|
export const cfg = settings({ schema: schema as never, sources: ['.env', 'env'], prefix: 'APP_' });
|
|
109
|
-
`,
|
|
110
|
-
arktype: `import { type } from 'arktype';
|
|
18
|
+
`,arktype:`import { type } from 'arktype';
|
|
111
19
|
import { settings } from 'typed-settings';
|
|
20
|
+
import 'typed-settings/node';
|
|
112
21
|
|
|
113
|
-
const schema = type({
|
|
22
|
+
export const schema = type({ port: 'number = 3000' });
|
|
114
23
|
|
|
115
24
|
export const cfg = settings({ schema: schema as never, sources: ['.env', 'env'], prefix: 'APP_' });
|
|
116
|
-
`,
|
|
117
|
-
|
|
25
|
+
`,yup:`// npm install yup
|
|
26
|
+
import * as yup from 'yup';
|
|
118
27
|
import { settings } from 'typed-settings';
|
|
28
|
+
import 'typed-settings/node';
|
|
119
29
|
import { yupAdapter } from 'typed-settings/yup';
|
|
120
30
|
|
|
121
|
-
const schema = yup.object({ port: yup.number().default(3000) });
|
|
31
|
+
export const schema = yup.object({ port: yup.number().default(3000) });
|
|
122
32
|
|
|
123
33
|
export const cfg = settings({ schema: yupAdapter(schema) as never, sources: ['.env', 'env'], prefix: 'APP_' });
|
|
124
|
-
`,
|
|
125
|
-
|
|
34
|
+
`,joi:`// npm install joi
|
|
35
|
+
import Joi from 'joi';
|
|
126
36
|
import { settings } from 'typed-settings';
|
|
37
|
+
import 'typed-settings/node';
|
|
127
38
|
import { joiAdapter } from 'typed-settings/joi';
|
|
128
39
|
|
|
129
|
-
const schema = Joi.object({ port: Joi.number().default(3000) });
|
|
40
|
+
export const schema = Joi.object({ port: Joi.number().default(3000) });
|
|
130
41
|
|
|
131
42
|
export const cfg = settings({ schema: joiAdapter(schema as never) as never, sources: ['.env', 'env'], prefix: 'APP_' });
|
|
132
|
-
`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
console.error(`E_USAGE: --format must be ${Object.keys(CONFIG_TPL).join("|")} (got ${JSON.stringify(format)})`);
|
|
148
|
-
return 2;
|
|
149
|
-
}
|
|
150
|
-
const dir = resolve(args.dir ?? ".");
|
|
151
|
-
const settingsTpl = SETTINGS_TPL[lib];
|
|
152
|
-
const configName = format === "env" ? ".env.example" : `config.${format}`;
|
|
153
|
-
const files = [
|
|
154
|
-
{ path: join(dir, "src", "settings.ts"), content: settingsTpl },
|
|
155
|
-
{ path: join(dir, configName), content: CONFIG_TPL[format] }
|
|
156
|
-
];
|
|
157
|
-
const currentOf = (p) => {
|
|
158
|
-
try {
|
|
159
|
-
return readFileSync(p, "utf8");
|
|
160
|
-
} catch {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
const drifted = files.filter((f) => currentOf(f.path) !== f.content);
|
|
165
|
-
if (args.dryRun || args.check) {
|
|
166
|
-
for (const f of files) {
|
|
167
|
-
const cur = currentOf(f.path);
|
|
168
|
-
console.log(`${cur === null ? "create" : cur === f.content ? "exists" : "overwrite"} ${f.path}`);
|
|
169
|
-
}
|
|
170
|
-
if (args.check && drifted.length > 0) return 2;
|
|
171
|
-
return 0;
|
|
172
|
-
}
|
|
173
|
-
let written = 0;
|
|
174
|
-
for (const f of files) {
|
|
175
|
-
if (currentOf(f.path) === f.content) {
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (existsSync(f.path) && !args.force) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
mkdirSync(dirname(f.path), { recursive: true });
|
|
182
|
-
writeFileSync(f.path, f.content, { mode: 384 });
|
|
183
|
-
try {
|
|
184
|
-
chmodSync(f.path, 384);
|
|
185
|
-
} catch {
|
|
186
|
-
}
|
|
187
|
-
written++;
|
|
188
|
-
}
|
|
189
|
-
console.log(`init: ${lib} + ${format} scaffolding ${written > 0 ? `written (${written} file${written === 1 ? "" : "s"})` : "already up to date"}`);
|
|
190
|
-
return 0;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// src/cli/describe.ts
|
|
194
|
-
function getShape(schema) {
|
|
195
|
-
const s = schema;
|
|
196
|
-
if (s && typeof s.shape === "object" && s.shape !== null) return s.shape;
|
|
197
|
-
if (s && typeof s.shape === "function") {
|
|
198
|
-
try {
|
|
199
|
-
return s.shape();
|
|
200
|
-
} catch {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const d = s?._def?.shape;
|
|
205
|
-
if (typeof d === "function") {
|
|
206
|
-
try {
|
|
207
|
-
return d();
|
|
208
|
-
} catch {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (d && typeof d === "object") return d;
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
function unwrapField(t) {
|
|
216
|
-
let cur = t;
|
|
217
|
-
let optional = false;
|
|
218
|
-
let def;
|
|
219
|
-
for (let i = 0; i < 10; i++) {
|
|
220
|
-
const c = cur;
|
|
221
|
-
const z4type = c?.type ?? c?.def?.type;
|
|
222
|
-
if (z4type === "optional") {
|
|
223
|
-
optional = true;
|
|
224
|
-
cur = c?.def?.innerType ?? (typeof c?.unwrap === "function" ? safeUnwrap(c.unwrap) : void 0) ?? cur;
|
|
225
|
-
if (cur === t) break;
|
|
226
|
-
t = cur;
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (z4type === "nullable") {
|
|
230
|
-
cur = c?.def?.innerType ?? (typeof c?.unwrap === "function" ? safeUnwrap(c.unwrap) : void 0) ?? cur;
|
|
231
|
-
if (cur === t) break;
|
|
232
|
-
t = cur;
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
if (z4type === "catch" || z4type === "prefault" || z4type === "pipe" || z4type === "brand" || z4type === "readonly") {
|
|
236
|
-
const inner = c?.def?.innerType ?? (typeof c?.unwrap === "function" ? safeUnwrap(c.unwrap) : void 0);
|
|
237
|
-
if (inner === void 0 || inner === cur) break;
|
|
238
|
-
cur = inner;
|
|
239
|
-
t = cur;
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (z4type === "default") {
|
|
243
|
-
const dv = c?.def?.defaultValue;
|
|
244
|
-
if (def === void 0) def = typeof dv === "function" ? safeCall(dv) : dv;
|
|
245
|
-
optional = true;
|
|
246
|
-
cur = c?.def?.innerType ?? (typeof c?.unwrap === "function" ? safeUnwrap(c.unwrap) : void 0) ?? cur;
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
if (z4type === "readonly" || z4type === "nonoptional" || z4type === "success") {
|
|
250
|
-
cur = c?.def?.innerType ?? cur;
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
const tn = c?._def?.typeName;
|
|
254
|
-
if (tn === "ZodOptional") {
|
|
255
|
-
optional = true;
|
|
256
|
-
cur = c?._def?.innerType;
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
if (tn === "ZodNullable") {
|
|
260
|
-
cur = c?._def?.innerType;
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
if (tn === "ZodDefault") {
|
|
264
|
-
if (def === void 0) {
|
|
265
|
-
const dv = c?._def?.defaultValue;
|
|
266
|
-
def = typeof dv === "function" ? safeCall(dv) : dv;
|
|
267
|
-
}
|
|
268
|
-
optional = true;
|
|
269
|
-
cur = c?._def?.innerType;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
if (tn === "ZodReadonly") {
|
|
273
|
-
cur = c?._def?.innerType;
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
const desc = cur?.description ?? cur?.def?.description ?? t?.description;
|
|
279
|
-
return { inner: cur, optional, def, desc };
|
|
280
|
-
}
|
|
281
|
-
function safeUnwrap(fn) {
|
|
282
|
-
try {
|
|
283
|
-
return fn();
|
|
284
|
-
} catch {
|
|
285
|
-
return void 0;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function safeCall(fn) {
|
|
289
|
-
try {
|
|
290
|
-
return fn();
|
|
291
|
-
} catch {
|
|
292
|
-
return void 0;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
function enumMembers(def) {
|
|
296
|
-
const d = def;
|
|
297
|
-
if (Array.isArray(d?.values)) return d.values;
|
|
298
|
-
if (d?.entries && typeof d.entries === "object") return Object.values(d.entries);
|
|
299
|
-
return [];
|
|
300
|
-
}
|
|
301
|
-
function unwrapZod(t) {
|
|
302
|
-
return unwrapField(t);
|
|
303
|
-
}
|
|
304
|
-
function zodTypeName(t) {
|
|
305
|
-
const c = t;
|
|
306
|
-
const z4 = c?.type ?? c?.def?.type;
|
|
307
|
-
if (z4) {
|
|
308
|
-
switch (z4) {
|
|
309
|
-
case "string":
|
|
310
|
-
return "string";
|
|
311
|
-
case "number":
|
|
312
|
-
return "number";
|
|
313
|
-
case "boolean":
|
|
314
|
-
return "boolean";
|
|
315
|
-
case "array":
|
|
316
|
-
return "array";
|
|
317
|
-
case "object":
|
|
318
|
-
return "object";
|
|
319
|
-
// Zod 4 keeps enum members in `def.entries` (object); older shapes use `def.values`.
|
|
320
|
-
case "enum":
|
|
321
|
-
return `enum(${enumMembers(c?.def).join("|")})`;
|
|
322
|
-
case "literal":
|
|
323
|
-
return "literal";
|
|
324
|
-
case "union":
|
|
325
|
-
return "union";
|
|
326
|
-
default:
|
|
327
|
-
return z4;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
const tn = c?._def?.typeName ?? "unknown";
|
|
331
|
-
switch (tn) {
|
|
332
|
-
case "ZodString":
|
|
333
|
-
return "string";
|
|
334
|
-
case "ZodNumber":
|
|
335
|
-
return "number";
|
|
336
|
-
case "ZodBoolean":
|
|
337
|
-
return "boolean";
|
|
338
|
-
case "ZodArray":
|
|
339
|
-
return "array";
|
|
340
|
-
case "ZodObject":
|
|
341
|
-
return "object";
|
|
342
|
-
case "ZodEnum":
|
|
343
|
-
return `enum(${(c?._def?.values ?? []).join("|")})`;
|
|
344
|
-
case "ZodLiteral":
|
|
345
|
-
return "literal";
|
|
346
|
-
case "ZodUnion":
|
|
347
|
-
return "union";
|
|
348
|
-
default:
|
|
349
|
-
return "string";
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
function walkZod(prefix, t, out) {
|
|
353
|
-
const { inner, optional, def, desc } = unwrapZod(t);
|
|
354
|
-
const shape = getShape(inner);
|
|
355
|
-
if (shape) {
|
|
356
|
-
for (const [k, v] of Object.entries(shape)) {
|
|
357
|
-
walkZod(prefix ? `${prefix}.${k}` : k, v, out);
|
|
358
|
-
}
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
out.push({
|
|
362
|
-
path: prefix || "<root>",
|
|
363
|
-
type: zodTypeName(inner),
|
|
364
|
-
required: !optional && def === void 0,
|
|
365
|
-
default: def,
|
|
366
|
-
description: desc
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
function describeSchema(schema) {
|
|
370
|
-
try {
|
|
371
|
-
if (getShape(schema)) {
|
|
372
|
-
const out = [];
|
|
373
|
-
walkZod("", schema, out);
|
|
374
|
-
return { fields: out, bestEffort: false };
|
|
375
|
-
}
|
|
376
|
-
} catch {
|
|
377
|
-
}
|
|
378
|
-
try {
|
|
379
|
-
if (isValibotObject(schema)) {
|
|
380
|
-
const out = [];
|
|
381
|
-
walkValibot("", schema, out);
|
|
382
|
-
return { fields: out, bestEffort: false };
|
|
383
|
-
}
|
|
384
|
-
} catch {
|
|
385
|
-
}
|
|
386
|
-
try {
|
|
387
|
-
const j = arktypeJson(schema);
|
|
388
|
-
if (j && j.domain === "object") {
|
|
389
|
-
const out = [];
|
|
390
|
-
walkArktype(j, out);
|
|
391
|
-
return { fields: out, bestEffort: false };
|
|
392
|
-
}
|
|
393
|
-
} catch {
|
|
394
|
-
}
|
|
395
|
-
return { fields: [{ path: "<root>", type: "string", required: false, description: "GEN_BEST_EFFORT: unknown schema (Effect/others), fill manually" }], bestEffort: true };
|
|
396
|
-
}
|
|
397
|
-
function isValibotObject(schema) {
|
|
398
|
-
const s = schema;
|
|
399
|
-
return !!s && s.type === "object" && !!s.entries && typeof s.entries === "object";
|
|
400
|
-
}
|
|
401
|
-
function unwrapValibot(t) {
|
|
402
|
-
let cur = t;
|
|
403
|
-
let optional = false;
|
|
404
|
-
let def;
|
|
405
|
-
for (let i = 0; i < 10; i++) {
|
|
406
|
-
const c = cur;
|
|
407
|
-
const ty = c?.type;
|
|
408
|
-
if (ty === "optional" || ty === "nullish") {
|
|
409
|
-
optional = true;
|
|
410
|
-
if (def === void 0 && c?.default !== void 0) def = c.default;
|
|
411
|
-
if (!c?.wrapped) break;
|
|
412
|
-
cur = c.wrapped;
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
|
-
if (ty === "nullable") {
|
|
416
|
-
if (!c?.wrapped) break;
|
|
417
|
-
cur = c.wrapped;
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
if (ty === "non_optional" || ty === "non_nullable" || ty === "non_nullish") {
|
|
421
|
-
if (!c?.wrapped) break;
|
|
422
|
-
cur = c.wrapped;
|
|
423
|
-
continue;
|
|
424
|
-
}
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
const leaf = cur;
|
|
428
|
-
if (def === void 0 && leaf?.default !== void 0) def = leaf.default;
|
|
429
|
-
return { inner: cur, optional, def, desc: leaf?.description };
|
|
430
|
-
}
|
|
431
|
-
function valibotTypeName(t) {
|
|
432
|
-
const c = t;
|
|
433
|
-
switch (c?.type) {
|
|
434
|
-
case "string":
|
|
435
|
-
return "string";
|
|
436
|
-
case "number":
|
|
437
|
-
return "number";
|
|
438
|
-
case "boolean":
|
|
439
|
-
return "boolean";
|
|
440
|
-
case "bigint":
|
|
441
|
-
return "bigint";
|
|
442
|
-
case "date":
|
|
443
|
-
return "date";
|
|
444
|
-
case "array":
|
|
445
|
-
return "array";
|
|
446
|
-
case "object":
|
|
447
|
-
return "object";
|
|
448
|
-
case "record":
|
|
449
|
-
return "object";
|
|
450
|
-
case "tuple":
|
|
451
|
-
return "array";
|
|
452
|
-
case "map":
|
|
453
|
-
return "object";
|
|
454
|
-
case "picklist":
|
|
455
|
-
case "enum_":
|
|
456
|
-
return `enum(${(c.options ?? []).map((o) => JSON.stringify(o)).join("|")})`;
|
|
457
|
-
case "literal":
|
|
458
|
-
return `literal(${JSON.stringify(c.literal)})`;
|
|
459
|
-
case "union":
|
|
460
|
-
case "variant":
|
|
461
|
-
case "intersect":
|
|
462
|
-
return c.type;
|
|
463
|
-
case "nan":
|
|
464
|
-
return "number";
|
|
465
|
-
default:
|
|
466
|
-
return typeof c?.type === "string" ? c.type : "string";
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
function walkValibot(prefix, t, out) {
|
|
470
|
-
const { inner, optional, def, desc } = unwrapValibot(t);
|
|
471
|
-
const c = inner;
|
|
472
|
-
if (c?.type === "object" && c.entries && typeof c.entries === "object") {
|
|
473
|
-
for (const [k, v] of Object.entries(c.entries)) {
|
|
474
|
-
walkValibot(prefix ? `${prefix}.${k}` : k, v, out);
|
|
475
|
-
}
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
out.push({
|
|
479
|
-
path: prefix || "<root>",
|
|
480
|
-
type: valibotTypeName(inner),
|
|
481
|
-
required: !optional && def === void 0,
|
|
482
|
-
default: def,
|
|
483
|
-
description: desc
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
function arktypeJson(schema) {
|
|
487
|
-
const s = schema;
|
|
488
|
-
const j = s?.json;
|
|
489
|
-
if (typeof j === "function") {
|
|
490
|
-
try {
|
|
491
|
-
return j();
|
|
492
|
-
} catch {
|
|
493
|
-
return null;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
return j ?? null;
|
|
497
|
-
}
|
|
498
|
-
function arktypeTypeName(v) {
|
|
499
|
-
if (typeof v === "string") return v;
|
|
500
|
-
if (Array.isArray(v)) return "union";
|
|
501
|
-
if (v && typeof v === "object") {
|
|
502
|
-
const o = v;
|
|
503
|
-
if ("sequence" in o) return "array";
|
|
504
|
-
if (o["domain"] === "object") return "object";
|
|
505
|
-
if (typeof o["proto"] === "string") return String(o["proto"]).toLowerCase();
|
|
506
|
-
}
|
|
507
|
-
return "string";
|
|
508
|
-
}
|
|
509
|
-
function walkArktype(node, out) {
|
|
510
|
-
const n = node;
|
|
511
|
-
if (n && n.domain === "object" && (Array.isArray(n.required) || Array.isArray(n.optional))) {
|
|
512
|
-
for (const r of n.required ?? []) walkArktypeValue(r.key, r.value, true, void 0, out);
|
|
513
|
-
for (const r of n.optional ?? []) walkArktypeValue(r.key, r.value, false, r.default, out);
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
out.push({ path: "<root>", type: arktypeTypeName(node), required: true, default: void 0 });
|
|
517
|
-
}
|
|
518
|
-
function walkArktypeValue(path, value, required, def, out) {
|
|
519
|
-
if (value && typeof value === "object" && !Array.isArray(value) && value.domain === "object") {
|
|
520
|
-
const n = value;
|
|
521
|
-
if (Array.isArray(n.required) || Array.isArray(n.optional)) {
|
|
522
|
-
for (const r of n.required ?? []) walkArktypeValue(`${path}.${r.key}`, r.value, true, void 0, out);
|
|
523
|
-
for (const r of n.optional ?? []) walkArktypeValue(`${path}.${r.key}`, r.value, false, r.default, out);
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
out.push({ path, type: arktypeTypeName(value), required: required && def === void 0, default: def });
|
|
528
|
-
}
|
|
529
|
-
function toEnvExample(fields, prefix = "") {
|
|
530
|
-
const lines = ["# Generated by typed-settings gen \u2014 edit values, keep keys"];
|
|
531
|
-
const sorted = [...fields].sort((a, b) => a.path.localeCompare(b.path));
|
|
532
|
-
for (const f of sorted) {
|
|
533
|
-
if (f.path === "<root>") {
|
|
534
|
-
lines.push(`# ${f.description ?? "no fields discovered \u2014 fill manually"}`);
|
|
535
|
-
continue;
|
|
536
|
-
}
|
|
537
|
-
const key = (prefix + f.path.replace(/\./g, "__")).toUpperCase();
|
|
538
|
-
const val = f.default !== void 0 && typeof f.default !== "object" ? String(f.default) : f.type === "number" ? "0" : f.type === "boolean" ? "false" : "";
|
|
539
|
-
const comment = f.required ? "required" : `default: ${f.default !== void 0 ? JSON.stringify(f.default) : "none"}`;
|
|
540
|
-
if (f.description) lines.push(`# ${f.description}`);
|
|
541
|
-
lines.push(`${key}=${val} # ${f.type} ${comment}`);
|
|
542
|
-
}
|
|
543
|
-
lines.push("");
|
|
544
|
-
return lines.join("\n");
|
|
545
|
-
}
|
|
546
|
-
function toMarkdownDocs(fields, prefix = "") {
|
|
547
|
-
const lines = ["# Configuration", "", "| Env | Path | Type | Required | Default |", "| --- | --- | --- | --- | --- |"];
|
|
548
|
-
for (const f of [...fields].sort((a, b) => a.path.localeCompare(b.path))) {
|
|
549
|
-
const env = (prefix + f.path.replace(/\./g, "__")).toUpperCase();
|
|
550
|
-
lines.push(`| \`${env}\` | \`${f.path}\` | ${f.type} | ${f.required} | ${f.default !== void 0 ? `\`${JSON.stringify(f.default)}\`` : "-"} |`);
|
|
551
|
-
}
|
|
552
|
-
lines.push("");
|
|
553
|
-
return lines.join("\n");
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// src/cli/gen.ts
|
|
557
|
-
async function runGen(args) {
|
|
558
|
-
let schema;
|
|
559
|
-
try {
|
|
560
|
-
schema = await loadSchema(args.schema, args.schemaExport);
|
|
561
|
-
} catch (e) {
|
|
562
|
-
console.error(e.message);
|
|
563
|
-
return 2;
|
|
564
|
-
}
|
|
565
|
-
const { fields, bestEffort } = describeSchema(schema);
|
|
566
|
-
if (bestEffort) console.error("GEN_BEST_EFFORT: non-Zod schema, output is a starting point \u2014 verify types manually");
|
|
567
|
-
const envText = toEnvExample(fields, args.prefix);
|
|
568
|
-
const mdText = toMarkdownDocs(fields, args.prefix);
|
|
569
|
-
const writeGuarded = (p, content) => {
|
|
570
|
-
if (existsSync(p) && !args.force) {
|
|
571
|
-
console.error(`E_EXISTS: ${p} exists \u2014 pass --force to overwrite`);
|
|
572
|
-
return 2;
|
|
573
|
-
}
|
|
574
|
-
mkdirSync(dirname(p), { recursive: true });
|
|
575
|
-
writeFileSync(p, content, { mode: 384 });
|
|
576
|
-
console.log(`gen: wrote ${p}`);
|
|
577
|
-
return 0;
|
|
578
|
-
};
|
|
579
|
-
if (args.out) {
|
|
580
|
-
const code = writeGuarded(resolve(args.out), envText);
|
|
581
|
-
if (code !== 0) return code;
|
|
582
|
-
} else {
|
|
583
|
-
console.log(envText);
|
|
584
|
-
}
|
|
585
|
-
if (args.docs) {
|
|
586
|
-
const code = writeGuarded(resolve(args.docs), mdText);
|
|
587
|
-
if (code !== 0) return code;
|
|
588
|
-
} else if (!args.out) {
|
|
589
|
-
console.log(mdText);
|
|
590
|
-
}
|
|
591
|
-
return 0;
|
|
592
|
-
}
|
|
593
|
-
async function runWatch(args) {
|
|
594
|
-
if (!args.schema) {
|
|
595
|
-
console.error("Usage: watch -s schema.ts -c base.yaml,.env [-- cmd...]");
|
|
596
|
-
return 2;
|
|
597
|
-
}
|
|
598
|
-
const schema = await loadSchema(args.schema, args.schemaExport).catch((e) => {
|
|
599
|
-
console.error(e.message);
|
|
600
|
-
return null;
|
|
601
|
-
});
|
|
602
|
-
if (!schema) return 2;
|
|
603
|
-
const sources = (args.config ?? ".env,env").split(",").map((s) => s.trim()).filter(Boolean);
|
|
604
|
-
let child = null;
|
|
605
|
-
const startChild = () => {
|
|
606
|
-
if (!args.cmd?.length) return;
|
|
607
|
-
stopChild();
|
|
608
|
-
child = spawn(args.cmd[0], args.cmd.slice(1), { stdio: "inherit", shell: false });
|
|
609
|
-
child.on("error", (e) => {
|
|
610
|
-
console.error(`watch: child failed to start: ${e.message}`);
|
|
611
|
-
child = null;
|
|
612
|
-
});
|
|
613
|
-
child.on("exit", () => {
|
|
614
|
-
child = null;
|
|
615
|
-
});
|
|
616
|
-
};
|
|
617
|
-
const stopChild = () => {
|
|
618
|
-
try {
|
|
619
|
-
child?.kill();
|
|
620
|
-
} catch {
|
|
621
|
-
}
|
|
622
|
-
child = null;
|
|
623
|
-
};
|
|
624
|
-
if (args.once) {
|
|
625
|
-
if (args.cmd?.length) console.error("watch: -- cmd is ignored with --once (single validation, nothing to supervise)");
|
|
626
|
-
const { settings: settings2, ConfigError: ConfigError2 } = await import('../index.js');
|
|
627
|
-
try {
|
|
628
|
-
settings2({ schema, sources: sources.length ? sources : [".env", "env"], unknownKeys: args.strict ? "reject" : "strip", expand: args.expand ?? true, arrayStrategy: args.array ?? "replace", prefix: args.prefix });
|
|
629
|
-
console.log("OK: config valid");
|
|
630
|
-
return 0;
|
|
631
|
-
} catch (e) {
|
|
632
|
-
console.error(e.message);
|
|
633
|
-
return e instanceof ConfigError2 ? 1 : 2;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
const sub = watchSettings(
|
|
637
|
-
// NOTE: no `env` override — inherit process.env exactly like --once/check do.
|
|
638
|
-
{ schema, sources: sources.length ? sources : [".env", "env"], unknownKeys: args.strict ? "reject" : "strip", expand: args.expand ?? true, arrayStrategy: args.array ?? "replace", prefix: args.prefix },
|
|
639
|
-
{
|
|
640
|
-
onUpdate: (cfg, changed) => {
|
|
641
|
-
console.log(`reload: ok changed=[${changed.join(",")}]`);
|
|
642
|
-
if (args.cmd?.length) startChild();
|
|
643
|
-
},
|
|
644
|
-
onError: (e) => {
|
|
645
|
-
console.error(`reload: invalid, kept old
|
|
646
|
-
${e.message}`);
|
|
647
|
-
if (args.exitOnError) {
|
|
648
|
-
void sub.dispose().then(() => {
|
|
649
|
-
stopChild();
|
|
650
|
-
process.exitCode = 1;
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
);
|
|
656
|
-
startChild();
|
|
657
|
-
const shutdown = () => {
|
|
658
|
-
void sub.dispose().then(() => {
|
|
659
|
-
stopChild();
|
|
660
|
-
});
|
|
661
|
-
};
|
|
662
|
-
process.once("SIGINT", shutdown);
|
|
663
|
-
process.once("SIGTERM", shutdown);
|
|
664
|
-
await new Promise((resolve4) => {
|
|
665
|
-
const t = setInterval(() => {
|
|
666
|
-
if ((process.exitCode ?? 0) !== 0) {
|
|
667
|
-
clearInterval(t);
|
|
668
|
-
resolve4();
|
|
669
|
-
}
|
|
670
|
-
}, 100);
|
|
671
|
-
process.once("SIGINT", () => {
|
|
672
|
-
clearInterval(t);
|
|
673
|
-
resolve4();
|
|
674
|
-
});
|
|
675
|
-
process.once("SIGTERM", () => {
|
|
676
|
-
clearInterval(t);
|
|
677
|
-
resolve4();
|
|
678
|
-
});
|
|
679
|
-
});
|
|
680
|
-
return Number(process.exitCode ?? 0);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// src/cli/main.ts
|
|
684
|
-
async function main(argv = process.argv.slice(2)) {
|
|
685
|
-
const [cmd, ...rest] = argv;
|
|
686
|
-
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
687
|
-
console.log(`typed-settings <check|init|gen|watch>
|
|
688
|
-
|
|
689
|
-
check -s schema.ts -c base.yaml,.env [--strict] [--no-expand] [--array replace|concat|mergeIndex] [--format human|json]
|
|
690
|
-
init --lib zod|valibot|arktype|yup|joi --format yaml|toml|env [--force|--dry-run|--check]
|
|
691
|
-
gen --schema src/config.ts --out .env.example --docs CONFIG.md
|
|
692
|
-
watch -s schema.ts -c base.yaml --once [--exit-on-error] [-- cmd...]`);
|
|
693
|
-
return 0;
|
|
694
|
-
}
|
|
695
|
-
try {
|
|
696
|
-
if (cmd === "check") {
|
|
697
|
-
const { values } = parseArgs({
|
|
698
|
-
args: rest,
|
|
699
|
-
// allowNegative enables the advertised `--no-expand` negation flag.
|
|
700
|
-
allowNegative: true,
|
|
701
|
-
options: {
|
|
702
|
-
schema: { type: "string", short: "s" },
|
|
703
|
-
config: { type: "string", short: "c" },
|
|
704
|
-
strict: { type: "boolean", default: false },
|
|
705
|
-
expand: { type: "boolean", default: true },
|
|
706
|
-
array: { type: "string", default: "replace" },
|
|
707
|
-
format: { type: "string", default: "human" },
|
|
708
|
-
"schema-export": { type: "string" },
|
|
709
|
-
prefix: { type: "string" }
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
if (!values.schema) {
|
|
713
|
-
console.error("Usage: check -s schema.ts -c base.yaml,.env");
|
|
714
|
-
return 2;
|
|
715
|
-
}
|
|
716
|
-
if (values.array !== void 0 && !["replace", "concat", "mergeIndex"].includes(values.array)) {
|
|
717
|
-
console.error(`E_USAGE: --array must be replace|concat|mergeIndex (got ${JSON.stringify(values.array)})`);
|
|
718
|
-
return 2;
|
|
719
|
-
}
|
|
720
|
-
if (values.format !== void 0 && !["human", "json"].includes(values.format)) {
|
|
721
|
-
console.error(`E_USAGE: --format must be human|json (got ${JSON.stringify(values.format)})`);
|
|
722
|
-
return 2;
|
|
723
|
-
}
|
|
724
|
-
return await runCheck({
|
|
725
|
-
schema: values.schema,
|
|
726
|
-
config: values.config,
|
|
727
|
-
strict: values.strict,
|
|
728
|
-
expand: values.expand,
|
|
729
|
-
array: values.array,
|
|
730
|
-
format: values.format,
|
|
731
|
-
schemaExport: values["schema-export"],
|
|
732
|
-
prefix: values.prefix
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
if (cmd === "init") {
|
|
736
|
-
const { values } = parseArgs({
|
|
737
|
-
args: rest,
|
|
738
|
-
options: {
|
|
739
|
-
lib: { type: "string", default: "zod" },
|
|
740
|
-
format: { type: "string", default: "env" },
|
|
741
|
-
dir: { type: "string", default: "." },
|
|
742
|
-
force: { type: "boolean", default: false },
|
|
743
|
-
"dry-run": { type: "boolean", default: false },
|
|
744
|
-
check: { type: "boolean", default: false }
|
|
745
|
-
}
|
|
746
|
-
});
|
|
747
|
-
return await runInit({
|
|
748
|
-
lib: values.lib,
|
|
749
|
-
format: values.format,
|
|
750
|
-
dir: values.dir,
|
|
751
|
-
force: values.force,
|
|
752
|
-
dryRun: values["dry-run"],
|
|
753
|
-
check: values.check
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
if (cmd === "gen") {
|
|
757
|
-
const { values } = parseArgs({
|
|
758
|
-
args: rest,
|
|
759
|
-
options: {
|
|
760
|
-
schema: { type: "string" },
|
|
761
|
-
"schema-export": { type: "string" },
|
|
762
|
-
out: { type: "string" },
|
|
763
|
-
docs: { type: "string" },
|
|
764
|
-
prefix: { type: "string" },
|
|
765
|
-
force: { type: "boolean", default: false }
|
|
766
|
-
}
|
|
767
|
-
});
|
|
768
|
-
if (!values.schema) {
|
|
769
|
-
console.error("Usage: gen --schema src/config.ts --out .env.example --docs CONFIG.md");
|
|
770
|
-
return 2;
|
|
771
|
-
}
|
|
772
|
-
return await runGen({
|
|
773
|
-
schema: values.schema,
|
|
774
|
-
schemaExport: values["schema-export"],
|
|
775
|
-
out: values.out,
|
|
776
|
-
docs: values.docs,
|
|
777
|
-
prefix: values.prefix,
|
|
778
|
-
force: values.force
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
if (cmd === "watch") {
|
|
782
|
-
const dashIdx = rest.indexOf("--");
|
|
783
|
-
const watchArgs = dashIdx === -1 ? rest : rest.slice(0, dashIdx);
|
|
784
|
-
const childCmd = dashIdx === -1 ? [] : rest.slice(dashIdx + 1);
|
|
785
|
-
const { values } = parseArgs({
|
|
786
|
-
args: watchArgs,
|
|
787
|
-
allowNegative: true,
|
|
788
|
-
options: {
|
|
789
|
-
schema: { type: "string", short: "s" },
|
|
790
|
-
config: { type: "string", short: "c" },
|
|
791
|
-
strict: { type: "boolean", default: false },
|
|
792
|
-
expand: { type: "boolean", default: true },
|
|
793
|
-
array: { type: "string", default: "replace" },
|
|
794
|
-
once: { type: "boolean", default: false },
|
|
795
|
-
"exit-on-error": { type: "boolean", default: false },
|
|
796
|
-
"schema-export": { type: "string" },
|
|
797
|
-
prefix: { type: "string" }
|
|
798
|
-
}
|
|
799
|
-
});
|
|
800
|
-
if (values.array !== void 0 && !["replace", "concat", "mergeIndex"].includes(values.array)) {
|
|
801
|
-
console.error(`E_USAGE: --array must be replace|concat|mergeIndex (got ${JSON.stringify(values.array)})`);
|
|
802
|
-
return 2;
|
|
803
|
-
}
|
|
804
|
-
return await runWatch({
|
|
805
|
-
schema: values.schema,
|
|
806
|
-
config: values.config,
|
|
807
|
-
schemaExport: values["schema-export"],
|
|
808
|
-
strict: values.strict,
|
|
809
|
-
expand: values.expand,
|
|
810
|
-
array: values.array,
|
|
811
|
-
prefix: values.prefix,
|
|
812
|
-
once: values.once,
|
|
813
|
-
exitOnError: values["exit-on-error"],
|
|
814
|
-
cmd: childCmd.length ? childCmd : void 0
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
console.error(`unknown command: ${cmd}`);
|
|
818
|
-
return 2;
|
|
819
|
-
} catch (e) {
|
|
820
|
-
console.error(e.message);
|
|
821
|
-
return 2;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
var isDirect = process.argv[1]?.endsWith("main.js") || process.argv[1]?.endsWith("main.cjs");
|
|
825
|
-
if (isDirect) {
|
|
826
|
-
main().then((code) => {
|
|
827
|
-
process.exitCode = code;
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
export { main };
|
|
43
|
+
`},x={env:`# APP_PORT=3000
|
|
44
|
+
APP_PORT=3000
|
|
45
|
+
`,yaml:`# config.yaml
|
|
46
|
+
port: 3000
|
|
47
|
+
`,toml:`# config.toml
|
|
48
|
+
port = 3000
|
|
49
|
+
`};async function T(n){let e=n.lib??"zod",o=n.format??"env";if(!Object.hasOwn(w,e))return console.error(`E_USAGE: --lib must be ${Object.keys(w).join("|")} (got ${JSON.stringify(e)})`),2;if(!Object.hasOwn(x,o))return console.error(`E_USAGE: --format must be ${Object.keys(x).join("|")} (got ${JSON.stringify(o)})`),2;let t=resolve(n.dir??"."),i=w[e],c=o==="env"?".env.example":`config.${o}`,r=[{path:join(t,"src","settings.ts"),content:i},{path:join(t,c),content:x[o]}],a=f=>{try{return readFileSync(f,"utf8")}catch{return null}},l=r.filter(f=>a(f.path)!==f.content);if(n.dryRun||n.check){for(let f of r){let m=a(f.path);console.log(`${m===null?"create":m===f.content?"exists":"overwrite"} ${f.path}`);}return n.check&&l.length>0?2:0}let s=0;for(let f of r){if(a(f.path)===f.content){continue}if(existsSync(f.path)&&!n.force){continue}mkdirSync(dirname(f.path),{recursive:true}),writeFileSync(f.path,f.content,{mode:384});try{chmodSync(f.path,384);}catch{}s++;}return console.log(`init: ${e} + ${o} scaffolding ${s>0?`written (${s} file${s===1?"":"s"})`:"already up to date"}`),o==="env"&&s>0&&console.log("hint: copy .env.example to .env and fill in real values (example files are never loaded)"),0}function I(n){let e=n;if(e&&typeof e.shape=="object"&&e.shape!==null)return e.shape;if(e&&typeof e.shape=="function")try{return e.shape()}catch{return null}let o=e?._def?.shape;if(typeof o=="function")try{return o()}catch{return null}return o&&typeof o=="object"?o:null}function X(n){let e=n,o=false,t;for(let c=0;c<10;c++){let r=e,a=r?.type??r?.def?.type;if(a==="optional"){if(o=true,e=r?.def?.innerType??(typeof r?.unwrap=="function"?g(r.unwrap):void 0)??e,e===n)break;n=e;continue}if(a==="nullable"){if(e=r?.def?.innerType??(typeof r?.unwrap=="function"?g(r.unwrap):void 0)??e,e===n)break;n=e;continue}if(a==="catch"||a==="prefault"||a==="pipe"||a==="brand"||a==="readonly"){let s=r?.def?.innerType??(typeof r?.unwrap=="function"?g(r.unwrap):void 0);if(s===void 0||s===e)break;e=s,n=e;continue}if(a==="default"){let s=r?.def?.defaultValue;t===void 0&&(t=typeof s=="function"?O(s):s),o=true,e=r?.def?.innerType??(typeof r?.unwrap=="function"?g(r.unwrap):void 0)??e;continue}if(a==="nonoptional"||a==="success"){let s=r?.def?.innerType;if(s===void 0||s===e)break;e=s,n=e;continue}let l=r?._def?.typeName;if(l==="ZodOptional"){o=true,e=r?._def?.innerType;continue}if(l==="ZodNullable"){e=r?._def?.innerType;continue}if(l==="ZodDefault"){if(t===void 0){let s=r?._def?.defaultValue;t=typeof s=="function"?O(s):s;}o=true,e=r?._def?.innerType;continue}if(l==="ZodReadonly"){e=r?._def?.innerType;continue}break}let i=e?.description??e?.def?.description??n?.description;return {inner:e,optional:o,def:t,desc:i}}function g(n){try{return n()}catch{return}}function O(n){try{return n()}catch{return}}function Y(n){let e=n;return Array.isArray(e?.values)?e.values:e?.entries&&typeof e.entries=="object"?Object.values(e.entries):[]}function H(n){return X(n)}function Q(n){let e=n,o=e?.type??e?.def?.type;if(o)switch(o){case "string":return "string";case "number":return "number";case "boolean":return "boolean";case "array":return "array";case "object":return "object";case "enum":return `enum(${Y(e?.def).join("|")})`;case "literal":return "literal";case "union":return "union";default:return o}switch(e?._def?.typeName??"unknown"){case "ZodString":return "string";case "ZodNumber":return "number";case "ZodBoolean":return "boolean";case "ZodBigInt":return "bigint";case "ZodDate":return "date";case "ZodNaN":return "number";case "ZodArray":return "array";case "ZodObject":return "object";case "ZodEnum":return `enum(${(e?._def?.values??[]).join("|")})`;case "ZodLiteral":return "literal";case "ZodUnion":return "union";default:return "string"}}function F(n,e,o,t=false){let{inner:i,optional:c,def:r,desc:a}=H(e),l=t||c,s=I(i);if(s){for(let[p,f]of Object.entries(s))F(n?`${n}.${p}`:p,f,o,l);return}o.push({path:n||"<root>",type:Q(i),required:!l&&r===void 0,default:r,description:a});}function G(n){try{if(I(n)){let e=[];return F("",n,e),{fields:e,bestEffort:!1}}}catch{}try{if(ee(n)){let e=[];return P("",n,e),{fields:e,bestEffort:!1}}}catch{}try{let e=re(n);if(e&&e.domain==="object"){let o=[];return oe(e,o),{fields:o,bestEffort:!1}}}catch{}return {fields:[{path:"<root>",type:"string",required:false,description:"GEN_BEST_EFFORT: unknown schema (Effect/others), fill manually"}],bestEffort:true}}function ee(n){let e=n;return !!e&&e.type==="object"&&!!e.entries&&typeof e.entries=="object"}function ne(n){let e=n,o=false,t;for(let c=0;c<10;c++){let r=e,a=r?.type;if(a==="optional"||a==="nullish"){if(o=true,t===void 0&&r?.default!==void 0&&(t=r.default),!r?.wrapped)break;e=r.wrapped;continue}if(a==="nullable"){if(!r?.wrapped)break;e=r.wrapped;continue}if(a==="non_optional"||a==="non_nullable"||a==="non_nullish"){if(!r?.wrapped)break;e=r.wrapped;continue}break}let i=e;return t===void 0&&i?.default!==void 0&&(t=i.default),{inner:e,optional:o,def:t,desc:i?.description}}function te(n){let e=n;switch(e?.type){case "string":return "string";case "number":return "number";case "boolean":return "boolean";case "bigint":return "bigint";case "date":return "date";case "array":return "array";case "object":return "object";case "record":return "object";case "tuple":return "array";case "map":return "object";case "picklist":case "enum_":return `enum(${(e.options??[]).map(o=>JSON.stringify(o)).join("|")})`;case "literal":return `literal(${JSON.stringify(e.literal)})`;case "union":case "variant":case "intersect":return e.type;case "nan":return "number";default:return typeof e?.type=="string"?e.type:"string"}}function P(n,e,o,t=false){let{inner:i,optional:c,def:r,desc:a}=ne(e),l=t||c,s=i;if(s?.type==="object"&&s.entries&&typeof s.entries=="object"){for(let[p,f]of Object.entries(s.entries))P(n?`${n}.${p}`:p,f,o,l);return}o.push({path:n||"<root>",type:te(i),required:!l&&r===void 0,default:r,description:a});}function re(n){let o=n?.json;if(typeof o=="function")try{return o()}catch{return null}return o??null}function C(n){if(typeof n=="string")return n;if(Array.isArray(n))return "union";if(n&&typeof n=="object"){let e=n;if("sequence"in e)return "array";if(e.domain==="object")return "object";if(typeof e.proto=="string")return String(e.proto).toLowerCase()}return "string"}function oe(n,e){let o=n;if(o&&o.domain==="object"&&(Array.isArray(o.required)||Array.isArray(o.optional))){for(let t of o.required??[])b(t.key,t.value,true,void 0,e);for(let t of o.optional??[])b(t.key,t.value,false,t.default,e);return}e.push({path:"<root>",type:C(n),required:true,default:void 0});}function b(n,e,o,t,i){if(e&&typeof e=="object"&&!Array.isArray(e)&&e.domain==="object"){let c=e;if(Array.isArray(c.required)||Array.isArray(c.optional)){for(let r of c.required??[])b(`${n}.${r.key}`,r.value,true,void 0,i);for(let r of c.optional??[])b(`${n}.${r.key}`,r.value,false,r.default,i);return}}i.push({path:n,type:C(e),required:o&&t===void 0,default:t});}function R(n,e){return ((n&&!n.endsWith("_")?`${n}_`:n)+e.replace(/\./g,"__")).toUpperCase().replace(/[^A-Z0-9_]/g,"_")}function D(n,e=""){let o=["# Generated by typed-settings gen \u2014 edit values, keep keys"],t=[...n].sort((i,c)=>i.path.localeCompare(c.path));for(let i of t){if(i.path==="<root>"){o.push(`# ${i.description??"no fields discovered \u2014 fill manually"}`);continue}let c=R(e,i.path),r=i.default!==void 0&&typeof i.default!="object"?String(i.default):i.type==="number"?"0":i.type==="boolean"?"false":"",a=i.required?"required":`default: ${i.default!==void 0?JSON.stringify(i.default):"none"}`;i.description&&o.push(`# ${i.description}`),o.push(`${c}=${r} # ${i.type} ${a}`);}return o.push(""),o.join(`
|
|
50
|
+
`)}function Z(n,e=""){let o=["# Configuration","","| Env | Path | Type | Required | Default |","| --- | --- | --- | --- | --- |"];for(let t of [...n].sort((i,c)=>i.path.localeCompare(c.path))){if(t.path==="<root>")continue;let i=R(e,t.path);o.push(`| \`${i}\` | \`${t.path}\` | ${t.type} | ${t.required} | ${t.default!==void 0?`\`${JSON.stringify(t.default)}\``:"-"} |`);}return o.push(""),o.join(`
|
|
51
|
+
`)}function le(n){try{return lstatSync(n).isSymbolicLink()}catch{return false}}async function U(n){let e;try{e=await a(n.schema,n.schemaExport);}catch(s){return console.error(s.message),2}let{fields:o,bestEffort:t}=G(e);t&&console.error("GEN_BEST_EFFORT: non-Zod schema, output is a starting point \u2014 verify types manually");let i=D(o,n.prefix),c=Z(o,n.prefix),r=n.out?resolve(n.out):null,a$1=n.docs?resolve(n.docs):null;for(let s of [r,a$1])if(s){if(le(s))return console.error(`E_SYMLINK: ${s} is a symlink \u2014 refusing to write through it`),2;if(existsSync(s)&&!n.force)return console.error(`E_EXISTS: ${s} exists \u2014 pass --force to overwrite`),2}let l=(s,p)=>{mkdirSync(dirname(s),{recursive:true}),writeFileSync(s,p,{mode:384});try{chmodSync(s,384);}catch{}console.log(`gen: wrote ${s}`);};return r?l(r,i):console.log(i),a$1?l(a$1,c):r||(console.log("# ---- CONFIG.md below ----"),console.log(c)),0}async function V(n){if(!n.schema)return console.error("Usage: watch -s schema.ts -c base.yaml,.env [-- cmd...]"),2;let e=await a(n.schema,n.schemaExport).catch(u=>(console.error(u.message),null));if(!e)return 2;let o=(n.config??".env,env").split(",").map(u=>u.trim()).filter(Boolean),t=null,i=()=>{if(!n.cmd?.length)return;c();let u=spawn(n.cmd[0],n.cmd.slice(1),{stdio:"inherit",shell:false});t=u,u.on("error",d=>{console.error(`watch: child failed to start: ${d.message}`),t===u&&(t=null);}),u.on("exit",(d,$)=>{t===u&&(t=null,typeof d=="number"&&d!==0?(console.error(`watch: child exited with code ${d}`),n.exitOnChildError&&p(d)):typeof $=="string"&&(console.error(`watch: child exited on signal ${$}`),n.exitOnChildError&&p(1)));});},c=()=>{let u=t;if(t=null,!u)return;try{u.kill();}catch{}setTimeout(()=>{try{u.kill("SIGKILL");}catch{}},2e3).unref();};if(n.once){n.cmd?.length&&console.error("watch: -- cmd is ignored with --once (single validation, nothing to supervise)");let{runCheck:u}=await import('../check-XVFZEVNV.js');return u({schema:n.schema,config:n.config,schemaExport:n.schemaExport,strict:n.strict,expand:n.expand,array:n.array,prefix:n.prefix,format:n.format,secretKeys:n.secretKeys})}let r=d({schema:e,sources:o.length?o:[".env","env"],unknownKeys:n.strict?"reject":"strip",expand:n.expand??true,arrayStrategy:n.array??"replace",prefix:n.prefix},{onUpdate:(u,d)=>{d.length!==0&&(console.log(`reload: ok changed=[${d.join(",")}]`),n.cmd?.length&&i());},onError:u=>{console.error(`reload: invalid, kept old
|
|
52
|
+
${u.message}`),n.exitOnError&&r.dispose().then(()=>{c(),process.exitCode=1,s(1);});}}),a$1,l=new Promise(u=>{a$1=u;}),s=u=>{process.removeListener("SIGINT",j),process.removeListener("SIGTERM",S),process.removeListener("SIGINT",y),process.removeListener("SIGTERM",y),clearInterval(E),a$1(u);},p=u=>{r.dispose().then(()=>{c(),process.exitCode=u,s(u);});};i();let f=null,m=new Promise(u=>{f=u;}),v=()=>{r.dispose().then(()=>{c(),f();});},E=setInterval(()=>{(process.exitCode??0)!==0&&s(Number(process.exitCode));},100),y=()=>{clearInterval(E),m.then(()=>s(Number(process.exitCode??0)));};function j(){v();}function S(){v();}return process.once("SIGINT",j),process.once("SIGTERM",S),process.once("SIGINT",y),process.once("SIGTERM",y),l}async function de(n=process.argv.slice(2)){let[e,...o]=n;if(!e||e==="--help"||e==="-h")return console.log(`typed-settings <check|init|gen|watch>
|
|
53
|
+
|
|
54
|
+
check -s schema.ts -c base.yaml,.env [--strict] [--no-expand] [--array replace|concat|mergeIndex] [--format human|json] [--schema-export NAME] [--prefix PFX] [--secret-keys a,b]
|
|
55
|
+
init --lib zod|valibot|arktype|yup|joi --format yaml|toml|env [--dir .] [--force|--dry-run|--check]
|
|
56
|
+
gen --schema src/config.ts --out .env.example --docs CONFIG.md [--schema-export NAME] [--prefix PFX] [--force]
|
|
57
|
+
watch -s schema.ts -c base.yaml [--strict] [--no-expand] [--array ...] [--format human|json] [--schema-export NAME] [--prefix PFX] [--secret-keys a,b] [--once] [--exit-on-error] [--exit-on-child-error] [-- cmd...]`),0;try{if(e==="check"){let{values:t}=parseArgs({args:o,allowNegative:!0,options:{schema:{type:"string",short:"s"},config:{type:"string",short:"c"},strict:{type:"boolean",default:!1},expand:{type:"boolean",default:!0},array:{type:"string",default:"replace"},format:{type:"string",default:"human"},"schema-export":{type:"string"},prefix:{type:"string"},"secret-keys":{type:"string"}}});return t.schema?t.array!==void 0&&!["replace","concat","mergeIndex"].includes(t.array)?(console.error(`E_USAGE: --array must be replace|concat|mergeIndex (got ${JSON.stringify(t.array)})`),2):t.format!==void 0&&!["human","json"].includes(t.format)?(console.error(`E_USAGE: --format must be human|json (got ${JSON.stringify(t.format)})`),2):await b$1({schema:t.schema,config:t.config,strict:t.strict,expand:t.expand,array:t.array,format:t.format,schemaExport:t["schema-export"],prefix:t.prefix,secretKeys:t["secret-keys"]?.split(",").map(i=>i.trim()).filter(Boolean)}):(console.error("E_USAGE: check -s schema.ts -c base.yaml,.env"),2)}if(e==="init"){let{values:t}=parseArgs({args:o,options:{lib:{type:"string",default:"zod"},format:{type:"string",default:"env"},dir:{type:"string",default:"."},force:{type:"boolean",default:!1},"dry-run":{type:"boolean",default:!1},check:{type:"boolean",default:!1}}});return await T({lib:t.lib,format:t.format,dir:t.dir,force:t.force,dryRun:t["dry-run"],check:t.check})}if(e==="gen"){let{values:t}=parseArgs({args:o,options:{schema:{type:"string",short:"s"},"schema-export":{type:"string"},out:{type:"string"},docs:{type:"string"},prefix:{type:"string"},force:{type:"boolean",default:!1}}});return t.schema?await U({schema:t.schema,schemaExport:t["schema-export"],out:t.out,docs:t.docs,prefix:t.prefix,force:t.force}):(console.error("E_USAGE: gen --schema src/config.ts --out .env.example --docs CONFIG.md"),2)}if(e==="watch"){let t=o.indexOf("--"),i=t===-1?o:o.slice(0,t),c=t===-1?[]:o.slice(t+1),{values:r}=parseArgs({args:i,allowNegative:!0,options:{schema:{type:"string",short:"s"},config:{type:"string",short:"c"},strict:{type:"boolean",default:!1},expand:{type:"boolean",default:!0},array:{type:"string",default:"replace"},format:{type:"string",default:"human"},once:{type:"boolean",default:!1},"exit-on-error":{type:"boolean",default:!1},"exit-on-child-error":{type:"boolean",default:!1},"schema-export":{type:"string"},prefix:{type:"string"},"secret-keys":{type:"string"}}});return r.array!==void 0&&!["replace","concat","mergeIndex"].includes(r.array)?(console.error(`E_USAGE: --array must be replace|concat|mergeIndex (got ${JSON.stringify(r.array)})`),2):r.format!==void 0&&!["human","json"].includes(r.format)?(console.error(`E_USAGE: --format must be human|json (got ${JSON.stringify(r.format)})`),2):await V({schema:r.schema,config:r.config,schemaExport:r["schema-export"],strict:r.strict,expand:r.expand,array:r.array,format:r.format,prefix:r.prefix,secretKeys:r["secret-keys"]?.split(",").map(a=>a.trim()).filter(Boolean),once:r.once,exitOnError:r["exit-on-error"],exitOnChildError:r["exit-on-child-error"],cmd:c.length?c:void 0})}return console.error(`E_USAGE: unknown command: ${e}`),2}catch(t){return console.error(`E_USAGE: ${t.message}`),2}}var me=process.argv[1]?.endsWith("main.js")||process.argv[1]?.endsWith("main.cjs");me&&de().then(n=>{process.exitCode=n;}).catch(n=>{console.error(n?.message??n),process.exitCode=2;});export{de as main};
|