@mudah-cli/core 0.1.0 → 0.3.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/application.d.ts +33 -2
- package/dist/application.js +47 -12
- package/dist/application.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins.d.ts +58 -0
- package/dist/plugins.js +175 -0
- package/dist/plugins.js.map +1 -0
- package/dist/updates.d.ts +82 -0
- package/dist/updates.js +225 -0
- package/dist/updates.js.map +1 -0
- package/package.json +3 -3
package/dist/application.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ConfigRepository } from '@mudah-cli/config';
|
|
|
2
2
|
import { Container, type Abstract, type Constructor } from '@mudah-cli/container';
|
|
3
3
|
import { EventBus } from './events.js';
|
|
4
4
|
import { type MudahManifest } from './manifest.js';
|
|
5
|
+
import { type PluginDiscoveryOptions, type PluginInfo } from './plugins.js';
|
|
5
6
|
import { ServiceProvider } from './service-provider.js';
|
|
6
7
|
export type ProviderClass = new (app: Application) => ServiceProvider;
|
|
7
8
|
export interface LazyProviderOptions {
|
|
@@ -12,6 +13,25 @@ export interface LazyProviderOptions {
|
|
|
12
13
|
/** Boot this provider when the predicate returns true (checked on demand). */
|
|
13
14
|
bootWhen?: (app: Application) => boolean;
|
|
14
15
|
}
|
|
16
|
+
export interface BootOptions {
|
|
17
|
+
/** Collect per-hook timings (default: honors `MUDAH_BOOT_PROFILE=1`). */
|
|
18
|
+
profile?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** One provider hook, timed. */
|
|
21
|
+
export interface ProviderTiming {
|
|
22
|
+
/** Provider class name. */
|
|
23
|
+
readonly provider: string;
|
|
24
|
+
/** Which phase of the two-phase boot. */
|
|
25
|
+
readonly hook: 'register' | 'boot';
|
|
26
|
+
readonly durationMs: number;
|
|
27
|
+
}
|
|
28
|
+
/** Provider boot timings, for `--profile`. */
|
|
29
|
+
export interface BootProfile {
|
|
30
|
+
/** Wall-clock time across both phases. */
|
|
31
|
+
readonly totalMs: number;
|
|
32
|
+
/** Timings in execution order: all `register` hooks, then all `boot` hooks. */
|
|
33
|
+
readonly providers: readonly ProviderTiming[];
|
|
34
|
+
}
|
|
15
35
|
/** A command module: a module whose default export is a command class. */
|
|
16
36
|
export interface CommandModule {
|
|
17
37
|
default: CommandClass;
|
|
@@ -53,9 +73,11 @@ export declare class Application extends Container {
|
|
|
53
73
|
* 1. `register()` — container bindings
|
|
54
74
|
* 2. `boot()` — everything else
|
|
55
75
|
*
|
|
56
|
-
*
|
|
76
|
+
* Returns per-hook timings when profiling is on (`{ profile: true }`, or
|
|
77
|
+
* `MUDAH_BOOT_PROFILE=1`). The env var also prints the old one-line report
|
|
78
|
+
* to stderr for anyone scripting against it.
|
|
57
79
|
*/
|
|
58
|
-
boot(): Promise<
|
|
80
|
+
boot(options?: BootOptions): Promise<BootProfile | undefined>;
|
|
59
81
|
/** Boot lazy providers that declared an interest in `command`. */
|
|
60
82
|
bootLazyForCommand(command: string): Promise<void>;
|
|
61
83
|
/** Boot lazy providers that declared an interest in `abstract`. */
|
|
@@ -68,6 +90,15 @@ export declare class Application extends Container {
|
|
|
68
90
|
* (sorted for determinism). Exports whose name ends in `Provider` win.
|
|
69
91
|
*/
|
|
70
92
|
discoverProviders(dir?: string): Promise<this>;
|
|
93
|
+
/**
|
|
94
|
+
* Discover plugins among installed packages and register what they
|
|
95
|
+
* provide. A plugin is any dependency declaring the `mudah-plugin`
|
|
96
|
+
* keyword; see {@link discoverPlugins}.
|
|
97
|
+
*
|
|
98
|
+
* Broken plugins are skipped rather than fatal — a third-party package
|
|
99
|
+
* must never be able to take down the host app at boot.
|
|
100
|
+
*/
|
|
101
|
+
discoverPlugins(options?: PluginDiscoveryOptions): Promise<PluginInfo[]>;
|
|
71
102
|
/**
|
|
72
103
|
* Discover command modules in the given directory (sorted). A command
|
|
73
104
|
* module is any file whose default export is a class with `handle`.
|
package/dist/application.js
CHANGED
|
@@ -5,7 +5,12 @@ import { ConfigRepository } from '@mudah-cli/config';
|
|
|
5
5
|
import { Container, isClassLike } from '@mudah-cli/container';
|
|
6
6
|
import { EventBus } from './events.js';
|
|
7
7
|
import { loadManifest } from './manifest.js';
|
|
8
|
+
import { discoverPlugins } from './plugins.js';
|
|
8
9
|
import { ServiceProvider } from './service-provider.js';
|
|
10
|
+
/** Round to whole milliseconds — sub-ms noise tells us nothing here. */
|
|
11
|
+
function round(ms) {
|
|
12
|
+
return Math.round(ms);
|
|
13
|
+
}
|
|
9
14
|
/** True when the value is a class with a `handle` prototype method. */
|
|
10
15
|
export function isCommandExport(value) {
|
|
11
16
|
if (typeof value !== 'function' || !isClassLike(value))
|
|
@@ -58,33 +63,47 @@ export class Application extends Container {
|
|
|
58
63
|
* 1. `register()` — container bindings
|
|
59
64
|
* 2. `boot()` — everything else
|
|
60
65
|
*
|
|
61
|
-
*
|
|
66
|
+
* Returns per-hook timings when profiling is on (`{ profile: true }`, or
|
|
67
|
+
* `MUDAH_BOOT_PROFILE=1`). The env var also prints the old one-line report
|
|
68
|
+
* to stderr for anyone scripting against it.
|
|
62
69
|
*/
|
|
63
|
-
async boot() {
|
|
70
|
+
async boot(options = {}) {
|
|
71
|
+
// Boot is idempotent: providers already ran, so there is nothing to time.
|
|
64
72
|
if (this.booted)
|
|
65
|
-
return;
|
|
66
|
-
const profile = process.env['MUDAH_BOOT_PROFILE'] === '1';
|
|
67
|
-
const
|
|
73
|
+
return undefined;
|
|
74
|
+
const profile = options.profile ?? process.env['MUDAH_BOOT_PROFILE'] === '1';
|
|
75
|
+
const hooks = [];
|
|
68
76
|
const started = performance.now();
|
|
69
77
|
for (const Provider of this.providers) {
|
|
70
78
|
const provider = new Provider(this);
|
|
71
79
|
const t0 = profile ? performance.now() : 0;
|
|
72
80
|
await provider.register?.();
|
|
73
|
-
if (profile)
|
|
74
|
-
|
|
81
|
+
if (profile) {
|
|
82
|
+
hooks.push({ provider: Provider.name, hook: 'register', durationMs: round(performance.now() - t0) });
|
|
83
|
+
}
|
|
75
84
|
}
|
|
76
85
|
for (const Provider of this.providers) {
|
|
77
86
|
const provider = new Provider(this);
|
|
78
87
|
const t0 = profile ? performance.now() : 0;
|
|
79
88
|
await provider.boot?.();
|
|
80
|
-
if (profile)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (profile) {
|
|
84
|
-
console.error(`[boot-profile] total=${Math.round(performance.now() - started)}ms ${timings.join(' ')}`);
|
|
89
|
+
if (profile) {
|
|
90
|
+
hooks.push({ provider: Provider.name, hook: 'boot', durationMs: round(performance.now() - t0) });
|
|
91
|
+
}
|
|
85
92
|
}
|
|
86
93
|
this.booted = true;
|
|
87
94
|
await this.events().emit('app.booted', { app: this });
|
|
95
|
+
if (!profile)
|
|
96
|
+
return undefined;
|
|
97
|
+
const result = {
|
|
98
|
+
totalMs: round(performance.now() - started),
|
|
99
|
+
providers: hooks,
|
|
100
|
+
};
|
|
101
|
+
if (process.env['MUDAH_BOOT_PROFILE'] === '1') {
|
|
102
|
+
console.error(`[boot-profile] total=${result.totalMs}ms ${hooks
|
|
103
|
+
.map((h) => `${h.provider}.${h.hook}=${h.durationMs}ms`)
|
|
104
|
+
.join(' ')}`);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
88
107
|
}
|
|
89
108
|
/** Boot lazy providers that declared an interest in `command`. */
|
|
90
109
|
async bootLazyForCommand(command) {
|
|
@@ -140,6 +159,22 @@ export class Application extends Container {
|
|
|
140
159
|
}
|
|
141
160
|
return this;
|
|
142
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Discover plugins among installed packages and register what they
|
|
164
|
+
* provide. A plugin is any dependency declaring the `mudah-plugin`
|
|
165
|
+
* keyword; see {@link discoverPlugins}.
|
|
166
|
+
*
|
|
167
|
+
* Broken plugins are skipped rather than fatal — a third-party package
|
|
168
|
+
* must never be able to take down the host app at boot.
|
|
169
|
+
*/
|
|
170
|
+
async discoverPlugins(options = {}) {
|
|
171
|
+
const plugins = await discoverPlugins(this.basePath, options);
|
|
172
|
+
for (const plugin of plugins) {
|
|
173
|
+
for (const provider of plugin.providers)
|
|
174
|
+
this.register(provider);
|
|
175
|
+
}
|
|
176
|
+
return plugins;
|
|
177
|
+
}
|
|
143
178
|
/**
|
|
144
179
|
* Discover command modules in the given directory (sorted). A command
|
|
145
180
|
* module is any file whose default export is a class with `handle`.
|
package/dist/application.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAmC,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAsB,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { Application, isCommandExport, type CommandClass, type CommandModule, type CommandShape, type LazyProviderOptions, type ProviderClass, } from './application.js';
|
|
1
|
+
export { Application, isCommandExport, type CommandClass, type BootOptions, type BootProfile, type CommandModule, type CommandShape, type LazyProviderOptions, type ProviderClass, type ProviderTiming, } from './application.js';
|
|
2
2
|
export { EventBus, type AppEvents, type EventHandler } from './events.js';
|
|
3
3
|
export { CommandCancelled, ExitError, UsageError } from './exceptions.js';
|
|
4
4
|
export { loadManifest, MudahManifestError, type MudahManifest, type MudahUiOptions } from './manifest.js';
|
|
5
5
|
export { ServiceProvider } from './service-provider.js';
|
|
6
|
+
export { discoverPlugins, findPluginPackages, loadPlugin, type PluginDiscoveryOptions, type PluginInfo, } from './plugins.js';
|
|
7
|
+
export { checkForUpdate, clearUpdateCache, compareSemVer, defaultCacheDir, formatUpdateNudge, isNewer, parseSemVer, updateKind, type SemVer, type UpdateCheckOptions, type UpdateCheckResult, type UpdateInfo, } from './updates.js';
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,6 @@ export { EventBus } from './events.js';
|
|
|
3
3
|
export { CommandCancelled, ExitError, UsageError } from './exceptions.js';
|
|
4
4
|
export { loadManifest, MudahManifestError } from './manifest.js';
|
|
5
5
|
export { ServiceProvider } from './service-provider.js';
|
|
6
|
+
export { discoverPlugins, findPluginPackages, loadPlugin, } from './plugins.js';
|
|
7
|
+
export { checkForUpdate, clearUpdateCache, compareSemVer, defaultCacheDir, formatUpdateNudge, isNewer, parseSemVer, updateKind, } from './updates.js';
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,GAShB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAqC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAA2C,MAAM,eAAe,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,UAAU,GAGX,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,WAAW,EACX,UAAU,GAKX,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type CommandModule, type ProviderClass } from './application.js';
|
|
2
|
+
/**
|
|
3
|
+
* Plugin discovery: register providers and commands shipped by other
|
|
4
|
+
* packages rather than by the app itself.
|
|
5
|
+
*
|
|
6
|
+
* A plugin is any installed package that declares the `mudah-plugin`
|
|
7
|
+
* keyword in its `package.json`. Its entry point may export:
|
|
8
|
+
*
|
|
9
|
+
* - a `default` export that is a provider class, or
|
|
10
|
+
* - a named export whose name ends in `Provider`, or
|
|
11
|
+
* - a named `providers` array, and/or
|
|
12
|
+
* - a named `commands` array of command modules.
|
|
13
|
+
*
|
|
14
|
+
* Discovery is best-effort: a plugin that fails to import is reported and
|
|
15
|
+
* skipped rather than taking the host app down with it.
|
|
16
|
+
*/
|
|
17
|
+
export interface PluginInfo {
|
|
18
|
+
/** Package name, as installed. */
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** Provider classes found in the package. */
|
|
21
|
+
readonly providers: readonly ProviderClass[];
|
|
22
|
+
/** Command modules found in the package. */
|
|
23
|
+
readonly commands: readonly CommandModule[];
|
|
24
|
+
}
|
|
25
|
+
export interface PluginDiscoveryOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Package keyword that marks a plugin. Default `mudah-plugin`.
|
|
28
|
+
*/
|
|
29
|
+
keyword?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Extra package names to treat as plugins regardless of keyword. Handy for
|
|
32
|
+
* bundled apps that can't rely on a published manifest.
|
|
33
|
+
*/
|
|
34
|
+
include?: readonly string[];
|
|
35
|
+
/** Package names to skip even if they declare the keyword. */
|
|
36
|
+
exclude?: readonly string[];
|
|
37
|
+
/** Resolve a package name to a module URL. Injectable for tests. */
|
|
38
|
+
resolve?: (name: string, from: string) => string | Promise<string>;
|
|
39
|
+
/** Read a package's manifest. Injectable for tests. */
|
|
40
|
+
readPackage?: (path: string) => Promise<unknown>;
|
|
41
|
+
/** Import a resolved module URL. Injectable for tests. */
|
|
42
|
+
importModule?: (url: string) => Promise<Record<string, unknown>>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Find installed plugin packages: dependencies of `basePath` that declare
|
|
46
|
+
* the keyword (or are listed in `include`), minus anything excluded.
|
|
47
|
+
*/
|
|
48
|
+
export declare function findPluginPackages(basePath: string, options?: PluginDiscoveryOptions): Promise<string[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Load a plugin package and pull the providers and commands out of it.
|
|
51
|
+
* Returns empty arrays (not an error) when the package exports neither.
|
|
52
|
+
*/
|
|
53
|
+
export declare function loadPlugin(name: string, basePath: string, options?: PluginDiscoveryOptions): Promise<PluginInfo>;
|
|
54
|
+
/**
|
|
55
|
+
* Discover every installed plugin and collect what it provides. Individual
|
|
56
|
+
* failures are skipped: one broken plugin must not break the host app.
|
|
57
|
+
*/
|
|
58
|
+
export declare function discoverPlugins(basePath: string, options?: PluginDiscoveryOptions): Promise<PluginInfo[]>;
|
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { isCommandExport } from './application.js';
|
|
6
|
+
const DEFAULT_KEYWORD = 'mudah-plugin';
|
|
7
|
+
/**
|
|
8
|
+
* Find installed plugin packages: dependencies of `basePath` that declare
|
|
9
|
+
* the keyword (or are listed in `include`), minus anything excluded.
|
|
10
|
+
*/
|
|
11
|
+
export async function findPluginPackages(basePath, options = {}) {
|
|
12
|
+
const keyword = options.keyword ?? DEFAULT_KEYWORD;
|
|
13
|
+
const exclude = new Set(options.exclude ?? []);
|
|
14
|
+
const readPackage = options.readPackage ?? readJson;
|
|
15
|
+
const resolve = options.resolve ?? defaultResolve;
|
|
16
|
+
// No manifest means no dependency graph to scan — an app run straight from
|
|
17
|
+
// a directory (tests, single-file tools) simply has no plugins.
|
|
18
|
+
let manifest;
|
|
19
|
+
try {
|
|
20
|
+
manifest = (await readPackage(join(basePath, 'package.json')));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return explicitlyIncludedNames(options.include, exclude);
|
|
24
|
+
}
|
|
25
|
+
const candidates = [
|
|
26
|
+
...Object.keys(manifest.dependencies ?? {}),
|
|
27
|
+
...Object.keys(manifest.peerDependencies ?? {}),
|
|
28
|
+
...(options.include ?? []),
|
|
29
|
+
];
|
|
30
|
+
// devDependencies are deliberately excluded: a plugin used only for
|
|
31
|
+
// development shouldn't ship in a production install.
|
|
32
|
+
const unique = [...new Set(candidates)].filter((name) => !exclude.has(name)).sort();
|
|
33
|
+
const plugins = [];
|
|
34
|
+
for (const name of unique) {
|
|
35
|
+
if (explicitlyIncluded(name, options.include)) {
|
|
36
|
+
plugins.push(name);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const entry = await readPluginManifest(basePath, name, readPackage, resolve);
|
|
40
|
+
if (entry !== null && declaresKeyword(entry, keyword))
|
|
41
|
+
plugins.push(name);
|
|
42
|
+
}
|
|
43
|
+
return plugins;
|
|
44
|
+
}
|
|
45
|
+
function explicitlyIncluded(name, include) {
|
|
46
|
+
return include !== undefined && include.includes(name);
|
|
47
|
+
}
|
|
48
|
+
/** Explicit includes still apply when there is no manifest to scan. */
|
|
49
|
+
function explicitlyIncludedNames(include, exclude) {
|
|
50
|
+
return (include ?? []).filter((name) => !exclude.has(name));
|
|
51
|
+
}
|
|
52
|
+
async function readPluginManifest(basePath, name, readPackage, resolve) {
|
|
53
|
+
try {
|
|
54
|
+
// Resolve to the package's entry file, then find its manifest.
|
|
55
|
+
const entryUrl = await resolve(name, basePath);
|
|
56
|
+
const entryPath = entryUrl.startsWith('file:')
|
|
57
|
+
? new URL(entryUrl).pathname
|
|
58
|
+
: entryUrl;
|
|
59
|
+
const packageRoot = await packageRootFromEntry(entryPath, name, readPackage);
|
|
60
|
+
if (packageRoot === null)
|
|
61
|
+
return null;
|
|
62
|
+
return (await readPackage(join(packageRoot, 'package.json')));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Prefer the `node_modules/<name>/` segment when it is present. Workspace
|
|
70
|
+
* and `file:` installs often resolve to a real path that no longer contains
|
|
71
|
+
* the package name, so fall back to walking up until a package.json exists.
|
|
72
|
+
*/
|
|
73
|
+
async function packageRootFromEntry(entryPath, name, readPackage) {
|
|
74
|
+
const marker = `/${name}/`;
|
|
75
|
+
const named = entryPath.lastIndexOf(marker);
|
|
76
|
+
if (named !== -1)
|
|
77
|
+
return entryPath.slice(0, named + marker.length - 1);
|
|
78
|
+
let dir = dirname(entryPath);
|
|
79
|
+
for (let i = 0; i < 8; i++) {
|
|
80
|
+
try {
|
|
81
|
+
await readPackage(join(dir, 'package.json'));
|
|
82
|
+
return dir;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
const parent = dirname(dir);
|
|
86
|
+
if (parent === dir)
|
|
87
|
+
return null;
|
|
88
|
+
dir = parent;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function declaresKeyword(pkg, keyword) {
|
|
94
|
+
return Array.isArray(pkg.keywords) && pkg.keywords.includes(keyword);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Load a plugin package and pull the providers and commands out of it.
|
|
98
|
+
* Returns empty arrays (not an error) when the package exports neither.
|
|
99
|
+
*/
|
|
100
|
+
export async function loadPlugin(name, basePath, options = {}) {
|
|
101
|
+
const resolve = options.resolve ?? defaultResolve;
|
|
102
|
+
const importModule = options.importModule ?? defaultImport;
|
|
103
|
+
const url = await resolve(name, basePath);
|
|
104
|
+
const mod = await importModule(url);
|
|
105
|
+
const providers = [];
|
|
106
|
+
const commands = [];
|
|
107
|
+
for (const value of Object.values(mod)) {
|
|
108
|
+
if (typeof value === 'function' && /Provider$/.test(value.name ?? '')) {
|
|
109
|
+
providers.push(value);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (mod.default !== undefined && providers.length === 0) {
|
|
113
|
+
const fallback = mod.default;
|
|
114
|
+
if (typeof fallback === 'function' && /Provider$/.test(fallback.name ?? '')) {
|
|
115
|
+
providers.push(fallback);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const declaredProviders = mod.providers;
|
|
119
|
+
if (Array.isArray(declaredProviders)) {
|
|
120
|
+
for (const value of declaredProviders) {
|
|
121
|
+
if (typeof value === 'function' && !providers.includes(value)) {
|
|
122
|
+
providers.push(value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const declaredCommands = mod.commands;
|
|
127
|
+
if (Array.isArray(declaredCommands)) {
|
|
128
|
+
for (const value of declaredCommands) {
|
|
129
|
+
if (isCommandExport(value))
|
|
130
|
+
commands.push({ default: value });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (isCommandExport(mod.default) && commands.length === 0) {
|
|
134
|
+
commands.push({ default: mod.default });
|
|
135
|
+
}
|
|
136
|
+
return { name, providers, commands };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Discover every installed plugin and collect what it provides. Individual
|
|
140
|
+
* failures are skipped: one broken plugin must not break the host app.
|
|
141
|
+
*/
|
|
142
|
+
export async function discoverPlugins(basePath, options = {}) {
|
|
143
|
+
const loaded = [];
|
|
144
|
+
let names;
|
|
145
|
+
try {
|
|
146
|
+
names = await findPluginPackages(basePath, options);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// A malformed host manifest shouldn't stop the app from booting.
|
|
150
|
+
return loaded;
|
|
151
|
+
}
|
|
152
|
+
for (const name of names) {
|
|
153
|
+
try {
|
|
154
|
+
loaded.push(await loadPlugin(name, basePath, options));
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Unresolvable or unimportable: skip it.
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return loaded;
|
|
161
|
+
}
|
|
162
|
+
async function readJson(path) {
|
|
163
|
+
const text = await readFile(path, 'utf8');
|
|
164
|
+
return JSON.parse(text);
|
|
165
|
+
}
|
|
166
|
+
function defaultResolve(specifier, from) {
|
|
167
|
+
// Resolve as the app would: from its own package.json, not from this
|
|
168
|
+
// module. Workspace and nested `node_modules` then match a real install.
|
|
169
|
+
const require = createRequire(join(from, 'package.json'));
|
|
170
|
+
return pathToFileURL(require.resolve(specifier)).href;
|
|
171
|
+
}
|
|
172
|
+
function defaultImport(url) {
|
|
173
|
+
return import(/* @vite-ignore */ url);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update nudge: compare the app's version against the npm registry and
|
|
3
|
+
* mention it once in a while.
|
|
4
|
+
*
|
|
5
|
+
* Everything here fails soft. A machine with no network, no writable cache
|
|
6
|
+
* dir, or a firewalled registry gets no nudge and no error — the check runs
|
|
7
|
+
* on the slow path of an already-successful command, so it must never be the
|
|
8
|
+
* reason a command failed.
|
|
9
|
+
*/
|
|
10
|
+
export interface UpdateCheckOptions {
|
|
11
|
+
/** Package name as published to npm. */
|
|
12
|
+
packageName: string;
|
|
13
|
+
/** The version currently running. */
|
|
14
|
+
currentVersion: string;
|
|
15
|
+
/** Skip the network when a cached answer is younger than this. Default 24h. */
|
|
16
|
+
cacheTtlMs?: number;
|
|
17
|
+
/** Overall deadline for the network call. Default 1500ms. */
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
/** Registry base URL. Defaults to `https://registry.npmjs.org`. */
|
|
20
|
+
registry?: string;
|
|
21
|
+
/** Override the cache directory (tests, sandboxes). */
|
|
22
|
+
cacheDir?: string;
|
|
23
|
+
/** Fetch implementation (tests inject a stub). */
|
|
24
|
+
fetch?: typeof fetch;
|
|
25
|
+
/** Dist-tag to compare against. Default `latest`. */
|
|
26
|
+
distTag?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface UpdateInfo {
|
|
29
|
+
/** A newer version is available. */
|
|
30
|
+
readonly updateAvailable: boolean;
|
|
31
|
+
readonly currentVersion: string;
|
|
32
|
+
/** The newest published version, when one was resolved. */
|
|
33
|
+
readonly latestVersion?: string;
|
|
34
|
+
/** How big the gap is: `major`, `minor`, `patch`, or `prerelease`. */
|
|
35
|
+
readonly kind?: 'major' | 'minor' | 'patch' | 'prerelease';
|
|
36
|
+
}
|
|
37
|
+
export interface UpdateCheckResult extends UpdateInfo {
|
|
38
|
+
/** False when the check was skipped or failed; never throws. */
|
|
39
|
+
readonly checked: boolean;
|
|
40
|
+
/** Why nothing was resolved, when `checked` is false. */
|
|
41
|
+
readonly reason?: 'disabled' | 'cached-no-update' | 'offline' | 'invalid-version' | 'not-found';
|
|
42
|
+
/** Where the answer came from. */
|
|
43
|
+
readonly source?: 'cache' | 'registry';
|
|
44
|
+
}
|
|
45
|
+
export interface SemVer {
|
|
46
|
+
major: number;
|
|
47
|
+
minor: number;
|
|
48
|
+
patch: number;
|
|
49
|
+
/** Prerelease identifiers, e.g. `['alpha', '1']`. */
|
|
50
|
+
prerelease: ReadonlyArray<string | number>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse a semver string. Tolerates a leading `v`/`=` and missing minor/patch
|
|
54
|
+
* (`1` → `1.0.0`). Returns null when the string isn't a version at all.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseSemVer(version: string): SemVer | null;
|
|
57
|
+
/**
|
|
58
|
+
* Compare two versions. Returns -1 when `a` sorts first, 1 when `a` sorts
|
|
59
|
+
* after, 0 when they're equal. Prereleases sort below their release
|
|
60
|
+
* (`1.0.0-alpha` < `1.0.0`).
|
|
61
|
+
*/
|
|
62
|
+
export declare function compareSemVer(a: string, b: string): number;
|
|
63
|
+
/** Classify the gap between two versions, from the older one's perspective. */
|
|
64
|
+
export declare function updateKind(current: string, latest: string): UpdateInfo['kind'];
|
|
65
|
+
/** True when `latest` is newer than `current`. */
|
|
66
|
+
export declare function isNewer(latest: string, current: string): boolean;
|
|
67
|
+
/** The default cache directory: XDG on Unix, `%LOCALAPPDATA%` on Windows. */
|
|
68
|
+
export declare function defaultCacheDir(appName?: string): string;
|
|
69
|
+
/** Clear a cached answer. Used by `--no-update-check`-style tooling and tests. */
|
|
70
|
+
export declare function clearUpdateCache(packageName: string, options?: {
|
|
71
|
+
cacheDir?: string;
|
|
72
|
+
}): void;
|
|
73
|
+
/**
|
|
74
|
+
* Check npm for a newer version. Resolves `checked: false` rather than
|
|
75
|
+
* throwing, so callers can fire-and-forget it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function checkForUpdate(options: UpdateCheckOptions): Promise<UpdateCheckResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Format a one-line nudge. Returns null when there's nothing to say, so
|
|
80
|
+
* callers can `if (line) output.muted(line)`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatUpdateNudge(info: UpdateCheckResult, binName: string): string | null;
|
package/dist/updates.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/** No update. Returned for every non-actionable outcome. */
|
|
4
|
+
const NO_UPDATE = {
|
|
5
|
+
updateAvailable: false,
|
|
6
|
+
currentVersion: '',
|
|
7
|
+
checked: false,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Parse a semver string. Tolerates a leading `v`/`=` and missing minor/patch
|
|
11
|
+
* (`1` → `1.0.0`). Returns null when the string isn't a version at all.
|
|
12
|
+
*/
|
|
13
|
+
export function parseSemVer(version) {
|
|
14
|
+
const text = version.trim().replace(/^[v=]+/, '');
|
|
15
|
+
const match = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z-.]+))?(?:\+[0-9A-Za-z-.]+)?$/.exec(text);
|
|
16
|
+
if (!match)
|
|
17
|
+
return null;
|
|
18
|
+
return {
|
|
19
|
+
major: Number(match[1]),
|
|
20
|
+
minor: Number(match[2] ?? '0'),
|
|
21
|
+
patch: Number(match[3] ?? '0'),
|
|
22
|
+
prerelease: match[4] === undefined ? [] : match[4].split('.').map(identifier),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function identifier(part) {
|
|
26
|
+
return /^\d+$/.test(part) ? Number(part) : part;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Compare two versions. Returns -1 when `a` sorts first, 1 when `a` sorts
|
|
30
|
+
* after, 0 when they're equal. Prereleases sort below their release
|
|
31
|
+
* (`1.0.0-alpha` < `1.0.0`).
|
|
32
|
+
*/
|
|
33
|
+
export function compareSemVer(a, b) {
|
|
34
|
+
const left = parseSemVer(a);
|
|
35
|
+
const right = parseSemVer(b);
|
|
36
|
+
if (left === null || right === null)
|
|
37
|
+
return 0;
|
|
38
|
+
for (const field of ['major', 'minor', 'patch']) {
|
|
39
|
+
if (left[field] !== right[field])
|
|
40
|
+
return left[field] < right[field] ? -1 : 1;
|
|
41
|
+
}
|
|
42
|
+
return comparePrerelease(left.prerelease, right.prerelease);
|
|
43
|
+
}
|
|
44
|
+
function comparePrerelease(a, b) {
|
|
45
|
+
// A version without a prerelease is newer than one with it.
|
|
46
|
+
if (a.length === 0 && b.length === 0)
|
|
47
|
+
return 0;
|
|
48
|
+
if (a.length === 0)
|
|
49
|
+
return 1;
|
|
50
|
+
if (b.length === 0)
|
|
51
|
+
return -1;
|
|
52
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
53
|
+
const left = a[i];
|
|
54
|
+
const right = b[i];
|
|
55
|
+
if (left === undefined)
|
|
56
|
+
return -1;
|
|
57
|
+
if (right === undefined)
|
|
58
|
+
return 1;
|
|
59
|
+
if (left === right)
|
|
60
|
+
continue;
|
|
61
|
+
const leftIsNumber = typeof left === 'number';
|
|
62
|
+
const rightIsNumber = typeof right === 'number';
|
|
63
|
+
if (leftIsNumber && rightIsNumber)
|
|
64
|
+
return left < right ? -1 : 1;
|
|
65
|
+
// Numeric identifiers always sort below alphanumeric ones.
|
|
66
|
+
if (leftIsNumber)
|
|
67
|
+
return -1;
|
|
68
|
+
if (rightIsNumber)
|
|
69
|
+
return 1;
|
|
70
|
+
return String(left) < String(right) ? -1 : 1;
|
|
71
|
+
}
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
/** Classify the gap between two versions, from the older one's perspective. */
|
|
75
|
+
export function updateKind(current, latest) {
|
|
76
|
+
const from = parseSemVer(current);
|
|
77
|
+
const to = parseSemVer(latest);
|
|
78
|
+
if (from === null || to === null)
|
|
79
|
+
return undefined;
|
|
80
|
+
if (to.prerelease.length > 0 && from.prerelease.length === 0)
|
|
81
|
+
return 'prerelease';
|
|
82
|
+
if (to.major !== from.major)
|
|
83
|
+
return 'major';
|
|
84
|
+
if (to.minor !== from.minor)
|
|
85
|
+
return 'minor';
|
|
86
|
+
return 'patch';
|
|
87
|
+
}
|
|
88
|
+
/** True when `latest` is newer than `current`. */
|
|
89
|
+
export function isNewer(latest, current) {
|
|
90
|
+
return compareSemVer(latest, current) > 0;
|
|
91
|
+
}
|
|
92
|
+
/** The default cache directory: XDG on Unix, `%LOCALAPPDATA%` on Windows. */
|
|
93
|
+
export function defaultCacheDir(appName = 'mudah') {
|
|
94
|
+
const home = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '';
|
|
95
|
+
if (process.platform === 'win32') {
|
|
96
|
+
return process.env['LOCALAPPDATA'] ?? join(home, 'AppData', 'Local', appName, 'cache');
|
|
97
|
+
}
|
|
98
|
+
const xdg = process.env['XDG_CACHE_HOME'];
|
|
99
|
+
if (xdg !== undefined && xdg.length > 0)
|
|
100
|
+
return join(xdg, appName);
|
|
101
|
+
return join(home, '.cache', appName);
|
|
102
|
+
}
|
|
103
|
+
function cacheFile(cacheDir, packageName) {
|
|
104
|
+
return join(cacheDir, `${packageName.replace(/[^a-zA-Z0-9._-]/g, '_')}.json`);
|
|
105
|
+
}
|
|
106
|
+
function readCache(file) {
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
109
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
110
|
+
return null;
|
|
111
|
+
const entry = parsed;
|
|
112
|
+
if (typeof entry.latestVersion !== 'string' || typeof entry.checkedAt !== 'number') {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return { latestVersion: entry.latestVersion, checkedAt: entry.checkedAt };
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function writeCache(file, entry) {
|
|
122
|
+
try {
|
|
123
|
+
mkdirSync(join(file, '..'), { recursive: true });
|
|
124
|
+
writeFileSync(file, JSON.stringify(entry), 'utf8');
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// A read-only filesystem just means no caching next time.
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Clear a cached answer. Used by `--no-update-check`-style tooling and tests. */
|
|
131
|
+
export function clearUpdateCache(packageName, options = {}) {
|
|
132
|
+
try {
|
|
133
|
+
rmSync(cacheFile(options.cacheDir ?? defaultCacheDir(), packageName), { force: true });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Nothing cached, or not removable — either way, mission accomplished.
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check npm for a newer version. Resolves `checked: false` rather than
|
|
141
|
+
* throwing, so callers can fire-and-forget it.
|
|
142
|
+
*/
|
|
143
|
+
export async function checkForUpdate(options) {
|
|
144
|
+
const current = options.currentVersion;
|
|
145
|
+
if (parseSemVer(current) === null) {
|
|
146
|
+
return { ...NO_UPDATE, currentVersion: current, reason: 'invalid-version' };
|
|
147
|
+
}
|
|
148
|
+
const ttlMs = options.cacheTtlMs ?? 24 * 60 * 60 * 1000;
|
|
149
|
+
const file = cacheFile(options.cacheDir ?? defaultCacheDir(), options.packageName);
|
|
150
|
+
// A fresh cache hit answers without touching the network.
|
|
151
|
+
const cached = readCache(file);
|
|
152
|
+
if (cached !== null && Date.now() - cached.checkedAt < ttlMs) {
|
|
153
|
+
return resolve(current, cached.latestVersion, 'cache');
|
|
154
|
+
}
|
|
155
|
+
const latest = await fetchLatest(options);
|
|
156
|
+
if (latest === null) {
|
|
157
|
+
// Offline: fall back to a stale cache entry rather than saying nothing.
|
|
158
|
+
if (cached !== null)
|
|
159
|
+
return resolve(current, cached.latestVersion, 'cache');
|
|
160
|
+
return { ...NO_UPDATE, currentVersion: current, reason: 'offline' };
|
|
161
|
+
}
|
|
162
|
+
writeCache(file, { latestVersion: latest, checkedAt: Date.now() });
|
|
163
|
+
return resolve(current, latest, 'registry');
|
|
164
|
+
}
|
|
165
|
+
function resolve(current, latest, source) {
|
|
166
|
+
if (!isNewer(latest, current)) {
|
|
167
|
+
return {
|
|
168
|
+
updateAvailable: false,
|
|
169
|
+
currentVersion: current,
|
|
170
|
+
latestVersion: latest,
|
|
171
|
+
checked: true,
|
|
172
|
+
reason: 'cached-no-update',
|
|
173
|
+
source,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
updateAvailable: true,
|
|
178
|
+
currentVersion: current,
|
|
179
|
+
latestVersion: latest,
|
|
180
|
+
kind: updateKind(current, latest),
|
|
181
|
+
checked: true,
|
|
182
|
+
source,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async function fetchLatest(options) {
|
|
186
|
+
const doFetch = options.fetch ?? fetch;
|
|
187
|
+
const registry = (options.registry ??
|
|
188
|
+
process.env['MUDAH_UPDATE_REGISTRY'] ??
|
|
189
|
+
'https://registry.npmjs.org').replace(/\/+$/, '');
|
|
190
|
+
const name = options.packageName.replace(/^\//, '').replace(/\//g, '%2f');
|
|
191
|
+
const tag = options.distTag ?? 'latest';
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 1500);
|
|
194
|
+
try {
|
|
195
|
+
const response = await doFetch(`${registry}/${name}/${tag}`, {
|
|
196
|
+
signal: controller.signal,
|
|
197
|
+
headers: { accept: 'application/json' },
|
|
198
|
+
});
|
|
199
|
+
if (!response.ok)
|
|
200
|
+
return null;
|
|
201
|
+
const body = (await response.json());
|
|
202
|
+
if (typeof body !== 'object' || body === null)
|
|
203
|
+
return null;
|
|
204
|
+
const version = body.version;
|
|
205
|
+
if (typeof version !== 'string' || parseSemVer(version) === null)
|
|
206
|
+
return null;
|
|
207
|
+
return version;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
clearTimeout(timer);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Format a one-line nudge. Returns null when there's nothing to say, so
|
|
218
|
+
* callers can `if (line) output.muted(line)`.
|
|
219
|
+
*/
|
|
220
|
+
export function formatUpdateNudge(info, binName) {
|
|
221
|
+
if (!info.updateAvailable || info.latestVersion === undefined)
|
|
222
|
+
return null;
|
|
223
|
+
return `Update available: ${info.currentVersion} → ${info.latestVersion} (npm i -g ${binName})`;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=updates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updates.js","sourceRoot":"","sources":["../src/updates.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mudah-cli/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Mudah application kernel: service providers, two-phase boot, events, and discovery.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
],
|
|
19
19
|
"sideEffects": false,
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@mudah-cli/config": "^0.
|
|
22
|
-
"@mudah-cli/container": "^0.
|
|
21
|
+
"@mudah-cli/config": "^0.3.0",
|
|
22
|
+
"@mudah-cli/container": "^0.3.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|