@open-audio-stack/core 0.1.4 → 0.1.6

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.
@@ -3,7 +3,7 @@ import { Package } from './Package.js';
3
3
  import { Manager } from './Manager.js';
4
4
  import { archiveExtract, dirCreate, dirDelete, dirEmpty, dirRead, fileCreate, fileCreateJson, fileExists, fileHash, fileOpen, fileReadJson, filesMove, isAdmin, runCliAsAdmin, } from '../helpers/file.js';
5
5
  import { pathGetSlug, pathGetVersion } from '../helpers/utils.js';
6
- import { getArchitecture, getSystem, isTests } from '../helpers/utilsLocal.js';
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';
9
9
  import { RegistryType } from '../types/Registry.js';
@@ -12,6 +12,7 @@ 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, SystemType } from '../index-browser.js';
15
16
  export class ManagerLocal extends Manager {
16
17
  typeDir;
17
18
  constructor(type, config) {
@@ -53,8 +54,17 @@ export class ManagerLocal extends Manager {
53
54
  // Create temporary directory to store downloaded files.
54
55
  const dirDownloads = path.join(this.config.get('appDir'), 'downloads', this.type, slug, versionNum);
55
56
  dirCreate(dirDownloads);
57
+ // Not all Linux distributions support all file formats.
58
+ const excludedFormats = [];
59
+ const system = getSystem();
60
+ if (system === SystemType.Linux) {
61
+ if (!(await commandExists('dpkg')))
62
+ excludedFormats.push(FileFormat.DebianPackage);
63
+ if (!(await commandExists('rpm')))
64
+ excludedFormats.push(FileFormat.RedHatPackage);
65
+ }
56
66
  // Filter for compatible files and download.
57
- const files = packageCompatibleFiles(pkgVersion, [getArchitecture()], [getSystem()]);
67
+ const files = packageCompatibleFiles(pkgVersion, [getArchitecture()], [getSystem()], excludedFormats);
58
68
  if (!files.length)
59
69
  return console.error(`Error: No compatible files found for ${slug}`);
60
70
  for (const key in files) {
@@ -37,9 +37,9 @@ export async function archiveExtract(filePath, dirPath) {
37
37
  }
38
38
  }
39
39
  export function dirApp(dirName = 'open-audio-stack') {
40
- if (getSystem() === SystemType.Windows)
40
+ if (getSystem() === SystemType.Win)
41
41
  return process.env.APPDATA || path.join(os.homedir(), dirName);
42
- else if (getSystem() === SystemType.Macintosh)
42
+ else if (getSystem() === SystemType.Mac)
43
43
  return path.join(os.homedir(), 'Library', 'Preferences', dirName);
44
44
  return path.join(os.homedir(), '.local', 'share', dirName);
45
45
  }
@@ -83,9 +83,9 @@ export function dirOpen(dir) {
83
83
  let command = '';
84
84
  if (process.env.CI)
85
85
  return Buffer.from('');
86
- if (getSystem() === SystemType.Windows)
86
+ if (getSystem() === SystemType.Win)
87
87
  command = 'start ""';
88
- else if (getSystem() === SystemType.Macintosh)
88
+ else if (getSystem() === SystemType.Mac)
89
89
  command = 'open';
90
90
  else
91
91
  command = 'xdg-open';
@@ -98,25 +98,25 @@ export function dirPackage(pkg) {
98
98
  return path.join(...parts);
99
99
  }
100
100
  export function dirPlugins() {
101
- if (getSystem() === SystemType.Windows)
101
+ if (getSystem() === SystemType.Win)
102
102
  return path.join('Program Files', 'Common Files');
103
- else if (getSystem() === SystemType.Macintosh)
103
+ else if (getSystem() === SystemType.Mac)
104
104
  return path.join(os.homedir(), 'Library', 'Audio', 'Plug-ins');
105
105
  return path.join('usr', 'local', 'lib');
106
106
  }
107
107
  export function dirPresets() {
108
- if (getSystem() === SystemType.Windows)
108
+ if (getSystem() === SystemType.Win)
109
109
  return path.join(os.homedir(), 'Documents', 'VST3 Presets');
110
- else if (getSystem() === SystemType.Macintosh)
110
+ else if (getSystem() === SystemType.Mac)
111
111
  return path.join(os.homedir(), 'Library', 'Audio', 'Presets');
112
112
  return path.join(os.homedir(), '.vst3', 'presets');
113
113
  }
114
114
  export function dirProjects() {
115
115
  // Windows throws permissions errors if you scan hidden folders
116
116
  // Therefore set to a more specific path than Documents
117
- if (getSystem() === SystemType.Windows)
117
+ if (getSystem() === SystemType.Win)
118
118
  return path.join(os.homedir(), 'Documents', 'Audio');
119
- else if (getSystem() === SystemType.Macintosh)
119
+ else if (getSystem() === SystemType.Mac)
120
120
  return path.join(os.homedir(), 'Documents', 'Audio');
121
121
  return path.join(os.homedir(), 'Documents', 'Audio');
122
122
  }
@@ -124,7 +124,7 @@ export function dirRead(dir, options) {
124
124
  console.log('⌕', dir);
125
125
  // Glob now expects forward slashes on Windows
126
126
  // Convert backslashes from path.join() to forwardslashes
127
- if (getSystem() === SystemType.Windows) {
127
+ if (getSystem() === SystemType.Win) {
128
128
  dir = dir.replace(/\\/g, '/');
129
129
  }
130
130
  return globSync(dir, options);
@@ -199,10 +199,10 @@ export function fileOpen(filePath) {
199
199
  let command = '';
200
200
  if (process.env.CI)
201
201
  return Buffer.from('');
202
- if (getSystem() === SystemType.Windows)
203
- command = 'open';
204
- else if (getSystem() === SystemType.Macintosh)
202
+ if (getSystem() === SystemType.Win)
205
203
  command = 'start ""';
204
+ else if (getSystem() === SystemType.Mac)
205
+ command = 'open';
206
206
  else
207
207
  command = 'xdg-open';
208
208
  console.log('⎋', `${command} "${filePath}"`);
@@ -268,10 +268,10 @@ export async function fileValidateMetadata(filePath, fileMetadata) {
268
268
  return errors;
269
269
  }
270
270
  export function getPlatform() {
271
- if (getSystem() === SystemType.Windows)
272
- return SystemType.Windows;
273
- else if (getSystem() === SystemType.Macintosh)
274
- return SystemType.Macintosh;
271
+ if (getSystem() === SystemType.Win)
272
+ return SystemType.Win;
273
+ else if (getSystem() === SystemType.Mac)
274
+ return SystemType.Mac;
275
275
  return SystemType.Linux;
276
276
  }
277
277
  export function runCliAsAdmin(args) {
@@ -10,8 +10,9 @@ import { PresetType } from '../types/PresetType.js';
10
10
  import { ProjectFile } from '../types/Project.js';
11
11
  import { ProjectType } from '../types/ProjectType.js';
12
12
  import { SystemType } from '../types/SystemType.js';
13
- import { PackageInterface, PackageValidationRec, PackageVersion } from '../types/Package.js';
14
- export declare function packageCompatibleFiles(pkg: PackageVersion, arch: Architecture[], sys: SystemType[]): (PluginFile | PresetFile | ProjectFile)[];
13
+ import { PackageFileMap, PackageInterface, PackageValidationRec, PackageVersion } from '../types/Package.js';
14
+ export declare function packageCompatibleFiles(pkg: PackageVersion, arch: Architecture[], sys: SystemType[], excludedFormats?: FileFormat[]): (PluginFile | PresetFile | ProjectFile)[];
15
+ export declare function packageFileMap(pkgVersion: PackageVersion): PackageFileMap;
15
16
  export declare function packageVersionLatest(pkg: PackageInterface): string;
16
17
  export declare const PackageSystemValidator: z.ZodObject<{
17
18
  max: z.ZodOptional<z.ZodNumber>;
@@ -8,7 +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
- export function packageCompatibleFiles(pkg, arch, sys) {
11
+ export function packageCompatibleFiles(pkg, arch, sys, excludedFormats) {
12
12
  return pkg.files.filter((file) => {
13
13
  const archMatches = file.architectures.filter(architecture => {
14
14
  return arch.includes(architecture);
@@ -16,9 +16,21 @@ export function packageCompatibleFiles(pkg, arch, sys) {
16
16
  const sysMatches = file.systems.filter(system => {
17
17
  return sys.includes(system.type);
18
18
  });
19
- return archMatches.length && sysMatches.length;
19
+ const formatAllowed = excludedFormats && excludedFormats.includes(file.format) ? false : true;
20
+ return archMatches.length && sysMatches.length && formatAllowed;
20
21
  });
21
22
  }
23
+ export function packageFileMap(pkgVersion) {
24
+ return pkgVersion.files.reduce((result, file) => {
25
+ file.systems.forEach(system => {
26
+ if (!result[system.type]) {
27
+ result[system.type] = [];
28
+ }
29
+ result[system.type].push(file);
30
+ });
31
+ return result;
32
+ }, {});
33
+ }
22
34
  export function packageVersionLatest(pkg) {
23
35
  return Array.from(Object.keys(pkg.versions)).sort(semver.rcompare)[0] || '0.0.0';
24
36
  }
@@ -85,6 +97,24 @@ export function packageRecommendations(pkgVersion) {
85
97
  });
86
98
  supportedFileFormats[file.format] = true;
87
99
  packageRecommendationsUrl(file, recs, 'url');
100
+ // Formats which do not support headless installation.
101
+ if (file.format === FileFormat.AppImage)
102
+ recs.push({ field: 'format', rec: 'requires manual installation steps, consider .deb and .rpm instead' });
103
+ if (file.format === FileFormat.AppleDiskImage)
104
+ recs.push({ field: 'format', rec: 'requires mounting step, consider .pkg instead' });
105
+ if (!file.url.includes(file.format))
106
+ recs.push({ field: 'format', rec: 'should match url field' });
107
+ // Validate format is a supported installer format
108
+ const installerFormats = [
109
+ FileFormat.AppleDiskImage,
110
+ FileFormat.ApplePackage,
111
+ FileFormat.DebianPackage,
112
+ FileFormat.ExecutableInstaller,
113
+ FileFormat.RedHatPackage,
114
+ FileFormat.WindowsInstaller,
115
+ ];
116
+ if (file.type === FileType.Installer && !installerFormats.includes(file.format))
117
+ recs.push({ field: 'type', rec: 'should match format field' });
88
118
  });
89
119
  // Architectures
90
120
  if (!supportedArchitectures.arm64)
@@ -98,17 +128,6 @@ export function packageRecommendations(pkgVersion) {
98
128
  recs.push({ field: 'systems', rec: 'should support Mac' });
99
129
  if (!supportedSystems.win)
100
130
  recs.push({ field: 'systems', rec: 'should support Windows' });
101
- // Formats
102
- if (supportedFileFormats.deb)
103
- recs.push({
104
- field: 'format',
105
- rec: 'should support all Linux distributions, consider using AppImage or Tarball instead',
106
- });
107
- if (supportedFileFormats.rpm)
108
- recs.push({
109
- field: 'format',
110
- rec: 'should support all Linux distributions, consider using AppImage or Tarball instead',
111
- });
112
131
  // Tags
113
132
  const pluginTags = pkgVersion.tags.map(tag => tag.trim().toLowerCase());
114
133
  if (pluginTags.length < 2)
@@ -3,3 +3,4 @@ import { Architecture } from '../types/Architecture.js';
3
3
  export declare function getArchitecture(): Architecture;
4
4
  export declare function getSystem(): SystemType;
5
5
  export declare function isTests(): boolean;
6
+ export declare function commandExists(cmd: string): Promise<boolean>;
@@ -1,3 +1,4 @@
1
+ import { exec } from 'child_process';
1
2
  import { SystemType } from '../types/SystemType.js';
2
3
  import { Architecture } from '../types/Architecture.js';
3
4
  export function getArchitecture() {
@@ -11,9 +12,9 @@ export function getArchitecture() {
11
12
  }
12
13
  export function getSystem() {
13
14
  if (process.platform === 'win32')
14
- return SystemType.Windows;
15
+ return SystemType.Win;
15
16
  else if (process.platform === 'darwin')
16
- return SystemType.Macintosh;
17
+ return SystemType.Mac;
17
18
  return SystemType.Linux;
18
19
  }
19
20
  export function isTests() {
@@ -21,3 +22,10 @@ export function isTests() {
21
22
  const vitest = process.env.VITEST_WORKER_ID !== undefined;
22
23
  return jest || vitest;
23
24
  }
25
+ export function commandExists(cmd) {
26
+ return new Promise(resolve => {
27
+ exec(`command -v ${cmd}`, (error, stdout) => {
28
+ resolve(Boolean(stdout.trim()) && !error);
29
+ });
30
+ });
31
+ }
@@ -1,14 +1,14 @@
1
1
  export declare enum FileFormat {
2
- AppImage = "appimage",
3
- AppleDiskImage = "dmg",
4
- ApplePackage = "pkg",
5
- DebianPackage = "deb",
6
- ExecutableInstaller = "exe",
7
- RedHatPackage = "rpm",
8
- Tarball = "tar.gz",
9
- TarballLegacy = "tgz",
10
- WindowsInstaller = "msi",
11
- Zip = "zip",
2
+ AppImage = "appimage",// No headless automatic installation, avoid using
3
+ AppleDiskImage = "dmg",// Has to be mounted to access installer inside
4
+ ApplePackage = "pkg",// Installer
5
+ DebianPackage = "deb",// Installer
6
+ ExecutableInstaller = "exe",// Installer
7
+ RedHatPackage = "rpm",// Installer
8
+ Tarball = "tar.gz",// Archive
9
+ TarballLegacy = "tgz",// Archive
10
+ WindowsInstaller = "msi",// Installer
11
+ Zip = "zip",// Archive
12
12
  Zip7 = "7z"
13
13
  }
14
14
  export interface FileFormatOption {
@@ -1,3 +1,6 @@
1
+ // File formats have different advantages for users
2
+ // but some do not support headless automated installation
3
+ // which is required for a fully automated package manager.
1
4
  export var FileFormat;
2
5
  (function (FileFormat) {
3
6
  FileFormat["AppImage"] = "appimage";
@@ -1,6 +1,6 @@
1
- import { PluginInterface } from './Plugin.js';
2
- import { PresetInterface } from './Preset.js';
3
- import { ProjectInterface } from './Project.js';
1
+ import { PluginFile, PluginFileMap, PluginInterface } from './Plugin.js';
2
+ import { PresetFile, PresetFileMap, PresetInterface } from './Preset.js';
3
+ import { ProjectFile, ProjectFileMap, ProjectInterface } from './Project.js';
4
4
  import { License } from './License.js';
5
5
  export interface PackageInterface {
6
6
  slug: string;
@@ -24,6 +24,8 @@ export interface PackageBase {
24
24
  url: string;
25
25
  }
26
26
  export type PackageVersion = PluginInterface | PresetInterface | ProjectInterface;
27
+ export type PackageFile = PluginFile | PresetFile | ProjectFile;
28
+ export type PackageFileMap = PluginFileMap | PresetFileMap | ProjectFileMap;
27
29
  export interface PackageValidationField {
28
30
  name: string;
29
31
  type: string;
@@ -9,3 +9,6 @@ export interface PluginInterface extends PackageBase {
9
9
  export interface PluginFile extends FileInterface {
10
10
  contains: PluginFormat[];
11
11
  }
12
+ export interface PluginFileMap {
13
+ [key: string]: PluginFile[];
14
+ }
@@ -10,6 +10,9 @@ export interface PresetInterface extends PackageBase {
10
10
  export interface PresetFile extends FileInterface {
11
11
  contains: PresetFormat[];
12
12
  }
13
+ export interface PresetFileMap {
14
+ [key: string]: PresetFile[];
15
+ }
13
16
  export interface PresetPlugins {
14
17
  [slug: string]: string;
15
18
  }
@@ -10,6 +10,9 @@ export interface ProjectInterface extends PackageBase {
10
10
  export interface ProjectFile extends FileInterface {
11
11
  contains: ProjectFormat[];
12
12
  }
13
+ export interface ProjectFileMap {
14
+ [key: string]: ProjectFile[];
15
+ }
13
16
  export interface ProjectPlugins {
14
17
  [slug: string]: string;
15
18
  }
@@ -1,7 +1,7 @@
1
1
  export declare enum SystemType {
2
- Macintosh = "mac",
2
+ Mac = "mac",
3
3
  Linux = "linux",
4
- Windows = "win"
4
+ Win = "win"
5
5
  }
6
6
  export interface SystemTypeOption {
7
7
  description: string;
@@ -1,8 +1,8 @@
1
1
  export var SystemType;
2
2
  (function (SystemType) {
3
- SystemType["Macintosh"] = "mac";
3
+ SystemType["Mac"] = "mac";
4
4
  SystemType["Linux"] = "linux";
5
- SystemType["Windows"] = "win";
5
+ SystemType["Win"] = "win";
6
6
  })(SystemType || (SystemType = {}));
7
7
  export const systemTypes = [
8
8
  {
@@ -12,12 +12,12 @@ export const systemTypes = [
12
12
  },
13
13
  {
14
14
  description: 'Operating system designed and sold by Apple, and is known for its ease of use.',
15
- value: SystemType.Macintosh,
16
- name: 'Macintosh',
15
+ value: SystemType.Mac,
16
+ name: 'Mac',
17
17
  },
18
18
  {
19
19
  description: 'Most popular home operating system, preloaded on most new personal computers.',
20
- value: SystemType.Windows,
20
+ value: SystemType.Win,
21
21
  name: 'Windows',
22
22
  },
23
23
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-audio-stack/core",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Open-source audio plugin management software",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",