@vltpkg/config 0.0.0-19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (c) vlt technology, Inc.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
7
+ Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
8
+
9
+ (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or
10
+ (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
11
+ Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise.
12
+
13
+ DISCLAIMER
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @vltpkg/config
2
+
3
+ Config utilities for the vlt client.
4
+
5
+ **[Usage](#usage)** · **[API](#api)**
6
+
7
+ ## Overview
8
+
9
+ These are config utilities used by the vlt cli.
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import { get, set, edit, list, del } from '@vltpkg/config'
15
+
16
+ // Assuming you have a LoadedConfig instance from @vltpkg/cli-sdk
17
+ // const conf = await loadConfig()
18
+
19
+ // Get a config value by key
20
+ const colorValue = await get(conf) // requires conf.positionals[1] to be the key name
21
+ // Supports dot notation for nested properties in record fields
22
+ // e.g., conf.positionals = ['get', 'registry.npmjs.org']
23
+
24
+ // Set config values using key=value pairs
25
+ // conf.positionals should contain pairs like ['set', 'color=auto', 'timeout=5000']
26
+ await set(conf)
27
+ // Creates config file if it doesn't exist when called with no pairs
28
+ // Supports dot notation for record fields: 'registry.npmjs.org=https://registry.npmjs.org/'
29
+
30
+ // Edit the config file with the configured editor
31
+ await edit(conf) // opens conf.get('config') file in conf.get('editor')
32
+
33
+ // List all current config values as key=value pairs
34
+ const allConfig = list(conf) // returns array of 'key=value' strings
35
+
36
+ // Delete config keys
37
+ // conf.positionals should contain keys to delete: ['del', 'color', 'timeout']
38
+ await del(conf) // removes specified keys from conf.get('config') file
39
+ ```
40
+
41
+ ### API
42
+
43
+ #### `get(conf: LoadedConfig)`
44
+
45
+ - Retrieves a single config value by key
46
+ - Supports dot notation for accessing nested properties in record
47
+ fields
48
+ - Returns the value as
49
+ `string | number | boolean | string[] | Record<string, string> | undefined`
50
+ - Requires exactly one key in `conf.positionals[1]`
51
+
52
+ #### `set(conf: LoadedConfig)`
53
+
54
+ - Sets config values from key=value pairs in `conf.positionals`
55
+ - Creates an empty config file if no pairs are provided
56
+ - Supports dot notation for record fields (e.g.,
57
+ `registry.npmjs.org=value`)
58
+ - Writes to the config file specified by `conf.get('config')`
59
+
60
+ #### `edit(conf: LoadedConfig)`
61
+
62
+ - Opens the config file in the editor specified by
63
+ `conf.get('editor')`
64
+ - Uses `spawnSync` to launch the editor with the config file path
65
+ - Throws an error if the editor command fails
66
+
67
+ #### `list(conf: LoadedConfig)`
68
+
69
+ - Returns all current config options as an array of 'key=value'
70
+ strings
71
+ - Converts the internal config records to a flat list format
72
+
73
+ #### `del(conf: LoadedConfig)`
74
+
75
+ - Deletes specified config keys from the config file
76
+ - Requires at least one key in `conf.positionals` (after the command
77
+ name)
78
+ - Removes keys from the file specified by `conf.get('config')`
@@ -0,0 +1,7 @@
1
+ import type { LoadedConfig } from '@vltpkg/cli-sdk/config';
2
+ export declare const list: (conf: LoadedConfig) => import("@vltpkg/cli-sdk/config").RecordPairs;
3
+ export declare const del: (conf: LoadedConfig) => Promise<void>;
4
+ export declare const get: (conf: LoadedConfig) => Promise<string | number | boolean | string[] | import("@vltpkg/cli-sdk/config").RecordString | undefined>;
5
+ export declare const edit: (conf: LoadedConfig) => Promise<void>;
6
+ export declare const set: (conf: LoadedConfig) => Promise<void>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAEV,YAAY,EACb,MAAM,wBAAwB,CAAA;AAE/B,eAAO,MAAM,IAAI,SAAU,YAAY,iDAEtC,CAAA;AAED,eAAO,MAAM,GAAG,SAAgB,YAAY,kBAS3C,CAAA;AAED,eAAO,MAAM,GAAG,SAAgB,YAAY,8GAqC3C,CAAA;AAED,eAAO,MAAM,IAAI,SAAgB,YAAY,kBAkB5C,CAAA;AAED,eAAO,MAAM,GAAG,SAAgB,YAAY,kBA8E3C,CAAA"}
@@ -0,0 +1,165 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import * as dotProp from '@vltpkg/dot-prop';
3
+ import { error } from '@vltpkg/error-cause';
4
+ import { asRootError } from '@vltpkg/output/error';
5
+ import { isObject } from '@vltpkg/types';
6
+ import { getSortedKeys } from '@vltpkg/cli-sdk/definition';
7
+ import { isRecordField, pairsToRecords, recordsToPairs, } from '@vltpkg/cli-sdk/config';
8
+ export const list = (conf) => {
9
+ return recordsToPairs(conf.options);
10
+ };
11
+ export const del = async (conf) => {
12
+ const fields = conf.positionals.slice(1);
13
+ if (!fields.length) {
14
+ throw error('At least one key is required', {
15
+ code: 'EUSAGE',
16
+ });
17
+ }
18
+ await conf.deleteConfigKeys(conf.get('config'), fields);
19
+ };
20
+ export const get = async (conf) => {
21
+ const keys = conf.positionals.slice(1);
22
+ const k = keys[0];
23
+ if (!k || keys.length > 1) {
24
+ throw error('Exactly one key is required', {
25
+ code: 'EUSAGE',
26
+ });
27
+ }
28
+ // check if this is a dot-prop path into a record field, in which case
29
+ // we need to get the record first and then use dot-prop to get the value
30
+ if (k.includes('.')) {
31
+ const [field, ...rest] = k.split('.');
32
+ const subKey = rest.join('.');
33
+ if (!field || !subKey) {
34
+ throw error('Could not read property', {
35
+ found: k,
36
+ });
37
+ }
38
+ // we'd need a type assertion helper from jackspeak definition
39
+ // options in order to cast the field to a known name type
40
+ // @ts-expect-error @typescript-eslint/no-unsafe-argument
41
+ const record = conf.getRecord(field);
42
+ return dotProp.get(record, subKey);
43
+ }
44
+ // otherwise just get the value directly from the config getter
45
+ return isRecordField(k) ?
46
+ conf.getRecord(k)
47
+ : conf.get(k);
48
+ };
49
+ export const edit = async (conf) => {
50
+ const [command, ...args] = conf.get('editor').split(' ');
51
+ if (!command) {
52
+ throw error(`editor is empty`);
53
+ }
54
+ await conf.editConfigFile(conf.get('config'), file => {
55
+ args.push(file);
56
+ const res = spawnSync(command, args, {
57
+ stdio: 'inherit',
58
+ });
59
+ if (res.status !== 0) {
60
+ throw error(`${command} command failed`, {
61
+ ...res,
62
+ command,
63
+ args,
64
+ });
65
+ }
66
+ });
67
+ };
68
+ export const set = async (conf) => {
69
+ const pairs = conf.positionals.slice(1);
70
+ if (!pairs.length) {
71
+ // Create an empty config file
72
+ await conf.addConfigToFile(conf.get('config'), {});
73
+ return;
74
+ }
75
+ const which = conf.get('config');
76
+ // separate dot-prop paths from simple keys for different handling
77
+ // any keys that include a dot (.) will be treated as dotPropPairs
78
+ // other keys/value pairs are handled as simplePairs
79
+ const dotPropPairs = [];
80
+ const simplePairs = [];
81
+ for (const pair of pairs) {
82
+ const eq = pair.indexOf('=');
83
+ if (eq === -1) {
84
+ throw error('Set arguments must contain `=`', {
85
+ code: 'EUSAGE',
86
+ });
87
+ }
88
+ const key = pair.substring(0, eq);
89
+ const value = pair.substring(eq + 1);
90
+ if (key.includes('.')) {
91
+ const [field, ...rest] = key.split('.');
92
+ const subKey = rest.join('.');
93
+ if (field && subKey) {
94
+ dotPropPairs.push({ key, field, subKey, value });
95
+ }
96
+ else {
97
+ throw error('Could not read property', {
98
+ found: pair,
99
+ });
100
+ }
101
+ }
102
+ else {
103
+ simplePairs.push(pair);
104
+ }
105
+ }
106
+ // Handle keys that consists of a single name (e.g., `--foo`)
107
+ // so that it doesn't need the dot-prop logic to handle values
108
+ if (simplePairs.length > 0) {
109
+ try {
110
+ const parsed = conf.jack.parseRaw(simplePairs.map(kv => `--${kv}`)).values;
111
+ await conf.addConfigToFile(which, pairsToRecords(parsed));
112
+ }
113
+ catch (err) {
114
+ handleSetError(simplePairs, err);
115
+ }
116
+ }
117
+ // Handle dot-prop paths for record fields
118
+ if (dotPropPairs.length > 0) {
119
+ for (const { field, subKey, value } of dotPropPairs) {
120
+ if (isRecordField(field)) {
121
+ // For record fields, we add entries in the format field=key=value
122
+ const recordPair = `${field}=${subKey}=${value}`;
123
+ try {
124
+ const parsed = conf.jack.parseRaw([
125
+ `--${recordPair}`,
126
+ ]).values;
127
+ await conf.addConfigToFile(which, pairsToRecords(parsed));
128
+ /* c8 ignore start */
129
+ }
130
+ catch (err) {
131
+ handleSetError([recordPair], err);
132
+ }
133
+ /* c8 ignore end */
134
+ }
135
+ }
136
+ }
137
+ };
138
+ const handleSetError = (simplePairs, err) => {
139
+ const { name, found, validOptions } = asRootError(err).cause;
140
+ // when a boolean gets a value, it throw a parse error
141
+ if (isObject(found) &&
142
+ typeof found.name === 'string' &&
143
+ typeof found.value === 'string') {
144
+ const { name, value } = found;
145
+ throw error(`Boolean flag must be "${name}" or "no-${name}", not a value`, {
146
+ code: 'ECONFIG',
147
+ name,
148
+ found: `${name}=${value}`,
149
+ });
150
+ }
151
+ if (Array.isArray(validOptions)) {
152
+ throw error(`Invalid value provided for ${name}`, {
153
+ code: 'ECONFIG',
154
+ found,
155
+ validOptions,
156
+ });
157
+ }
158
+ // an unknown property
159
+ throw error('Invalid config keys', {
160
+ code: 'ECONFIG',
161
+ found: simplePairs.map(kv => kv.split('=')[0]),
162
+ validOptions: getSortedKeys(),
163
+ });
164
+ };
165
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from 'node:child_process'\nimport * as dotProp from '@vltpkg/dot-prop'\nimport { error } from '@vltpkg/error-cause'\nimport { asRootError } from '@vltpkg/output/error'\nimport { isObject } from '@vltpkg/types'\nimport { getSortedKeys } from '@vltpkg/cli-sdk/definition'\nimport {\n isRecordField,\n pairsToRecords,\n recordsToPairs,\n} from '@vltpkg/cli-sdk/config'\n\nimport type {\n ConfigDefinitions,\n LoadedConfig,\n} from '@vltpkg/cli-sdk/config'\n\nexport const list = (conf: LoadedConfig) => {\n return recordsToPairs(conf.options)\n}\n\nexport const del = async (conf: LoadedConfig) => {\n const fields = conf.positionals.slice(1)\n if (!fields.length) {\n throw error('At least one key is required', {\n code: 'EUSAGE',\n })\n }\n\n await conf.deleteConfigKeys(conf.get('config'), fields)\n}\n\nexport const get = async (conf: LoadedConfig) => {\n const keys = conf.positionals.slice(1)\n const k = keys[0]\n if (!k || keys.length > 1) {\n throw error('Exactly one key is required', {\n code: 'EUSAGE',\n })\n }\n // check if this is a dot-prop path into a record field, in which case\n // we need to get the record first and then use dot-prop to get the value\n if (k.includes('.')) {\n const [field, ...rest] = k.split('.')\n const subKey = rest.join('.')\n\n if (!field || !subKey) {\n throw error('Could not read property', {\n found: k,\n })\n }\n\n // we'd need a type assertion helper from jackspeak definition\n // options in order to cast the field to a known name type\n // @ts-expect-error @typescript-eslint/no-unsafe-argument\n const record = conf.getRecord(field)\n\n return dotProp.get(record, subKey) as\n | string\n | number\n | boolean\n | string[]\n | undefined\n }\n\n // otherwise just get the value directly from the config getter\n return isRecordField(k) ?\n conf.getRecord(k)\n : conf.get(k as keyof ConfigDefinitions)\n}\n\nexport const edit = async (conf: LoadedConfig) => {\n const [command, ...args] = conf.get('editor').split(' ')\n if (!command) {\n throw error(`editor is empty`)\n }\n await conf.editConfigFile(conf.get('config'), file => {\n args.push(file)\n const res = spawnSync(command, args, {\n stdio: 'inherit',\n })\n if (res.status !== 0) {\n throw error(`${command} command failed`, {\n ...res,\n command,\n args,\n })\n }\n })\n}\n\nexport const set = async (conf: LoadedConfig) => {\n const pairs = conf.positionals.slice(1)\n if (!pairs.length) {\n // Create an empty config file\n await conf.addConfigToFile(conf.get('config'), {})\n return\n }\n\n const which = conf.get('config')\n\n // separate dot-prop paths from simple keys for different handling\n // any keys that include a dot (.) will be treated as dotPropPairs\n // other keys/value pairs are handled as simplePairs\n const dotPropPairs: {\n key: string\n field: string\n subKey: string\n value: string\n }[] = []\n const simplePairs: string[] = []\n\n for (const pair of pairs) {\n const eq = pair.indexOf('=')\n if (eq === -1) {\n throw error('Set arguments must contain `=`', {\n code: 'EUSAGE',\n })\n }\n\n const key = pair.substring(0, eq)\n const value = pair.substring(eq + 1)\n if (key.includes('.')) {\n const [field, ...rest] = key.split('.')\n const subKey = rest.join('.')\n if (field && subKey) {\n dotPropPairs.push({ key, field, subKey, value })\n } else {\n throw error('Could not read property', {\n found: pair,\n })\n }\n } else {\n simplePairs.push(pair)\n }\n }\n\n // Handle keys that consists of a single name (e.g., `--foo`)\n // so that it doesn't need the dot-prop logic to handle values\n if (simplePairs.length > 0) {\n try {\n const parsed = conf.jack.parseRaw(\n simplePairs.map(kv => `--${kv}`),\n ).values\n await conf.addConfigToFile(which, pairsToRecords(parsed))\n } catch (err) {\n handleSetError(simplePairs, err)\n }\n }\n\n // Handle dot-prop paths for record fields\n if (dotPropPairs.length > 0) {\n for (const { field, subKey, value } of dotPropPairs) {\n if (isRecordField(field)) {\n // For record fields, we add entries in the format field=key=value\n const recordPair = `${field}=${subKey}=${value}`\n try {\n const parsed = conf.jack.parseRaw([\n `--${recordPair}`,\n ]).values\n await conf.addConfigToFile(which, pairsToRecords(parsed))\n /* c8 ignore start */\n } catch (err) {\n handleSetError([recordPair], err)\n }\n /* c8 ignore end */\n }\n }\n }\n}\n\nconst handleSetError = (simplePairs: string[], err: unknown) => {\n const { name, found, validOptions } = asRootError(err).cause\n // when a boolean gets a value, it throw a parse error\n if (\n isObject(found) &&\n typeof found.name === 'string' &&\n typeof found.value === 'string'\n ) {\n const { name, value } = found\n throw error(\n `Boolean flag must be \"${name}\" or \"no-${name}\", not a value`,\n {\n code: 'ECONFIG',\n name,\n found: `${name}=${value}`,\n },\n )\n }\n if (Array.isArray(validOptions)) {\n throw error(`Invalid value provided for ${name}`, {\n code: 'ECONFIG',\n found,\n validOptions,\n })\n }\n // an unknown property\n throw error('Invalid config keys', {\n code: 'ECONFIG',\n found: simplePairs.map(kv => kv.split('=')[0]),\n validOptions: getSortedKeys(),\n })\n}\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@vltpkg/config",
3
+ "description": "Project config logic for vltpkg",
4
+ "version": "0.0.0-19",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/vltpkg/vltpkg.git",
8
+ "directory": "src/config"
9
+ },
10
+ "tshy": {
11
+ "selfLink": false,
12
+ "liveDev": true,
13
+ "dialects": [
14
+ "esm"
15
+ ],
16
+ "exports": {
17
+ "./package.json": "./package.json",
18
+ ".": "./src/index.ts"
19
+ }
20
+ },
21
+ "dependencies": {
22
+ "path-scurry": "^2.0.0",
23
+ "@vltpkg/cli-sdk": "0.0.0-19",
24
+ "@vltpkg/dot-prop": "0.0.0-19",
25
+ "@vltpkg/output": "0.0.0-19",
26
+ "@vltpkg/package-json": "0.0.0-19",
27
+ "@vltpkg/error-cause": "0.0.0-19",
28
+ "@vltpkg/types": "0.0.0-19"
29
+ },
30
+ "devDependencies": {
31
+ "@eslint/js": "^9.28.0",
32
+ "@types/node": "^22.15.29",
33
+ "eslint": "^9.28.0",
34
+ "prettier": "^3.6.0",
35
+ "tap": "^21.1.0",
36
+ "tshy": "^3.0.2",
37
+ "typedoc": "~0.27.9",
38
+ "typescript": "5.7.3",
39
+ "typescript-eslint": "^8.33.1"
40
+ },
41
+ "license": "BSD-2-Clause-Patent",
42
+ "engines": {
43
+ "node": ">=22"
44
+ },
45
+ "tap": {
46
+ "extends": "../../tap-config.yaml"
47
+ },
48
+ "prettier": "../../.prettierrc.js",
49
+ "module": "./dist/esm/index.js",
50
+ "type": "module",
51
+ "exports": {
52
+ "./package.json": "./package.json",
53
+ ".": {
54
+ "import": {
55
+ "types": "./dist/esm/index.d.ts",
56
+ "default": "./dist/esm/index.js"
57
+ }
58
+ }
59
+ },
60
+ "files": [
61
+ "dist"
62
+ ],
63
+ "keywords": [
64
+ "vltpkg",
65
+ "config"
66
+ ],
67
+ "scripts": {
68
+ "format": "prettier --write . --log-level warn --ignore-path ../../.prettierignore --cache",
69
+ "format:check": "prettier --check . --ignore-path ../../.prettierignore --cache",
70
+ "lint": "eslint . --fix",
71
+ "lint:check": "eslint .",
72
+ "snap": "tap",
73
+ "test": "tap",
74
+ "posttest": "tsc --noEmit",
75
+ "tshy": "tshy",
76
+ "typecheck": "tsc --noEmit"
77
+ }
78
+ }