@systemix/env 0.3.0 → 0.3.2
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/dist/index.cjs +217 -226
- package/dist/index.d.cts +36 -34
- package/dist/index.d.ts +36 -34
- package/dist/index.js +214 -197
- package/package.json +23 -20
package/dist/index.cjs
CHANGED
|
@@ -1,243 +1,234 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
Config: () => Config,
|
|
24
|
-
load: () => load,
|
|
25
|
-
parseEnvFile: () => parseEnvFile
|
|
26
|
-
});
|
|
27
|
-
module.exports = __toCommonJS(index_exports);
|
|
28
|
-
|
|
29
|
-
// src/env/parse.ts
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
//#region src/env/parse.ts
|
|
5
|
+
/**
|
|
6
|
+
* Parse a string to boolean. Accepts: true/false, 1/0, yes/no, empty string.
|
|
7
|
+
* @param raw - Raw string value
|
|
8
|
+
* @throws When value is not a valid boolean
|
|
9
|
+
*/
|
|
30
10
|
function parseBoolean(raw) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
throw new Error(`Invalid boolean value: "${raw}"`);
|
|
11
|
+
const lower = raw.toLowerCase().trim();
|
|
12
|
+
if (lower === "true" || lower === "1" || lower === "yes") return true;
|
|
13
|
+
if (lower === "false" || lower === "0" || lower === "no" || lower === "") return false;
|
|
14
|
+
throw new Error(`Invalid boolean value: "${raw}"`);
|
|
36
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse a string to number. Uses Number() and checks isFinite.
|
|
18
|
+
* @param raw - Raw string value
|
|
19
|
+
* @throws When value is not a valid number
|
|
20
|
+
*/
|
|
37
21
|
function parseNumber(raw) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
return n;
|
|
22
|
+
const n = Number(raw);
|
|
23
|
+
if (!Number.isFinite(n)) throw new Error(`Invalid number value: "${raw}"`);
|
|
24
|
+
return n;
|
|
43
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse .env file content into key-value pairs.
|
|
28
|
+
* Skips comments (#) and empty lines; unquotes " and ' values.
|
|
29
|
+
*
|
|
30
|
+
* @param content - Raw file content
|
|
31
|
+
* @returns Map of KEY -> value strings
|
|
32
|
+
*/
|
|
44
33
|
function parseEnvFile(content) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return result;
|
|
34
|
+
const result = {};
|
|
35
|
+
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
const trimmed = line.trim();
|
|
38
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
39
|
+
const eqIndex = trimmed.indexOf("=");
|
|
40
|
+
if (eqIndex === -1) continue;
|
|
41
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
42
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
43
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
44
|
+
result[key] = value;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
60
47
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/env/file.ts
|
|
50
|
+
/**
|
|
51
|
+
* Load env vars from a .env file. Returns {} if file does not exist.
|
|
52
|
+
*
|
|
53
|
+
* @param filePath - Path relative to cwd or absolute
|
|
54
|
+
* @param cwd - Working directory for resolution
|
|
55
|
+
* @returns Parsed key-value pairs
|
|
56
|
+
*/
|
|
65
57
|
function loadEnvFile(filePath, cwd) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
const content = (0, import_node_fs.readFileSync)(resolved, "utf-8");
|
|
71
|
-
return parseEnvFile(content);
|
|
58
|
+
const resolved = (0, node_path.resolve)(cwd, filePath);
|
|
59
|
+
if (!(0, node_fs.existsSync)(resolved)) return {};
|
|
60
|
+
return parseEnvFile((0, node_fs.readFileSync)(resolved, "utf-8"));
|
|
72
61
|
}
|
|
62
|
+
/** Get current working directory, or '.' if process.cwd is unavailable. */
|
|
73
63
|
function getCwd() {
|
|
74
|
-
|
|
64
|
+
return typeof globalThis.process?.cwd === "function" ? globalThis.process.cwd() : ".";
|
|
75
65
|
}
|
|
66
|
+
/** Merge overlay env into base. Overlay keys override base. */
|
|
76
67
|
function mergeEnv(base, overlay) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
return merged;
|
|
68
|
+
const merged = { ...base };
|
|
69
|
+
for (const [k, v] of Object.entries(overlay)) merged[k] = v;
|
|
70
|
+
return merged;
|
|
82
71
|
}
|
|
72
|
+
/** Get process.env as a plain object, or {} if unavailable. */
|
|
83
73
|
function getEnvSource() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
return {};
|
|
74
|
+
const env = globalThis.process?.env;
|
|
75
|
+
if (env && typeof env === "object") return env;
|
|
76
|
+
return {};
|
|
89
77
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/env/load.ts
|
|
80
|
+
const MASK = "***";
|
|
81
|
+
/**
|
|
82
|
+
* Load and validate environment variables from a schema.
|
|
83
|
+
* Parses strings, numbers, and booleans; validates min/max, regex, oneOf.
|
|
84
|
+
* Returns a typed object with toSafeLog() for safe logging (masks secrets).
|
|
85
|
+
*
|
|
86
|
+
* @param schema - Map of var names to schema definitions
|
|
87
|
+
* @param options - Source override, fromFile, or strict mode. Legacy: plain object as source.
|
|
88
|
+
* @returns Typed env object with toSafeLog()
|
|
89
|
+
* @throws When required vars are missing or validation fails
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const env = load(
|
|
93
|
+
* { PORT: { type: 'number', default: 3000 }, API_KEY: { type: 'string', required: true } },
|
|
94
|
+
* { source: process.env }
|
|
95
|
+
* );
|
|
96
|
+
*/
|
|
93
97
|
function load(schema, options) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
throw new Error(
|
|
178
|
-
`Env validation failed:
|
|
179
|
-
${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
const toSafeLog = () => {
|
|
183
|
-
const safe = {};
|
|
184
|
-
for (const key of schemaKeys) {
|
|
185
|
-
const def = schema[key];
|
|
186
|
-
const val = result[key];
|
|
187
|
-
safe[key] = def.secret && val !== void 0 ? MASK : val;
|
|
188
|
-
}
|
|
189
|
-
return safe;
|
|
190
|
-
};
|
|
191
|
-
return Object.assign(result, { toSafeLog });
|
|
98
|
+
const opts = options ?? {};
|
|
99
|
+
const { source, fromFile, strict = false } = opts && typeof opts === "object" && !Array.isArray(opts) && !("source" in opts) && !("fromFile" in opts) && !("strict" in opts) ? { source: opts } : opts;
|
|
100
|
+
let env = source ?? getEnvSource();
|
|
101
|
+
if (fromFile) {
|
|
102
|
+
const cwd = getCwd();
|
|
103
|
+
const files = Array.isArray(fromFile) ? fromFile : [fromFile];
|
|
104
|
+
for (const f of files) {
|
|
105
|
+
const loaded = loadEnvFile(f, cwd);
|
|
106
|
+
env = mergeEnv(env, loaded);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (strict) {
|
|
110
|
+
const schemaKeys = new Set(Object.keys(schema));
|
|
111
|
+
const filtered = {};
|
|
112
|
+
for (const [k, v] of Object.entries(env)) if (schemaKeys.has(k)) filtered[k] = v;
|
|
113
|
+
env = filtered;
|
|
114
|
+
}
|
|
115
|
+
const schemaKeys = Object.keys(schema);
|
|
116
|
+
const errors = [];
|
|
117
|
+
const result = {};
|
|
118
|
+
for (const key of schemaKeys) {
|
|
119
|
+
const def = schema[key];
|
|
120
|
+
const raw = env[key];
|
|
121
|
+
const { type, required = false, default: defaultValue } = def;
|
|
122
|
+
if (raw === void 0 || raw === "") {
|
|
123
|
+
if (required && defaultValue === void 0) {
|
|
124
|
+
errors.push(`Missing required env var: ${key}`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
result[key] = defaultValue ?? void 0;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
let value;
|
|
132
|
+
if (def.transform) value = def.transform(raw);
|
|
133
|
+
else switch (type) {
|
|
134
|
+
case "string":
|
|
135
|
+
value = raw;
|
|
136
|
+
break;
|
|
137
|
+
case "number":
|
|
138
|
+
value = parseNumber(raw);
|
|
139
|
+
break;
|
|
140
|
+
case "boolean":
|
|
141
|
+
value = parseBoolean(raw);
|
|
142
|
+
break;
|
|
143
|
+
default: value = raw;
|
|
144
|
+
}
|
|
145
|
+
if (type === "number" && typeof value === "number") {
|
|
146
|
+
if (def.min !== void 0 && value < def.min) {
|
|
147
|
+
errors.push(`${key}: must be >= ${def.min}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (def.max !== void 0 && value > def.max) {
|
|
151
|
+
errors.push(`${key}: must be <= ${def.max}`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (type === "string" && typeof value === "string") {
|
|
156
|
+
if (def.regex && !def.regex.test(value)) {
|
|
157
|
+
errors.push(`${key}: must match ${def.regex}`);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (def.oneOf && !def.oneOf.includes(value)) {
|
|
161
|
+
errors.push(`${key}: must be one of [${def.oneOf.join(", ")}]`);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
result[key] = value;
|
|
166
|
+
} catch (err) {
|
|
167
|
+
errors.push(`${key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (errors.length > 0) throw new Error(`Env validation failed:\n${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
171
|
+
const toSafeLog = () => {
|
|
172
|
+
const safe = {};
|
|
173
|
+
for (const key of schemaKeys) {
|
|
174
|
+
const def = schema[key];
|
|
175
|
+
const val = result[key];
|
|
176
|
+
safe[key] = def.secret && val !== void 0 ? MASK : val;
|
|
177
|
+
}
|
|
178
|
+
return safe;
|
|
179
|
+
};
|
|
180
|
+
return Object.assign(result, { toSafeLog });
|
|
192
181
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/config.ts
|
|
184
|
+
/**
|
|
185
|
+
* Typed configuration container built from env vars.
|
|
186
|
+
* Use fromEnv() to load from schema, or from() to wrap an existing result.
|
|
187
|
+
*
|
|
188
|
+
* @template T - Schema type (keys and value types)
|
|
189
|
+
*/
|
|
190
|
+
var Config = class Config {
|
|
191
|
+
constructor(config) {
|
|
192
|
+
this.config = config;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Create Config from a schema and env source.
|
|
196
|
+
* @param schema - Env variable definitions
|
|
197
|
+
* @param options - Source, fromFile, or strict. Legacy: plain object as source.
|
|
198
|
+
*/
|
|
199
|
+
static fromEnv(schema, options) {
|
|
200
|
+
const config = load(schema, options);
|
|
201
|
+
return new Config(config);
|
|
202
|
+
}
|
|
203
|
+
/** Create Config from an existing load() result. */
|
|
204
|
+
static from(config) {
|
|
205
|
+
return new Config(config);
|
|
206
|
+
}
|
|
207
|
+
get(key, defaultValue) {
|
|
208
|
+
const val = this.config[key];
|
|
209
|
+
if (defaultValue !== void 0 && val === void 0) return defaultValue;
|
|
210
|
+
return val;
|
|
211
|
+
}
|
|
212
|
+
/** Get value by key, or throw if not set. */
|
|
213
|
+
getOrThrow(key) {
|
|
214
|
+
const val = this.config[key];
|
|
215
|
+
if (val === void 0) throw new Error(`Config key "${String(key)}" is not set`);
|
|
216
|
+
return val;
|
|
217
|
+
}
|
|
218
|
+
/** Check if key is set. */
|
|
219
|
+
has(key) {
|
|
220
|
+
return this.config[key] !== void 0;
|
|
221
|
+
}
|
|
222
|
+
/** Return a copy with secret values masked as '***'. */
|
|
223
|
+
toSafeLog() {
|
|
224
|
+
return this.config.toSafeLog();
|
|
225
|
+
}
|
|
226
|
+
/** Get the raw env result (no masking). */
|
|
227
|
+
getRaw() {
|
|
228
|
+
return this.config;
|
|
229
|
+
}
|
|
237
230
|
};
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
parseEnvFile
|
|
243
|
-
});
|
|
231
|
+
//#endregion
|
|
232
|
+
exports.Config = Config;
|
|
233
|
+
exports.load = load;
|
|
234
|
+
exports.parseEnvFile = parseEnvFile;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,43 +1,45 @@
|
|
|
1
|
+
//#region src/types/env.d.ts
|
|
1
2
|
type EnvVarType = 'string' | 'number' | 'boolean';
|
|
2
3
|
interface EnvVarSchema<T extends EnvVarType = EnvVarType> {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
type: T;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
default?: T extends 'string' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : never;
|
|
7
|
+
min?: number;
|
|
8
|
+
max?: number;
|
|
9
|
+
regex?: RegExp;
|
|
10
|
+
oneOf?: readonly string[];
|
|
11
|
+
transform?: (raw: string) => string | number | boolean;
|
|
12
|
+
secret?: boolean;
|
|
12
13
|
}
|
|
13
14
|
type EnvSchema = Record<string, EnvVarSchema<'string' | 'number' | 'boolean'>>;
|
|
14
15
|
type EnvValueFor<S extends EnvVarSchema<EnvVarType>> = S['type'] extends 'string' ? string : S['type'] extends 'number' ? number : S['type'] extends 'boolean' ? boolean : never;
|
|
15
|
-
type EnvResult<T extends EnvSchema> = {
|
|
16
|
-
|
|
17
|
-
} & {
|
|
18
|
-
toSafeLog(): Record<string, string | number | boolean | undefined>;
|
|
16
|
+
type EnvResult<T extends EnvSchema> = { [K in keyof T]: T[K]['required'] extends true ? EnvValueFor<T[K]> : T[K]['default'] extends EnvValueFor<T[K]> ? EnvValueFor<T[K]> : EnvValueFor<T[K]> | undefined; } & {
|
|
17
|
+
toSafeLog(): Record<string, string | number | boolean | undefined>;
|
|
19
18
|
};
|
|
20
19
|
interface LoadOptions {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
source?: Record<string, string | undefined>;
|
|
21
|
+
fromFile?: string | string[];
|
|
22
|
+
strict?: boolean;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
declare
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/env/parse.d.ts
|
|
26
|
+
export declare function parseEnvFile(content: string): Record<string, string>;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/env/load.d.ts
|
|
29
|
+
export declare function load<T extends EnvSchema>(schema: T, options?: LoadOptions | Record<string, string | undefined>): EnvResult<T>;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/config.d.ts
|
|
32
|
+
export declare class Config<T extends EnvSchema> {
|
|
33
|
+
private readonly config;
|
|
34
|
+
private constructor();
|
|
35
|
+
static fromEnv<S extends EnvSchema>(schema: S, options?: LoadOptions | Record<string, string | undefined>): Config<S>;
|
|
36
|
+
static from<S extends EnvSchema>(config: EnvResult<S>): Config<S>;
|
|
37
|
+
get<K extends keyof T>(key: K): EnvResult<T>[K] | undefined;
|
|
38
|
+
get<K extends keyof T>(key: K, defaultValue: EnvResult<T>[K]): EnvResult<T>[K];
|
|
39
|
+
getOrThrow<K extends keyof T>(key: K): EnvResult<T>[K];
|
|
40
|
+
has(key: keyof T): boolean;
|
|
41
|
+
toSafeLog(): Record<string, string | number | boolean | undefined>;
|
|
42
|
+
getRaw(): EnvResult<T>;
|
|
41
43
|
}
|
|
42
|
-
|
|
43
|
-
export {
|
|
44
|
+
//#endregion
|
|
45
|
+
export type { EnvResult, EnvSchema, EnvVarSchema, EnvVarType, LoadOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,43 +1,45 @@
|
|
|
1
|
+
//#region src/types/env.d.ts
|
|
1
2
|
type EnvVarType = 'string' | 'number' | 'boolean';
|
|
2
3
|
interface EnvVarSchema<T extends EnvVarType = EnvVarType> {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
type: T;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
default?: T extends 'string' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : never;
|
|
7
|
+
min?: number;
|
|
8
|
+
max?: number;
|
|
9
|
+
regex?: RegExp;
|
|
10
|
+
oneOf?: readonly string[];
|
|
11
|
+
transform?: (raw: string) => string | number | boolean;
|
|
12
|
+
secret?: boolean;
|
|
12
13
|
}
|
|
13
14
|
type EnvSchema = Record<string, EnvVarSchema<'string' | 'number' | 'boolean'>>;
|
|
14
15
|
type EnvValueFor<S extends EnvVarSchema<EnvVarType>> = S['type'] extends 'string' ? string : S['type'] extends 'number' ? number : S['type'] extends 'boolean' ? boolean : never;
|
|
15
|
-
type EnvResult<T extends EnvSchema> = {
|
|
16
|
-
|
|
17
|
-
} & {
|
|
18
|
-
toSafeLog(): Record<string, string | number | boolean | undefined>;
|
|
16
|
+
type EnvResult<T extends EnvSchema> = { [K in keyof T]: T[K]['required'] extends true ? EnvValueFor<T[K]> : T[K]['default'] extends EnvValueFor<T[K]> ? EnvValueFor<T[K]> : EnvValueFor<T[K]> | undefined; } & {
|
|
17
|
+
toSafeLog(): Record<string, string | number | boolean | undefined>;
|
|
19
18
|
};
|
|
20
19
|
interface LoadOptions {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
source?: Record<string, string | undefined>;
|
|
21
|
+
fromFile?: string | string[];
|
|
22
|
+
strict?: boolean;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
declare
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/env/parse.d.ts
|
|
26
|
+
export declare function parseEnvFile(content: string): Record<string, string>;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/env/load.d.ts
|
|
29
|
+
export declare function load<T extends EnvSchema>(schema: T, options?: LoadOptions | Record<string, string | undefined>): EnvResult<T>;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/config.d.ts
|
|
32
|
+
export declare class Config<T extends EnvSchema> {
|
|
33
|
+
private readonly config;
|
|
34
|
+
private constructor();
|
|
35
|
+
static fromEnv<S extends EnvSchema>(schema: S, options?: LoadOptions | Record<string, string | undefined>): Config<S>;
|
|
36
|
+
static from<S extends EnvSchema>(config: EnvResult<S>): Config<S>;
|
|
37
|
+
get<K extends keyof T>(key: K): EnvResult<T>[K] | undefined;
|
|
38
|
+
get<K extends keyof T>(key: K, defaultValue: EnvResult<T>[K]): EnvResult<T>[K];
|
|
39
|
+
getOrThrow<K extends keyof T>(key: K): EnvResult<T>[K];
|
|
40
|
+
has(key: keyof T): boolean;
|
|
41
|
+
toSafeLog(): Record<string, string | number | boolean | undefined>;
|
|
42
|
+
getRaw(): EnvResult<T>;
|
|
41
43
|
}
|
|
42
|
-
|
|
43
|
-
export {
|
|
44
|
+
//#endregion
|
|
45
|
+
export type { EnvResult, EnvSchema, EnvVarSchema, EnvVarType, LoadOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,214 +1,231 @@
|
|
|
1
|
-
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
//#region src/env/parse.ts
|
|
4
|
+
/**
|
|
5
|
+
* Parse a string to boolean. Accepts: true/false, 1/0, yes/no, empty string.
|
|
6
|
+
* @param raw - Raw string value
|
|
7
|
+
* @throws When value is not a valid boolean
|
|
8
|
+
*/
|
|
2
9
|
function parseBoolean(raw) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
throw new Error(`Invalid boolean value: "${raw}"`);
|
|
10
|
+
const lower = raw.toLowerCase().trim();
|
|
11
|
+
if (lower === "true" || lower === "1" || lower === "yes") return true;
|
|
12
|
+
if (lower === "false" || lower === "0" || lower === "no" || lower === "") return false;
|
|
13
|
+
throw new Error(`Invalid boolean value: "${raw}"`);
|
|
8
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse a string to number. Uses Number() and checks isFinite.
|
|
17
|
+
* @param raw - Raw string value
|
|
18
|
+
* @throws When value is not a valid number
|
|
19
|
+
*/
|
|
9
20
|
function parseNumber(raw) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
return n;
|
|
21
|
+
const n = Number(raw);
|
|
22
|
+
if (!Number.isFinite(n)) throw new Error(`Invalid number value: "${raw}"`);
|
|
23
|
+
return n;
|
|
15
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse .env file content into key-value pairs.
|
|
27
|
+
* Skips comments (#) and empty lines; unquotes " and ' values.
|
|
28
|
+
*
|
|
29
|
+
* @param content - Raw file content
|
|
30
|
+
* @returns Map of KEY -> value strings
|
|
31
|
+
*/
|
|
16
32
|
function parseEnvFile(content) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
return result;
|
|
33
|
+
const result = {};
|
|
34
|
+
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const trimmed = line.trim();
|
|
37
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
38
|
+
const eqIndex = trimmed.indexOf("=");
|
|
39
|
+
if (eqIndex === -1) continue;
|
|
40
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
41
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
42
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
43
|
+
result[key] = value;
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
32
46
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/env/file.ts
|
|
49
|
+
/**
|
|
50
|
+
* Load env vars from a .env file. Returns {} if file does not exist.
|
|
51
|
+
*
|
|
52
|
+
* @param filePath - Path relative to cwd or absolute
|
|
53
|
+
* @param cwd - Working directory for resolution
|
|
54
|
+
* @returns Parsed key-value pairs
|
|
55
|
+
*/
|
|
37
56
|
function loadEnvFile(filePath, cwd) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const content = readFileSync(resolved, "utf-8");
|
|
43
|
-
return parseEnvFile(content);
|
|
57
|
+
const resolved = resolve(cwd, filePath);
|
|
58
|
+
if (!existsSync(resolved)) return {};
|
|
59
|
+
return parseEnvFile(readFileSync(resolved, "utf-8"));
|
|
44
60
|
}
|
|
61
|
+
/** Get current working directory, or '.' if process.cwd is unavailable. */
|
|
45
62
|
function getCwd() {
|
|
46
|
-
|
|
63
|
+
return typeof globalThis.process?.cwd === "function" ? globalThis.process.cwd() : ".";
|
|
47
64
|
}
|
|
65
|
+
/** Merge overlay env into base. Overlay keys override base. */
|
|
48
66
|
function mergeEnv(base, overlay) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return merged;
|
|
67
|
+
const merged = { ...base };
|
|
68
|
+
for (const [k, v] of Object.entries(overlay)) merged[k] = v;
|
|
69
|
+
return merged;
|
|
54
70
|
}
|
|
71
|
+
/** Get process.env as a plain object, or {} if unavailable. */
|
|
55
72
|
function getEnvSource() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
return {};
|
|
73
|
+
const env = globalThis.process?.env;
|
|
74
|
+
if (env && typeof env === "object") return env;
|
|
75
|
+
return {};
|
|
61
76
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/env/load.ts
|
|
79
|
+
const MASK = "***";
|
|
80
|
+
/**
|
|
81
|
+
* Load and validate environment variables from a schema.
|
|
82
|
+
* Parses strings, numbers, and booleans; validates min/max, regex, oneOf.
|
|
83
|
+
* Returns a typed object with toSafeLog() for safe logging (masks secrets).
|
|
84
|
+
*
|
|
85
|
+
* @param schema - Map of var names to schema definitions
|
|
86
|
+
* @param options - Source override, fromFile, or strict mode. Legacy: plain object as source.
|
|
87
|
+
* @returns Typed env object with toSafeLog()
|
|
88
|
+
* @throws When required vars are missing or validation fails
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* const env = load(
|
|
92
|
+
* { PORT: { type: 'number', default: 3000 }, API_KEY: { type: 'string', required: true } },
|
|
93
|
+
* { source: process.env }
|
|
94
|
+
* );
|
|
95
|
+
*/
|
|
65
96
|
function load(schema, options) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
throw new Error(
|
|
150
|
-
`Env validation failed:
|
|
151
|
-
${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
const toSafeLog = () => {
|
|
155
|
-
const safe = {};
|
|
156
|
-
for (const key of schemaKeys) {
|
|
157
|
-
const def = schema[key];
|
|
158
|
-
const val = result[key];
|
|
159
|
-
safe[key] = def.secret && val !== void 0 ? MASK : val;
|
|
160
|
-
}
|
|
161
|
-
return safe;
|
|
162
|
-
};
|
|
163
|
-
return Object.assign(result, { toSafeLog });
|
|
97
|
+
const opts = options ?? {};
|
|
98
|
+
const { source, fromFile, strict = false } = opts && typeof opts === "object" && !Array.isArray(opts) && !("source" in opts) && !("fromFile" in opts) && !("strict" in opts) ? { source: opts } : opts;
|
|
99
|
+
let env = source ?? getEnvSource();
|
|
100
|
+
if (fromFile) {
|
|
101
|
+
const cwd = getCwd();
|
|
102
|
+
const files = Array.isArray(fromFile) ? fromFile : [fromFile];
|
|
103
|
+
for (const f of files) {
|
|
104
|
+
const loaded = loadEnvFile(f, cwd);
|
|
105
|
+
env = mergeEnv(env, loaded);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (strict) {
|
|
109
|
+
const schemaKeys = new Set(Object.keys(schema));
|
|
110
|
+
const filtered = {};
|
|
111
|
+
for (const [k, v] of Object.entries(env)) if (schemaKeys.has(k)) filtered[k] = v;
|
|
112
|
+
env = filtered;
|
|
113
|
+
}
|
|
114
|
+
const schemaKeys = Object.keys(schema);
|
|
115
|
+
const errors = [];
|
|
116
|
+
const result = {};
|
|
117
|
+
for (const key of schemaKeys) {
|
|
118
|
+
const def = schema[key];
|
|
119
|
+
const raw = env[key];
|
|
120
|
+
const { type, required = false, default: defaultValue } = def;
|
|
121
|
+
if (raw === void 0 || raw === "") {
|
|
122
|
+
if (required && defaultValue === void 0) {
|
|
123
|
+
errors.push(`Missing required env var: ${key}`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
result[key] = defaultValue ?? void 0;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
let value;
|
|
131
|
+
if (def.transform) value = def.transform(raw);
|
|
132
|
+
else switch (type) {
|
|
133
|
+
case "string":
|
|
134
|
+
value = raw;
|
|
135
|
+
break;
|
|
136
|
+
case "number":
|
|
137
|
+
value = parseNumber(raw);
|
|
138
|
+
break;
|
|
139
|
+
case "boolean":
|
|
140
|
+
value = parseBoolean(raw);
|
|
141
|
+
break;
|
|
142
|
+
default: value = raw;
|
|
143
|
+
}
|
|
144
|
+
if (type === "number" && typeof value === "number") {
|
|
145
|
+
if (def.min !== void 0 && value < def.min) {
|
|
146
|
+
errors.push(`${key}: must be >= ${def.min}`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (def.max !== void 0 && value > def.max) {
|
|
150
|
+
errors.push(`${key}: must be <= ${def.max}`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (type === "string" && typeof value === "string") {
|
|
155
|
+
if (def.regex && !def.regex.test(value)) {
|
|
156
|
+
errors.push(`${key}: must match ${def.regex}`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (def.oneOf && !def.oneOf.includes(value)) {
|
|
160
|
+
errors.push(`${key}: must be one of [${def.oneOf.join(", ")}]`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
result[key] = value;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
errors.push(`${key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (errors.length > 0) throw new Error(`Env validation failed:\n${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
170
|
+
const toSafeLog = () => {
|
|
171
|
+
const safe = {};
|
|
172
|
+
for (const key of schemaKeys) {
|
|
173
|
+
const def = schema[key];
|
|
174
|
+
const val = result[key];
|
|
175
|
+
safe[key] = def.secret && val !== void 0 ? MASK : val;
|
|
176
|
+
}
|
|
177
|
+
return safe;
|
|
178
|
+
};
|
|
179
|
+
return Object.assign(result, { toSafeLog });
|
|
164
180
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
parseEnvFile
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/config.ts
|
|
183
|
+
/**
|
|
184
|
+
* Typed configuration container built from env vars.
|
|
185
|
+
* Use fromEnv() to load from schema, or from() to wrap an existing result.
|
|
186
|
+
*
|
|
187
|
+
* @template T - Schema type (keys and value types)
|
|
188
|
+
*/
|
|
189
|
+
var Config = class Config {
|
|
190
|
+
constructor(config) {
|
|
191
|
+
this.config = config;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Create Config from a schema and env source.
|
|
195
|
+
* @param schema - Env variable definitions
|
|
196
|
+
* @param options - Source, fromFile, or strict. Legacy: plain object as source.
|
|
197
|
+
*/
|
|
198
|
+
static fromEnv(schema, options) {
|
|
199
|
+
const config = load(schema, options);
|
|
200
|
+
return new Config(config);
|
|
201
|
+
}
|
|
202
|
+
/** Create Config from an existing load() result. */
|
|
203
|
+
static from(config) {
|
|
204
|
+
return new Config(config);
|
|
205
|
+
}
|
|
206
|
+
get(key, defaultValue) {
|
|
207
|
+
const val = this.config[key];
|
|
208
|
+
if (defaultValue !== void 0 && val === void 0) return defaultValue;
|
|
209
|
+
return val;
|
|
210
|
+
}
|
|
211
|
+
/** Get value by key, or throw if not set. */
|
|
212
|
+
getOrThrow(key) {
|
|
213
|
+
const val = this.config[key];
|
|
214
|
+
if (val === void 0) throw new Error(`Config key "${String(key)}" is not set`);
|
|
215
|
+
return val;
|
|
216
|
+
}
|
|
217
|
+
/** Check if key is set. */
|
|
218
|
+
has(key) {
|
|
219
|
+
return this.config[key] !== void 0;
|
|
220
|
+
}
|
|
221
|
+
/** Return a copy with secret values masked as '***'. */
|
|
222
|
+
toSafeLog() {
|
|
223
|
+
return this.config.toSafeLog();
|
|
224
|
+
}
|
|
225
|
+
/** Get the raw env result (no masking). */
|
|
226
|
+
getRaw() {
|
|
227
|
+
return this.config;
|
|
228
|
+
}
|
|
214
229
|
};
|
|
230
|
+
//#endregion
|
|
231
|
+
export { Config, load, parseEnvFile };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemix/env",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
"directory": "packages/env"
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/shahadathhs/systemix/tree/main/packages/env#readme",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown",
|
|
27
|
+
"test": "node tests/run.mjs",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "oxlint . --type-aware",
|
|
30
|
+
"lint:fix": "oxlint . --type-aware --fix",
|
|
31
|
+
"format": "prettier --check . --ignore-path .prettierignore",
|
|
32
|
+
"format:fix": "prettier --write . --ignore-path .prettierignore",
|
|
33
|
+
"clean": "rimraf dist node_modules",
|
|
34
|
+
"clean:turbo": "rimraf .turbo dist node_modules",
|
|
35
|
+
"prepublishOnly": "pnpm build"
|
|
36
|
+
},
|
|
25
37
|
"keywords": [
|
|
26
38
|
"systemix",
|
|
27
39
|
"env",
|
|
@@ -37,13 +49,14 @@
|
|
|
37
49
|
"license": "MIT",
|
|
38
50
|
"description": "Typed environment variable loading and validation. Part of the Systemix toolkit.",
|
|
39
51
|
"devDependencies": {
|
|
40
|
-
"@
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
52
|
+
"@systemix/runner": "workspace:*",
|
|
53
|
+
"@systemix/typescript": "workspace:*",
|
|
54
|
+
"@types/node": "catalog:",
|
|
55
|
+
"eslint": "catalog:",
|
|
56
|
+
"oxlint": "catalog:",
|
|
57
|
+
"oxlint-tsgolint": "catalog:",
|
|
58
|
+
"tsdown": "catalog:",
|
|
59
|
+
"typescript": "catalog:"
|
|
47
60
|
},
|
|
48
61
|
"directories": {
|
|
49
62
|
"test": "tests"
|
|
@@ -58,15 +71,5 @@
|
|
|
58
71
|
"LICENSE",
|
|
59
72
|
"package.json"
|
|
60
73
|
],
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
"test": "pnpm build && node tests/run.mjs",
|
|
64
|
-
"typecheck": "tsc --noEmit",
|
|
65
|
-
"lint": "eslint .",
|
|
66
|
-
"lint:fix": "eslint . --fix",
|
|
67
|
-
"format": "prettier --check . --ignore-path .prettierignore",
|
|
68
|
-
"format:fix": "prettier --write . --ignore-path .prettierignore",
|
|
69
|
-
"clean": "rimraf dist node_modules",
|
|
70
|
-
"clean:turbo": "rimraf .turbo dist node_modules"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
74
|
+
"tsconfig": null
|
|
75
|
+
}
|