@james-pre/config 0.0.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/manager.js CHANGED
@@ -228,8 +228,8 @@ let Manager = (() => {
228
228
  data: file,
229
229
  path,
230
230
  });
231
- this.merge(file);
232
231
  this.emit('load', path, file);
232
+ this.merge(file);
233
233
  if (canInclude(this.options, file))
234
234
  for (const include of file.include ?? []) {
235
235
  this.loadFile(resolve(dirname(path), include), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@james-pre/config",
3
- "version": "0.0.1",
3
+ "version": "1.0.1",
4
4
  "description": "Manage configuration files",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -14,7 +14,8 @@
14
14
  "./*": "./dist/*.js"
15
15
  },
16
16
  "files": [
17
- "dist"
17
+ "dist",
18
+ "README.md"
18
19
  ],
19
20
  "scripts": {
20
21
  "format:check": "prettier --check .",
@@ -25,14 +26,14 @@
25
26
  },
26
27
  "repository": {
27
28
  "type": "git",
28
- "url": "git+https://github.com/james-pre/@james-pre/config.git"
29
+ "url": "git+https://github.com/james-pre/config"
29
30
  },
30
31
  "author": "James Prevett <jp@jamespre.dev> (https://jamespre.dev)",
31
32
  "license": "LGPL-3.0-or-later",
32
33
  "bugs": {
33
- "url": "https://github.com/james-pre/@james-pre/config/issues"
34
+ "url": "https://github.com/james-pre/config/issues"
34
35
  },
35
- "homepage": "https://github.com/james-pre/@james-pre/config#readme",
36
+ "homepage": "https://github.com/james-pre/config#readme",
36
37
  "engines": {
37
38
  "node": ">=22.0.0"
38
39
  },
@@ -62,5 +63,6 @@
62
63
  "printWidth": 120,
63
64
  "arrowParens": "avoid",
64
65
  "experimentalOperatorPosition": "start"
65
- }
66
+ },
67
+ "sideEffects": false
66
68
  }
