@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.
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Discovery — turns configuration into an ordered list of candidate entries, each tagged with
3
+ * where it came from. Order matters: the ClassFactory's load-order priority means a later
4
+ * registration wins, so the host's generated packages go first, then installed Open Apps
5
+ * (from `dynamicPackages`), then the app whose repository we are standing in (from
6
+ * `mj-app.json`), so the most local definition overrides the most generic one.
7
+ */
8
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
9
+ import path from 'node:path';
10
+ /**
11
+ * `codeGeneration.packages.<type>.name` keys that make sense for each tier. `angularForms` is
12
+ * a browser library and must never be imported into a Node process; `graphqlResolvers` has no
13
+ * meaning in a browser bundle.
14
+ */
15
+ export const GENERATED_PACKAGE_TYPES_BY_TIER = {
16
+ server: ['entities', 'actions', 'graphqlResolvers'],
17
+ client: ['entities', 'actions', 'angularForms'],
18
+ };
19
+ /** The manifest file every Open App repository carries at its root. */
20
+ export const APP_MANIFEST_FILE_NAME = 'mj-app.json';
21
+ /** Reads the `dynamicPackages` section off a raw config object, tolerating any shape. */
22
+ export function ReadDynamicPackagesConfig(config) {
23
+ const raw = config?.dynamicPackages;
24
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
25
+ return {};
26
+ }
27
+ const section = raw;
28
+ return {
29
+ server: sanitizeEntries(section.server),
30
+ client: sanitizeEntries(section.client),
31
+ policy: sanitizePolicy(section.policy),
32
+ };
33
+ }
34
+ function sanitizeEntries(raw) {
35
+ if (!Array.isArray(raw)) {
36
+ return undefined;
37
+ }
38
+ const entries = [];
39
+ for (const item of raw) {
40
+ if (!item || typeof item !== 'object') {
41
+ continue;
42
+ }
43
+ const rec = item;
44
+ const name = typeof rec.PackageName === 'string' ? rec.PackageName.trim() : '';
45
+ if (!name) {
46
+ continue;
47
+ }
48
+ entries.push({
49
+ PackageName: name,
50
+ StartupExport: typeof rec.StartupExport === 'string' && rec.StartupExport.trim() ? rec.StartupExport.trim() : undefined,
51
+ AppName: typeof rec.AppName === 'string' ? rec.AppName : undefined,
52
+ Enabled: rec.Enabled === false ? false : true,
53
+ Processes: stringList(rec.Processes),
54
+ ExcludeProcesses: stringList(rec.ExcludeProcesses),
55
+ });
56
+ }
57
+ return entries;
58
+ }
59
+ function stringList(raw) {
60
+ if (!Array.isArray(raw)) {
61
+ return undefined;
62
+ }
63
+ const list = raw.filter((v) => typeof v === 'string' && v.trim().length > 0);
64
+ return list.length > 0 ? list : undefined;
65
+ }
66
+ function sanitizePolicy(raw) {
67
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
68
+ return undefined;
69
+ }
70
+ const policy = {};
71
+ for (const [key, value] of Object.entries(raw)) {
72
+ if (typeof value === 'string') {
73
+ // Kept as-is (typed loosely); mode parsing decides validity and reports it.
74
+ policy[key] = value;
75
+ }
76
+ }
77
+ return Object.keys(policy).length > 0 ? policy : undefined;
78
+ }
79
+ /** Entries for the host's own generated packages (`codeGeneration.packages`). */
80
+ export function DiscoverGeneratedPackages(config, tier) {
81
+ const codeGeneration = config?.codeGeneration;
82
+ const packages = codeGeneration?.packages;
83
+ if (!packages || typeof packages !== 'object') {
84
+ return [];
85
+ }
86
+ const found = [];
87
+ for (const type of GENERATED_PACKAGE_TYPES_BY_TIER[tier]) {
88
+ const name = packages[type]?.name;
89
+ if (typeof name === 'string' && name.trim().length > 0) {
90
+ found.push({ Source: 'generated', Entry: { PackageName: name.trim(), Enabled: true } });
91
+ }
92
+ }
93
+ return found;
94
+ }
95
+ /**
96
+ * Reads `mj-app.json` from `repoDir` and returns the packages a process of `tier` should
97
+ * load: `shared` libraries first (entities/actions register on import), then the tier's own
98
+ * packages, with each `startupExport` carried through. Returns `null` when there is no
99
+ * manifest, and throws only when a manifest exists but is not valid JSON — a corrupt file is
100
+ * a problem the operator must see, an absent one is the common case.
101
+ */
102
+ export function DiscoverAppManifestPackages(repoDir, tier) {
103
+ const manifestPath = path.join(repoDir, APP_MANIFEST_FILE_NAME);
104
+ if (!existsSync(manifestPath)) {
105
+ return null;
106
+ }
107
+ let manifest;
108
+ try {
109
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
110
+ }
111
+ catch (error) {
112
+ throw new Error(`Could not parse ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
113
+ }
114
+ const appName = typeof manifest.name === 'string' ? manifest.name : path.basename(repoDir);
115
+ const sourceDirectory = typeof manifest.code?.sourceDirectory === 'string' && manifest.code.sourceDirectory.trim()
116
+ ? manifest.code.sourceDirectory.trim()
117
+ : 'packages';
118
+ const entries = [];
119
+ const push = (pkg) => {
120
+ const name = typeof pkg?.name === 'string' ? pkg.name.trim() : '';
121
+ if (!name) {
122
+ return;
123
+ }
124
+ entries.push({
125
+ Source: 'manifest',
126
+ WorkspaceHome: { RepoDir: repoDir, SourceDirectory: sourceDirectory },
127
+ Entry: {
128
+ PackageName: name,
129
+ StartupExport: typeof pkg.startupExport === 'string' && pkg.startupExport.trim() ? pkg.startupExport.trim() : undefined,
130
+ AppName: appName,
131
+ Enabled: true,
132
+ },
133
+ });
134
+ };
135
+ for (const pkg of manifest.packages?.shared ?? []) {
136
+ push(pkg);
137
+ }
138
+ for (const pkg of manifest.packages?.[tier] ?? []) {
139
+ push(pkg);
140
+ }
141
+ return { RepoDir: repoDir, AppName: appName, SourceDirectory: sourceDirectory, Entries: entries };
142
+ }
143
+ /**
144
+ * Locates a workspace package by name under an app repo's source directory (one level deep,
145
+ * matching `code.sourceDirectory`), returning its directory. Used when the process runs inside
146
+ * the app's own repository: the package is a workspace member there, but under pnpm's strict
147
+ * layout nothing at the repo root can `require.resolve` it, so we find it on disk instead.
148
+ */
149
+ export function FindWorkspacePackageDir(repoDir, sourceDirectory, packageName) {
150
+ const root = path.resolve(repoDir, sourceDirectory);
151
+ if (!existsSync(root)) {
152
+ return null;
153
+ }
154
+ let children;
155
+ try {
156
+ children = readdirSync(root);
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ for (const child of children) {
162
+ const pkgJsonPath = path.join(root, child, 'package.json');
163
+ if (!existsSync(pkgJsonPath)) {
164
+ continue;
165
+ }
166
+ try {
167
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
168
+ if (pkgJson.name === packageName) {
169
+ return path.join(root, child);
170
+ }
171
+ }
172
+ catch {
173
+ // unreadable package.json — not the one we want
174
+ }
175
+ }
176
+ return null;
177
+ }
178
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * True when the error is a module-RESOLUTION failure (the module could not be found or
3
+ * reached), as opposed to a module that was found but threw while loading — the latter is
4
+ * a real error that must surface. ESM raises ERR_MODULE_NOT_FOUND, CommonJS resolution
5
+ * (createRequire) raises MODULE_NOT_FOUND, and an exports-map mismatch raises
6
+ * ERR_PACKAGE_PATH_NOT_EXPORTED. Some ESM loader shims (ts-node's, notably — the loader
7
+ * MJAPI runs under) throw resolution failures as PLAIN Errors with no code at all, so
8
+ * when there is no code, recognize Node's own resolver message instead.
9
+ *
10
+ * ⚠ Under ts-node's shim the coded branch never fires (the shim strips custom error
11
+ * properties crossing the module-hooks thread), so the message branch is LOAD-BEARING
12
+ * there: if a future Node rewords its resolver messages, this predicate must be updated
13
+ * or the pnpm fallback silently stops working under ts-node hosts.
14
+ *
15
+ * Keep in sync with `IsModuleResolutionFailure` in @memberjunction/open-app-engine's
16
+ * `src/install/migration-runner.ts` — same heuristic, duplicated because the engine
17
+ * cannot depend on this package and cross-package re-exports are disallowed.
18
+ */
19
+ export declare function isResolutionFailure(error: unknown): boolean;
20
+ /**
21
+ * Resolves the on-disk `package.json` of a host-visible package so callers can
22
+ * introspect `memberjunction.serverExtensions` without relying on an exports map.
23
+ * Returns `null` when no host anchor can see the package.
24
+ */
25
+ export declare function resolvePackageJsonFromHost(pkgName: string, configFilePath?: string): string | null;
26
+ /**
27
+ * Imports a runtime-configured package from the HOST application's context.
28
+ *
29
+ * Resolution and evaluation are handled separately on the fallback path: an anchor that
30
+ * cannot SEE the package means "try the next anchor", but once an anchor resolves it,
31
+ * any failure from loading the module (a missing transitive dependency, a throw in its
32
+ * top-level code) is the module's own problem and is surfaced as-is — never masked by
33
+ * the original "cannot find package" error.
34
+ *
35
+ * Note on the resolver: `createRequire().resolve` runs under CommonJS conditions, so a
36
+ * package whose exports map declares ONLY an `"import"` condition cannot be resolved by
37
+ * the fallback (surfaced with an actionable error). On a dual CJS/ESM package it selects
38
+ * the CJS entry, so `import()` of that file would load a second physical module instance
39
+ * alongside any ESM copy already in the process — fine for MJ-shaped single-condition
40
+ * packages, but keep it in mind before widening this mechanism.
41
+ */
42
+ export declare function importFromHost(pkgName: string, configFilePath?: string): Promise<Record<string, unknown>>;
43
+ //# sourceMappingURL=host-import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-import.d.ts","sourceRoot":"","sources":["../src/host-import.ts"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAM3D;AAuCD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBlG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAyC/G"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Host-anchored dynamic import for runtime-configured packages.
3
+ *
4
+ * A bare `import(pkgName)` resolves from THIS package (dynamic-packages), which cannot
5
+ * declare packages whose names are only known at runtime — mj.config.cjs supplies them.
6
+ * npm's hoisted node_modules let that bare import resolve by accident; pnpm's strict
7
+ * per-package layout does not, because the packages are declared by (and linked into) the
8
+ * HOST application, e.g. MJAPI. `importFromHost` tries the bare import first (identical
9
+ * behavior to before on npm layouts) and, when the package cannot be resolved, retries
10
+ * from each host anchor — the working directory, the mj.config.cjs that named the
11
+ * package, and the process entrypoint. (Dynamic import is justified here as runtime
12
+ * plugin discovery: the names come from configuration, not code.)
13
+ */
14
+ import { createRequire } from 'node:module';
15
+ import { readFileSync } from 'node:fs';
16
+ import { pathToFileURL } from 'node:url';
17
+ import path from 'node:path';
18
+ /**
19
+ * True when the error is a module-RESOLUTION failure (the module could not be found or
20
+ * reached), as opposed to a module that was found but threw while loading — the latter is
21
+ * a real error that must surface. ESM raises ERR_MODULE_NOT_FOUND, CommonJS resolution
22
+ * (createRequire) raises MODULE_NOT_FOUND, and an exports-map mismatch raises
23
+ * ERR_PACKAGE_PATH_NOT_EXPORTED. Some ESM loader shims (ts-node's, notably — the loader
24
+ * MJAPI runs under) throw resolution failures as PLAIN Errors with no code at all, so
25
+ * when there is no code, recognize Node's own resolver message instead.
26
+ *
27
+ * ⚠ Under ts-node's shim the coded branch never fires (the shim strips custom error
28
+ * properties crossing the module-hooks thread), so the message branch is LOAD-BEARING
29
+ * there: if a future Node rewords its resolver messages, this predicate must be updated
30
+ * or the pnpm fallback silently stops working under ts-node hosts.
31
+ *
32
+ * Keep in sync with `IsModuleResolutionFailure` in @memberjunction/open-app-engine's
33
+ * `src/install/migration-runner.ts` — same heuristic, duplicated because the engine
34
+ * cannot depend on this package and cross-package re-exports are disallowed.
35
+ */
36
+ export function isResolutionFailure(error) {
37
+ const { code, message } = error ?? {};
38
+ if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND' || code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
39
+ return true;
40
+ }
41
+ return code === undefined && typeof message === 'string' && /^Cannot find (package|module) /.test(message);
42
+ }
43
+ /**
44
+ * Host anchors used to resolve runtime-configured packages. The mj.config.cjs that
45
+ * named the package is first — cwd can be a different checkout.
46
+ */
47
+ function hostAnchors(configFilePath) {
48
+ return [
49
+ configFilePath,
50
+ path.join(process.cwd(), 'package.json'),
51
+ process.argv[1],
52
+ ].filter((anchor) => typeof anchor === 'string' && anchor.length > 0);
53
+ }
54
+ /**
55
+ * Walks up from a resolved file looking for a `package.json` whose `name` matches.
56
+ * Used when the package's exports map does not expose `./package.json`.
57
+ */
58
+ function findPackageJsonWithName(fromFile, pkgName) {
59
+ let dir = path.dirname(fromFile);
60
+ for (let i = 0; i < 8; i++) {
61
+ const candidate = path.join(dir, 'package.json');
62
+ try {
63
+ const json = JSON.parse(readFileSync(candidate, 'utf8'));
64
+ if (json.name === pkgName) {
65
+ return candidate;
66
+ }
67
+ }
68
+ catch {
69
+ // missing or unreadable — keep walking
70
+ }
71
+ const parent = path.dirname(dir);
72
+ if (parent === dir) {
73
+ break;
74
+ }
75
+ dir = parent;
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * Resolves the on-disk `package.json` of a host-visible package so callers can
81
+ * introspect `memberjunction.serverExtensions` without relying on an exports map.
82
+ * Returns `null` when no host anchor can see the package.
83
+ */
84
+ export function resolvePackageJsonFromHost(pkgName, configFilePath) {
85
+ for (const anchor of hostAnchors(configFilePath)) {
86
+ const req = createRequire(anchor);
87
+ try {
88
+ return req.resolve(`${pkgName}/package.json`);
89
+ }
90
+ catch (resolveError) {
91
+ if (resolveError?.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
92
+ // exports map omits package.json — resolve the main entry and walk up.
93
+ }
94
+ else if (!isResolutionFailure(resolveError)) {
95
+ throw resolveError;
96
+ }
97
+ }
98
+ try {
99
+ const main = req.resolve(pkgName);
100
+ const found = findPackageJsonWithName(main, pkgName);
101
+ if (found) {
102
+ return found;
103
+ }
104
+ }
105
+ catch (mainError) {
106
+ if (!isResolutionFailure(mainError) && mainError?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
107
+ throw mainError;
108
+ }
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ /**
114
+ * Imports a runtime-configured package from the HOST application's context.
115
+ *
116
+ * Resolution and evaluation are handled separately on the fallback path: an anchor that
117
+ * cannot SEE the package means "try the next anchor", but once an anchor resolves it,
118
+ * any failure from loading the module (a missing transitive dependency, a throw in its
119
+ * top-level code) is the module's own problem and is surfaced as-is — never masked by
120
+ * the original "cannot find package" error.
121
+ *
122
+ * Note on the resolver: `createRequire().resolve` runs under CommonJS conditions, so a
123
+ * package whose exports map declares ONLY an `"import"` condition cannot be resolved by
124
+ * the fallback (surfaced with an actionable error). On a dual CJS/ESM package it selects
125
+ * the CJS entry, so `import()` of that file would load a second physical module instance
126
+ * alongside any ESM copy already in the process — fine for MJ-shaped single-condition
127
+ * packages, but keep it in mind before widening this mechanism.
128
+ */
129
+ export async function importFromHost(pkgName, configFilePath) {
130
+ try {
131
+ return (await import(pkgName));
132
+ }
133
+ catch (error) {
134
+ if (!isResolutionFailure(error)) {
135
+ throw error;
136
+ }
137
+ // Anchor priority: the mj.config.cjs that NAMED the package is the authoritative host,
138
+ // so it is consulted first — cwd can be a different checkout entirely (an operator
139
+ // launching instance A's server from instance B's directory would otherwise silently
140
+ // load B's copy). cwd and the process entrypoint are fallbacks for hosts whose config
141
+ // lives outside the tree that carries the packages (e.g. a workspace-root config with
142
+ // the packages linked into the app directory).
143
+ const anchors = hostAnchors(configFilePath);
144
+ let sawExportsMapMismatch = false;
145
+ for (const anchor of anchors) {
146
+ let resolved;
147
+ try {
148
+ resolved = createRequire(anchor).resolve(pkgName);
149
+ }
150
+ catch (resolveError) {
151
+ if (resolveError?.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
152
+ sawExportsMapMismatch = true;
153
+ continue;
154
+ }
155
+ if (!isResolutionFailure(resolveError)) {
156
+ throw resolveError;
157
+ }
158
+ continue; // this anchor can't see the package — try the next
159
+ }
160
+ // Resolved. Anything that fails from here is the module's own problem — surface it.
161
+ return (await import(pathToFileURL(resolved).href));
162
+ }
163
+ if (sawExportsMapMismatch) {
164
+ throw new Error(`Package '${pkgName}' is reachable from the host, but its exports map has no CJS-resolvable condition ` +
165
+ `(add a "default" or "require" condition to its package.json exports), so the host-anchored fallback cannot load it.`, { cause: error });
166
+ }
167
+ throw error; // no anchor resolved it — surface the original bare-import failure
168
+ }
169
+ }
170
+ //# sourceMappingURL=host-import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-import.js","sourceRoot":"","sources":["../src/host-import.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @module @memberjunction/dynamic-packages
3
+ *
4
+ * Process-agnostic loader for packages whose names are only known at runtime: Open App server
5
+ * and client packages recorded in `mj.config.cjs` `dynamicPackages.*[]` by `mj app install`,
6
+ * a host's generated packages under `codeGeneration.packages`, and the packages of the Open App
7
+ * whose repository a process is standing in (`mj-app.json`).
8
+ *
9
+ * MJAPI loads these at boot so the ClassFactory hands back an app's entity/action/provider
10
+ * subclasses. Every other MJ process — the `mj` CLI (`sync push`, `app …`, `test`, …), the MCP
11
+ * and A2A servers, the integration-test bootstrap, an ad-hoc script — needs the same behaviour,
12
+ * and this package is where that behaviour lives so each host is one call:
13
+ *
14
+ * ```ts
15
+ * import { LoadDynamicPackages, DiscoverMJConfig } from '@memberjunction/dynamic-packages';
16
+ *
17
+ * const { config, configFilePath } = DiscoverMJConfig();
18
+ * await LoadDynamicPackages({ processId: 'mcp', config, configFilePath });
19
+ * ```
20
+ *
21
+ * Entries can be scoped per process with `Processes` / `ExcludeProcesses`, whole processes can
22
+ * be switched off with `dynamicPackages.policy`, and `MJ_DYNAMIC_PACKAGES=none` (or a host
23
+ * flag that sets it) disables loading for one invocation.
24
+ */
25
+ export type { DiscoveredDynamicPackage, DynamicPackageEntry, DynamicPackageSkipReason, DynamicPackageSource, DynamicPackageTier, DynamicPackagesConfig, DynamicPackagesLogger, DynamicPackagesMode, DynamicPackagesModeSource, DynamicPackagesReport, FailedDynamicPackage, LoadDynamicPackagesOptions, LoadedDynamicPackage, SkippedDynamicPackage, WorkspaceHome, } from './types.js';
26
+ export { importFromHost, isResolutionFailure, resolvePackageJsonFromHost } from './host-import.js';
27
+ export { ANY_PROCESS, CliProcessId, DYNAMIC_PACKAGES_PROCESS_ENV_VAR, EffectiveProcessId, MatchesProcess, NormalizeProcessId, ProcessIdMatches, ResolveMostSpecific, } from './process-id.js';
28
+ export { DYNAMIC_PACKAGES_MODE_ENV_VAR, ResolveDynamicPackagesMode } from './mode.js';
29
+ export type { ResolvedDynamicPackagesMode } from './mode.js';
30
+ export { APP_MANIFEST_FILE_NAME, DiscoverAppManifestPackages, DiscoverGeneratedPackages, FindWorkspacePackageDir, GENERATED_PACKAGE_TYPES_BY_TIER, ReadDynamicPackagesConfig, } from './discover.js';
31
+ export type { AppManifestDiscovery } from './discover.js';
32
+ export type { MJConfigSearchStrategy } from './loader.js';
33
+ export { ConsoleDynamicPackagesLogger, DiscoverMJConfig, LoadDynamicPackages, mergeCandidates, ResetLoadedDynamicPackages, SilentDynamicPackagesLogger, StderrDynamicPackagesLogger, } from './loader.js';
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,YAAY,EACR,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACnG,OAAO,EACH,WAAW,EACX,YAAY,EACZ,gCAAgC,EAChC,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACtF,YAAY,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EACH,sBAAsB,EACtB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,+BAA+B,EAC/B,yBAAyB,GAC5B,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EACH,4BAA4B,EAC5B,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,0BAA0B,EAC1B,2BAA2B,EAC3B,2BAA2B,GAC9B,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { importFromHost, isResolutionFailure, resolvePackageJsonFromHost } from './host-import.js';
2
+ export { ANY_PROCESS, CliProcessId, DYNAMIC_PACKAGES_PROCESS_ENV_VAR, EffectiveProcessId, MatchesProcess, NormalizeProcessId, ProcessIdMatches, ResolveMostSpecific, } from './process-id.js';
3
+ export { DYNAMIC_PACKAGES_MODE_ENV_VAR, ResolveDynamicPackagesMode } from './mode.js';
4
+ export { APP_MANIFEST_FILE_NAME, DiscoverAppManifestPackages, DiscoverGeneratedPackages, FindWorkspacePackageDir, GENERATED_PACKAGE_TYPES_BY_TIER, ReadDynamicPackagesConfig, } from './discover.js';
5
+ export { ConsoleDynamicPackagesLogger, DiscoverMJConfig, LoadDynamicPackages, mergeCandidates, ResetLoadedDynamicPackages, SilentDynamicPackagesLogger, StderrDynamicPackagesLogger, } from './loader.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACnG,OAAO,EACH,WAAW,EACX,YAAY,EACZ,gCAAgC,EAChC,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAEtF,OAAO,EACH,sBAAsB,EACtB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,+BAA+B,EAC/B,yBAAyB,GAC5B,MAAM,eAAe,CAAC;AAGvB,OAAO,EACH,4BAA4B,EAC5B,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,0BAA0B,EAC1B,2BAA2B,EAC3B,2BAA2B,GAC9B,MAAM,aAAa,CAAC"}
@@ -0,0 +1,114 @@
1
+ import type { DiscoveredDynamicPackage, DynamicPackagesLogger, DynamicPackagesReport, LoadDynamicPackagesOptions } from './types.js';
2
+ /** Default output channel: plain console, the way MJAPI has always logged its boot. */
3
+ export declare const ConsoleDynamicPackagesLogger: DynamicPackagesLogger;
4
+ /**
5
+ * A logger that keeps stdout clean: progress and warnings go to stderr, verbose detail is dropped.
6
+ * For CLI hosts whose stdout is a machine-readable envelope (`--format=json`, `--output=json`) —
7
+ * the default console logger would print "Loading Open App server packages..." ahead of the JSON.
8
+ */
9
+ export declare const StderrDynamicPackagesLogger: DynamicPackagesLogger;
10
+ /** A logger that says nothing — for hosts that read the report and render it themselves. */
11
+ export declare const SilentDynamicPackagesLogger: DynamicPackagesLogger;
12
+ /** cosmiconfig search strategies accepted by {@link DiscoverMJConfig}. */
13
+ export type MJConfigSearchStrategy = 'none' | 'project' | 'global';
14
+ /**
15
+ * Discovers `mj.config.cjs` with cosmiconfig (module name `mj`) and returns the RAW object plus its
16
+ * path. Hosts whose own config loader Zod-strips `dynamicPackages` use this to hand the loader an
17
+ * unstripped view.
18
+ *
19
+ * @param searchFrom - Directory to start from. Defaults to `process.cwd()`.
20
+ * @param options.searchStrategy - cosmiconfig's search strategy. Defaults to `'global'` (walk up
21
+ * to the home directory — what MJAPI, the `mj` CLI and CodeGen do). A host whose own config
22
+ * loader only looks in the working directory should pass `'none'` so the packages it loads come
23
+ * from the same file its database settings came from.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const { config, configFilePath } = DiscoverMJConfig();
28
+ * await LoadDynamicPackages({ processId: 'mcp', config, configFilePath });
29
+ * ```
30
+ */
31
+ export declare function DiscoverMJConfig(searchFrom?: string, options?: {
32
+ searchStrategy?: MJConfigSearchStrategy;
33
+ }): {
34
+ config: Record<string, unknown>;
35
+ configFilePath?: string;
36
+ };
37
+ /**
38
+ * Loads every dynamic package that applies to `options.processId` and returns a report of what
39
+ * happened to each entry. This is the one call a host makes; everything else in the package is a
40
+ * primitive it composes.
41
+ *
42
+ * @remarks
43
+ * **Order of operations.** Mode is resolved first (`MJ_DYNAMIC_PACKAGES` env var → `options.mode`
44
+ * → `dynamicPackages.policy` → `'load'`). Candidates are then discovered generic-to-specific —
45
+ * the host's `codeGeneration.packages`, then `dynamicPackages.<tier>[]`, then the `mj-app.json`
46
+ * beside the config — and merged by package name ({@link mergeCandidates}). Under mode `'none'`
47
+ * every candidate is reported as skipped and nothing is imported. Otherwise each candidate is
48
+ * filtered (`Enabled === false`, then `Processes` / `ExcludeProcesses` via {@link MatchesProcess}),
49
+ * served from the per-process cache when an earlier call already loaded it (module returned,
50
+ * startup export **not** re-run), or imported through {@link importFromHost} with an on-disk
51
+ * workspace fallback for manifest entries.
52
+ *
53
+ * **Why order matters.** `@RegisterClass` resolves by load-order priority — the last registration
54
+ * for a key wins — so importing generic-to-specific is what makes an Open App's server subclass
55
+ * beat its generated one, and the app you are standing in beat an installed copy.
56
+ *
57
+ * **When to call it.** After the host's class-registration manifest has been imported (so app
58
+ * registrations land last) and before any database provider exists (a `StartupExport` may rely on
59
+ * nothing but the ClassFactory).
60
+ *
61
+ * **Failure model.** Throws only when `processId` is missing. A package that no anchor can resolve
62
+ * is `NotFound` (expected before `npm install`, or for an unbuilt workspace member); one that
63
+ * resolves but throws while loading is `Failed` with its own error, logged on the warn path and
64
+ * never masked by a resolution message. Boot never crashes because of an app package.
65
+ *
66
+ * @param options - Process identity, the raw config and its path, tier, discovery switches, a
67
+ * programmatic mode override, and the logger. See {@link LoadDynamicPackagesOptions}.
68
+ * @returns The {@link DynamicPackagesReport}: `Loaded`, `Skipped` (with a reason), `NotFound`,
69
+ * `Failed`, plus the resolved mode and where it came from.
70
+ *
71
+ * @example Minimal host
72
+ * ```ts
73
+ * const { config, configFilePath } = DiscoverMJConfig();
74
+ * const report = await LoadDynamicPackages({ processId: 'mcp', config, configFilePath });
75
+ * for (const failed of report.Failed) {
76
+ * console.error(`app package ${failed.Entry.PackageName} failed to load`, failed.Error);
77
+ * }
78
+ * ```
79
+ *
80
+ * @example Reading a convention off a loaded module (what MJAPI does for RESOLVER_PATHS)
81
+ * ```ts
82
+ * const resolverPaths = report.Loaded.flatMap((l) => {
83
+ * const paths = l.Module.RESOLVER_PATHS;
84
+ * return Array.isArray(paths) ? (paths as string[]) : [];
85
+ * });
86
+ * ```
87
+ *
88
+ * @example A nested host that inherits the outer process identity
89
+ * ```ts
90
+ * await LoadDynamicPackages({
91
+ * processId: EffectiveProcessId('ai-cli'), // 'cli:ai:agents:run' when run under `mj ai …`
92
+ * config, configFilePath,
93
+ * log: StderrDynamicPackagesLogger, // stdout may be a JSON envelope
94
+ * });
95
+ * ```
96
+ */
97
+ export declare function LoadDynamicPackages(options: LoadDynamicPackagesOptions): Promise<DynamicPackagesReport>;
98
+ /**
99
+ * Collapses candidates that name the same package into one, in discovery order.
100
+ *
101
+ * The `mj.config.cjs` entry is the operator's authority for a package: it carries `Enabled`
102
+ * (what `mj app disable` writes) and the process scoping. So when an `mj-app.json` beside the
103
+ * config names a package the config also names, the config entry decides whether and where it
104
+ * loads — but the manifest's on-disk location is kept as the resolution fallback, so a package
105
+ * the host cannot `require.resolve` still loads from the workspace. Every later duplicate is
106
+ * returned separately so the report can list it as skipped.
107
+ */
108
+ export declare function mergeCandidates(all: DiscoveredDynamicPackage[]): {
109
+ candidates: DiscoveredDynamicPackage[];
110
+ duplicates: DiscoveredDynamicPackage[];
111
+ };
112
+ /** Test seam: forget what has been loaded so a fresh process can be simulated. */
113
+ export declare function ResetLoadedDynamicPackages(): void;
114
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EACR,wBAAwB,EAExB,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAE7B,MAAM,YAAY,CAAC;AAEpB,uFAAuF;AACvF,eAAO,MAAM,4BAA4B,EAAE,qBAI1C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAE,qBAOzC,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,2BAA2B,EAAE,qBAIzC,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC5B,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,sBAAsB,CAAA;CAAE,GACtD;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAO9D;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAkF7G;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,wBAAwB,EAAE,GAAG;IAC9D,UAAU,EAAE,wBAAwB,EAAE,CAAC;IACvC,UAAU,EAAE,wBAAwB,EAAE,CAAC;CAC1C,CAuBA;AAiHD,kFAAkF;AAClF,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}