@platformatic/foundation 3.15.0 → 3.17.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/index.d.ts +7 -0
- package/index.js +1 -0
- package/lib/cli.js +1 -0
- package/lib/configuration.js +29 -25
- package/lib/env-file-tool.js +107 -0
- package/package.json +1 -2
package/index.d.ts
CHANGED
|
@@ -101,6 +101,13 @@ export declare function replaceEnv (
|
|
|
101
101
|
onMissingEnv?: (key: string) => string | undefined,
|
|
102
102
|
ignore?: string[]
|
|
103
103
|
): RawConfiguration
|
|
104
|
+
export declare function validate (
|
|
105
|
+
schema: JSONSchemaType<any>,
|
|
106
|
+
config: RawConfiguration,
|
|
107
|
+
validationOptions?: object,
|
|
108
|
+
fixPaths?: boolean,
|
|
109
|
+
root?: string
|
|
110
|
+
): void
|
|
104
111
|
export declare function loadConfiguration (
|
|
105
112
|
source: string | RawConfiguration,
|
|
106
113
|
schema?: any,
|
package/index.js
CHANGED
package/lib/cli.js
CHANGED
package/lib/configuration.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import toml from '@iarna/toml'
|
|
2
2
|
import Ajv from 'ajv'
|
|
3
|
-
import { parse as parseEnvFile } from 'dotenv'
|
|
4
3
|
import jsonPatch from 'fast-json-patch'
|
|
5
4
|
import JSON5 from 'json5'
|
|
6
5
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
7
6
|
import { createRequire } from 'node:module'
|
|
8
7
|
import { dirname, extname, isAbsolute, parse, resolve } from 'node:path'
|
|
8
|
+
import { parseEnv } from 'node:util'
|
|
9
9
|
import { parse as rawParseYAML, stringify as stringifyYAML } from 'yaml'
|
|
10
10
|
import {
|
|
11
11
|
AddAModulePropertyToTheConfigOrAddAKnownSchemaError,
|
|
@@ -316,6 +316,30 @@ export function createValidator (schema, validationOptions, context = {}) {
|
|
|
316
316
|
return ajv.compile(schema)
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
export function validate (schema, config, validationOptions = {}, fixPaths = true, root = '') {
|
|
320
|
+
const validator = createValidator(schema, validationOptions, { root, fixPaths })
|
|
321
|
+
const valid = validator(config)
|
|
322
|
+
|
|
323
|
+
if (!valid) {
|
|
324
|
+
const validationErrors = []
|
|
325
|
+
let errors = ':'
|
|
326
|
+
|
|
327
|
+
for (const validationError of validator.errors) {
|
|
328
|
+
/* c8 ignore next - else */
|
|
329
|
+
const path = validationError.instancePath === '' ? '/' : validationError.instancePath
|
|
330
|
+
|
|
331
|
+
validationErrors.push({ path, message: validationError.message, params: validationError.params })
|
|
332
|
+
errors += `\n - ${path}: ${validationError.message}`
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const error = new ConfigurationDoesNotValidateAgainstSchemaError()
|
|
336
|
+
error.message += errors + '\n'
|
|
337
|
+
Object.defineProperty(error, 'validationErrors', { value: validationErrors })
|
|
338
|
+
|
|
339
|
+
throw error
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
319
343
|
export async function loadEnv (root, ignoreProcessEnv = false, additionalEnv = {}) {
|
|
320
344
|
if (!isAbsolute(root)) {
|
|
321
345
|
root = resolve(process.cwd(), root)
|
|
@@ -347,7 +371,7 @@ export async function loadEnv (root, ignoreProcessEnv = false, additionalEnv = {
|
|
|
347
371
|
}
|
|
348
372
|
|
|
349
373
|
const baseEnv = ignoreProcessEnv ? {} : process.env
|
|
350
|
-
const envFromFile = envFile ?
|
|
374
|
+
const envFromFile = envFile ? parseEnv(await readFile(envFile, 'utf-8')) : {}
|
|
351
375
|
|
|
352
376
|
return {
|
|
353
377
|
...baseEnv,
|
|
@@ -411,7 +435,7 @@ export function replaceEnv (config, env, onMissingEnv, ignore) {
|
|
|
411
435
|
|
|
412
436
|
export async function loadConfiguration (source, schema, options = {}) {
|
|
413
437
|
const {
|
|
414
|
-
validate,
|
|
438
|
+
validate: shouldValidate,
|
|
415
439
|
validationOptions,
|
|
416
440
|
transform,
|
|
417
441
|
upgrade,
|
|
@@ -470,32 +494,12 @@ export async function loadConfiguration (source, schema, options = {}) {
|
|
|
470
494
|
}
|
|
471
495
|
}
|
|
472
496
|
|
|
473
|
-
if (
|
|
497
|
+
if (shouldValidate) {
|
|
474
498
|
if (typeof schema === 'undefined') {
|
|
475
499
|
throw new SourceMissingError()
|
|
476
500
|
}
|
|
477
501
|
|
|
478
|
-
|
|
479
|
-
const valid = validator(config)
|
|
480
|
-
|
|
481
|
-
if (!valid) {
|
|
482
|
-
const validationErrors = []
|
|
483
|
-
let errors = ':'
|
|
484
|
-
|
|
485
|
-
for (const validationError of validator.errors) {
|
|
486
|
-
/* c8 ignore next - else */
|
|
487
|
-
const path = validationError.instancePath === '' ? '/' : validationError.instancePath
|
|
488
|
-
|
|
489
|
-
validationErrors.push({ path, message: validationError.message, params: validationError.params })
|
|
490
|
-
errors += `\n - ${path}: ${validationError.message}`
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
const error = new ConfigurationDoesNotValidateAgainstSchemaError()
|
|
494
|
-
error.message += errors + '\n'
|
|
495
|
-
Object.defineProperty(error, 'validationErrors', { value: validationErrors })
|
|
496
|
-
|
|
497
|
-
throw error
|
|
498
|
-
}
|
|
502
|
+
validate(schema, config, validationOptions, fixPaths, root)
|
|
499
503
|
}
|
|
500
504
|
|
|
501
505
|
if (!skipMetadata) {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { parseEnv } from 'node:util'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A utility class for programmatically manipulating .env files.
|
|
7
|
+
* This is a replacement for the dotenv-tool package using native Node.js APIs.
|
|
8
|
+
*/
|
|
9
|
+
export class EnvFileTool {
|
|
10
|
+
constructor (options = {}) {
|
|
11
|
+
this.path = options.path
|
|
12
|
+
this.env = {}
|
|
13
|
+
this.loaded = false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Load the .env file and parse its contents
|
|
18
|
+
*/
|
|
19
|
+
async load () {
|
|
20
|
+
if (!existsSync(this.path)) {
|
|
21
|
+
this.env = {}
|
|
22
|
+
this.loaded = true
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const contents = await readFile(this.path, 'utf-8')
|
|
27
|
+
this.env = parseEnv(contents)
|
|
28
|
+
this.loaded = true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Save the current environment variables to the .env file
|
|
33
|
+
*/
|
|
34
|
+
async save () {
|
|
35
|
+
const lines = []
|
|
36
|
+
for (const [key, value] of Object.entries(this.env)) {
|
|
37
|
+
// Escape values that contain special characters
|
|
38
|
+
const escapedValue = this._escapeValue(value)
|
|
39
|
+
lines.push(`${key}=${escapedValue}`)
|
|
40
|
+
}
|
|
41
|
+
await writeFile(this.path, lines.join('\n') + '\n', { encoding: 'utf-8', flush: true })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get all environment variable keys
|
|
46
|
+
*/
|
|
47
|
+
getKeys () {
|
|
48
|
+
return Object.keys(this.env)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a key exists
|
|
53
|
+
*/
|
|
54
|
+
hasKey (key) {
|
|
55
|
+
return key in this.env
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Add a new key-value pair
|
|
60
|
+
*/
|
|
61
|
+
addKey (key, value) {
|
|
62
|
+
this.env[key] = value
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Update an existing key's value
|
|
67
|
+
*/
|
|
68
|
+
updateKey (key, value) {
|
|
69
|
+
this.env[key] = value
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete a key
|
|
74
|
+
*/
|
|
75
|
+
deleteKey (key) {
|
|
76
|
+
delete this.env[key]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get a value by key
|
|
81
|
+
*/
|
|
82
|
+
getValue (key) {
|
|
83
|
+
return this.env[key]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Escape special characters in environment variable values
|
|
88
|
+
*/
|
|
89
|
+
_escapeValue (value) {
|
|
90
|
+
if (typeof value !== 'string') {
|
|
91
|
+
return value
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// If the value contains spaces, newlines, or special characters, wrap it in quotes
|
|
95
|
+
if (/[\s\n\r"'#\\]/.test(value)) {
|
|
96
|
+
// Escape existing quotes and backslashes
|
|
97
|
+
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
98
|
+
return `"${escaped}"`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return value
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function createEnvFileTool (options) {
|
|
106
|
+
return new EnvFileTool(options)
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/foundation",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"description": "Platformatic Foundation",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"ajv": "^8.12.0",
|
|
24
24
|
"boring-name-generator": "^1.0.3",
|
|
25
25
|
"colorette": "^2.0.19",
|
|
26
|
-
"dotenv": "^16.4.5",
|
|
27
26
|
"fast-json-patch": "^3.1.1",
|
|
28
27
|
"json5": "^2.2.3",
|
|
29
28
|
"leven": "~3.1.0",
|