@push.rocks/smartconfig 6.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/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.appdata.d.ts +72 -0
- package/dist_ts/classes.appdata.js +454 -0
- package/dist_ts/classes.keyvaluestore.d.ts +63 -0
- package/dist_ts/classes.keyvaluestore.js +169 -0
- package/dist_ts/classes.smartconfig.d.ts +29 -0
- package/dist_ts/classes.smartconfig.js +67 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -0
- package/dist_ts/paths.d.ts +8 -0
- package/dist_ts/paths.js +15 -0
- package/dist_ts/plugins.d.ts +12 -0
- package/dist_ts/plugins.js +13 -0
- package/package.json +73 -0
- package/readme.hints.md +0 -0
- package/readme.md +517 -0
- package/readme.plan.md +225 -0
- package/smartconfig.json +42 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.appdata.ts +548 -0
- package/ts/classes.keyvaluestore.ts +222 -0
- package/ts/classes.smartconfig.ts +79 -0
- package/ts/index.ts +3 -0
- package/ts/paths.ts +22 -0
- package/ts/plugins.ts +25 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as paths from './paths.js';
|
|
3
|
+
|
|
4
|
+
import { Task } from '@push.rocks/taskbuffer';
|
|
5
|
+
|
|
6
|
+
export type TKeyValueStore = 'custom' | 'userHomeDir' | 'ephemeral';
|
|
7
|
+
|
|
8
|
+
export interface IKvStoreConstructorOptions<T> {
|
|
9
|
+
typeArg: TKeyValueStore;
|
|
10
|
+
identityArg: string;
|
|
11
|
+
customPath?: string;
|
|
12
|
+
mandatoryKeys?: Array<keyof T>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* kvStore is a simple key value store to store data about projects between runs
|
|
17
|
+
*/
|
|
18
|
+
export class KeyValueStore<T = any> {
|
|
19
|
+
private dataObject: Partial<T> = {};
|
|
20
|
+
private deletedObject: Partial<T> = {};
|
|
21
|
+
private mandatoryKeys: Set<keyof T> = new Set();
|
|
22
|
+
public changeSubject = new plugins.smartrx.rxjs.Subject<Partial<T>>();
|
|
23
|
+
|
|
24
|
+
private storedStateString: string = '';
|
|
25
|
+
public syncTask = new Task({
|
|
26
|
+
name: 'syncTask',
|
|
27
|
+
buffered: true,
|
|
28
|
+
bufferMax: 1,
|
|
29
|
+
execDelay: 0,
|
|
30
|
+
taskFunction: async () => {
|
|
31
|
+
if (this.type !== 'ephemeral') {
|
|
32
|
+
this.dataObject = {
|
|
33
|
+
...plugins.smartfile.fs.toObjectSync(this.filePath),
|
|
34
|
+
...this.dataObject,
|
|
35
|
+
};
|
|
36
|
+
for (const key of Object.keys(this.deletedObject) as Array<keyof T>) {
|
|
37
|
+
delete this.dataObject[key];
|
|
38
|
+
}
|
|
39
|
+
this.deletedObject = {};
|
|
40
|
+
await plugins.smartfile.memory.toFs(
|
|
41
|
+
plugins.smartjson.stringifyPretty(this.dataObject),
|
|
42
|
+
this.filePath,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const newStateString = plugins.smartjson.stringify(this.dataObject);
|
|
46
|
+
|
|
47
|
+
// change detection
|
|
48
|
+
if (newStateString !== this.storedStateString) {
|
|
49
|
+
this.storedStateString = newStateString;
|
|
50
|
+
this.changeSubject.next(this.dataObject);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* computes the identity and filePath
|
|
57
|
+
*/
|
|
58
|
+
private initFilePath = () => {
|
|
59
|
+
if (this.type === 'ephemeral') {
|
|
60
|
+
// No file path is needed for ephemeral type
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (this.customPath) {
|
|
64
|
+
// Use custom path if provided
|
|
65
|
+
const absolutePath = plugins.smartpath.transform.makeAbsolute(
|
|
66
|
+
this.customPath,
|
|
67
|
+
paths.cwd,
|
|
68
|
+
);
|
|
69
|
+
this.filePath = absolutePath;
|
|
70
|
+
if (plugins.smartfile.fs.isDirectorySync(this.filePath)) {
|
|
71
|
+
this.filePath = plugins.path.join(
|
|
72
|
+
this.filePath,
|
|
73
|
+
this.identity + '.json',
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
plugins.smartfile.fs.ensureFileSync(this.filePath, '{}');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let baseDir: string;
|
|
81
|
+
if (this.type === 'userHomeDir') {
|
|
82
|
+
baseDir = paths.kvUserHomeDirBase;
|
|
83
|
+
} else {
|
|
84
|
+
throw new Error('kv type not supported');
|
|
85
|
+
}
|
|
86
|
+
this.filePath = plugins.path.join(baseDir, this.identity + '.json');
|
|
87
|
+
plugins.smartfile.fs.ensureDirSync(baseDir);
|
|
88
|
+
plugins.smartfile.fs.ensureFileSync(this.filePath, '{}');
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// if no custom path is provided, try to store at home directory
|
|
92
|
+
public type: TKeyValueStore;
|
|
93
|
+
public identity: string;
|
|
94
|
+
public filePath?: string;
|
|
95
|
+
private customPath?: string; // Optionally allow custom path
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* the constructor of keyvalue store
|
|
99
|
+
* @param typeArg
|
|
100
|
+
* @param identityArg
|
|
101
|
+
* @param customPath Optional custom path for the keyValue store
|
|
102
|
+
*/
|
|
103
|
+
constructor(optionsArg: IKvStoreConstructorOptions<T>) {
|
|
104
|
+
if (optionsArg.customPath && optionsArg.typeArg !== 'custom') {
|
|
105
|
+
throw new Error('customPath can only be provided if typeArg is custom');
|
|
106
|
+
}
|
|
107
|
+
if (optionsArg.typeArg === 'custom' && !optionsArg.customPath) {
|
|
108
|
+
throw new Error('customPath must be provided if typeArg is custom');
|
|
109
|
+
}
|
|
110
|
+
this.type = optionsArg.typeArg;
|
|
111
|
+
this.identity = optionsArg.identityArg;
|
|
112
|
+
this.customPath = optionsArg.customPath; // Store custom path if provided
|
|
113
|
+
this.initFilePath();
|
|
114
|
+
if (optionsArg.mandatoryKeys) {
|
|
115
|
+
this.setMandatoryKeys(optionsArg.mandatoryKeys);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* reads all keyValue pairs at once and returns them
|
|
121
|
+
*/
|
|
122
|
+
public async readAll(): Promise<Partial<T>> {
|
|
123
|
+
await this.syncTask.trigger();
|
|
124
|
+
return this.dataObject;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* reads a keyValueFile from disk
|
|
129
|
+
*/
|
|
130
|
+
public async readKey<K extends keyof T>(keyArg: K): Promise<T[K]> {
|
|
131
|
+
await this.syncTask.trigger();
|
|
132
|
+
return this.dataObject[keyArg] as T[K];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* writes a specific key to the keyValueStore
|
|
137
|
+
*/
|
|
138
|
+
public async writeKey<K extends keyof T>(
|
|
139
|
+
keyArg: K,
|
|
140
|
+
valueArg: T[K],
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
await this.writeAll({
|
|
143
|
+
[keyArg]: valueArg,
|
|
144
|
+
} as unknown as Partial<T>);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async deleteKey<K extends keyof T>(keyArg: K): Promise<void> {
|
|
148
|
+
this.deletedObject[keyArg] = this.dataObject[keyArg];
|
|
149
|
+
await this.syncTask.trigger();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* writes all keyValue pairs in the object argument
|
|
154
|
+
*/
|
|
155
|
+
public async writeAll(keyValueObject: Partial<T>): Promise<void> {
|
|
156
|
+
this.dataObject = { ...this.dataObject, ...keyValueObject };
|
|
157
|
+
await this.syncTask.trigger();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* wipes a key value store from disk
|
|
162
|
+
*/
|
|
163
|
+
public async wipe(): Promise<void> {
|
|
164
|
+
this.dataObject = {};
|
|
165
|
+
if (this.type !== 'ephemeral') {
|
|
166
|
+
await plugins.smartfile.fs.remove(this.filePath);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* resets the KeyValueStore to the initial state by syncing first, deleting all keys, and then triggering a sync again
|
|
172
|
+
*/
|
|
173
|
+
public async reset(): Promise<void> {
|
|
174
|
+
await this.syncTask.trigger(); // Sync to get the latest state
|
|
175
|
+
|
|
176
|
+
// Delete all keys from the dataObject and add them to deletedObject
|
|
177
|
+
for (const key of Object.keys(this.dataObject) as Array<keyof T>) {
|
|
178
|
+
this.deletedObject[key] = this.dataObject[key];
|
|
179
|
+
delete this.dataObject[key];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await this.syncTask.trigger(); // Sync again to reflect the deletion
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private setMandatoryKeys(keys: Array<keyof T>) {
|
|
186
|
+
keys.forEach((key) => this.mandatoryKeys.add(key));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public async getMissingMandatoryKeys(): Promise<Array<keyof T>> {
|
|
190
|
+
await this.readAll();
|
|
191
|
+
return Array.from(this.mandatoryKeys).filter(
|
|
192
|
+
(key) => !(key in this.dataObject),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public async waitForKeysPresent<K extends keyof T>(
|
|
197
|
+
keysArg: K[],
|
|
198
|
+
): Promise<void> {
|
|
199
|
+
const missingKeys = keysArg.filter((keyArg) => !this.dataObject[keyArg]);
|
|
200
|
+
if (missingKeys.length === 0) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
return new Promise<void>((resolve, reject) => {
|
|
204
|
+
const subscription = this.changeSubject.subscribe(() => {
|
|
205
|
+
const missingKeys = keysArg.filter(
|
|
206
|
+
(keyArg) => !this.dataObject[keyArg],
|
|
207
|
+
);
|
|
208
|
+
if (missingKeys.length === 0) {
|
|
209
|
+
subscription.unsubscribe();
|
|
210
|
+
resolve();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public async waitForAndGetKey<K extends keyof T>(
|
|
217
|
+
keyArg: K,
|
|
218
|
+
): Promise<T[K] | undefined> {
|
|
219
|
+
await this.waitForKeysPresent([keyArg]);
|
|
220
|
+
return this.readKey(keyArg);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as paths from './paths.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Smartconfig class allows easy configuration of tools
|
|
6
|
+
*/
|
|
7
|
+
export class Smartconfig {
|
|
8
|
+
cwd: string;
|
|
9
|
+
lookupPath: string;
|
|
10
|
+
smartconfigJsonExists: boolean;
|
|
11
|
+
smartconfigJsonData: any;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* creates instance of Smartconfig
|
|
15
|
+
*/
|
|
16
|
+
constructor(cwdArg?: string) {
|
|
17
|
+
if (cwdArg) {
|
|
18
|
+
this.cwd = cwdArg;
|
|
19
|
+
} else {
|
|
20
|
+
this.cwd = paths.cwd;
|
|
21
|
+
}
|
|
22
|
+
this.checkLookupPath();
|
|
23
|
+
this.checkSmartconfigJsonExists();
|
|
24
|
+
this.checkSmartconfigJsonData();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* merges the supplied options with the ones from smartconfig.json
|
|
29
|
+
*/
|
|
30
|
+
dataFor<IToolConfig>(
|
|
31
|
+
toolnameArg: string,
|
|
32
|
+
defaultOptionsArg: any,
|
|
33
|
+
): IToolConfig {
|
|
34
|
+
let smartconfigToolOptions;
|
|
35
|
+
if (this.smartconfigJsonData[toolnameArg]) {
|
|
36
|
+
smartconfigToolOptions = this.smartconfigJsonData[toolnameArg];
|
|
37
|
+
} else {
|
|
38
|
+
smartconfigToolOptions = {};
|
|
39
|
+
}
|
|
40
|
+
let mergedOptions = {
|
|
41
|
+
...defaultOptionsArg,
|
|
42
|
+
...smartconfigToolOptions,
|
|
43
|
+
};
|
|
44
|
+
return mergedOptions;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* checks if the JSON exists
|
|
49
|
+
*/
|
|
50
|
+
private checkSmartconfigJsonExists() {
|
|
51
|
+
this.smartconfigJsonExists = plugins.smartfile.fs.fileExistsSync(
|
|
52
|
+
this.lookupPath,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* gets lookupPath
|
|
58
|
+
*/
|
|
59
|
+
private checkLookupPath() {
|
|
60
|
+
if (this.cwd) {
|
|
61
|
+
this.lookupPath = plugins.path.join(this.cwd, 'smartconfig.json');
|
|
62
|
+
} else {
|
|
63
|
+
this.lookupPath = paths.configFile;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* get smartconfigJsonData
|
|
69
|
+
*/
|
|
70
|
+
private checkSmartconfigJsonData() {
|
|
71
|
+
if (this.smartconfigJsonExists) {
|
|
72
|
+
this.smartconfigJsonData = plugins.smartfile.fs.toObjectSync(
|
|
73
|
+
this.lookupPath,
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
this.smartconfigJsonData = {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
package/ts/index.ts
ADDED
package/ts/paths.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
// directories
|
|
4
|
+
export let cwd = process.cwd();
|
|
5
|
+
export let packageDir = plugins.path.join(
|
|
6
|
+
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
|
7
|
+
'../',
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
// ----------------------
|
|
11
|
+
// keyValueStore specific
|
|
12
|
+
// ----------------------
|
|
13
|
+
|
|
14
|
+
export let home = plugins.smartpath.get.home();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* keyValue base path
|
|
18
|
+
*/
|
|
19
|
+
export let kvUserHomeDirBase = plugins.path.join(home, '.smartconfig/kv');
|
|
20
|
+
|
|
21
|
+
// files
|
|
22
|
+
export let configFile = plugins.path.join(cwd, 'smartconfig.json');
|
package/ts/plugins.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as tsclass from '@tsclass/tsclass';
|
|
2
|
+
|
|
3
|
+
export { tsclass };
|
|
4
|
+
|
|
5
|
+
import * as qenv from '@push.rocks/qenv';
|
|
6
|
+
import * as smartlog from '@push.rocks/smartlog';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as smartfile from '@push.rocks/smartfile';
|
|
9
|
+
import * as smartjson from '@push.rocks/smartjson';
|
|
10
|
+
import * as smartpath from '@push.rocks/smartpath';
|
|
11
|
+
import * as smartpromise from '@push.rocks/smartpromise';
|
|
12
|
+
import * as smartrx from '@push.rocks/smartrx';
|
|
13
|
+
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
qenv,
|
|
17
|
+
smartlog,
|
|
18
|
+
path,
|
|
19
|
+
smartfile,
|
|
20
|
+
smartjson,
|
|
21
|
+
smartpath,
|
|
22
|
+
smartpromise,
|
|
23
|
+
smartrx,
|
|
24
|
+
taskbuffer,
|
|
25
|
+
};
|