@memberjunction/dynamic-packages 0.0.0 → 6.1.0-edge.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.
- package/LICENSE +183 -0
- package/README.md +385 -28
- package/dist/discover.d.ts +38 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +178 -0
- package/dist/discover.js.map +1 -0
- package/dist/host-import.d.ts +43 -0
- package/dist/host-import.d.ts.map +1 -0
- package/dist/host-import.js +170 -0
- package/dist/host-import.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +114 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +404 -0
- package/dist/loader.js.map +1 -0
- package/dist/mode.d.ts +27 -0
- package/dist/mode.d.ts.map +1 -0
- package/dist/mode.js +60 -0
- package/dist/mode.js.map +1 -0
- package/dist/process-id.d.ts +54 -0
- package/dist/process-id.d.ts.map +1 -0
- package/dist/process-id.js +104 -0
- package/dist/process-id.js.map +1 -0
- package/dist/types.d.ts +153 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +36 -7
package/dist/loader.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The loader. One call per process, right after configuration is discovered and BEFORE any
|
|
3
|
+
* database provider is set up — so registration happens exactly where it does in MJAPI, and a
|
|
4
|
+
* `StartupExport` can rely on nothing but the ClassFactory (the contract MJAPI has always
|
|
5
|
+
* imposed: startup exports register classes, they do not touch a provider).
|
|
6
|
+
*
|
|
7
|
+
* Robustness contract, identical to the loader this replaces in @memberjunction/server-bootstrap:
|
|
8
|
+
* no-op when nothing is configured, per-package try/catch, a package that cannot be resolved is
|
|
9
|
+
* reported as not-found (expected before `npm install`, and for a workspace member found on disk
|
|
10
|
+
* whose entry file has not been built yet), a
|
|
11
|
+
* package that resolves but throws surfaces its own error on the warn path, and boot never
|
|
12
|
+
* crashes because of an app package.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { pathToFileURL } from 'node:url';
|
|
17
|
+
import { cosmiconfigSync } from 'cosmiconfig';
|
|
18
|
+
import { DiscoverAppManifestPackages, DiscoverGeneratedPackages, FindWorkspacePackageDir, ReadDynamicPackagesConfig, } from './discover.js';
|
|
19
|
+
import { importFromHost, isResolutionFailure } from './host-import.js';
|
|
20
|
+
import { ResolveDynamicPackagesMode } from './mode.js';
|
|
21
|
+
import { MatchesProcess, NormalizeProcessId } from './process-id.js';
|
|
22
|
+
/** Default output channel: plain console, the way MJAPI has always logged its boot. */
|
|
23
|
+
export const ConsoleDynamicPackagesLogger = {
|
|
24
|
+
info: (message) => console.log(message),
|
|
25
|
+
warn: (message, error) => (error === undefined ? console.warn(message) : console.warn(message, error)),
|
|
26
|
+
verbose: () => undefined,
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A logger that keeps stdout clean: progress and warnings go to stderr, verbose detail is dropped.
|
|
30
|
+
* For CLI hosts whose stdout is a machine-readable envelope (`--format=json`, `--output=json`) —
|
|
31
|
+
* the default console logger would print "Loading Open App server packages..." ahead of the JSON.
|
|
32
|
+
*/
|
|
33
|
+
export const StderrDynamicPackagesLogger = {
|
|
34
|
+
info: (message) => process.stderr.write(`${message}\n`),
|
|
35
|
+
warn: (message, error) => {
|
|
36
|
+
const detail = error === undefined ? '' : ` ${error instanceof Error ? error.message : String(error)}`;
|
|
37
|
+
process.stderr.write(`${message}${detail}\n`);
|
|
38
|
+
},
|
|
39
|
+
verbose: () => undefined,
|
|
40
|
+
};
|
|
41
|
+
/** A logger that says nothing — for hosts that read the report and render it themselves. */
|
|
42
|
+
export const SilentDynamicPackagesLogger = {
|
|
43
|
+
info: () => undefined,
|
|
44
|
+
warn: () => undefined,
|
|
45
|
+
verbose: () => undefined,
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Discovers `mj.config.cjs` with cosmiconfig (module name `mj`) and returns the RAW object plus its
|
|
49
|
+
* path. Hosts whose own config loader Zod-strips `dynamicPackages` use this to hand the loader an
|
|
50
|
+
* unstripped view.
|
|
51
|
+
*
|
|
52
|
+
* @param searchFrom - Directory to start from. Defaults to `process.cwd()`.
|
|
53
|
+
* @param options.searchStrategy - cosmiconfig's search strategy. Defaults to `'global'` (walk up
|
|
54
|
+
* to the home directory — what MJAPI, the `mj` CLI and CodeGen do). A host whose own config
|
|
55
|
+
* loader only looks in the working directory should pass `'none'` so the packages it loads come
|
|
56
|
+
* from the same file its database settings came from.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const { config, configFilePath } = DiscoverMJConfig();
|
|
61
|
+
* await LoadDynamicPackages({ processId: 'mcp', config, configFilePath });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function DiscoverMJConfig(searchFrom, options) {
|
|
65
|
+
const explorer = cosmiconfigSync('mj', { searchStrategy: options?.searchStrategy ?? 'global' });
|
|
66
|
+
const result = explorer.search(searchFrom ?? process.cwd());
|
|
67
|
+
return {
|
|
68
|
+
config: (result?.config ?? {}),
|
|
69
|
+
configFilePath: result?.filepath,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Human-readable label for a discovered entry's origin, used in log lines. */
|
|
73
|
+
function describeSource(pkg, tier) {
|
|
74
|
+
switch (pkg.Source) {
|
|
75
|
+
case 'generated':
|
|
76
|
+
return 'generated package';
|
|
77
|
+
case 'manifest':
|
|
78
|
+
return `Open App ${tier} package (from mj-app.json${pkg.Entry.AppName ? ` of ${pkg.Entry.AppName}` : ''})`;
|
|
79
|
+
default:
|
|
80
|
+
return `Open App ${tier} package`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Loads every dynamic package that applies to `options.processId` and returns a report of what
|
|
85
|
+
* happened to each entry. This is the one call a host makes; everything else in the package is a
|
|
86
|
+
* primitive it composes.
|
|
87
|
+
*
|
|
88
|
+
* @remarks
|
|
89
|
+
* **Order of operations.** Mode is resolved first (`MJ_DYNAMIC_PACKAGES` env var → `options.mode`
|
|
90
|
+
* → `dynamicPackages.policy` → `'load'`). Candidates are then discovered generic-to-specific —
|
|
91
|
+
* the host's `codeGeneration.packages`, then `dynamicPackages.<tier>[]`, then the `mj-app.json`
|
|
92
|
+
* beside the config — and merged by package name ({@link mergeCandidates}). Under mode `'none'`
|
|
93
|
+
* every candidate is reported as skipped and nothing is imported. Otherwise each candidate is
|
|
94
|
+
* filtered (`Enabled === false`, then `Processes` / `ExcludeProcesses` via {@link MatchesProcess}),
|
|
95
|
+
* served from the per-process cache when an earlier call already loaded it (module returned,
|
|
96
|
+
* startup export **not** re-run), or imported through {@link importFromHost} with an on-disk
|
|
97
|
+
* workspace fallback for manifest entries.
|
|
98
|
+
*
|
|
99
|
+
* **Why order matters.** `@RegisterClass` resolves by load-order priority — the last registration
|
|
100
|
+
* for a key wins — so importing generic-to-specific is what makes an Open App's server subclass
|
|
101
|
+
* beat its generated one, and the app you are standing in beat an installed copy.
|
|
102
|
+
*
|
|
103
|
+
* **When to call it.** After the host's class-registration manifest has been imported (so app
|
|
104
|
+
* registrations land last) and before any database provider exists (a `StartupExport` may rely on
|
|
105
|
+
* nothing but the ClassFactory).
|
|
106
|
+
*
|
|
107
|
+
* **Failure model.** Throws only when `processId` is missing. A package that no anchor can resolve
|
|
108
|
+
* is `NotFound` (expected before `npm install`, or for an unbuilt workspace member); one that
|
|
109
|
+
* resolves but throws while loading is `Failed` with its own error, logged on the warn path and
|
|
110
|
+
* never masked by a resolution message. Boot never crashes because of an app package.
|
|
111
|
+
*
|
|
112
|
+
* @param options - Process identity, the raw config and its path, tier, discovery switches, a
|
|
113
|
+
* programmatic mode override, and the logger. See {@link LoadDynamicPackagesOptions}.
|
|
114
|
+
* @returns The {@link DynamicPackagesReport}: `Loaded`, `Skipped` (with a reason), `NotFound`,
|
|
115
|
+
* `Failed`, plus the resolved mode and where it came from.
|
|
116
|
+
*
|
|
117
|
+
* @example Minimal host
|
|
118
|
+
* ```ts
|
|
119
|
+
* const { config, configFilePath } = DiscoverMJConfig();
|
|
120
|
+
* const report = await LoadDynamicPackages({ processId: 'mcp', config, configFilePath });
|
|
121
|
+
* for (const failed of report.Failed) {
|
|
122
|
+
* console.error(`app package ${failed.Entry.PackageName} failed to load`, failed.Error);
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @example Reading a convention off a loaded module (what MJAPI does for RESOLVER_PATHS)
|
|
127
|
+
* ```ts
|
|
128
|
+
* const resolverPaths = report.Loaded.flatMap((l) => {
|
|
129
|
+
* const paths = l.Module.RESOLVER_PATHS;
|
|
130
|
+
* return Array.isArray(paths) ? (paths as string[]) : [];
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @example A nested host that inherits the outer process identity
|
|
135
|
+
* ```ts
|
|
136
|
+
* await LoadDynamicPackages({
|
|
137
|
+
* processId: EffectiveProcessId('ai-cli'), // 'cli:ai:agents:run' when run under `mj ai …`
|
|
138
|
+
* config, configFilePath,
|
|
139
|
+
* log: StderrDynamicPackagesLogger, // stdout may be a JSON envelope
|
|
140
|
+
* });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export async function LoadDynamicPackages(options) {
|
|
144
|
+
const processId = NormalizeProcessId(options.processId);
|
|
145
|
+
if (!processId) {
|
|
146
|
+
throw new Error('LoadDynamicPackages: processId is required (e.g. "mjapi", "cli:sync:push", "mcp").');
|
|
147
|
+
}
|
|
148
|
+
const tier = options.tier ?? 'server';
|
|
149
|
+
const log = options.log ?? ConsoleDynamicPackagesLogger;
|
|
150
|
+
const config = options.config ?? null;
|
|
151
|
+
const section = ReadDynamicPackagesConfig(config);
|
|
152
|
+
const resolved = ResolveDynamicPackagesMode({ processId, option: options.mode, policy: section.policy });
|
|
153
|
+
if (resolved.ignoredInvalid) {
|
|
154
|
+
log.warn(`[dynamic-packages] Ignoring invalid ${resolved.ignoredInvalid} (expected 'load' or 'none')`);
|
|
155
|
+
}
|
|
156
|
+
log.verbose?.(`[dynamic-packages] process '${processId}', tier '${tier}', mode '${resolved.mode}' (source: ${resolved.source})`);
|
|
157
|
+
const report = {
|
|
158
|
+
ProcessId: processId,
|
|
159
|
+
Tier: tier,
|
|
160
|
+
Mode: resolved.mode,
|
|
161
|
+
ModeSource: resolved.source,
|
|
162
|
+
Loaded: [],
|
|
163
|
+
Skipped: [],
|
|
164
|
+
NotFound: [],
|
|
165
|
+
Failed: [],
|
|
166
|
+
};
|
|
167
|
+
const { candidates, duplicates } = mergeCandidates(collectCandidates(options, tier, config, section, log));
|
|
168
|
+
for (const duplicate of duplicates) {
|
|
169
|
+
report.Skipped.push({ ...duplicate, Reason: 'duplicate' });
|
|
170
|
+
}
|
|
171
|
+
if (candidates.length === 0) {
|
|
172
|
+
return report;
|
|
173
|
+
}
|
|
174
|
+
if (resolved.mode === 'none') {
|
|
175
|
+
for (const candidate of candidates) {
|
|
176
|
+
report.Skipped.push({ ...candidate, Reason: 'mode-none' });
|
|
177
|
+
}
|
|
178
|
+
log.info(`[dynamic-packages] Skipping ${candidates.length} package(s): mode 'none' (source: ${resolved.source})`);
|
|
179
|
+
return report;
|
|
180
|
+
}
|
|
181
|
+
const alreadyLoaded = loadedInThisProcess();
|
|
182
|
+
let announcedGenerated = false;
|
|
183
|
+
let announcedApps = false;
|
|
184
|
+
for (const candidate of candidates) {
|
|
185
|
+
const { Entry: entry } = candidate;
|
|
186
|
+
// Filters first — an entry disabled or out of scope must stay skipped even when an
|
|
187
|
+
// earlier call in this process loaded the same package (the cache is not a bypass).
|
|
188
|
+
if (entry.Enabled === false) {
|
|
189
|
+
report.Skipped.push({ ...candidate, Reason: 'disabled' });
|
|
190
|
+
log.verbose?.(`[dynamic-packages] Skipping ${entry.PackageName}: disabled`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!MatchesProcess(processId, entry)) {
|
|
194
|
+
report.Skipped.push({ ...candidate, Reason: 'process-filter' });
|
|
195
|
+
log.verbose?.(`[dynamic-packages] Skipping ${entry.PackageName}: not scoped to process '${processId}'`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const cached = alreadyLoaded.get(entry.PackageName);
|
|
199
|
+
if (cached) {
|
|
200
|
+
// A second host in the same process (e.g. `mj-ai` driven by `mj`) still needs the
|
|
201
|
+
// module — to read RESOLVER_PATHS, MJ_SERVER_EXTENSIONS, … — but must not re-run the
|
|
202
|
+
// startup export. ESM caches the module anyway; this only prevents the double hook.
|
|
203
|
+
report.Loaded.push({ ...candidate, Module: cached, RanStartupExport: false });
|
|
204
|
+
log.verbose?.(`[dynamic-packages] ${entry.PackageName} already loaded in this process; startup export not re-run`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
// Section headers mirror the lines MJAPI has printed at boot for years.
|
|
208
|
+
if (candidate.Source === 'generated' && !announcedGenerated) {
|
|
209
|
+
log.info('Loading generated packages...');
|
|
210
|
+
announcedGenerated = true;
|
|
211
|
+
}
|
|
212
|
+
else if (candidate.Source !== 'generated' && !announcedApps) {
|
|
213
|
+
log.info(`Loading Open App ${tier} packages...`);
|
|
214
|
+
announcedApps = true;
|
|
215
|
+
}
|
|
216
|
+
await loadOne(candidate, options, report, log);
|
|
217
|
+
}
|
|
218
|
+
return report;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Collapses candidates that name the same package into one, in discovery order.
|
|
222
|
+
*
|
|
223
|
+
* The `mj.config.cjs` entry is the operator's authority for a package: it carries `Enabled`
|
|
224
|
+
* (what `mj app disable` writes) and the process scoping. So when an `mj-app.json` beside the
|
|
225
|
+
* config names a package the config also names, the config entry decides whether and where it
|
|
226
|
+
* loads — but the manifest's on-disk location is kept as the resolution fallback, so a package
|
|
227
|
+
* the host cannot `require.resolve` still loads from the workspace. Every later duplicate is
|
|
228
|
+
* returned separately so the report can list it as skipped.
|
|
229
|
+
*/
|
|
230
|
+
export function mergeCandidates(all) {
|
|
231
|
+
const byName = new Map();
|
|
232
|
+
const candidates = [];
|
|
233
|
+
const duplicates = [];
|
|
234
|
+
for (const candidate of all) {
|
|
235
|
+
const name = candidate.Entry.PackageName;
|
|
236
|
+
const existing = byName.get(name);
|
|
237
|
+
if (!existing) {
|
|
238
|
+
const first = { ...candidate };
|
|
239
|
+
byName.set(name, first);
|
|
240
|
+
candidates.push(first);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (candidate.Source === 'config' && existing.Source !== 'config') {
|
|
244
|
+
existing.Entry = candidate.Entry;
|
|
245
|
+
existing.Source = 'config';
|
|
246
|
+
}
|
|
247
|
+
if (!existing.WorkspaceHome && candidate.WorkspaceHome) {
|
|
248
|
+
existing.WorkspaceHome = candidate.WorkspaceHome;
|
|
249
|
+
}
|
|
250
|
+
duplicates.push(candidate);
|
|
251
|
+
}
|
|
252
|
+
return { candidates, duplicates };
|
|
253
|
+
}
|
|
254
|
+
/** Orders the three discovery sources. Manifest discovery failures are the operator's to see, not fatal. */
|
|
255
|
+
function collectCandidates(options, tier, config, section, log) {
|
|
256
|
+
const candidates = [];
|
|
257
|
+
if (options.includeGeneratedPackages !== false) {
|
|
258
|
+
candidates.push(...DiscoverGeneratedPackages(config, tier));
|
|
259
|
+
}
|
|
260
|
+
for (const entry of section[tier] ?? []) {
|
|
261
|
+
candidates.push({ Source: 'config', Entry: entry });
|
|
262
|
+
}
|
|
263
|
+
if (options.discoverAppManifest !== false) {
|
|
264
|
+
const repoDir = options.appManifestDir ?? (options.configFilePath ? path.dirname(options.configFilePath) : process.cwd());
|
|
265
|
+
try {
|
|
266
|
+
const manifest = DiscoverAppManifestPackages(repoDir, tier);
|
|
267
|
+
if (manifest) {
|
|
268
|
+
candidates.push(...manifest.Entries);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
log.warn(`[dynamic-packages] Could not read ${path.join(repoDir, 'mj-app.json')}:`, error);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return candidates;
|
|
276
|
+
}
|
|
277
|
+
async function loadOne(candidate, options, report, log) {
|
|
278
|
+
const { Entry: entry } = candidate;
|
|
279
|
+
const pkgName = entry.PackageName;
|
|
280
|
+
const label = describeSource(candidate, report.Tier);
|
|
281
|
+
const manifestHome = candidate.WorkspaceHome;
|
|
282
|
+
try {
|
|
283
|
+
let mod;
|
|
284
|
+
try {
|
|
285
|
+
mod = await importFromHost(pkgName, options.configFilePath);
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
// A manifest-sourced package may be a workspace member nothing can require.resolve
|
|
289
|
+
// (pnpm strict layout, root without the package as a dependency). Find it on disk.
|
|
290
|
+
const onDisk = manifestHome && isOwnResolutionFailure(error, pkgName)
|
|
291
|
+
? FindWorkspacePackageDir(manifestHome.RepoDir, manifestHome.SourceDirectory, pkgName)
|
|
292
|
+
: null;
|
|
293
|
+
if (!onDisk) {
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
const entryFile = resolvePackageEntryFile(onDisk);
|
|
297
|
+
if (!existsSync(entryFile)) {
|
|
298
|
+
// The workspace member exists but has not been built (no dist yet) — the expected
|
|
299
|
+
// state before the app's own build, not an error. Report it as not-found, with the
|
|
300
|
+
// file the build is expected to produce, and never on the warn path.
|
|
301
|
+
report.NotFound.push(candidate);
|
|
302
|
+
log.info(` ${label} ${pkgName} found at ${onDisk} but not built (missing ${path.relative(onDisk, entryFile)}) — build it first`);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
mod = await importPackageDir(entryFile);
|
|
306
|
+
}
|
|
307
|
+
const startup = entry.StartupExport ? mod[entry.StartupExport] : undefined;
|
|
308
|
+
let ranStartupExport = false;
|
|
309
|
+
if (typeof startup === 'function') {
|
|
310
|
+
await Promise.resolve(startup());
|
|
311
|
+
ranStartupExport = true;
|
|
312
|
+
}
|
|
313
|
+
else if (entry.StartupExport) {
|
|
314
|
+
// A named export that is missing is a real mis-configuration (renamed export, stale
|
|
315
|
+
// config) — say so instead of silently skipping it.
|
|
316
|
+
log.warn(` ${label} ${pkgName} has no export named '${entry.StartupExport}' — startup hook not run`);
|
|
317
|
+
}
|
|
318
|
+
const loaded = { ...candidate, Module: mod, RanStartupExport: ranStartupExport };
|
|
319
|
+
report.Loaded.push(loaded);
|
|
320
|
+
loadedInThisProcess().set(pkgName, mod);
|
|
321
|
+
log.info(` Loaded ${label}: ${pkgName}${ranStartupExport ? ` (ran ${entry.StartupExport})` : ''}`);
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
if (isOwnResolutionFailure(error, pkgName)) {
|
|
325
|
+
report.NotFound.push(candidate);
|
|
326
|
+
log.info(candidate.Source === 'generated'
|
|
327
|
+
? ` Generated package not found (may not exist yet): ${pkgName}`
|
|
328
|
+
: ` Open App ${report.Tier} package not found (run 'npm install'?): ${pkgName}`);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
report.Failed.push({ ...candidate, Error: error });
|
|
332
|
+
log.warn(` Error loading ${label} ${pkgName}:`, error);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Modules already loaded by ANY copy of this module in the process, by package name. Kept on
|
|
338
|
+
* `globalThis` (not a module variable) because a duplicated module copy — two dist paths under
|
|
339
|
+
* pnpm, a bundled and an unbundled copy — must still agree on what has run.
|
|
340
|
+
*/
|
|
341
|
+
const LOADED_STORE_KEY = Symbol.for('memberjunction.dynamic-packages.loaded');
|
|
342
|
+
function loadedInThisProcess() {
|
|
343
|
+
const store = globalThis;
|
|
344
|
+
let map = store[LOADED_STORE_KEY];
|
|
345
|
+
if (!map) {
|
|
346
|
+
map = new Map();
|
|
347
|
+
store[LOADED_STORE_KEY] = map;
|
|
348
|
+
}
|
|
349
|
+
return map;
|
|
350
|
+
}
|
|
351
|
+
/** Test seam: forget what has been loaded so a fresh process can be simulated. */
|
|
352
|
+
export function ResetLoadedDynamicPackages() {
|
|
353
|
+
loadedInThisProcess().clear();
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* True only when THIS package is the error's quoted subject ("Cannot find package '<name>'").
|
|
357
|
+
* A missing TRANSITIVE dependency of a package that WAS found quotes the transitive name
|
|
358
|
+
* instead (this package's name still appears unquoted in the imported-from path, which is why
|
|
359
|
+
* a bare `includes(pkgName)` is not enough); that message is the true cause and must reach
|
|
360
|
+
* the operator via the warn path.
|
|
361
|
+
*/
|
|
362
|
+
function isOwnResolutionFailure(error, pkgName) {
|
|
363
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
364
|
+
return isResolutionFailure(error) && message.includes(`'${pkgName}'`);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Resolves a package directory's entry file, honouring a string/"."/conditional `exports` map,
|
|
368
|
+
* else `main`, else `index.js`. Pure path work — whether the file exists is the caller's question.
|
|
369
|
+
*/
|
|
370
|
+
function resolvePackageEntryFile(dir) {
|
|
371
|
+
const pkgJson = JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
372
|
+
const entry = resolveExportsEntry(pkgJson.exports) ?? (typeof pkgJson.main === 'string' ? pkgJson.main : 'index.js');
|
|
373
|
+
return path.resolve(dir, entry);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Imports a package by its resolved entry file. (Dynamic import is justified here as runtime
|
|
377
|
+
* plugin discovery: the path comes from a manifest on disk, not from code.)
|
|
378
|
+
*/
|
|
379
|
+
async function importPackageDir(entryFile) {
|
|
380
|
+
return (await import(pathToFileURL(entryFile).href));
|
|
381
|
+
}
|
|
382
|
+
function resolveExportsEntry(exportsField) {
|
|
383
|
+
if (typeof exportsField === 'string') {
|
|
384
|
+
return exportsField;
|
|
385
|
+
}
|
|
386
|
+
if (!exportsField || typeof exportsField !== 'object') {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
const rec = exportsField;
|
|
390
|
+
const root = '.' in rec ? rec['.'] : rec;
|
|
391
|
+
if (typeof root === 'string') {
|
|
392
|
+
return root;
|
|
393
|
+
}
|
|
394
|
+
if (root && typeof root === 'object') {
|
|
395
|
+
const cond = root;
|
|
396
|
+
for (const key of ['import', 'default', 'require', 'node']) {
|
|
397
|
+
if (typeof cond[key] === 'string') {
|
|
398
|
+
return cond[key];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
|
package/dist/mode.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DynamicPackagesMode, DynamicPackagesModeSource } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Environment variable for a per-invocation override, e.g.
|
|
4
|
+
* `MJ_DYNAMIC_PACKAGES=none npx mj sync push` to push with generic BaseEntity only.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DYNAMIC_PACKAGES_MODE_ENV_VAR = "MJ_DYNAMIC_PACKAGES";
|
|
7
|
+
export interface ResolvedDynamicPackagesMode {
|
|
8
|
+
mode: DynamicPackagesMode;
|
|
9
|
+
source: DynamicPackagesModeSource;
|
|
10
|
+
/** Set when an env/policy value was present but unparseable — the caller should surface it. */
|
|
11
|
+
ignoredInvalid?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Highest wins:
|
|
15
|
+
* 1. `MJ_DYNAMIC_PACKAGES` env var
|
|
16
|
+
* 2. `option` — programmatic override from the entry point (e.g. a CLI flag)
|
|
17
|
+
* 3. `policy` — `dynamicPackages.policy` from mj.config.cjs, most specific process key
|
|
18
|
+
* 4. `'load'`
|
|
19
|
+
*
|
|
20
|
+
* An invalid env/policy value never crashes a process; it is reported and falls through.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ResolveDynamicPackagesMode(args: {
|
|
23
|
+
processId: string;
|
|
24
|
+
option?: DynamicPackagesMode;
|
|
25
|
+
policy?: Record<string, string> | null;
|
|
26
|
+
}): ResolvedDynamicPackagesMode;
|
|
27
|
+
//# sourceMappingURL=mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;AA0BnE,MAAM,WAAW,2BAA2B;IACxC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,yBAAyB,CAAC;IAClC,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC1C,GAAG,2BAA2B,CAoB9B"}
|
package/dist/mode.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode resolution — the same precedence shape as `ResolveStartupMode` in @memberjunction/core
|
|
3
|
+
* (env var > programmatic option > config > default), so an operator who already knows
|
|
4
|
+
* `MJ_STARTUP_MODE=task npx mj sync push` can reach for `MJ_DYNAMIC_PACKAGES=none` the same way.
|
|
5
|
+
*/
|
|
6
|
+
import { ResolveMostSpecific } from './process-id.js';
|
|
7
|
+
/**
|
|
8
|
+
* Environment variable for a per-invocation override, e.g.
|
|
9
|
+
* `MJ_DYNAMIC_PACKAGES=none npx mj sync push` to push with generic BaseEntity only.
|
|
10
|
+
*/
|
|
11
|
+
export const DYNAMIC_PACKAGES_MODE_ENV_VAR = 'MJ_DYNAMIC_PACKAGES';
|
|
12
|
+
const MODES = ['load', 'none'];
|
|
13
|
+
/** Accepts the canonical values plus the obvious spellings people will try. */
|
|
14
|
+
function parseMode(value) {
|
|
15
|
+
if (!value) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const normalized = value.trim().toLowerCase();
|
|
19
|
+
if (normalized === 'off' || normalized === 'skip' || normalized === 'false' || normalized === '0') {
|
|
20
|
+
return 'none';
|
|
21
|
+
}
|
|
22
|
+
if (normalized === 'on' || normalized === 'true' || normalized === '1' || normalized === 'full') {
|
|
23
|
+
return 'load';
|
|
24
|
+
}
|
|
25
|
+
return MODES.find((m) => m === normalized);
|
|
26
|
+
}
|
|
27
|
+
function readEnv() {
|
|
28
|
+
if (typeof process === 'undefined' || !process.env) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return process.env[DYNAMIC_PACKAGES_MODE_ENV_VAR];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Highest wins:
|
|
35
|
+
* 1. `MJ_DYNAMIC_PACKAGES` env var
|
|
36
|
+
* 2. `option` — programmatic override from the entry point (e.g. a CLI flag)
|
|
37
|
+
* 3. `policy` — `dynamicPackages.policy` from mj.config.cjs, most specific process key
|
|
38
|
+
* 4. `'load'`
|
|
39
|
+
*
|
|
40
|
+
* An invalid env/policy value never crashes a process; it is reported and falls through.
|
|
41
|
+
*/
|
|
42
|
+
export function ResolveDynamicPackagesMode(args) {
|
|
43
|
+
const envRaw = readEnv();
|
|
44
|
+
const envMode = parseMode(envRaw);
|
|
45
|
+
if (envMode) {
|
|
46
|
+
return { mode: envMode, source: 'env' };
|
|
47
|
+
}
|
|
48
|
+
const ignoredInvalid = envRaw && !envMode ? `${DYNAMIC_PACKAGES_MODE_ENV_VAR}='${envRaw}'` : undefined;
|
|
49
|
+
if (args.option) {
|
|
50
|
+
return { mode: args.option, source: 'option', ignoredInvalid };
|
|
51
|
+
}
|
|
52
|
+
const policyRaw = ResolveMostSpecific(args.processId, args.policy);
|
|
53
|
+
const policyMode = parseMode(policyRaw);
|
|
54
|
+
if (policyMode) {
|
|
55
|
+
return { mode: policyMode, source: 'policy', ignoredInvalid };
|
|
56
|
+
}
|
|
57
|
+
const policyInvalid = policyRaw && !policyMode ? `dynamicPackages.policy value '${policyRaw}'` : undefined;
|
|
58
|
+
return { mode: 'load', source: 'default', ignoredInvalid: ignoredInvalid ?? policyInvalid };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=mode.js.map
|
package/dist/mode.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.js","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGtD;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,qBAAqB,CAAC;AAEnE,MAAM,KAAK,GAAmC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/D,+EAA+E;AAC/E,SAAS,SAAS,CAAC,KAAgC;IAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,UAAU,KAAK,KAAK,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QAChG,OAAO,MAAM,CAAC;IAClB,CAAC;IACD,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC9F,OAAO,MAAM,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,OAAO;IACZ,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjD,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AACtD,CAAC;AASD;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAI1C;IACG,MAAM,MAAM,GAAG,OAAO,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,6BAA6B,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAEvG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IACnE,CAAC;IAED,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IAClE,CAAC;IACD,MAAM,aAAa,GAAG,SAAS,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,iCAAiC,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3G,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,IAAI,aAAa,EAAE,CAAC;AAChG,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process identity and prefix matching.
|
|
3
|
+
*
|
|
4
|
+
* Process IDs are hierarchical, colon-separated, lowercase strings: `mjapi`, `cli`,
|
|
5
|
+
* `cli:sync`, `cli:sync:push`, `mcp`. A pattern matches an ID when it equals the ID or names
|
|
6
|
+
* one of its ancestor segments — `cli:sync` matches `cli:sync:push` but not `cli:syncother`
|
|
7
|
+
* or `cli:migrate`. This is what lets a config author scope a package to "all of the CLI",
|
|
8
|
+
* "just mj sync", or exactly one command with the same field.
|
|
9
|
+
*/
|
|
10
|
+
/** Wildcard pattern that matches every process. */
|
|
11
|
+
export declare const ANY_PROCESS = "*";
|
|
12
|
+
/** Lowercases, trims, and collapses whitespace around the colon separators. */
|
|
13
|
+
export declare function NormalizeProcessId(value: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Builds a CLI process ID from an oclif command ID. oclif reports IDs as either `sync:push`
|
|
16
|
+
* or `sync push` (with `topicSeparator: ' '`); both become `cli:sync:push`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function CliProcessId(commandId: string | undefined | null): string;
|
|
19
|
+
/** True when `pattern` equals `processId` or is an ancestor prefix of it (segment-aware). */
|
|
20
|
+
export declare function ProcessIdMatches(processId: string, pattern: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Evaluates an entry's `Processes` / `ExcludeProcesses` against a process ID.
|
|
23
|
+
* No `Processes` (or an empty list) means "everywhere"; `ExcludeProcesses` is applied after.
|
|
24
|
+
*/
|
|
25
|
+
export declare function MatchesProcess(processId: string, filter: {
|
|
26
|
+
Processes?: string[] | null;
|
|
27
|
+
ExcludeProcesses?: string[] | null;
|
|
28
|
+
} | null | undefined): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Picks the value of the most specific key in `map` that matches `processId`.
|
|
31
|
+
* `'cli:sync'` beats `'cli'` beats `'*'`. Returns `undefined` when nothing matches.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ResolveMostSpecific<T>(processId: string, map: Record<string, T> | null | undefined): T | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Environment variable through which an OUTER host tells nested hosts which process they are
|
|
36
|
+
* running inside. The `mj` CLI's prerun hook sets it to its own ID (`cli:ai:agents:run`) before
|
|
37
|
+
* the command imports `@memberjunction/ai-cli` or `@memberjunction/testing-cli`; those packages'
|
|
38
|
+
* provider bootstraps then evaluate `Processes` / `ExcludeProcesses` / `policy` under the SAME
|
|
39
|
+
* ID instead of their standalone `ai-cli` / `testing-cli` identity. Without it, an entry an
|
|
40
|
+
* operator excluded from `cli` would be loaded by the nested host a moment later.
|
|
41
|
+
*/
|
|
42
|
+
export declare const DYNAMIC_PACKAGES_PROCESS_ENV_VAR = "MJ_DYNAMIC_PACKAGES_PROCESS";
|
|
43
|
+
/**
|
|
44
|
+
* The process ID a host should load under: the one an enclosing host published through
|
|
45
|
+
* {@link DYNAMIC_PACKAGES_PROCESS_ENV_VAR} when there is one, else the host's own default.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* // packages/AI/AICLI — `mj-ai` standalone loads as 'ai-cli'; under `mj ai …` it inherits 'cli:ai:…'
|
|
50
|
+
* await LoadDynamicPackages({ processId: EffectiveProcessId('ai-cli'), ... });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function EffectiveProcessId(hostDefault: string): string;
|
|
54
|
+
//# sourceMappingURL=process-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-id.d.ts","sourceRoot":"","sources":["../src/process-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,mDAAmD;AACnD,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAGzE;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAU5E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC/F,OAAO,CAUT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,GAAG,SAAS,CAkBlH;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,gCAAgC,CAAC;AAE9E;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}
|