package/dist/core.d.ts DELETED
@@ -1,43 +0,0 @@
1
- /**
2
- * Get a human-readable string for a date that also fits into CLIs well (fixed-width)
3
- */
4
- export declare function prettyDate(date: Date): string;
5
- export declare let _debugOutput: boolean;
6
- /**
7
- * Enable or disable debug output.
8
- */
9
- export declare function _setDebugOutput(enabled: boolean): void;
10
- export declare function start(message: string): Disposable;
11
- export declare let progress: (value: number, max: number, message?: any, valueText?: string) => void;
12
- export declare let done: (noPrint?: boolean) => void;
13
- export interface ProgressIO {
14
- start(message: string): void;
15
- progress(value: number, max: number, message?: any, valueText?: string): void;
16
- done(noPrint?: boolean): void;
17
- }
18
- export declare function useProgress(io: ProgressIO): void;
19
- export declare let debug: (...args: any[]) => void;
20
- export declare let log: (...args: any[]) => void;
21
- export declare let info: (...args: any[]) => void;
22
- export declare let warn: (...args: any[]) => void;
23
- export declare let error: (...args: any[]) => void;
24
- export interface ConsoleLike {
25
- debug(...args: any[]): void;
26
- log(...args: any[]): void;
27
- info(...args: any[]): void;
28
- warn(...args: any[]): void;
29
- error(...args: any[]): void;
30
- }
31
- export declare function useOutput(output: ConsoleLike): void;
32
- export declare function warnOnce(...args: any[]): void;
33
- /**
34
- * This is a factory for handling errors when performing operations.
35
- * The handler will allow the parent scope to continue if certain errors occur,
36
- * rather than fatally exiting.
37
- */
38
- export declare function someWarnings(...allowList: [RegExp, string?][]): (error: string | Error) => void;
39
- export declare function errorText(error: unknown): string;
40
- /** @hidden @internal for other APIs that take a `Console` but check `.constructor.name` */
41
- export declare const constructor: {
42
- name: string;
43
- };
package/dist/core.js DELETED
@@ -1,79 +0,0 @@
1
- // SPDX-License-Identifier: LGPL-3.0-or-later
2
- /* eslint-disable @typescript-eslint/only-throw-error */
3
- const _month = new Intl.DateTimeFormat(undefined, { month: 'short' });
4
- // Shortcut to convert to 2-digit. Mostly used to make the line shorter.
5
- const _2 = (v) => v.toString().padStart(2, '0');
6
- /**
7
- * Get a human-readable string for a date that also fits into CLIs well (fixed-width)
8
- */
9
- export function prettyDate(date) {
10
- return `${date.getFullYear()} ${_month.format(date)} ${_2(date.getDate())} ${_2(date.getHours())}:${_2(date.getMinutes())}:${_2(date.getSeconds())}.${date.getMilliseconds().toString().padStart(3, '0')}`;
11
- }
12
- export let _debugOutput = false;
13
- /**
14
- * Enable or disable debug output.
15
- */
16
- export function _setDebugOutput(enabled) {
17
- _debugOutput = enabled;
18
- }
19
- // I/O for "progressive" actions
20
- let _start;
21
- export function start(message) {
22
- _start(message);
23
- return { [Symbol.dispose]: () => done(true) };
24
- }
25
- export let progress;
26
- export let done;
27
- export function useProgress(io) {
28
- _start = io.start.bind(io);
29
- progress = io.progress.bind(io);
30
- done = io.done.bind(io);
31
- }
32
- // User-facing messaging
33
- export let debug = console.debug;
34
- export let log = console.log;
35
- export let info = console.info;
36
- export let warn = console.warn;
37
- export let error = console.error;
38
- export function useOutput(output) {
39
- debug = output.debug.bind(output);
40
- log = output.log.bind(output);
41
- info = output.info.bind(output);
42
- warn = output.warn.bind(output);
43
- error = output.error.bind(output);
44
- }
45
- const _warned = new Set();
46
- export function warnOnce(...args) {
47
- const key = args.map(String).join(' ');
48
- if (_warned.has(key))
49
- return;
50
- _warned.add(key);
51
- warn(...args);
52
- }
53
- /**
54
- * This is a factory for handling errors when performing operations.
55
- * The handler will allow the parent scope to continue if certain errors occur,
56
- * rather than fatally exiting.
57
- */
58
- export function someWarnings(...allowList) {
59
- return (error) => {
60
- error = typeof error == 'object' && 'message' in error ? error.message : error;
61
- for (const [pattern, message = error] of allowList) {
62
- if (!pattern.test(error))
63
- continue;
64
- warn(message);
65
- return;
66
- }
67
- throw error;
68
- };
69
- }
70
- const zod = await import('zod/v4/core').catch(() => null);
71
- export function errorText(error) {
72
- if (zod && error instanceof zod.$ZodError)
73
- return zod.prettifyError(error);
74
- if (error instanceof Error)
75
- return error.message;
76
- return String(error);
77
- }
78
- /** @hidden @internal for other APIs that take a `Console` but check `.constructor.name` */
79
- export const constructor = { name: 'Console' };
package/dist/jobs.d.ts DELETED
@@ -1,48 +0,0 @@
1
- export interface JobResult {
2
- status: 'succeeded' | 'failed';
3
- text: {
4
- toString(): string;
5
- };
6
- }
7
- export declare function useClear(clear: (lastDrawLineCount: number) => void): void;
8
- export declare function useDraw(draw: (lines: string[]) => void): void;
9
- export declare function useResultFormat(format: (result: JobResult) => string): void;
10
- /** @internal */
11
- export declare function _disableInPlaceUpdates(): void;
12
- export interface Options {
13
- concurrency: number;
14
- jobStartText?: string;
15
- }
16
- export interface Results {
17
- failed: number;
18
- noJobs?: boolean;
19
- }
20
- export interface Context {
21
- nextIndex: number;
22
- remaining: number;
23
- failed: number;
24
- finished: number;
25
- prefix(i: number, number?: number): string;
26
- }
27
- export interface JobContext {
28
- progress(this: void, ...args: any[]): void;
29
- }
30
- type JobFn = (context: JobContext) => JobResult | Promise<JobResult>;
31
- export type Job = JobFn | {
32
- run: JobFn;
33
- name?: string;
34
- };
35
- /**
36
- * A position in a job list that enforces job ordering.
37
- * Every job before it finishes before any job after it starts.
38
- * It is not work of its own, so it is not counted or numbered.
39
- */
40
- export declare const barrier: unique symbol;
41
- export declare function run(options: Options, entries: (Job | typeof barrier)[]): Promise<Results>;
42
- export interface OptionsWithData<T> extends Options {
43
- run(data: T, progress: (...args: any[]) => void): JobResult | Promise<JobResult>;
44
- name?(data: T): string | undefined;
45
- group?(data: T): unknown;
46
- }
47
- export declare function runWithData<T>(options: OptionsWithData<T>, data: T[]): Promise<Results>;
48
- export {};
package/dist/jobs.js DELETED
@@ -1,135 +0,0 @@
1
- // SPDX-License-Identifier: LGPL-3.0-or-later
2
- import * as io from './core.js';
3
- let clearActiveLines, drawActiveLines, formatJobResult = (result) => result.text ? result.text.toString() : result.status == 'failed' ? 'failed' : 'done', _noInPlaceUpdate = false;
4
- export function useClear(clear) {
5
- clearActiveLines = clear;
6
- }
7
- export function useDraw(draw) {
8
- drawActiveLines = draw;
9
- }
10
- export function useResultFormat(format) {
11
- formatJobResult = format;
12
- }
13
- /** @internal */
14
- export function _disableInPlaceUpdates() {
15
- _noInPlaceUpdate = true;
16
- }
17
- /**
18
- * A position in a job list that enforces job ordering.
19
- * Every job before it finishes before any job after it starts.
20
- * It is not work of its own, so it is not counted or numbered.
21
- */
22
- export const barrier = Symbol('ioium.jobs.barrier');
23
- export async function run(options, entries) {
24
- const jobs = [];
25
- const segments = [];
26
- for (const entry of entries) {
27
- if (entry !== barrier) {
28
- jobs.push(entry);
29
- continue;
30
- }
31
- if (jobs.length && segments.at(-1) !== jobs.length)
32
- segments.push(jobs.length);
33
- }
34
- if (!jobs.length) {
35
- return { failed: 0, noJobs: true };
36
- }
37
- if (segments.at(-1) !== jobs.length)
38
- segments.push(jobs.length);
39
- const totalWidth = jobs.length.toString().length;
40
- const $ = {
41
- nextIndex: 0,
42
- remaining: jobs.length,
43
- failed: 0,
44
- finished: 0,
45
- prefix(i, number = i + 1) {
46
- let prefix = `[${number.toString().padStart(totalWidth)}/${jobs.length}]`;
47
- if (typeof jobs[i] === 'object' && jobs[i].name)
48
- prefix += ' ' + jobs[i].name;
49
- return prefix;
50
- },
51
- };
52
- const activeJobs = [];
53
- let lastDrawLineCount = 0;
54
- function _draw() {
55
- if (_noInPlaceUpdate)
56
- return;
57
- drawActiveLines?.(activeJobs.map((j, i) => `${$.prefix(j.index, $.finished + 1 + i)} ${j.message}`));
58
- lastDrawLineCount = activeJobs.length;
59
- }
60
- function _clear() {
61
- if (!lastDrawLineCount || _noInPlaceUpdate)
62
- return;
63
- clearActiveLines?.(lastDrawLineCount);
64
- lastDrawLineCount = 0;
65
- }
66
- async function nextJob(limit, noInitialDraw = false) {
67
- if ($.nextIndex >= limit)
68
- return;
69
- const index = $.nextIndex++;
70
- const job = { message: options.jobStartText || 'starting...', index };
71
- if (!_noInPlaceUpdate && !noInitialDraw)
72
- _clear();
73
- activeJobs.push(job);
74
- if (_noInPlaceUpdate)
75
- io.log($.prefix(index, $.finished + activeJobs.length), job.message);
76
- else if (!noInitialDraw)
77
- _draw();
78
- function progress(...args) {
79
- const line = args.join(' ').trim();
80
- if (!line)
81
- return;
82
- job.message = line;
83
- _clear();
84
- _draw();
85
- }
86
- const runJob = typeof jobs[index] === 'function' ? jobs[index] : jobs[index].run;
87
- let result;
88
- try {
89
- result = await runJob({ progress });
90
- }
91
- catch (e) {
92
- result = { status: 'failed', text: io.errorText(e) };
93
- }
94
- if (result.status == 'failed')
95
- $.failed++;
96
- _clear();
97
- io.log($.prefix(index, ++$.finished), formatJobResult(result));
98
- const jobIdx = activeJobs.indexOf(job);
99
- if (jobIdx === -1)
100
- throw new Error('BUG: Could not remove a job from the active jobs list');
101
- activeJobs.splice(jobIdx, 1);
102
- _draw();
103
- $.remaining--;
104
- return await nextJob(limit);
105
- }
106
- for (const end of segments) {
107
- const allDone = [];
108
- for (let i = 0; i < options.concurrency; i++) {
109
- if ($.nextIndex >= end)
110
- break;
111
- allDone.push(nextJob(end, true));
112
- }
113
- _clear();
114
- _draw();
115
- await Promise.all(allDone);
116
- }
117
- return { failed: $.failed };
118
- }
119
- export async function runWithData(options, data) {
120
- const entries = [];
121
- let previous;
122
- for (const [i, d] of data.entries()) {
123
- if (options.group) {
124
- const group = options.group(d);
125
- if (i && !Object.is(group, previous))
126
- entries.push(barrier);
127
- previous = group;
128
- }
129
- entries.push({
130
- run: (context) => options.run(d, context.progress),
131
- name: options.name?.(d),
132
- });
133
- }
134
- return await run(options, entries);
135
- }
@@ -1,119 +0,0 @@
1
- import EventEmitter from 'node:events';
2
- import type { FlattenKeys, GetByString, PartialRecursive } from 'utilium';
3
- import * as z from 'zod';
4
- export type FileType = 'system' | 'user' | 'local';
5
- /** Information about a loaded config file */
6
- interface File<Value> {
7
- path: string;
8
- wasIncluded: boolean;
9
- data: Value;
10
- type: FileType;
11
- auto: boolean;
12
- }
13
- /**
14
- * How a file is being loaded
15
- * @internal
16
- */
17
- interface FileLoadInit {
18
- wasIncluded?: boolean;
19
- type?: FileType;
20
- auto?: boolean;
21
- }
22
- export interface LoadOptions {
23
- /**
24
- * If enabled, the config file will still be loaded if it does not match the schema.
25
- */
26
- loose?: boolean;
27
- /**
28
- * If enabled, the config file will be skipped if it does not exist.
29
- */
30
- optional?: boolean;
31
- /**
32
- * If enabled, an empty config file will be created when it does not exist.
33
- * Included files are never created.
34
- */
35
- create?: boolean;
36
- /**
37
- * Used to mark files that are included, auto-loaded, etc.
38
- * @internal
39
- */
40
- [kInit]?: FileLoadInit;
41
- }
42
- declare const kInit: unique symbol;
43
- export interface ManagerOptions {
44
- /** If true, allow specifying `include: [...]` to load additional files */
45
- enableIncludes?: boolean;
46
- /**
47
- * If set, try to load files in the XDG config directory (or the OS-equivalent).
48
- * This corresponds to the relative path, though you can omit `.json`
49
- */
50
- xdg?: string;
51
- /**
52
- * Try to load system-wide configuration from `/etc` (or the OS-equivalent).
53
- * Trailing `.json` can be omitted.
54
- */
55
- system?: string;
56
- }
57
- /**
58
- * Manage for configuration files
59
- */
60
- export declare class Manager<Shape extends Readonly<Record<string, z.ZodType>>, LoadOpts extends LoadOptions = LoadOptions, out In extends z.input<z.ZodObject<Shape>> = z.input<z.ZodObject<Shape>>> extends EventEmitter<{
61
- load: [path: string, config: z.output<ReturnType<typeof z.deepPartial<z.ZodObject<Shape>>>>];
62
- load_error: [path: string, stage: 'read' | 'create' | 'parse', error: Error];
63
- change: [];
64
- }> {
65
- protected options: ManagerOptions;
66
- readonly schema: z.ZodObject<Shape>;
67
- readonly fileSchema: ReturnType<typeof z.deepPartial<z.ZodObject<Shape>>>;
68
- protected files: Map<string, File<z.output<typeof this.fileSchema>>>;
69
- readonly data: z.output<z.ZodObject<Shape>>;
70
- constructor(shape: Shape, options?: ManagerOptions);
71
- /**
72
- * A fresh config with only the schema's defaults applied.
73
- */
74
- get defaults(): z.output<z.ZodObject<Shape>> & In;
75
- get<const K extends string | number = FlattenKeys<z.output<z.ZodObject<Shape>>>>(key: K): GetByString<z.output<z.ZodObject<Shape>>, K>;
76
- set<const K extends string | number = FlattenKeys<z.output<z.ZodObject<Shape>>>, V = GetByString<z.output<z.ZodObject<Shape>>, K>>(key: K, value: V): void;
77
- /**
78
- * Deeply merge `config` into the current config. Arrays are replaced rather than combined.
79
- */
80
- merge(config: PartialRecursive<In>): void;
81
- /** Replace the current config with `config`, applying defaults for anything missing. */
82
- replace(config: In): void;
83
- loadFile(path: string, options: LoadOpts): void;
84
- /** Get the entry for `path`, adding one for a file that has not been loaded */
85
- protected fileAt(path: string): File<z.output<typeof this.fileSchema>>;
86
- protected writeFile(file: File<z.output<typeof this.fileSchema>>): void;
87
- /**
88
- * Replace the contents of the config file at `path` and write it,
89
- * then rebuild the current config from the defaults and all loaded files.
90
- */
91
- replaceFile(path: string, config: In): void;
92
- /**
93
- * Merge `config` into the current config and into the config file at `path`, then write it.
94
- */
95
- updateFile(path: string, config: PartialRecursive<In>): void;
96
- /**
97
- * The files `loadDefaults` will try to load, ordered from most global to most local.
98
- * @internal
99
- */
100
- get defaultPaths(): ReadonlyArray<{
101
- path: string;
102
- type: FileType;
103
- }>;
104
- /**
105
- * Load the files from `defaultPaths`. Missing files are skipped unless `options` says otherwise.
106
- */
107
- loadDefaults(options: LoadOpts): void;
108
- /**
109
- * The file a change of the given type is written to:
110
- * the most recently loaded file of that type, or the path `loadDefaults` would use for it.
111
- */
112
- findPath(type?: FileType): string;
113
- /**
114
- * Merge `config` into the current config and save it.
115
- */
116
- update(config: PartialRecursive<In>, type?: FileType): void;
117
- reloadFiles(options: LoadOpts): void;
118
- }
119
- export {};