@perkos/util-config 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # @perkos/util-config
2
+
3
+ Configuration management utilities for PerkOS services.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @perkos/util-config
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Environment Variables
14
+
15
+ ```typescript
16
+ import {
17
+ getEnv,
18
+ getRequiredEnv,
19
+ getEnvNumber,
20
+ getEnvBoolean,
21
+ getEnvJson,
22
+ } from "@perkos/util-config";
23
+
24
+ // Get optional env var with default
25
+ const apiUrl = getEnv("API_URL", "http://localhost:3000");
26
+
27
+ // Get required env var (throws if missing)
28
+ const apiKey = getRequiredEnv("API_KEY");
29
+
30
+ // Get as number
31
+ const port = getEnvNumber("PORT", 3000);
32
+
33
+ // Get as boolean
34
+ const debug = getEnvBoolean("DEBUG", false);
35
+
36
+ // Get as parsed JSON
37
+ const config = getEnvJson<{ key: string }>("CONFIG_JSON");
38
+ ```
39
+
40
+ ### Typed Configuration Builder
41
+
42
+ ```typescript
43
+ import { createConfig, validateConfig } from "@perkos/util-config";
44
+
45
+ // Define configuration schema
46
+ const config = createConfig({
47
+ prefix: "APP",
48
+ strict: true,
49
+ vars: {
50
+ API_KEY: { required: true, type: "string" },
51
+ PORT: { type: "number", default: 3000 },
52
+ DEBUG: { type: "boolean", default: false },
53
+ SETTINGS: { type: "json", default: {} },
54
+ NETWORK: {
55
+ required: true,
56
+ validator: (v) => ["mainnet", "testnet"].includes(v),
57
+ },
58
+ },
59
+ });
60
+
61
+ // config is typed: { API_KEY: string, PORT: number, DEBUG: boolean, ... }
62
+
63
+ // Validate without creating
64
+ const result = validateConfig({
65
+ prefix: "APP",
66
+ vars: {
67
+ API_KEY: { required: true },
68
+ PORT: { type: "number" },
69
+ },
70
+ });
71
+
72
+ if (!result.valid) {
73
+ console.error("Config errors:", result.errors);
74
+ console.warn("Warnings:", result.warnings);
75
+ }
76
+ ```
77
+
78
+ ### Price Configuration
79
+
80
+ ```typescript
81
+ import { parsePrice, createPriceConfig, createRouteMapping } from "@perkos/util-config";
82
+
83
+ // Parse single price
84
+ const price = parsePrice(process.env.PRICE_USD, 0.01);
85
+
86
+ // Create price config from env
87
+ const prices = createPriceConfig({
88
+ analyze: { envKey: "AI_ANALYZE_PRICE_USD", default: 0.05 },
89
+ generate: { envKey: "AI_GENERATE_PRICE_USD", default: 0.15 },
90
+ transcribe: { envKey: "AI_TRANSCRIBE_PRICE_USD", default: 0.04 },
91
+ });
92
+
93
+ // Create route mappings
94
+ const routes = createRouteMapping(
95
+ [
96
+ { path: "/api/ai/analyze", priceKey: "analyze" },
97
+ { path: "/api/ai/generate", priceKey: "generate" },
98
+ { path: "/api/ai/transcribe", priceKey: "transcribe" },
99
+ ],
100
+ prices
101
+ );
102
+
103
+ // Get price for route
104
+ import { getRoutePrice } from "@perkos/util-config";
105
+ const price = getRoutePrice(routes, "/api/ai/analyze"); // 0.05
106
+ ```
107
+
108
+ ### Service Discovery
109
+
110
+ ```typescript
111
+ import { createServiceInfo } from "@perkos/util-config";
112
+
113
+ const serviceInfo = createServiceInfo({
114
+ name: "My AI Service",
115
+ version: "1.0.0",
116
+ description: "AI-powered services with micropayments",
117
+ capabilities: [
118
+ "image-analysis",
119
+ "image-generation",
120
+ "text-summarization",
121
+ ],
122
+ endpoints: {
123
+ analyze: {
124
+ path: "/api/ai/analyze",
125
+ method: "POST",
126
+ description: "Analyze images",
127
+ price: 0.05,
128
+ },
129
+ },
130
+ });
131
+ ```
132
+
133
+ ### Feature Flags
134
+
135
+ ```typescript
136
+ import { createFeatureFlags, isFeatureEnabled } from "@perkos/util-config";
137
+
138
+ const flags = createFeatureFlags({
139
+ newUI: { envKey: "FEATURE_NEW_UI", default: false },
140
+ betaAPI: { envKey: "FEATURE_BETA_API", default: false },
141
+ analytics: { envKey: "FEATURE_ANALYTICS", default: true },
142
+ });
143
+
144
+ if (isFeatureEnabled(flags, "newUI")) {
145
+ // Show new UI
146
+ }
147
+ ```
148
+
149
+ ## API Reference
150
+
151
+ ### Environment Utilities
152
+
153
+ | Function | Description |
154
+ |----------|-------------|
155
+ | `getEnv(key, default?)` | Get env var with optional default |
156
+ | `getRequiredEnv(key)` | Get required env var (throws if missing) |
157
+ | `getEnvNumber(key, default?)` | Get env var as number |
158
+ | `getEnvBoolean(key, default?)` | Get env var as boolean |
159
+ | `getEnvJson(key, default?)` | Get env var as parsed JSON |
160
+
161
+ ### Configuration Builder
162
+
163
+ | Function | Description |
164
+ |----------|-------------|
165
+ | `createConfig(config)` | Create typed config from env vars |
166
+ | `validateConfig(config)` | Validate config without creating |
167
+
168
+ ### Price Configuration
169
+
170
+ | Function | Description |
171
+ |----------|-------------|
172
+ | `parsePrice(envVar, fallback)` | Parse price with fallback |
173
+ | `createPriceConfig(priceMap)` | Create price config from env |
174
+ | `createRouteMapping(routes, prices)` | Map routes to prices |
175
+ | `getRoutePrice(routes, path)` | Get price for route |
176
+
177
+ ### Service Discovery
178
+
179
+ | Function | Description |
180
+ |----------|-------------|
181
+ | `createServiceInfo(info)` | Create service discovery info |
182
+
183
+ ### Feature Flags
184
+
185
+ | Function | Description |
186
+ |----------|-------------|
187
+ | `createFeatureFlags(flags)` | Create feature flags from env |
188
+ | `isFeatureEnabled(flags, feature)` | Check if feature is enabled |
189
+
190
+ ## License
191
+
192
+ MIT
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @perkos/config
3
+ * Configuration management utilities for PerkOS services
4
+ */
5
+ interface EnvConfig<T extends Record<string, EnvVarConfig>> {
6
+ vars: T;
7
+ prefix?: string;
8
+ strict?: boolean;
9
+ }
10
+ interface EnvVarConfig {
11
+ required?: boolean;
12
+ default?: string | number | boolean;
13
+ type?: "string" | "number" | "boolean" | "json";
14
+ validator?: (value: string) => boolean;
15
+ transform?: (value: string) => unknown;
16
+ }
17
+ type ParsedConfig<T extends Record<string, EnvVarConfig>> = {
18
+ [K in keyof T]: T[K]["type"] extends "number" ? number : T[K]["type"] extends "boolean" ? boolean : T[K]["type"] extends "json" ? unknown : string;
19
+ };
20
+ interface ConfigValidationResult {
21
+ valid: boolean;
22
+ errors: string[];
23
+ warnings: string[];
24
+ }
25
+ /**
26
+ * Get environment variable with optional default
27
+ */
28
+ declare function getEnv(key: string, defaultValue?: string): string | undefined;
29
+ /**
30
+ * Get required environment variable (throws if missing)
31
+ */
32
+ declare function getRequiredEnv(key: string): string;
33
+ /**
34
+ * Get environment variable as number
35
+ */
36
+ declare function getEnvNumber(key: string, defaultValue?: number): number | undefined;
37
+ /**
38
+ * Get environment variable as boolean
39
+ */
40
+ declare function getEnvBoolean(key: string, defaultValue?: boolean): boolean | undefined;
41
+ /**
42
+ * Get environment variable as JSON
43
+ */
44
+ declare function getEnvJson<T>(key: string, defaultValue?: T): T | undefined;
45
+ /**
46
+ * Create a typed configuration from environment variables
47
+ */
48
+ declare function createConfig<T extends Record<string, EnvVarConfig>>(config: EnvConfig<T>): ParsedConfig<T>;
49
+ /**
50
+ * Validate configuration without creating it
51
+ */
52
+ declare function validateConfig<T extends Record<string, EnvVarConfig>>(config: EnvConfig<T>): ConfigValidationResult;
53
+ interface ServiceInfo {
54
+ name: string;
55
+ version: string;
56
+ description?: string;
57
+ capabilities?: string[];
58
+ endpoints?: Record<string, EndpointInfo>;
59
+ }
60
+ interface EndpointInfo {
61
+ path: string;
62
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
63
+ description?: string;
64
+ price?: number;
65
+ rateLimit?: number;
66
+ }
67
+ /**
68
+ * Create service discovery info
69
+ */
70
+ declare function createServiceInfo(info: ServiceInfo): ServiceInfo;
71
+ interface PriceConfig {
72
+ [key: string]: number;
73
+ }
74
+ /**
75
+ * Parse price from environment variable with fallback
76
+ */
77
+ declare function parsePrice(envVar: string | undefined, fallback: number): number;
78
+ /**
79
+ * Create price configuration from environment
80
+ */
81
+ declare function createPriceConfig(priceMap: Record<string, {
82
+ envKey: string;
83
+ default: number;
84
+ }>): PriceConfig;
85
+ interface RouteConfig {
86
+ path: string;
87
+ price: number;
88
+ description?: string;
89
+ rateLimit?: number;
90
+ }
91
+ /**
92
+ * Create route price mapping from price config
93
+ */
94
+ declare function createRouteMapping(routes: Array<{
95
+ path: string;
96
+ priceKey: string;
97
+ description?: string;
98
+ }>, prices: PriceConfig): Record<string, RouteConfig>;
99
+ /**
100
+ * Get price for a route path
101
+ */
102
+ declare function getRoutePrice(routes: Record<string, RouteConfig>, path: string): number | undefined;
103
+ interface FeatureFlags {
104
+ [key: string]: boolean;
105
+ }
106
+ /**
107
+ * Create feature flags from environment
108
+ */
109
+ declare function createFeatureFlags(flags: Record<string, {
110
+ envKey: string;
111
+ default: boolean;
112
+ }>): FeatureFlags;
113
+ /**
114
+ * Check if a feature is enabled
115
+ */
116
+ declare function isFeatureEnabled(flags: FeatureFlags, feature: string): boolean;
117
+
118
+ export { type ConfigValidationResult, type EndpointInfo, type EnvConfig, type EnvVarConfig, type FeatureFlags, type ParsedConfig, type PriceConfig, type RouteConfig, type ServiceInfo, createConfig, createFeatureFlags, createPriceConfig, createRouteMapping, createServiceInfo, getEnv, getEnvBoolean, getEnvJson, getEnvNumber, getRequiredEnv, getRoutePrice, isFeatureEnabled, parsePrice, validateConfig };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @perkos/config
3
+ * Configuration management utilities for PerkOS services
4
+ */
5
+ interface EnvConfig<T extends Record<string, EnvVarConfig>> {
6
+ vars: T;
7
+ prefix?: string;
8
+ strict?: boolean;
9
+ }
10
+ interface EnvVarConfig {
11
+ required?: boolean;
12
+ default?: string | number | boolean;
13
+ type?: "string" | "number" | "boolean" | "json";
14
+ validator?: (value: string) => boolean;
15
+ transform?: (value: string) => unknown;
16
+ }
17
+ type ParsedConfig<T extends Record<string, EnvVarConfig>> = {
18
+ [K in keyof T]: T[K]["type"] extends "number" ? number : T[K]["type"] extends "boolean" ? boolean : T[K]["type"] extends "json" ? unknown : string;
19
+ };
20
+ interface ConfigValidationResult {
21
+ valid: boolean;
22
+ errors: string[];
23
+ warnings: string[];
24
+ }
25
+ /**
26
+ * Get environment variable with optional default
27
+ */
28
+ declare function getEnv(key: string, defaultValue?: string): string | undefined;
29
+ /**
30
+ * Get required environment variable (throws if missing)
31
+ */
32
+ declare function getRequiredEnv(key: string): string;
33
+ /**
34
+ * Get environment variable as number
35
+ */
36
+ declare function getEnvNumber(key: string, defaultValue?: number): number | undefined;
37
+ /**
38
+ * Get environment variable as boolean
39
+ */
40
+ declare function getEnvBoolean(key: string, defaultValue?: boolean): boolean | undefined;
41
+ /**
42
+ * Get environment variable as JSON
43
+ */
44
+ declare function getEnvJson<T>(key: string, defaultValue?: T): T | undefined;
45
+ /**
46
+ * Create a typed configuration from environment variables
47
+ */
48
+ declare function createConfig<T extends Record<string, EnvVarConfig>>(config: EnvConfig<T>): ParsedConfig<T>;
49
+ /**
50
+ * Validate configuration without creating it
51
+ */
52
+ declare function validateConfig<T extends Record<string, EnvVarConfig>>(config: EnvConfig<T>): ConfigValidationResult;
53
+ interface ServiceInfo {
54
+ name: string;
55
+ version: string;
56
+ description?: string;
57
+ capabilities?: string[];
58
+ endpoints?: Record<string, EndpointInfo>;
59
+ }
60
+ interface EndpointInfo {
61
+ path: string;
62
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
63
+ description?: string;
64
+ price?: number;
65
+ rateLimit?: number;
66
+ }
67
+ /**
68
+ * Create service discovery info
69
+ */
70
+ declare function createServiceInfo(info: ServiceInfo): ServiceInfo;
71
+ interface PriceConfig {
72
+ [key: string]: number;
73
+ }
74
+ /**
75
+ * Parse price from environment variable with fallback
76
+ */
77
+ declare function parsePrice(envVar: string | undefined, fallback: number): number;
78
+ /**
79
+ * Create price configuration from environment
80
+ */
81
+ declare function createPriceConfig(priceMap: Record<string, {
82
+ envKey: string;
83
+ default: number;
84
+ }>): PriceConfig;
85
+ interface RouteConfig {
86
+ path: string;
87
+ price: number;
88
+ description?: string;
89
+ rateLimit?: number;
90
+ }
91
+ /**
92
+ * Create route price mapping from price config
93
+ */
94
+ declare function createRouteMapping(routes: Array<{
95
+ path: string;
96
+ priceKey: string;
97
+ description?: string;
98
+ }>, prices: PriceConfig): Record<string, RouteConfig>;
99
+ /**
100
+ * Get price for a route path
101
+ */
102
+ declare function getRoutePrice(routes: Record<string, RouteConfig>, path: string): number | undefined;
103
+ interface FeatureFlags {
104
+ [key: string]: boolean;
105
+ }
106
+ /**
107
+ * Create feature flags from environment
108
+ */
109
+ declare function createFeatureFlags(flags: Record<string, {
110
+ envKey: string;
111
+ default: boolean;
112
+ }>): FeatureFlags;
113
+ /**
114
+ * Check if a feature is enabled
115
+ */
116
+ declare function isFeatureEnabled(flags: FeatureFlags, feature: string): boolean;
117
+
118
+ export { type ConfigValidationResult, type EndpointInfo, type EnvConfig, type EnvVarConfig, type FeatureFlags, type ParsedConfig, type PriceConfig, type RouteConfig, type ServiceInfo, createConfig, createFeatureFlags, createPriceConfig, createRouteMapping, createServiceInfo, getEnv, getEnvBoolean, getEnvJson, getEnvNumber, getRequiredEnv, getRoutePrice, isFeatureEnabled, parsePrice, validateConfig };
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
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
+ createConfig: () => createConfig,
24
+ createFeatureFlags: () => createFeatureFlags,
25
+ createPriceConfig: () => createPriceConfig,
26
+ createRouteMapping: () => createRouteMapping,
27
+ createServiceInfo: () => createServiceInfo,
28
+ getEnv: () => getEnv,
29
+ getEnvBoolean: () => getEnvBoolean,
30
+ getEnvJson: () => getEnvJson,
31
+ getEnvNumber: () => getEnvNumber,
32
+ getRequiredEnv: () => getRequiredEnv,
33
+ getRoutePrice: () => getRoutePrice,
34
+ isFeatureEnabled: () => isFeatureEnabled,
35
+ parsePrice: () => parsePrice,
36
+ validateConfig: () => validateConfig
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+ function getEnv(key, defaultValue) {
40
+ return process.env[key] ?? defaultValue;
41
+ }
42
+ function getRequiredEnv(key) {
43
+ const value = process.env[key];
44
+ if (value === void 0 || value === "") {
45
+ throw new Error(`Required environment variable ${key} is not set`);
46
+ }
47
+ return value;
48
+ }
49
+ function getEnvNumber(key, defaultValue) {
50
+ const value = process.env[key];
51
+ if (value === void 0) return defaultValue;
52
+ const parsed = parseFloat(value);
53
+ return isNaN(parsed) ? defaultValue : parsed;
54
+ }
55
+ function getEnvBoolean(key, defaultValue) {
56
+ const value = process.env[key];
57
+ if (value === void 0) return defaultValue;
58
+ return value.toLowerCase() === "true" || value === "1";
59
+ }
60
+ function getEnvJson(key, defaultValue) {
61
+ const value = process.env[key];
62
+ if (value === void 0) return defaultValue;
63
+ try {
64
+ return JSON.parse(value);
65
+ } catch {
66
+ return defaultValue;
67
+ }
68
+ }
69
+ function createConfig(config) {
70
+ const result = {};
71
+ const prefix = config.prefix ? `${config.prefix}_` : "";
72
+ for (const [key, varConfig] of Object.entries(config.vars)) {
73
+ const envKey = `${prefix}${key}`;
74
+ const rawValue = process.env[envKey];
75
+ if (rawValue === void 0 || rawValue === "") {
76
+ if (varConfig.required && config.strict !== false) {
77
+ throw new Error(`Required environment variable ${envKey} is not set`);
78
+ }
79
+ if (varConfig.default !== void 0) {
80
+ result[key] = varConfig.default;
81
+ continue;
82
+ }
83
+ result[key] = void 0;
84
+ continue;
85
+ }
86
+ if (varConfig.validator && !varConfig.validator(rawValue)) {
87
+ throw new Error(`Invalid value for environment variable ${envKey}`);
88
+ }
89
+ if (varConfig.transform) {
90
+ result[key] = varConfig.transform(rawValue);
91
+ continue;
92
+ }
93
+ switch (varConfig.type) {
94
+ case "number":
95
+ const num = parseFloat(rawValue);
96
+ if (isNaN(num)) {
97
+ throw new Error(`Invalid number for environment variable ${envKey}`);
98
+ }
99
+ result[key] = num;
100
+ break;
101
+ case "boolean":
102
+ result[key] = rawValue.toLowerCase() === "true" || rawValue === "1";
103
+ break;
104
+ case "json":
105
+ try {
106
+ result[key] = JSON.parse(rawValue);
107
+ } catch {
108
+ throw new Error(`Invalid JSON for environment variable ${envKey}`);
109
+ }
110
+ break;
111
+ default:
112
+ result[key] = rawValue;
113
+ }
114
+ }
115
+ return result;
116
+ }
117
+ function validateConfig(config) {
118
+ const errors = [];
119
+ const warnings = [];
120
+ const prefix = config.prefix ? `${config.prefix}_` : "";
121
+ for (const [key, varConfig] of Object.entries(config.vars)) {
122
+ const envKey = `${prefix}${key}`;
123
+ const rawValue = process.env[envKey];
124
+ if (rawValue === void 0 || rawValue === "") {
125
+ if (varConfig.required) {
126
+ errors.push(`Missing required: ${envKey}`);
127
+ } else if (varConfig.default === void 0) {
128
+ warnings.push(`Optional not set: ${envKey}`);
129
+ }
130
+ continue;
131
+ }
132
+ if (varConfig.validator && !varConfig.validator(rawValue)) {
133
+ errors.push(`Invalid value: ${envKey}`);
134
+ }
135
+ if (varConfig.type === "number" && isNaN(parseFloat(rawValue))) {
136
+ errors.push(`Invalid number: ${envKey}`);
137
+ }
138
+ if (varConfig.type === "json") {
139
+ try {
140
+ JSON.parse(rawValue);
141
+ } catch {
142
+ errors.push(`Invalid JSON: ${envKey}`);
143
+ }
144
+ }
145
+ }
146
+ return {
147
+ valid: errors.length === 0,
148
+ errors,
149
+ warnings
150
+ };
151
+ }
152
+ function createServiceInfo(info) {
153
+ return {
154
+ ...info,
155
+ capabilities: info.capabilities || [],
156
+ endpoints: info.endpoints || {}
157
+ };
158
+ }
159
+ function parsePrice(envVar, fallback) {
160
+ if (!envVar) return fallback;
161
+ const parsed = parseFloat(envVar);
162
+ return isNaN(parsed) ? fallback : parsed;
163
+ }
164
+ function createPriceConfig(priceMap) {
165
+ const result = {};
166
+ for (const [key, config] of Object.entries(priceMap)) {
167
+ result[key] = parsePrice(process.env[config.envKey], config.default);
168
+ }
169
+ return result;
170
+ }
171
+ function createRouteMapping(routes, prices) {
172
+ const result = {};
173
+ for (const route of routes) {
174
+ result[route.path] = {
175
+ path: route.path,
176
+ price: prices[route.priceKey] || 0,
177
+ description: route.description
178
+ };
179
+ }
180
+ return result;
181
+ }
182
+ function getRoutePrice(routes, path) {
183
+ const route = routes[path];
184
+ return route?.price;
185
+ }
186
+ function createFeatureFlags(flags) {
187
+ const result = {};
188
+ for (const [key, config] of Object.entries(flags)) {
189
+ const value = process.env[config.envKey];
190
+ result[key] = value !== void 0 ? value.toLowerCase() === "true" || value === "1" : config.default;
191
+ }
192
+ return result;
193
+ }
194
+ function isFeatureEnabled(flags, feature) {
195
+ return flags[feature] ?? false;
196
+ }
197
+ // Annotate the CommonJS export names for ESM import in node:
198
+ 0 && (module.exports = {
199
+ createConfig,
200
+ createFeatureFlags,
201
+ createPriceConfig,
202
+ createRouteMapping,
203
+ createServiceInfo,
204
+ getEnv,
205
+ getEnvBoolean,
206
+ getEnvJson,
207
+ getEnvNumber,
208
+ getRequiredEnv,
209
+ getRoutePrice,
210
+ isFeatureEnabled,
211
+ parsePrice,
212
+ validateConfig
213
+ });
214
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @perkos/config\n * Configuration management utilities for PerkOS services\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EnvConfig<T extends Record<string, EnvVarConfig>> {\n vars: T;\n prefix?: string;\n strict?: boolean;\n}\n\nexport interface EnvVarConfig {\n required?: boolean;\n default?: string | number | boolean;\n type?: \"string\" | \"number\" | \"boolean\" | \"json\";\n validator?: (value: string) => boolean;\n transform?: (value: string) => unknown;\n}\n\nexport type ParsedConfig<T extends Record<string, EnvVarConfig>> = {\n [K in keyof T]: T[K][\"type\"] extends \"number\"\n ? number\n : T[K][\"type\"] extends \"boolean\"\n ? boolean\n : T[K][\"type\"] extends \"json\"\n ? unknown\n : string;\n};\n\nexport interface ConfigValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n// ============================================================================\n// Environment Variable Utilities\n// ============================================================================\n\n/**\n * Get environment variable with optional default\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n return process.env[key] ?? defaultValue;\n}\n\n/**\n * Get required environment variable (throws if missing)\n */\nexport function getRequiredEnv(key: string): string {\n const value = process.env[key];\n if (value === undefined || value === \"\") {\n throw new Error(`Required environment variable ${key} is not set`);\n }\n return value;\n}\n\n/**\n * Get environment variable as number\n */\nexport function getEnvNumber(key: string, defaultValue?: number): number | undefined {\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Get environment variable as boolean\n */\nexport function getEnvBoolean(key: string, defaultValue?: boolean): boolean | undefined {\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n return value.toLowerCase() === \"true\" || value === \"1\";\n}\n\n/**\n * Get environment variable as JSON\n */\nexport function getEnvJson<T>(key: string, defaultValue?: T): T | undefined {\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n}\n\n// ============================================================================\n// Configuration Builder\n// ============================================================================\n\n/**\n * Create a typed configuration from environment variables\n */\nexport function createConfig<T extends Record<string, EnvVarConfig>>(\n config: EnvConfig<T>\n): ParsedConfig<T> {\n const result: Record<string, unknown> = {};\n const prefix = config.prefix ? `${config.prefix}_` : \"\";\n\n for (const [key, varConfig] of Object.entries(config.vars)) {\n const envKey = `${prefix}${key}`;\n const rawValue = process.env[envKey];\n\n // Handle missing values\n if (rawValue === undefined || rawValue === \"\") {\n if (varConfig.required && config.strict !== false) {\n throw new Error(`Required environment variable ${envKey} is not set`);\n }\n if (varConfig.default !== undefined) {\n result[key] = varConfig.default;\n continue;\n }\n result[key] = undefined;\n continue;\n }\n\n // Validate\n if (varConfig.validator && !varConfig.validator(rawValue)) {\n throw new Error(`Invalid value for environment variable ${envKey}`);\n }\n\n // Transform or parse by type\n if (varConfig.transform) {\n result[key] = varConfig.transform(rawValue);\n continue;\n }\n\n switch (varConfig.type) {\n case \"number\":\n const num = parseFloat(rawValue);\n if (isNaN(num)) {\n throw new Error(`Invalid number for environment variable ${envKey}`);\n }\n result[key] = num;\n break;\n case \"boolean\":\n result[key] = rawValue.toLowerCase() === \"true\" || rawValue === \"1\";\n break;\n case \"json\":\n try {\n result[key] = JSON.parse(rawValue);\n } catch {\n throw new Error(`Invalid JSON for environment variable ${envKey}`);\n }\n break;\n default:\n result[key] = rawValue;\n }\n }\n\n return result as ParsedConfig<T>;\n}\n\n/**\n * Validate configuration without creating it\n */\nexport function validateConfig<T extends Record<string, EnvVarConfig>>(\n config: EnvConfig<T>\n): ConfigValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const prefix = config.prefix ? `${config.prefix}_` : \"\";\n\n for (const [key, varConfig] of Object.entries(config.vars)) {\n const envKey = `${prefix}${key}`;\n const rawValue = process.env[envKey];\n\n if (rawValue === undefined || rawValue === \"\") {\n if (varConfig.required) {\n errors.push(`Missing required: ${envKey}`);\n } else if (varConfig.default === undefined) {\n warnings.push(`Optional not set: ${envKey}`);\n }\n continue;\n }\n\n if (varConfig.validator && !varConfig.validator(rawValue)) {\n errors.push(`Invalid value: ${envKey}`);\n }\n\n if (varConfig.type === \"number\" && isNaN(parseFloat(rawValue))) {\n errors.push(`Invalid number: ${envKey}`);\n }\n\n if (varConfig.type === \"json\") {\n try {\n JSON.parse(rawValue);\n } catch {\n errors.push(`Invalid JSON: ${envKey}`);\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n// ============================================================================\n// Service Discovery Configuration\n// ============================================================================\n\nexport interface ServiceInfo {\n name: string;\n version: string;\n description?: string;\n capabilities?: string[];\n endpoints?: Record<string, EndpointInfo>;\n}\n\nexport interface EndpointInfo {\n path: string;\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n description?: string;\n price?: number;\n rateLimit?: number;\n}\n\n/**\n * Create service discovery info\n */\nexport function createServiceInfo(info: ServiceInfo): ServiceInfo {\n return {\n ...info,\n capabilities: info.capabilities || [],\n endpoints: info.endpoints || {},\n };\n}\n\n// ============================================================================\n// Price Configuration\n// ============================================================================\n\nexport interface PriceConfig {\n [key: string]: number;\n}\n\n/**\n * Parse price from environment variable with fallback\n */\nexport function parsePrice(envVar: string | undefined, fallback: number): number {\n if (!envVar) return fallback;\n const parsed = parseFloat(envVar);\n return isNaN(parsed) ? fallback : parsed;\n}\n\n/**\n * Create price configuration from environment\n */\nexport function createPriceConfig(\n priceMap: Record<string, { envKey: string; default: number }>\n): PriceConfig {\n const result: PriceConfig = {};\n for (const [key, config] of Object.entries(priceMap)) {\n result[key] = parsePrice(process.env[config.envKey], config.default);\n }\n return result;\n}\n\n// ============================================================================\n// Route Configuration\n// ============================================================================\n\nexport interface RouteConfig {\n path: string;\n price: number;\n description?: string;\n rateLimit?: number;\n}\n\n/**\n * Create route price mapping from price config\n */\nexport function createRouteMapping(\n routes: Array<{ path: string; priceKey: string; description?: string }>,\n prices: PriceConfig\n): Record<string, RouteConfig> {\n const result: Record<string, RouteConfig> = {};\n for (const route of routes) {\n result[route.path] = {\n path: route.path,\n price: prices[route.priceKey] || 0,\n description: route.description,\n };\n }\n return result;\n}\n\n/**\n * Get price for a route path\n */\nexport function getRoutePrice(\n routes: Record<string, RouteConfig>,\n path: string\n): number | undefined {\n const route = routes[path];\n return route?.price;\n}\n\n// ============================================================================\n// Feature Flags\n// ============================================================================\n\nexport interface FeatureFlags {\n [key: string]: boolean;\n}\n\n/**\n * Create feature flags from environment\n */\nexport function createFeatureFlags(\n flags: Record<string, { envKey: string; default: boolean }>\n): FeatureFlags {\n const result: FeatureFlags = {};\n for (const [key, config] of Object.entries(flags)) {\n const value = process.env[config.envKey];\n result[key] =\n value !== undefined\n ? value.toLowerCase() === \"true\" || value === \"1\"\n : config.default;\n }\n return result;\n}\n\n/**\n * Check if a feature is enabled\n */\nexport function isFeatureEnabled(\n flags: FeatureFlags,\n feature: string\n): boolean {\n return flags[feature] ?? false;\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,176 @@
1
+ // src/index.ts
2
+ function getEnv(key, defaultValue) {
3
+ return process.env[key] ?? defaultValue;
4
+ }
5
+ function getRequiredEnv(key) {
6
+ const value = process.env[key];
7
+ if (value === void 0 || value === "") {
8
+ throw new Error(`Required environment variable ${key} is not set`);
9
+ }
10
+ return value;
11
+ }
12
+ function getEnvNumber(key, defaultValue) {
13
+ const value = process.env[key];
14
+ if (value === void 0) return defaultValue;
15
+ const parsed = parseFloat(value);
16
+ return isNaN(parsed) ? defaultValue : parsed;
17
+ }
18
+ function getEnvBoolean(key, defaultValue) {
19
+ const value = process.env[key];
20
+ if (value === void 0) return defaultValue;
21
+ return value.toLowerCase() === "true" || value === "1";
22
+ }
23
+ function getEnvJson(key, defaultValue) {
24
+ const value = process.env[key];
25
+ if (value === void 0) return defaultValue;
26
+ try {
27
+ return JSON.parse(value);
28
+ } catch {
29
+ return defaultValue;
30
+ }
31
+ }
32
+ function createConfig(config) {
33
+ const result = {};
34
+ const prefix = config.prefix ? `${config.prefix}_` : "";
35
+ for (const [key, varConfig] of Object.entries(config.vars)) {
36
+ const envKey = `${prefix}${key}`;
37
+ const rawValue = process.env[envKey];
38
+ if (rawValue === void 0 || rawValue === "") {
39
+ if (varConfig.required && config.strict !== false) {
40
+ throw new Error(`Required environment variable ${envKey} is not set`);
41
+ }
42
+ if (varConfig.default !== void 0) {
43
+ result[key] = varConfig.default;
44
+ continue;
45
+ }
46
+ result[key] = void 0;
47
+ continue;
48
+ }
49
+ if (varConfig.validator && !varConfig.validator(rawValue)) {
50
+ throw new Error(`Invalid value for environment variable ${envKey}`);
51
+ }
52
+ if (varConfig.transform) {
53
+ result[key] = varConfig.transform(rawValue);
54
+ continue;
55
+ }
56
+ switch (varConfig.type) {
57
+ case "number":
58
+ const num = parseFloat(rawValue);
59
+ if (isNaN(num)) {
60
+ throw new Error(`Invalid number for environment variable ${envKey}`);
61
+ }
62
+ result[key] = num;
63
+ break;
64
+ case "boolean":
65
+ result[key] = rawValue.toLowerCase() === "true" || rawValue === "1";
66
+ break;
67
+ case "json":
68
+ try {
69
+ result[key] = JSON.parse(rawValue);
70
+ } catch {
71
+ throw new Error(`Invalid JSON for environment variable ${envKey}`);
72
+ }
73
+ break;
74
+ default:
75
+ result[key] = rawValue;
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ function validateConfig(config) {
81
+ const errors = [];
82
+ const warnings = [];
83
+ const prefix = config.prefix ? `${config.prefix}_` : "";
84
+ for (const [key, varConfig] of Object.entries(config.vars)) {
85
+ const envKey = `${prefix}${key}`;
86
+ const rawValue = process.env[envKey];
87
+ if (rawValue === void 0 || rawValue === "") {
88
+ if (varConfig.required) {
89
+ errors.push(`Missing required: ${envKey}`);
90
+ } else if (varConfig.default === void 0) {
91
+ warnings.push(`Optional not set: ${envKey}`);
92
+ }
93
+ continue;
94
+ }
95
+ if (varConfig.validator && !varConfig.validator(rawValue)) {
96
+ errors.push(`Invalid value: ${envKey}`);
97
+ }
98
+ if (varConfig.type === "number" && isNaN(parseFloat(rawValue))) {
99
+ errors.push(`Invalid number: ${envKey}`);
100
+ }
101
+ if (varConfig.type === "json") {
102
+ try {
103
+ JSON.parse(rawValue);
104
+ } catch {
105
+ errors.push(`Invalid JSON: ${envKey}`);
106
+ }
107
+ }
108
+ }
109
+ return {
110
+ valid: errors.length === 0,
111
+ errors,
112
+ warnings
113
+ };
114
+ }
115
+ function createServiceInfo(info) {
116
+ return {
117
+ ...info,
118
+ capabilities: info.capabilities || [],
119
+ endpoints: info.endpoints || {}
120
+ };
121
+ }
122
+ function parsePrice(envVar, fallback) {
123
+ if (!envVar) return fallback;
124
+ const parsed = parseFloat(envVar);
125
+ return isNaN(parsed) ? fallback : parsed;
126
+ }
127
+ function createPriceConfig(priceMap) {
128
+ const result = {};
129
+ for (const [key, config] of Object.entries(priceMap)) {
130
+ result[key] = parsePrice(process.env[config.envKey], config.default);
131
+ }
132
+ return result;
133
+ }
134
+ function createRouteMapping(routes, prices) {
135
+ const result = {};
136
+ for (const route of routes) {
137
+ result[route.path] = {
138
+ path: route.path,
139
+ price: prices[route.priceKey] || 0,
140
+ description: route.description
141
+ };
142
+ }
143
+ return result;
144
+ }
145
+ function getRoutePrice(routes, path) {
146
+ const route = routes[path];
147
+ return route?.price;
148
+ }
149
+ function createFeatureFlags(flags) {
150
+ const result = {};
151
+ for (const [key, config] of Object.entries(flags)) {
152
+ const value = process.env[config.envKey];
153
+ result[key] = value !== void 0 ? value.toLowerCase() === "true" || value === "1" : config.default;
154
+ }
155
+ return result;
156
+ }
157
+ function isFeatureEnabled(flags, feature) {
158
+ return flags[feature] ?? false;
159
+ }
160
+ export {
161
+ createConfig,
162
+ createFeatureFlags,
163
+ createPriceConfig,
164
+ createRouteMapping,
165
+ createServiceInfo,
166
+ getEnv,
167
+ getEnvBoolean,
168
+ getEnvJson,
169
+ getEnvNumber,
170
+ getRequiredEnv,
171
+ getRoutePrice,
172
+ isFeatureEnabled,
173
+ parsePrice,
174
+ validateConfig
175
+ };
176
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @perkos/config\n * Configuration management utilities for PerkOS services\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EnvConfig<T extends Record<string, EnvVarConfig>> {\n vars: T;\n prefix?: string;\n strict?: boolean;\n}\n\nexport interface EnvVarConfig {\n required?: boolean;\n default?: string | number | boolean;\n type?: \"string\" | \"number\" | \"boolean\" | \"json\";\n validator?: (value: string) => boolean;\n transform?: (value: string) => unknown;\n}\n\nexport type ParsedConfig<T extends Record<string, EnvVarConfig>> = {\n [K in keyof T]: T[K][\"type\"] extends \"number\"\n ? number\n : T[K][\"type\"] extends \"boolean\"\n ? boolean\n : T[K][\"type\"] extends \"json\"\n ? unknown\n : string;\n};\n\nexport interface ConfigValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n// ============================================================================\n// Environment Variable Utilities\n// ============================================================================\n\n/**\n * Get environment variable with optional default\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n return process.env[key] ?? defaultValue;\n}\n\n/**\n * Get required environment variable (throws if missing)\n */\nexport function getRequiredEnv(key: string): string {\n const value = process.env[key];\n if (value === undefined || value === \"\") {\n throw new Error(`Required environment variable ${key} is not set`);\n }\n return value;\n}\n\n/**\n * Get environment variable as number\n */\nexport function getEnvNumber(key: string, defaultValue?: number): number | undefined {\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Get environment variable as boolean\n */\nexport function getEnvBoolean(key: string, defaultValue?: boolean): boolean | undefined {\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n return value.toLowerCase() === \"true\" || value === \"1\";\n}\n\n/**\n * Get environment variable as JSON\n */\nexport function getEnvJson<T>(key: string, defaultValue?: T): T | undefined {\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n}\n\n// ============================================================================\n// Configuration Builder\n// ============================================================================\n\n/**\n * Create a typed configuration from environment variables\n */\nexport function createConfig<T extends Record<string, EnvVarConfig>>(\n config: EnvConfig<T>\n): ParsedConfig<T> {\n const result: Record<string, unknown> = {};\n const prefix = config.prefix ? `${config.prefix}_` : \"\";\n\n for (const [key, varConfig] of Object.entries(config.vars)) {\n const envKey = `${prefix}${key}`;\n const rawValue = process.env[envKey];\n\n // Handle missing values\n if (rawValue === undefined || rawValue === \"\") {\n if (varConfig.required && config.strict !== false) {\n throw new Error(`Required environment variable ${envKey} is not set`);\n }\n if (varConfig.default !== undefined) {\n result[key] = varConfig.default;\n continue;\n }\n result[key] = undefined;\n continue;\n }\n\n // Validate\n if (varConfig.validator && !varConfig.validator(rawValue)) {\n throw new Error(`Invalid value for environment variable ${envKey}`);\n }\n\n // Transform or parse by type\n if (varConfig.transform) {\n result[key] = varConfig.transform(rawValue);\n continue;\n }\n\n switch (varConfig.type) {\n case \"number\":\n const num = parseFloat(rawValue);\n if (isNaN(num)) {\n throw new Error(`Invalid number for environment variable ${envKey}`);\n }\n result[key] = num;\n break;\n case \"boolean\":\n result[key] = rawValue.toLowerCase() === \"true\" || rawValue === \"1\";\n break;\n case \"json\":\n try {\n result[key] = JSON.parse(rawValue);\n } catch {\n throw new Error(`Invalid JSON for environment variable ${envKey}`);\n }\n break;\n default:\n result[key] = rawValue;\n }\n }\n\n return result as ParsedConfig<T>;\n}\n\n/**\n * Validate configuration without creating it\n */\nexport function validateConfig<T extends Record<string, EnvVarConfig>>(\n config: EnvConfig<T>\n): ConfigValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const prefix = config.prefix ? `${config.prefix}_` : \"\";\n\n for (const [key, varConfig] of Object.entries(config.vars)) {\n const envKey = `${prefix}${key}`;\n const rawValue = process.env[envKey];\n\n if (rawValue === undefined || rawValue === \"\") {\n if (varConfig.required) {\n errors.push(`Missing required: ${envKey}`);\n } else if (varConfig.default === undefined) {\n warnings.push(`Optional not set: ${envKey}`);\n }\n continue;\n }\n\n if (varConfig.validator && !varConfig.validator(rawValue)) {\n errors.push(`Invalid value: ${envKey}`);\n }\n\n if (varConfig.type === \"number\" && isNaN(parseFloat(rawValue))) {\n errors.push(`Invalid number: ${envKey}`);\n }\n\n if (varConfig.type === \"json\") {\n try {\n JSON.parse(rawValue);\n } catch {\n errors.push(`Invalid JSON: ${envKey}`);\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n// ============================================================================\n// Service Discovery Configuration\n// ============================================================================\n\nexport interface ServiceInfo {\n name: string;\n version: string;\n description?: string;\n capabilities?: string[];\n endpoints?: Record<string, EndpointInfo>;\n}\n\nexport interface EndpointInfo {\n path: string;\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n description?: string;\n price?: number;\n rateLimit?: number;\n}\n\n/**\n * Create service discovery info\n */\nexport function createServiceInfo(info: ServiceInfo): ServiceInfo {\n return {\n ...info,\n capabilities: info.capabilities || [],\n endpoints: info.endpoints || {},\n };\n}\n\n// ============================================================================\n// Price Configuration\n// ============================================================================\n\nexport interface PriceConfig {\n [key: string]: number;\n}\n\n/**\n * Parse price from environment variable with fallback\n */\nexport function parsePrice(envVar: string | undefined, fallback: number): number {\n if (!envVar) return fallback;\n const parsed = parseFloat(envVar);\n return isNaN(parsed) ? fallback : parsed;\n}\n\n/**\n * Create price configuration from environment\n */\nexport function createPriceConfig(\n priceMap: Record<string, { envKey: string; default: number }>\n): PriceConfig {\n const result: PriceConfig = {};\n for (const [key, config] of Object.entries(priceMap)) {\n result[key] = parsePrice(process.env[config.envKey], config.default);\n }\n return result;\n}\n\n// ============================================================================\n// Route Configuration\n// ============================================================================\n\nexport interface RouteConfig {\n path: string;\n price: number;\n description?: string;\n rateLimit?: number;\n}\n\n/**\n * Create route price mapping from price config\n */\nexport function createRouteMapping(\n routes: Array<{ path: string; priceKey: string; description?: string }>,\n prices: PriceConfig\n): Record<string, RouteConfig> {\n const result: Record<string, RouteConfig> = {};\n for (const route of routes) {\n result[route.path] = {\n path: route.path,\n price: prices[route.priceKey] || 0,\n description: route.description,\n };\n }\n return result;\n}\n\n/**\n * Get price for a route path\n */\nexport function getRoutePrice(\n routes: Record<string, RouteConfig>,\n path: string\n): number | undefined {\n const route = routes[path];\n return route?.price;\n}\n\n// ============================================================================\n// Feature Flags\n// ============================================================================\n\nexport interface FeatureFlags {\n [key: string]: boolean;\n}\n\n/**\n * Create feature flags from environment\n */\nexport function createFeatureFlags(\n flags: Record<string, { envKey: string; default: boolean }>\n): FeatureFlags {\n const result: FeatureFlags = {};\n for (const [key, config] of Object.entries(flags)) {\n const value = process.env[config.envKey];\n result[key] =\n value !== undefined\n ? value.toLowerCase() === \"true\" || value === \"1\"\n : config.default;\n }\n return result;\n}\n\n/**\n * Check if a feature is enabled\n */\nexport function isFeatureEnabled(\n flags: FeatureFlags,\n feature: string\n): boolean {\n return flags[feature] ?? false;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@perkos/util-config",
3
+ "version": "1.0.0",
4
+ "description": "Configuration management utilities for PerkOS services",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "lint": "tsc --noEmit",
22
+ "test": "vitest run",
23
+ "clean": "rm -rf dist",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "keywords": [
27
+ "config",
28
+ "configuration",
29
+ "environment",
30
+ "perkos",
31
+ "x402"
32
+ ],
33
+ "author": "PerkOS",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/PerkOS-xyz/pkg-util-config.git"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^20.10.0",
41
+ "tsup": "^8.0.1",
42
+ "typescript": "^5.3.3",
43
+ "vitest": "^1.2.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }