@open-audio-stack/core 0.1.9 → 0.1.11
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/build/classes/ManagerLocal.d.ts +7 -1
- package/build/classes/ManagerLocal.js +177 -7
- package/build/classes/Package.d.ts +3 -3
- package/build/classes/Package.js +11 -11
- package/build/classes/RegistryLocal.d.ts +1 -1
- package/build/classes/RegistryLocal.js +2 -2
- package/build/helpers/file.d.ts +1 -0
- package/build/helpers/file.js +37 -8
- package/build/helpers/package.d.ts +2 -2
- package/build/helpers/package.js +76 -49
- package/build/helpers/packageLocal.d.ts +3 -0
- package/build/helpers/packageLocal.js +24 -0
- package/build/helpers/utils.d.ts +2 -0
- package/build/helpers/utils.js +13 -0
- package/build/index-browser.js +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/types/File.d.ts +0 -2
- package/build/types/Preset.d.ts +2 -1
- package/build/types/Preset.js +1 -1
- package/build/types/Project.d.ts +3 -1
- package/build/types/Project.js +1 -1
- package/build/types/ProjectFormat.js +13 -12
- package/package.json +4 -1
|
@@ -5,8 +5,14 @@ import { ConfigInterface } from '../types/Config.js';
|
|
|
5
5
|
export declare class ManagerLocal extends Manager {
|
|
6
6
|
protected typeDir: string;
|
|
7
7
|
constructor(type: RegistryType, config: ConfigInterface);
|
|
8
|
-
|
|
8
|
+
create(): Promise<void>;
|
|
9
|
+
scan(ext?: string, installable?: boolean): void;
|
|
9
10
|
export(dir: string, ext?: string): boolean;
|
|
10
11
|
install(slug: string, version?: string): Promise<void | PackageVersion>;
|
|
12
|
+
installDependency(slug: string, version?: string, filePath?: string, type?: RegistryType): Promise<any>;
|
|
13
|
+
installDependencies(filePath?: string, type?: RegistryType): Promise<any>;
|
|
14
|
+
open(filePath?: string): void;
|
|
11
15
|
uninstall(slug: string, version?: string): Promise<void | PackageVersion>;
|
|
16
|
+
uninstallDependency(slug: string, version?: string, filePath?: string, type?: RegistryType): Promise<any>;
|
|
17
|
+
uninstallDependencies(filePath?: string, type?: RegistryType): Promise<any>;
|
|
12
18
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { Package } from './Package.js';
|
|
3
3
|
import { Manager } from './Manager.js';
|
|
4
|
-
import { archiveExtract, dirCreate, dirDelete, dirEmpty, dirRead, fileCreate, fileCreateJson, fileCreateYaml, fileExists, fileHash, fileOpen, fileReadJson, fileReadYaml, filesMove, isAdmin, runCliAsAdmin, } from '../helpers/file.js';
|
|
5
|
-
import { pathGetSlug, pathGetVersion } from '../helpers/utils.js';
|
|
4
|
+
import { archiveExtract, dirCreate, dirDelete, dirEmpty, dirRead, fileCreate, fileCreateJson, fileCreateYaml, fileExists, fileHash, fileInstall, fileOpen, fileReadJson, fileReadYaml, filesMove, isAdmin, runCliAsAdmin, } from '../helpers/file.js';
|
|
5
|
+
import { isValidVersion, pathGetSlug, pathGetVersion, toSlug } from '../helpers/utils.js';
|
|
6
6
|
import { commandExists, getArchitecture, getSystem, isTests } from '../helpers/utilsLocal.js';
|
|
7
7
|
import { apiBuffer } from '../helpers/api.js';
|
|
8
8
|
import { FileType } from '../types/FileType.js';
|
|
@@ -12,7 +12,14 @@ import { ConfigLocal } from './ConfigLocal.js';
|
|
|
12
12
|
import { packageCompatibleFiles } from '../helpers/package.js';
|
|
13
13
|
import { presetFormatDir } from '../types/PresetFormat.js';
|
|
14
14
|
import { projectFormatDir } from '../types/ProjectFormat.js';
|
|
15
|
-
import { FileFormat
|
|
15
|
+
import { FileFormat } from '../types/FileFormat.js';
|
|
16
|
+
import { licenses } from '../types/License.js';
|
|
17
|
+
import { pluginTypes } from '../types/PluginType.js';
|
|
18
|
+
import { presetTypes } from '../types/PresetType.js';
|
|
19
|
+
import { projectTypes } from '../types/ProjectType.js';
|
|
20
|
+
import { SystemType } from '../types/SystemType.js';
|
|
21
|
+
import { packageLoadFile, packageSaveFile } from '../helpers/packageLocal.js';
|
|
22
|
+
import inquirer from 'inquirer';
|
|
16
23
|
export class ManagerLocal extends Manager {
|
|
17
24
|
typeDir;
|
|
18
25
|
constructor(type, config) {
|
|
@@ -21,12 +28,97 @@ export class ManagerLocal extends Manager {
|
|
|
21
28
|
this.config = new ConfigLocal(configPath, config);
|
|
22
29
|
this.typeDir = this.config.get(`${type}Dir`);
|
|
23
30
|
}
|
|
24
|
-
|
|
31
|
+
async create() {
|
|
32
|
+
// TODO Rewrite this code after prototype is proven.
|
|
33
|
+
const pkgQuestions = [
|
|
34
|
+
{
|
|
35
|
+
name: 'org',
|
|
36
|
+
type: 'input',
|
|
37
|
+
message: 'Org id',
|
|
38
|
+
default: 'org-name',
|
|
39
|
+
validate: (value) => value === toSlug(value),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'package',
|
|
43
|
+
type: 'input',
|
|
44
|
+
message: 'Package id',
|
|
45
|
+
default: 'package-name',
|
|
46
|
+
validate: (value) => value === toSlug(value),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'version',
|
|
50
|
+
type: 'input',
|
|
51
|
+
message: 'Package version',
|
|
52
|
+
default: '1.0.0',
|
|
53
|
+
validate: (value) => isValidVersion(value),
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
const pkgAnswers = await inquirer.prompt(pkgQuestions);
|
|
57
|
+
let types = pluginTypes;
|
|
58
|
+
if (this.type === RegistryType.Presets) {
|
|
59
|
+
types = presetTypes;
|
|
60
|
+
}
|
|
61
|
+
else if (this.type === RegistryType.Projects) {
|
|
62
|
+
types = projectTypes;
|
|
63
|
+
}
|
|
64
|
+
const pkgVersionQuestions = [
|
|
65
|
+
{ name: 'name', type: 'input', message: 'Package name' },
|
|
66
|
+
{ name: 'author', type: 'input', message: 'Author name' },
|
|
67
|
+
{ name: 'description', type: 'input', message: 'Description' },
|
|
68
|
+
{ name: 'license', type: 'list', message: 'License', choices: licenses },
|
|
69
|
+
{ name: 'type', type: 'list', message: 'Type', choices: types },
|
|
70
|
+
{
|
|
71
|
+
name: 'tags',
|
|
72
|
+
type: 'input',
|
|
73
|
+
message: 'Tags (comma-separated)',
|
|
74
|
+
filter: (input) => input
|
|
75
|
+
.split(',')
|
|
76
|
+
.map(tag => tag.trim())
|
|
77
|
+
.filter(tag => tag.length > 0),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'url',
|
|
81
|
+
type: 'input',
|
|
82
|
+
message: 'Website url',
|
|
83
|
+
default: `https://github.com/${pkgAnswers.org}/${pkgAnswers.package}`,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'audio',
|
|
87
|
+
type: 'input',
|
|
88
|
+
message: 'Audio preview url',
|
|
89
|
+
default: `https://open-audio-stack.github.io/open-audio-stack-registry/${this.type}/${pkgAnswers.org}/${pkgAnswers.package}/${pkgAnswers.package}.flac`,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'image',
|
|
93
|
+
type: 'input',
|
|
94
|
+
message: 'Image preview url',
|
|
95
|
+
default: `https://open-audio-stack.github.io/open-audio-stack-registry/${this.type}/${pkgAnswers.org}/${pkgAnswers.package}/${pkgAnswers.package}.jpg`,
|
|
96
|
+
},
|
|
97
|
+
{ name: 'date', type: 'input', message: 'Date released', default: new Date().toISOString() },
|
|
98
|
+
{ name: 'changes', type: 'input', message: 'List of changes' },
|
|
99
|
+
];
|
|
100
|
+
if (this.type === RegistryType.Projects) {
|
|
101
|
+
pkgVersionQuestions.push({ name: 'open', type: 'input', message: 'File to open' });
|
|
102
|
+
}
|
|
103
|
+
const pkgVersionAnswers = await inquirer.prompt(pkgVersionQuestions);
|
|
104
|
+
// TODO prompt for each file.
|
|
105
|
+
pkgVersionAnswers.files = [];
|
|
106
|
+
if (this.type === RegistryType.Presets || this.type === RegistryType.Projects) {
|
|
107
|
+
pkgVersionAnswers.plugins = [];
|
|
108
|
+
}
|
|
109
|
+
console.log(pkgVersionAnswers);
|
|
110
|
+
const pkg = new Package(`${pkgAnswers.org}/${pkgAnswers.package}`);
|
|
111
|
+
pkg.addVersion(pkgAnswers.version, pkgVersionAnswers);
|
|
112
|
+
console.log(JSON.stringify(pkg.getReport(), null, 2));
|
|
113
|
+
this.addPackage(pkg);
|
|
114
|
+
}
|
|
115
|
+
scan(ext = 'json', installable = true) {
|
|
25
116
|
const filePaths = dirRead(`${this.typeDir}/**/index.${ext}`);
|
|
26
117
|
filePaths.forEach((filePath) => {
|
|
27
118
|
const subPath = filePath.replace(`${this.typeDir}/`, '');
|
|
28
119
|
const pkgJson = ext === 'yaml' ? fileReadYaml(filePath) : fileReadJson(filePath);
|
|
29
|
-
|
|
120
|
+
if (installable)
|
|
121
|
+
pkgJson.installed = true;
|
|
30
122
|
const pkg = new Package(pathGetSlug(subPath));
|
|
31
123
|
const version = pathGetVersion(subPath);
|
|
32
124
|
pkg.addVersion(version, pkgJson);
|
|
@@ -42,6 +134,7 @@ export class ManagerLocal extends Manager {
|
|
|
42
134
|
dirCreate(path.join(dir, pkgSlug, version));
|
|
43
135
|
saveFile(path.join(dir, pkgSlug, version, filename), pkgVersion);
|
|
44
136
|
}
|
|
137
|
+
dirCreate(path.join(dir, pkgSlug, filename));
|
|
45
138
|
saveFile(path.join(dir, pkgSlug, filename), pkg.toJSON());
|
|
46
139
|
// TODO find a more elegant way to handle org exports.
|
|
47
140
|
const pkgOrg = pkgSlug.split('/')[0];
|
|
@@ -105,9 +198,12 @@ export class ManagerLocal extends Manager {
|
|
|
105
198
|
const hash = await fileHash(filePath);
|
|
106
199
|
if (hash !== file.sha256)
|
|
107
200
|
return console.error(`Error: ${filePath} hash mismatch`);
|
|
108
|
-
// If installer, run the installer.
|
|
201
|
+
// If installer, run the installer headless (without the user interface).
|
|
109
202
|
if (file.type === FileType.Installer) {
|
|
110
|
-
|
|
203
|
+
if (isTests())
|
|
204
|
+
fileOpen(filePath);
|
|
205
|
+
else
|
|
206
|
+
fileInstall(filePath);
|
|
111
207
|
}
|
|
112
208
|
// If archive, extract the archive to temporary directory, then move individual files.
|
|
113
209
|
if (file.type === FileType.Archive) {
|
|
@@ -130,6 +226,50 @@ export class ManagerLocal extends Manager {
|
|
|
130
226
|
pkgVersion.installed = true;
|
|
131
227
|
return pkgVersion;
|
|
132
228
|
}
|
|
229
|
+
async installDependency(slug, version, filePath, type = RegistryType.Plugins) {
|
|
230
|
+
// Get dependency package information from registry.
|
|
231
|
+
const manager = new ManagerLocal(type, this.config.config);
|
|
232
|
+
await manager.sync();
|
|
233
|
+
const pkg = manager.getPackage(slug);
|
|
234
|
+
if (!pkg)
|
|
235
|
+
return console.error(`Package ${slug} not found in registry`);
|
|
236
|
+
const versionNum = version || pkg.latestVersion();
|
|
237
|
+
const pkgVersion = pkg?.getVersion(versionNum);
|
|
238
|
+
if (!pkgVersion)
|
|
239
|
+
return console.error(`Package ${slug} version ${versionNum} not found in registry`);
|
|
240
|
+
// Get local package file.
|
|
241
|
+
const pkgFile = packageLoadFile(filePath);
|
|
242
|
+
if (pkgFile[type] && pkgFile[type][slug] && pkgFile[type][slug] === versionNum) {
|
|
243
|
+
return console.error(`Package ${slug} version ${versionNum} is already a dependency`);
|
|
244
|
+
}
|
|
245
|
+
// Install dependency.
|
|
246
|
+
await manager.install(slug, version);
|
|
247
|
+
// Add dependency to local package file and save.
|
|
248
|
+
if (!pkgFile[type])
|
|
249
|
+
pkgFile[type] = {};
|
|
250
|
+
pkgFile[type][slug] = versionNum;
|
|
251
|
+
packageSaveFile(pkgFile, filePath);
|
|
252
|
+
pkgFile.installed = true;
|
|
253
|
+
return pkgFile;
|
|
254
|
+
}
|
|
255
|
+
async installDependencies(filePath, type = RegistryType.Plugins) {
|
|
256
|
+
// Loop through dependency packages and install each one.
|
|
257
|
+
const pkgFile = packageLoadFile(filePath);
|
|
258
|
+
const manager = new ManagerLocal(type, this.config.config);
|
|
259
|
+
await manager.sync();
|
|
260
|
+
for (const slug in pkgFile[type]) {
|
|
261
|
+
await manager.install(slug, pkgFile[type][slug]);
|
|
262
|
+
}
|
|
263
|
+
pkgFile.installed = true;
|
|
264
|
+
return pkgFile;
|
|
265
|
+
}
|
|
266
|
+
open(filePath) {
|
|
267
|
+
const pkgFile = packageLoadFile(filePath);
|
|
268
|
+
// If installer, run the installer.
|
|
269
|
+
if (pkgFile.open) {
|
|
270
|
+
fileOpen(pkgFile.open);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
133
273
|
async uninstall(slug, version) {
|
|
134
274
|
// Get package information from registry.
|
|
135
275
|
const pkg = this.getPackage(slug);
|
|
@@ -169,4 +309,34 @@ export class ManagerLocal extends Manager {
|
|
|
169
309
|
delete pkgVersion.installed;
|
|
170
310
|
return this.getPackage(slug)?.getVersion(versionNum);
|
|
171
311
|
}
|
|
312
|
+
async uninstallDependency(slug, version, filePath, type = RegistryType.Plugins) {
|
|
313
|
+
// Get local package file.
|
|
314
|
+
const pkgFile = packageLoadFile(filePath);
|
|
315
|
+
if (!pkgFile[type])
|
|
316
|
+
return console.error(`Package ${type} is missing`);
|
|
317
|
+
if (!pkgFile[type][slug])
|
|
318
|
+
return console.error(`Package ${type} ${slug} is not a dependency`);
|
|
319
|
+
// Uninstall dependency.
|
|
320
|
+
const manager = new ManagerLocal(type, this.config.config);
|
|
321
|
+
await manager.sync();
|
|
322
|
+
await manager.uninstall(slug, version || pkgFile[type][slug]);
|
|
323
|
+
// Remove dependency from local package file and save.
|
|
324
|
+
if (!pkgFile[type])
|
|
325
|
+
pkgFile[type] = {};
|
|
326
|
+
delete pkgFile[type][slug];
|
|
327
|
+
packageSaveFile(pkgFile, filePath);
|
|
328
|
+
pkgFile.installed = true;
|
|
329
|
+
return pkgFile;
|
|
330
|
+
}
|
|
331
|
+
async uninstallDependencies(filePath, type = RegistryType.Plugins) {
|
|
332
|
+
// Loop through dependency packages and uninstall each one.
|
|
333
|
+
const pkgFile = packageLoadFile(filePath);
|
|
334
|
+
const manager = new ManagerLocal(type, this.config.config);
|
|
335
|
+
await manager.sync();
|
|
336
|
+
for (const slug in pkgFile[type]) {
|
|
337
|
+
await manager.uninstall(slug, pkgFile[type][slug]);
|
|
338
|
+
}
|
|
339
|
+
pkgFile.installed = true;
|
|
340
|
+
return pkgFile;
|
|
341
|
+
}
|
|
172
342
|
}
|
|
@@ -5,12 +5,12 @@ export declare class Package {
|
|
|
5
5
|
version: string;
|
|
6
6
|
versions: Map<string, PackageVersion>;
|
|
7
7
|
constructor(slug: string, versions?: PackageVersions);
|
|
8
|
-
addVersion(
|
|
9
|
-
removeVersion(
|
|
8
|
+
addVersion(num: string, version: PackageVersion): void;
|
|
9
|
+
removeVersion(num: string): void;
|
|
10
10
|
getReport(): {
|
|
11
11
|
[k: string]: PackageReport;
|
|
12
12
|
};
|
|
13
|
-
getVersion(
|
|
13
|
+
getVersion(num: string): PackageVersion | undefined;
|
|
14
14
|
getVersionLatest(): PackageVersion | undefined;
|
|
15
15
|
latestVersion(): string;
|
|
16
16
|
toJSON(): {
|
package/build/classes/Package.js
CHANGED
|
@@ -14,33 +14,33 @@ export class Package {
|
|
|
14
14
|
this.versions = versions ? new Map(Object.entries(versions)) : new Map();
|
|
15
15
|
this.version = this.latestVersion();
|
|
16
16
|
}
|
|
17
|
-
addVersion(
|
|
18
|
-
if (this.versions.has(
|
|
17
|
+
addVersion(num, version) {
|
|
18
|
+
if (this.versions.has(num))
|
|
19
19
|
return;
|
|
20
|
-
const errors = packageErrors(
|
|
21
|
-
const recs = packageRecommendations(
|
|
20
|
+
const errors = packageErrors(version);
|
|
21
|
+
const recs = packageRecommendations(version);
|
|
22
22
|
const report = {
|
|
23
23
|
...(errors.length > 0 && { errors }),
|
|
24
24
|
...(recs.length > 0 && { recs }),
|
|
25
25
|
};
|
|
26
26
|
if (Object.keys(report).length > 0)
|
|
27
|
-
this.reports.set(
|
|
27
|
+
this.reports.set(num, report);
|
|
28
28
|
if (errors.length > 0)
|
|
29
29
|
return;
|
|
30
|
-
this.versions.set(
|
|
30
|
+
this.versions.set(num, version);
|
|
31
31
|
this.version = this.latestVersion();
|
|
32
32
|
}
|
|
33
|
-
removeVersion(
|
|
34
|
-
if (!this.versions.has(
|
|
33
|
+
removeVersion(num) {
|
|
34
|
+
if (!this.versions.has(num))
|
|
35
35
|
return;
|
|
36
|
-
this.versions.delete(
|
|
36
|
+
this.versions.delete(num);
|
|
37
37
|
this.version = this.latestVersion();
|
|
38
38
|
}
|
|
39
39
|
getReport() {
|
|
40
40
|
return Object.fromEntries(this.reports);
|
|
41
41
|
}
|
|
42
|
-
getVersion(
|
|
43
|
-
return this.versions.get(
|
|
42
|
+
getVersion(num) {
|
|
43
|
+
return this.versions.get(num);
|
|
44
44
|
}
|
|
45
45
|
getVersionLatest() {
|
|
46
46
|
return this.versions.get(this.latestVersion());
|
|
@@ -4,5 +4,5 @@ export declare class RegistryLocal extends Registry {
|
|
|
4
4
|
protected managers: Record<string, ManagerLocal>;
|
|
5
5
|
constructor(name: string, url: string, version: string);
|
|
6
6
|
export(dir: string, ext?: string): boolean;
|
|
7
|
-
scan(ext?: string): void;
|
|
7
|
+
scan(ext?: string, installable?: boolean): void;
|
|
8
8
|
}
|
|
@@ -16,9 +16,9 @@ export class RegistryLocal extends Registry {
|
|
|
16
16
|
saveFile(path.join(dir, `index.${ext}`), this.toJSON());
|
|
17
17
|
return true;
|
|
18
18
|
}
|
|
19
|
-
scan(ext = 'json') {
|
|
19
|
+
scan(ext = 'json', installable = true) {
|
|
20
20
|
for (const [, manager] of Object.entries(this.managers)) {
|
|
21
|
-
manager.scan(ext);
|
|
21
|
+
manager.scan(ext, installable);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
}
|
package/build/helpers/file.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare function fileDelete(filePath: string): boolean | void;
|
|
|
28
28
|
export declare function fileExec(filePath: string): void;
|
|
29
29
|
export declare function fileExists(filePath: string): boolean;
|
|
30
30
|
export declare function fileHash(filePath: string, algorithm?: string): Promise<string>;
|
|
31
|
+
export declare function fileInstall(filePath: string): Buffer;
|
|
31
32
|
export declare function fileMove(filePath: string, newPath: string): void | boolean;
|
|
32
33
|
export declare function filesMove(dirSource: string, dirTarget: string, dirSub: string, formatDir: Record<string, string>): string[];
|
|
33
34
|
export declare function fileOpen(filePath: string): Buffer;
|
package/build/helpers/file.js
CHANGED
|
@@ -14,7 +14,6 @@ import { ZodIssueCode } from 'zod';
|
|
|
14
14
|
import { SystemType } from '../types/SystemType.js';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import sudoPrompt from '@vscode/sudo-prompt';
|
|
17
|
-
import { log } from './utils.js';
|
|
18
17
|
import { getSystem } from './utilsLocal.js';
|
|
19
18
|
export async function archiveExtract(filePath, dirPath) {
|
|
20
19
|
console.log('⎋', dirPath);
|
|
@@ -168,6 +167,36 @@ export async function fileHash(filePath, algorithm = 'sha256') {
|
|
|
168
167
|
await stream.pipeline(input, hash);
|
|
169
168
|
return hash.digest('hex');
|
|
170
169
|
}
|
|
170
|
+
export function fileInstall(filePath) {
|
|
171
|
+
if (process.env.CI)
|
|
172
|
+
return Buffer.from('');
|
|
173
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
174
|
+
let command = null;
|
|
175
|
+
switch (ext) {
|
|
176
|
+
case '.dmg':
|
|
177
|
+
command = `hdiutil attach -nobrowse "${filePath}" && sudo installer -pkg "$(find /Volumes -name '*.pkg' -maxdepth 2 | head -n 1)" -target / && hdiutil detach "$(find /Volumes -type d -maxdepth 1 -mindepth 1 | grep -v 'Macintosh HD')"`;
|
|
178
|
+
break;
|
|
179
|
+
case '.pkg':
|
|
180
|
+
command = `sudo installer -pkg "${filePath}" -target /`;
|
|
181
|
+
break;
|
|
182
|
+
case '.deb':
|
|
183
|
+
command = `sudo dpkg -i "${filePath}" || sudo apt-get install -f -y`;
|
|
184
|
+
break;
|
|
185
|
+
case '.rpm':
|
|
186
|
+
command = `sudo rpm -i --nodigest --nofiledigest --nosignature --force "${filePath}" || sudo dnf install -y "${filePath}" || sudo yum install -y "${filePath}"`;
|
|
187
|
+
break;
|
|
188
|
+
case '.exe':
|
|
189
|
+
command = `start /wait "" "${filePath}" /quiet /norestart`;
|
|
190
|
+
break;
|
|
191
|
+
case '.msi':
|
|
192
|
+
command = `msiexec /i "${filePath}" /quiet /norestart`;
|
|
193
|
+
break;
|
|
194
|
+
default:
|
|
195
|
+
throw new Error(`Unsupported file format: ${ext}`);
|
|
196
|
+
}
|
|
197
|
+
console.log('⎋', command);
|
|
198
|
+
return execSync(command, { stdio: 'inherit' });
|
|
199
|
+
}
|
|
171
200
|
export function fileMove(filePath, newPath) {
|
|
172
201
|
if (fileExists(filePath)) {
|
|
173
202
|
console.log('-', filePath);
|
|
@@ -187,11 +216,11 @@ export function filesMove(dirSource, dirTarget, dirSub, formatDir) {
|
|
|
187
216
|
const fileExt = path.extname(fileSource).slice(1).toLowerCase();
|
|
188
217
|
const fileExtTarget = formatDir[fileExt];
|
|
189
218
|
// If this is not a supported file format, then ignore.
|
|
190
|
-
if (
|
|
191
|
-
return;
|
|
219
|
+
if (fileExtTarget === undefined)
|
|
220
|
+
return console.error(`${fileSource} - ${fileExt} not mapped to a installation folder, skipping.`);
|
|
192
221
|
const fileTarget = path.join(dirTarget, fileExtTarget, dirSub, path.basename(fileSource));
|
|
193
222
|
if (fileExists(fileTarget))
|
|
194
|
-
return;
|
|
223
|
+
return console.error(`${fileSource} - ${fileTarget} already exists, skipping.`);
|
|
195
224
|
dirCreate(path.dirname(fileTarget));
|
|
196
225
|
fileMove(fileSource, fileTarget);
|
|
197
226
|
filesMoved.push(fileTarget);
|
|
@@ -281,13 +310,13 @@ export function runCliAsAdmin(args) {
|
|
|
281
310
|
return new Promise((resolve, reject) => {
|
|
282
311
|
const filename = fileURLToPath(import.meta.url).replace('src/', 'build/');
|
|
283
312
|
const dirPathClean = dirname(filename).replace('app.asar', 'app.asar.unpacked');
|
|
284
|
-
|
|
285
|
-
sudoPrompt.exec(`node "${
|
|
313
|
+
const script = path.join(dirPathClean, 'admin.js');
|
|
314
|
+
sudoPrompt.exec(`node "${script}" ${args}`, { name: 'Open Audio Stack' }, (error, stdout, stderr) => {
|
|
286
315
|
if (stdout) {
|
|
287
|
-
log('runCliAsAdmin', stdout);
|
|
316
|
+
console.log('runCliAsAdmin', stdout);
|
|
288
317
|
}
|
|
289
318
|
if (stderr) {
|
|
290
|
-
log('runCliAsAdmin', stderr);
|
|
319
|
+
console.log('runCliAsAdmin', stderr);
|
|
291
320
|
}
|
|
292
321
|
if (error) {
|
|
293
322
|
reject(error);
|
|
@@ -172,10 +172,10 @@ export declare const PackageVersionValidator: z.ZodObject<{
|
|
|
172
172
|
}>;
|
|
173
173
|
url: z.ZodString;
|
|
174
174
|
}, "strip", z.ZodTypeAny, {
|
|
175
|
+
date: string;
|
|
175
176
|
audio: string;
|
|
176
177
|
author: string;
|
|
177
178
|
changes: string;
|
|
178
|
-
date: string;
|
|
179
179
|
description: string;
|
|
180
180
|
files: {
|
|
181
181
|
type: FileType;
|
|
@@ -197,10 +197,10 @@ export declare const PackageVersionValidator: z.ZodObject<{
|
|
|
197
197
|
name: string;
|
|
198
198
|
tags: string[];
|
|
199
199
|
}, {
|
|
200
|
+
date: string;
|
|
200
201
|
audio: string;
|
|
201
202
|
author: string;
|
|
202
203
|
changes: string;
|
|
203
|
-
date: string;
|
|
204
204
|
description: string;
|
|
205
205
|
files: {
|
|
206
206
|
type: FileType;
|
package/build/helpers/package.js
CHANGED
|
@@ -8,6 +8,7 @@ import { PluginType } from '../types/PluginType.js';
|
|
|
8
8
|
import { PresetType } from '../types/PresetType.js';
|
|
9
9
|
import { ProjectType } from '../types/ProjectType.js';
|
|
10
10
|
import { SystemType } from '../types/SystemType.js';
|
|
11
|
+
import { pathGetExt } from './utils.js';
|
|
11
12
|
export function packageCompatibleFiles(pkg, arch, sys, excludedFormats) {
|
|
12
13
|
return pkg.files.filter((file) => {
|
|
13
14
|
const archMatches = file.architectures.filter(architecture => {
|
|
@@ -16,7 +17,7 @@ export function packageCompatibleFiles(pkg, arch, sys, excludedFormats) {
|
|
|
16
17
|
const sysMatches = file.systems.filter(system => {
|
|
17
18
|
return sys.includes(system.type);
|
|
18
19
|
});
|
|
19
|
-
const formatAllowed = excludedFormats && excludedFormats.includes(file.
|
|
20
|
+
const formatAllowed = excludedFormats && excludedFormats.includes(pathGetExt(file.url)) ? false : true;
|
|
20
21
|
return archMatches.length && sysMatches.length && formatAllowed;
|
|
21
22
|
});
|
|
22
23
|
}
|
|
@@ -88,59 +89,85 @@ export function packageRecommendations(pkgVersion) {
|
|
|
88
89
|
rec: 'should use the flac format',
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
// Files
|
|
93
|
+
if (pkgVersion.files) {
|
|
94
|
+
const supportedArchitectures = {};
|
|
95
|
+
const supportedSystems = {};
|
|
96
|
+
const supportedFileFormats = {};
|
|
97
|
+
pkgVersion.files.forEach(file => {
|
|
98
|
+
file.architectures.forEach(architecture => {
|
|
99
|
+
supportedArchitectures[architecture] = true;
|
|
100
|
+
});
|
|
101
|
+
file.systems.forEach(system => {
|
|
102
|
+
supportedSystems[system.type] = true;
|
|
103
|
+
});
|
|
104
|
+
const ext = pathGetExt(file.url);
|
|
105
|
+
supportedFileFormats[ext] = true;
|
|
106
|
+
packageRecommendationsUrl(file, recs, 'url');
|
|
107
|
+
// Formats which do not support headless installation.
|
|
108
|
+
if (ext === FileFormat.AppImage)
|
|
109
|
+
recs.push({ field: 'url', rec: 'requires manual installation steps, consider .deb and .rpm instead' });
|
|
110
|
+
if (ext === FileFormat.AppleDiskImage)
|
|
111
|
+
recs.push({ field: 'url', rec: 'requires mounting step, consider .pkg instead' });
|
|
112
|
+
if (!Object.values(FileFormat).includes(ext))
|
|
113
|
+
recs.push({ field: 'url', rec: 'not a supported format' });
|
|
114
|
+
// Validate format is a supported installer format
|
|
115
|
+
const installerFormats = [
|
|
116
|
+
FileFormat.AppleDiskImage,
|
|
117
|
+
FileFormat.ApplePackage,
|
|
118
|
+
FileFormat.DebianPackage,
|
|
119
|
+
FileFormat.ExecutableInstaller,
|
|
120
|
+
FileFormat.RedHatPackage,
|
|
121
|
+
FileFormat.WindowsInstaller,
|
|
122
|
+
];
|
|
123
|
+
if (file.type === FileType.Installer && !installerFormats.includes(ext))
|
|
124
|
+
recs.push({ field: 'type', rec: 'should match url field' });
|
|
97
125
|
});
|
|
98
|
-
|
|
99
|
-
|
|
126
|
+
// Architectures
|
|
127
|
+
if (!supportedArchitectures.arm64)
|
|
128
|
+
recs.push({ field: 'architectures', rec: 'should support arm64' });
|
|
129
|
+
if (!supportedArchitectures.x64)
|
|
130
|
+
recs.push({ field: 'architectures', rec: 'should support x64' });
|
|
131
|
+
// Systems
|
|
132
|
+
if (!supportedSystems.linux)
|
|
133
|
+
recs.push({ field: 'systems', rec: 'should support Linux' });
|
|
134
|
+
if (!supportedSystems.mac)
|
|
135
|
+
recs.push({ field: 'systems', rec: 'should support Mac' });
|
|
136
|
+
if (!supportedSystems.win)
|
|
137
|
+
recs.push({ field: 'systems', rec: 'should support Windows' });
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
recs.push({
|
|
141
|
+
field: 'files',
|
|
142
|
+
rec: 'is missing',
|
|
100
143
|
});
|
|
101
|
-
|
|
102
|
-
packageRecommendationsUrl(file, recs, 'url');
|
|
103
|
-
// Formats which do not support headless installation.
|
|
104
|
-
if (file.format === FileFormat.AppImage)
|
|
105
|
-
recs.push({ field: 'format', rec: 'requires manual installation steps, consider .deb and .rpm instead' });
|
|
106
|
-
if (file.format === FileFormat.AppleDiskImage)
|
|
107
|
-
recs.push({ field: 'format', rec: 'requires mounting step, consider .pkg instead' });
|
|
108
|
-
if (!file.url.includes(file.format))
|
|
109
|
-
recs.push({ field: 'format', rec: 'should match url field' });
|
|
110
|
-
// Validate format is a supported installer format
|
|
111
|
-
const installerFormats = [
|
|
112
|
-
FileFormat.AppleDiskImage,
|
|
113
|
-
FileFormat.ApplePackage,
|
|
114
|
-
FileFormat.DebianPackage,
|
|
115
|
-
FileFormat.ExecutableInstaller,
|
|
116
|
-
FileFormat.RedHatPackage,
|
|
117
|
-
FileFormat.WindowsInstaller,
|
|
118
|
-
];
|
|
119
|
-
if (file.type === FileType.Installer && !installerFormats.includes(file.format))
|
|
120
|
-
recs.push({ field: 'type', rec: 'should match format field' });
|
|
121
|
-
});
|
|
122
|
-
// Architectures
|
|
123
|
-
if (!supportedArchitectures.arm64)
|
|
124
|
-
recs.push({ field: 'architectures', rec: 'should support arm64' });
|
|
125
|
-
if (!supportedArchitectures.x64)
|
|
126
|
-
recs.push({ field: 'architectures', rec: 'should support x64' });
|
|
127
|
-
// Systems
|
|
128
|
-
if (!supportedSystems.linux)
|
|
129
|
-
recs.push({ field: 'systems', rec: 'should support Linux' });
|
|
130
|
-
if (!supportedSystems.mac)
|
|
131
|
-
recs.push({ field: 'systems', rec: 'should support Mac' });
|
|
132
|
-
if (!supportedSystems.win)
|
|
133
|
-
recs.push({ field: 'systems', rec: 'should support Windows' });
|
|
144
|
+
}
|
|
134
145
|
// Tags
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
146
|
+
if (pkgVersion.tags) {
|
|
147
|
+
const pluginTags = pkgVersion.tags.map(tag => tag.trim().toLowerCase());
|
|
148
|
+
if (pluginTags.length < 2)
|
|
149
|
+
recs.push({ field: 'tags', rec: 'should have more items' });
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
recs.push({
|
|
153
|
+
field: 'tags',
|
|
154
|
+
rec: 'is missing',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
138
157
|
// Licence
|
|
139
|
-
if (
|
|
140
|
-
|
|
158
|
+
if (pkgVersion.license) {
|
|
159
|
+
if (!Object.values(License).includes(pkgVersion.license)) {
|
|
160
|
+
recs.push({ field: 'license', rec: 'should be from the supported list' });
|
|
161
|
+
}
|
|
162
|
+
else if (pkgVersion.license === License.Other) {
|
|
163
|
+
recs.push({ field: 'license', rec: 'should be more specific' });
|
|
164
|
+
}
|
|
141
165
|
}
|
|
142
|
-
else
|
|
143
|
-
recs.push({
|
|
166
|
+
else {
|
|
167
|
+
recs.push({
|
|
168
|
+
field: 'license',
|
|
169
|
+
rec: 'is missing',
|
|
170
|
+
});
|
|
144
171
|
}
|
|
145
172
|
return recs;
|
|
146
173
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileCreateJson, fileReadJson } from './file.js';
|
|
3
|
+
import { Package } from '../classes/Package.js';
|
|
4
|
+
import { pathGetSlug, pathGetVersion } from './utils.js';
|
|
5
|
+
export function packageLoadFile(filePath) {
|
|
6
|
+
// Read the file path and parse as json.
|
|
7
|
+
if (!filePath)
|
|
8
|
+
filePath = path.join('.', 'index.json');
|
|
9
|
+
const pkgFile = fileReadJson(filePath);
|
|
10
|
+
if (!pkgFile)
|
|
11
|
+
console.error(filePath, `not a valid json file`);
|
|
12
|
+
// Validate package json file structure, fields and values.
|
|
13
|
+
const pkg = new Package(pathGetSlug(filePath));
|
|
14
|
+
pkg.addVersion(pathGetVersion(filePath), pkgFile);
|
|
15
|
+
console.log(JSON.stringify(pkg.getReport()));
|
|
16
|
+
return pkgFile;
|
|
17
|
+
}
|
|
18
|
+
export function packageSaveFile(pkgFile, filePath) {
|
|
19
|
+
// Read the file path and parse as json.
|
|
20
|
+
if (!filePath)
|
|
21
|
+
filePath = path.join('.', 'index.json');
|
|
22
|
+
fileCreateJson(filePath, pkgFile);
|
|
23
|
+
return pkgFile;
|
|
24
|
+
}
|
package/build/helpers/utils.d.ts
CHANGED
|
@@ -12,4 +12,6 @@ export declare function pathGetExt(path: string, sep?: string): string;
|
|
|
12
12
|
export declare function pathGetFilename(path: string, sep?: string): string;
|
|
13
13
|
export declare function pathGetSlug(path: string, sep?: string): string;
|
|
14
14
|
export declare function pathGetVersion(path: string, sep?: string): string;
|
|
15
|
+
export declare function toSlug(val: string): string;
|
|
15
16
|
export declare function isValidSlug(slug: string): boolean;
|
|
17
|
+
export declare function isValidVersion(version: string): boolean;
|
package/build/helpers/utils.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import * as semver from 'semver';
|
|
3
|
+
import slugify from 'slugify';
|
|
2
4
|
let LOGGING_ENABLED = false;
|
|
5
|
+
const URLSAFE_REGEX = /[^\w\s$*_+~.()'"!\-:@/]+/g;
|
|
3
6
|
export function inputGetParts(input) {
|
|
4
7
|
return input.split('@');
|
|
5
8
|
}
|
|
@@ -62,6 +65,10 @@ export function pathGetVersion(path, sep = '/') {
|
|
|
62
65
|
const parts = path.split(sep);
|
|
63
66
|
return parts[parts.length - 2];
|
|
64
67
|
}
|
|
68
|
+
export function toSlug(val) {
|
|
69
|
+
// @ts-expect-error slugify library issue with ESM modules
|
|
70
|
+
return slugify(val, { lower: true, remove: URLSAFE_REGEX });
|
|
71
|
+
}
|
|
65
72
|
export function isValidSlug(slug) {
|
|
66
73
|
let valid = true;
|
|
67
74
|
// Must have exactly one slash.
|
|
@@ -70,5 +77,11 @@ export function isValidSlug(slug) {
|
|
|
70
77
|
// Must be lowercase.
|
|
71
78
|
if (slug !== slug.toLowerCase())
|
|
72
79
|
valid = false;
|
|
80
|
+
// Must pass slugify conversion
|
|
81
|
+
if (slug !== toSlug(slug))
|
|
82
|
+
valid = false;
|
|
73
83
|
return valid;
|
|
74
84
|
}
|
|
85
|
+
export function isValidVersion(version) {
|
|
86
|
+
return semver.valid(version) !== null;
|
|
87
|
+
}
|
package/build/index-browser.js
CHANGED
|
@@ -17,6 +17,7 @@ export * from './helpers/config.js';
|
|
|
17
17
|
// export * from './helpers/configLocal.js';
|
|
18
18
|
// export * from './helpers/file.js';
|
|
19
19
|
export * from './helpers/package.js';
|
|
20
|
+
// export * from './helpers/packageLocal.js';
|
|
20
21
|
export * from './helpers/registry.js';
|
|
21
22
|
export * from './helpers/utils.js';
|
|
22
23
|
// export * from './helpers/utilsLocal.js';
|
package/build/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './helpers/config.js';
|
|
|
10
10
|
export * from './helpers/configLocal.js';
|
|
11
11
|
export * from './helpers/file.js';
|
|
12
12
|
export * from './helpers/package.js';
|
|
13
|
+
export * from './helpers/packageLocal.js';
|
|
13
14
|
export * from './helpers/registry.js';
|
|
14
15
|
export * from './helpers/utils.js';
|
|
15
16
|
export * from './helpers/utilsLocal.js';
|
package/build/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export * from './helpers/config.js';
|
|
|
17
17
|
export * from './helpers/configLocal.js';
|
|
18
18
|
export * from './helpers/file.js';
|
|
19
19
|
export * from './helpers/package.js';
|
|
20
|
+
export * from './helpers/packageLocal.js';
|
|
20
21
|
export * from './helpers/registry.js';
|
|
21
22
|
export * from './helpers/utils.js';
|
|
22
23
|
export * from './helpers/utilsLocal.js';
|
package/build/types/File.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { Architecture } from './Architecture.js';
|
|
2
|
-
import { FileFormat } from './FileFormat.js';
|
|
3
2
|
import { FileType } from './FileType.js';
|
|
4
3
|
import { System } from './System.js';
|
|
5
4
|
export interface FileInterface {
|
|
6
5
|
architectures: Architecture[];
|
|
7
|
-
format: FileFormat;
|
|
8
6
|
sha256: string;
|
|
9
7
|
size: number;
|
|
10
8
|
systems: System[];
|
package/build/types/Preset.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { FileInterface } from './File.js';
|
|
|
2
2
|
import { PackageBase } from './Package.js';
|
|
3
3
|
import { PresetFormat } from './PresetFormat.js';
|
|
4
4
|
import { PresetType } from './PresetType.js';
|
|
5
|
+
import { RegistryType } from './Registry.js';
|
|
5
6
|
export interface PresetInterface extends PackageBase {
|
|
6
7
|
files: PresetFile[];
|
|
7
|
-
|
|
8
|
+
[RegistryType.Plugins]: PresetPlugins;
|
|
8
9
|
type: PresetType;
|
|
9
10
|
}
|
|
10
11
|
export interface PresetFile extends FileInterface {
|
package/build/types/Preset.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import { RegistryType } from './Registry.js';
|
package/build/types/Project.d.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { FileInterface } from './File.js';
|
|
|
2
2
|
import { PackageBase } from './Package.js';
|
|
3
3
|
import { ProjectFormat } from './ProjectFormat.js';
|
|
4
4
|
import { ProjectType } from './ProjectType.js';
|
|
5
|
+
import { RegistryType } from './Registry.js';
|
|
5
6
|
export interface ProjectInterface extends PackageBase {
|
|
6
7
|
files: ProjectFile[];
|
|
7
|
-
|
|
8
|
+
open: string;
|
|
9
|
+
[RegistryType.Plugins]: ProjectPlugins;
|
|
8
10
|
type: ProjectType;
|
|
9
11
|
}
|
|
10
12
|
export interface ProjectFile extends FileInterface {
|
package/build/types/Project.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import { RegistryType } from './Registry.js';
|
|
@@ -13,19 +13,20 @@ export var ProjectFormat;
|
|
|
13
13
|
ProjectFormat["Reaper"] = "rpp";
|
|
14
14
|
ProjectFormat["Sonar"] = "cwp";
|
|
15
15
|
})(ProjectFormat || (ProjectFormat = {}));
|
|
16
|
+
// TODO don't organize projects by type, as this requires knwoing the type in advance for other operations.
|
|
16
17
|
export const projectFormatDir = {
|
|
17
|
-
[ProjectFormat.AbletonLive]: '
|
|
18
|
-
[ProjectFormat.Bitwig]: '
|
|
19
|
-
[ProjectFormat.Cubase]: '
|
|
20
|
-
[ProjectFormat.DAWproject]: '
|
|
21
|
-
[ProjectFormat.FLStudio]: '
|
|
22
|
-
[ProjectFormat.Garageband]: '
|
|
23
|
-
[ProjectFormat.Lmms]: '
|
|
24
|
-
[ProjectFormat.Logic]: '
|
|
25
|
-
[ProjectFormat.Musescore]: '
|
|
26
|
-
[ProjectFormat.ProTools]: '
|
|
27
|
-
[ProjectFormat.Reaper]: '
|
|
28
|
-
[ProjectFormat.Sonar]: '
|
|
18
|
+
[ProjectFormat.AbletonLive]: '',
|
|
19
|
+
[ProjectFormat.Bitwig]: '',
|
|
20
|
+
[ProjectFormat.Cubase]: '',
|
|
21
|
+
[ProjectFormat.DAWproject]: '',
|
|
22
|
+
[ProjectFormat.FLStudio]: '',
|
|
23
|
+
[ProjectFormat.Garageband]: '',
|
|
24
|
+
[ProjectFormat.Lmms]: '',
|
|
25
|
+
[ProjectFormat.Logic]: '',
|
|
26
|
+
[ProjectFormat.Musescore]: '',
|
|
27
|
+
[ProjectFormat.ProTools]: '',
|
|
28
|
+
[ProjectFormat.Reaper]: '',
|
|
29
|
+
[ProjectFormat.Sonar]: '',
|
|
29
30
|
};
|
|
30
31
|
export const projectFormats = [
|
|
31
32
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-audio-stack/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Open-source audio plugin management software",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/index.js",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"check": "npm run format && npm run lint && npm run build",
|
|
17
18
|
"dev": "tsx ./src/index.ts",
|
|
18
19
|
"format": "prettier . --write",
|
|
19
20
|
"lint": "eslint .",
|
|
@@ -58,8 +59,10 @@
|
|
|
58
59
|
"chalk": "^5.3.0",
|
|
59
60
|
"fs-extra": "^11.2.0",
|
|
60
61
|
"glob": "^11.0.0",
|
|
62
|
+
"inquirer": "^12.4.1",
|
|
61
63
|
"js-yaml": "^4.1.0",
|
|
62
64
|
"semver": "^7.6.3",
|
|
65
|
+
"slugify": "^1.6.6",
|
|
63
66
|
"tar": "^7.4.3",
|
|
64
67
|
"zod": "^3.23.8"
|
|
65
68
|
},
|