@memberjunction/cli 6.2.0-edge.5 → 6.2.0-edge.7
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/README.md +95 -0
- package/dist/commands/agent/init.d.ts +5 -0
- package/dist/commands/agent/init.d.ts.map +1 -1
- package/dist/commands/agent/init.js +109 -0
- package/dist/commands/agent/init.js.map +1 -1
- package/dist/commands/ai/actions/run.d.ts.map +1 -1
- package/dist/commands/ai/actions/run.js +5 -1
- package/dist/commands/ai/actions/run.js.map +1 -1
- package/dist/commands/ai/agents/run.d.ts +1 -0
- package/dist/commands/ai/agents/run.d.ts.map +1 -1
- package/dist/commands/ai/agents/run.js +20 -6
- package/dist/commands/ai/agents/run.js.map +1 -1
- package/dist/commands/ai/prompts/run.d.ts.map +1 -1
- package/dist/commands/ai/prompts/run.js +5 -1
- package/dist/commands/ai/prompts/run.js.map +1 -1
- package/dist/commands/app/install.d.ts.map +1 -1
- package/dist/commands/app/install.js +17 -2
- package/dist/commands/app/install.js.map +1 -1
- package/dist/commands/app/upgrade.d.ts.map +1 -1
- package/dist/commands/app/upgrade.js +7 -1
- package/dist/commands/app/upgrade.js.map +1 -1
- package/dist/commands/dev/workspace/clean.d.ts +2 -1
- package/dist/commands/dev/workspace/clean.d.ts.map +1 -1
- package/dist/commands/dev/workspace/clean.js +4 -3
- package/dist/commands/dev/workspace/clean.js.map +1 -1
- package/dist/commands/dev/workspace/doctor.d.ts.map +1 -1
- package/dist/commands/dev/workspace/doctor.js +3 -2
- package/dist/commands/dev/workspace/doctor.js.map +1 -1
- package/dist/commands/dev/workspace/index.d.ts +2 -2
- package/dist/commands/dev/workspace/index.d.ts.map +1 -1
- package/dist/commands/dev/workspace/index.js +12 -9
- package/dist/commands/dev/workspace/index.js.map +1 -1
- package/dist/commands/dev/workspace/status.d.ts +1 -1
- package/dist/commands/dev/workspace/status.js +1 -1
- package/dist/commands/query/refresh.d.ts +25 -0
- package/dist/commands/query/refresh.d.ts.map +1 -0
- package/dist/commands/query/refresh.js +67 -0
- package/dist/commands/query/refresh.js.map +1 -0
- package/dist/commands/query/usage.d.ts +7 -0
- package/dist/commands/query/usage.d.ts.map +1 -0
- package/dist/commands/query/usage.js +10 -0
- package/dist/commands/query/usage.js.map +1 -0
- package/dist/commands/user/clone.d.ts.map +1 -1
- package/dist/commands/user/clone.js +1 -0
- package/dist/commands/user/clone.js.map +1 -1
- package/dist/init-templates/citizen-builder/.agents/skills/new-agent/SKILL.md +19 -6
- package/dist/init-templates/citizen-builder/.agents/skills/test-agent/SKILL.md +4 -1
- package/dist/init-templates/citizen-builder/.claude/skills/new-agent/SKILL.md +19 -6
- package/dist/init-templates/citizen-builder/.claude/skills/test-agent/SKILL.md +4 -1
- package/dist/init-templates/citizen-builder/.env.example +4 -2
- package/dist/init-templates/citizen-builder/AGENTS.md +136 -40
- package/dist/init-templates/citizen-builder/README.md +18 -5
- package/dist/init-templates/citizen-builder/docker-compose.yml +6 -5
- package/dist/init-templates/citizen-builder/metadata/.mj-sync.json +7 -4
- package/dist/init-templates/citizen-builder/metadata/agents/.customer-insight-agent.json +17 -13
- package/dist/init-templates/citizen-builder/metadata/prompts/.customer-insight-prompt.json +3 -1
- package/dist/init-templates/citizen-builder/metadata/prompts/templates/customer-insight.template.md +8 -0
- package/dist/init-templates/citizen-builder/scripts/docker-entrypoint.sh +140 -38
- package/dist/init-templates/citizen-builder/scripts/sync-metadata.sh +1 -1
- package/dist/lib/dev-workspace/build-output.d.ts +22 -0
- package/dist/lib/dev-workspace/build-output.d.ts.map +1 -0
- package/dist/lib/dev-workspace/build-output.js +146 -0
- package/dist/lib/dev-workspace/build-output.js.map +1 -0
- package/dist/lib/dev-workspace/build.d.ts +6 -6
- package/dist/lib/dev-workspace/build.d.ts.map +1 -1
- package/dist/lib/dev-workspace/build.js +59 -34
- package/dist/lib/dev-workspace/build.js.map +1 -1
- package/dist/lib/dev-workspace/clean.d.ts +19 -5
- package/dist/lib/dev-workspace/clean.d.ts.map +1 -1
- package/dist/lib/dev-workspace/clean.js +44 -12
- package/dist/lib/dev-workspace/clean.js.map +1 -1
- package/dist/lib/dev-workspace/detect.d.ts.map +1 -1
- package/dist/lib/dev-workspace/detect.js +0 -2
- package/dist/lib/dev-workspace/detect.js.map +1 -1
- package/dist/lib/dev-workspace/doctor.d.ts.map +1 -1
- package/dist/lib/dev-workspace/doctor.js +63 -5
- package/dist/lib/dev-workspace/doctor.js.map +1 -1
- package/dist/lib/dev-workspace/lockfile.d.ts +9 -1
- package/dist/lib/dev-workspace/lockfile.d.ts.map +1 -1
- package/dist/lib/dev-workspace/lockfile.js +19 -8
- package/dist/lib/dev-workspace/lockfile.js.map +1 -1
- package/dist/lib/dev-workspace/types.d.ts +40 -16
- package/dist/lib/dev-workspace/types.d.ts.map +1 -1
- package/dist/lib/dev-workspace/types.js +2 -2
- package/dist/lib/dev-workspace/write.d.ts +7 -2
- package/dist/lib/dev-workspace/write.d.ts.map +1 -1
- package/dist/lib/dev-workspace/write.js +7 -3
- package/dist/lib/dev-workspace/write.js.map +1 -1
- package/dist/lib/domain-profiles.d.ts.map +1 -1
- package/dist/lib/domain-profiles.js +4 -0
- package/dist/lib/domain-profiles.js.map +1 -1
- package/dist/lib/query-refresh.d.ts +23 -0
- package/dist/lib/query-refresh.d.ts.map +1 -0
- package/dist/lib/query-refresh.js +33 -0
- package/dist/lib/query-refresh.js.map +1 -0
- package/oclif.manifest.json +2615 -2471
- package/package.json +27 -26
|
@@ -58,31 +58,37 @@ if [ ! -f "$STATE_DIR/provisioned" ]; then
|
|
|
58
58
|
echo "========================================================================"
|
|
59
59
|
|
|
60
60
|
if [ ! -f "$STATE_DIR/core-installed" ]; then
|
|
61
|
-
# Generate install configuration
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
61
|
+
# Generate install configuration safely via JSON.stringify so special characters in passwords/keys are escaped
|
|
62
|
+
node -e "
|
|
63
|
+
const fs = require('fs');
|
|
64
|
+
const cfg = {
|
|
65
|
+
PackageManager: 'pnpm',
|
|
66
|
+
DatabaseHost: process.env.DB_HOST || 'sqlserver',
|
|
67
|
+
DatabasePort: Number(process.env.DB_PORT || 1433),
|
|
68
|
+
DatabaseName: process.env.DB_DATABASE || 'MemberJunction',
|
|
69
|
+
DatabaseTrustCert: true,
|
|
70
|
+
DatabaseAdminUser: 'sa',
|
|
71
|
+
DatabaseAdminPassword: process.env.DB_PASSWORD || '',
|
|
72
|
+
CodeGenUser: 'sa',
|
|
73
|
+
CodeGenPassword: process.env.DB_PASSWORD || '',
|
|
74
|
+
APIUser: 'sa',
|
|
75
|
+
APIPassword: process.env.DB_PASSWORD || '',
|
|
76
|
+
APIPort: 4000,
|
|
77
|
+
AuthProvider: 'local',
|
|
78
|
+
BaseEncryptionKey: process.env.MJ_BASE_ENCRYPTION_KEY || 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=',
|
|
79
|
+
CreateNewUser: {
|
|
80
|
+
Username: 'admin',
|
|
81
|
+
Email: 'admin@memberjunction.org',
|
|
82
|
+
FirstName: 'Admin',
|
|
83
|
+
LastName: 'User'
|
|
84
|
+
},
|
|
85
|
+
OpenAIKey: process.env.AI_VENDOR_API_KEY__OpenAILLM || '',
|
|
86
|
+
AnthropicKey: process.env.AI_VENDOR_API_KEY__AnthropicLLM || '',
|
|
87
|
+
GeminiKey: process.env.AI_VENDOR_API_KEY__GeminiLLM || '',
|
|
88
|
+
MistralKey: process.env.AI_VENDOR_API_KEY__MistralLLM || ''
|
|
89
|
+
};
|
|
90
|
+
fs.writeFileSync('/tmp/install.config.json', JSON.stringify(cfg, null, 2), { mode: 0o600 });
|
|
91
|
+
"
|
|
86
92
|
|
|
87
93
|
# Install the release this workspace is pinned to (MJ_VERSION, the same release as the CLI
|
|
88
94
|
# in this container). Without a tag, `mj install --yes` takes the newest stable GitHub
|
|
@@ -96,20 +102,17 @@ EOF
|
|
|
96
102
|
|
|
97
103
|
# Execute headless install. Safe to retry: the installer checkpoints completed phases.
|
|
98
104
|
mj install --yes --fast --config /tmp/install.config.json --dir /workspace "${INSTALL_TAG_ARGS[@]}"
|
|
105
|
+
rm -f /tmp/install.config.json
|
|
99
106
|
touch "$STATE_DIR/core-installed"
|
|
100
107
|
pnpm add -w @angular/compiler@21.2.22 2>/dev/null || true
|
|
101
108
|
fi
|
|
102
109
|
|
|
103
110
|
# Install Open App business context (More Cheese default or custom enterprise app)
|
|
111
|
+
# Note: GITHUB_TOKEN is read directly from the environment by OpenAppEngine's GitHubClient
|
|
104
112
|
if [ ! -f "$STATE_DIR/app-installed" ]; then
|
|
105
113
|
APP_URL="${OPEN_APP_INSTALL_URL:-https://github.com/MemberJunction/more-cheese}"
|
|
106
114
|
echo "Installing business context Open App: $APP_URL..."
|
|
107
|
-
|
|
108
|
-
AUTH_APP_URL=$(echo "$APP_URL" | sed -E "s|https://github.com/|https://$GITHUB_TOKEN@github.com/|")
|
|
109
|
-
mj app install "$AUTH_APP_URL"
|
|
110
|
-
else
|
|
111
|
-
mj app install "$APP_URL"
|
|
112
|
-
fi
|
|
115
|
+
mj app install "$APP_URL"
|
|
113
116
|
touch "$STATE_DIR/app-installed"
|
|
114
117
|
fi
|
|
115
118
|
|
|
@@ -120,8 +123,8 @@ EOF
|
|
|
120
123
|
|| echo "WARNING: capabilities catalog generation failed, so the catalog may be missing or stale. Re-run scripts/generate-capabilities.sh."
|
|
121
124
|
fi
|
|
122
125
|
|
|
123
|
-
# Push starter metadata. --ci: never prompt (there is no terminal here)
|
|
124
|
-
#
|
|
126
|
+
# Push starter metadata. --ci: never prompt (there is no terminal here), create
|
|
127
|
+
# Template records for @file: prompt templates on first push, and exit non-zero on error.
|
|
125
128
|
if [ -d "/work/metadata" ]; then
|
|
126
129
|
mj sync push --dir /work/metadata --ci
|
|
127
130
|
fi
|
|
@@ -156,10 +159,82 @@ if [ -f "apps/MJExplorer/package.json" ]; then
|
|
|
156
159
|
"
|
|
157
160
|
fi
|
|
158
161
|
|
|
162
|
+
# Ensure turbo.json passes through AI vendor API keys, DB settings, and MJ_DEV_AUTH_ENABLED to child processes
|
|
163
|
+
if [ -f "turbo.json" ]; then
|
|
164
|
+
node -e "
|
|
165
|
+
const fs = require('fs');
|
|
166
|
+
try {
|
|
167
|
+
const turbo = JSON.parse(fs.readFileSync('turbo.json', 'utf8'));
|
|
168
|
+
const passThrough = new Set(turbo.globalPassThroughEnv || []);
|
|
169
|
+
let changed = false;
|
|
170
|
+
for (const entry of ['AI_VENDOR_API_KEY__*', 'MJ_*', 'DB_*', 'CODEGEN_DB_*', 'GRAPHQL_*', 'OPEN_APP_*', 'GITHUB_TOKEN']) {
|
|
171
|
+
if (!passThrough.has(entry)) {
|
|
172
|
+
passThrough.add(entry);
|
|
173
|
+
changed = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (changed) {
|
|
177
|
+
turbo.globalPassThroughEnv = Array.from(passThrough);
|
|
178
|
+
fs.writeFileSync('turbo.json', JSON.stringify(turbo, null, 2) + '\n');
|
|
179
|
+
}
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.warn('Notice: turbo.json check skipped:', e.message);
|
|
182
|
+
}
|
|
183
|
+
"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# Sync container environment variables (API keys, encryption key, DB_USER, local dev auth) into workspace .env files
|
|
187
|
+
export MJ_DEV_AUTH_ENABLED="${MJ_DEV_AUTH_ENABLED:-true}"
|
|
188
|
+
node -e "
|
|
189
|
+
const fs = require('fs');
|
|
190
|
+
const upsertVar = (content, key, val) => {
|
|
191
|
+
const re = new RegExp('^' + key + '=.*$', 'm');
|
|
192
|
+
const line = key + '=' + JSON.stringify(String(val));
|
|
193
|
+
if (re.test(content)) return content.replace(re, () => line);
|
|
194
|
+
return content + (content.endsWith('\n') ? '' : '\n') + line + '\n';
|
|
195
|
+
};
|
|
196
|
+
const keysToSync = [
|
|
197
|
+
'DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD',
|
|
198
|
+
'MJ_BASE_ENCRYPTION_KEY', 'MJ_DEV_AUTH_ENABLED',
|
|
199
|
+
'AI_VENDOR_API_KEY__OpenAILLM', 'AI_VENDOR_API_KEY__AnthropicLLM',
|
|
200
|
+
'AI_VENDOR_API_KEY__GeminiLLM', 'AI_VENDOR_API_KEY__GroqLLM', 'AI_VENDOR_API_KEY__MistralLLM'
|
|
201
|
+
];
|
|
202
|
+
for (const envFile of ['.env', 'apps/MJAPI/.env', 'packages/MJAPI/.env']) {
|
|
203
|
+
if (fs.existsSync(envFile)) {
|
|
204
|
+
let content = fs.readFileSync(envFile, 'utf8');
|
|
205
|
+
for (const k of keysToSync) {
|
|
206
|
+
const v = process.env[k];
|
|
207
|
+
if (v !== undefined && v !== '') {
|
|
208
|
+
content = upsertVar(content, k, v);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
content = upsertVar(content, 'DB_USER', process.env.DB_USERNAME || 'sa');
|
|
212
|
+
fs.writeFileSync(envFile, content);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (process.env.MJ_DEV_AUTH_ENABLED === 'true') {
|
|
216
|
+
for (const ngEnv of [
|
|
217
|
+
'apps/MJExplorer/src/environments/environment.ts',
|
|
218
|
+
'apps/MJExplorer/src/environments/environment.development.ts',
|
|
219
|
+
'packages/MJExplorer/src/environments/environment.ts',
|
|
220
|
+
'packages/MJExplorer/src/environments/environment.development.ts',
|
|
221
|
+
]) {
|
|
222
|
+
if (fs.existsSync(ngEnv)) {
|
|
223
|
+
const src = fs.readFileSync(ngEnv, 'utf8');
|
|
224
|
+
const next = src.replace(/AUTH_TYPE:\s*['\"][^'\"]*['\"]/g, \"AUTH_TYPE: 'local'\");
|
|
225
|
+
if (next !== src) {
|
|
226
|
+
fs.writeFileSync(ngEnv, next);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
"
|
|
232
|
+
|
|
159
233
|
# Sanitize dynamicPackages.client so non-browser packages are not bundled into MJExplorer
|
|
160
234
|
if [ -f "mj.config.cjs" ]; then
|
|
161
235
|
node -e "
|
|
162
236
|
const fs = require('fs');
|
|
237
|
+
const vm = require('vm');
|
|
163
238
|
try {
|
|
164
239
|
const config = require('./mj.config.cjs');
|
|
165
240
|
if (config.dynamicPackages && Array.isArray(config.dynamicPackages.client)) {
|
|
@@ -173,10 +248,37 @@ if [ -f "mj.config.cjs" ]; then
|
|
|
173
248
|
}
|
|
174
249
|
});
|
|
175
250
|
if (changed) {
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
251
|
+
const code = fs.readFileSync('mj.config.cjs', 'utf8');
|
|
252
|
+
const dynMatch = code.match(/dynamicPackages\s*:\s*\{/);
|
|
253
|
+
if (dynMatch && dynMatch.index !== undefined) {
|
|
254
|
+
const afterDyn = code.slice(dynMatch.index);
|
|
255
|
+
const clientRel = afterDyn.match(/client\s*:\s*\[/);
|
|
256
|
+
if (clientRel && clientRel.index !== undefined) {
|
|
257
|
+
const openPos = dynMatch.index + clientRel.index + clientRel[0].length - 1;
|
|
258
|
+
let depth = 1, pos = openPos + 1, inStr = null;
|
|
259
|
+
while (pos < code.length && depth > 0) {
|
|
260
|
+
const ch = code[pos];
|
|
261
|
+
if (inStr) {
|
|
262
|
+
if (ch === '\\\\') { pos += 2; continue; }
|
|
263
|
+
if (ch === inStr) inStr = null;
|
|
264
|
+
} else if (ch === '\"' || ch === \"'\" || ch === '\`') {
|
|
265
|
+
inStr = ch;
|
|
266
|
+
} else if (ch === '[') {
|
|
267
|
+
depth++;
|
|
268
|
+
} else if (ch === ']') {
|
|
269
|
+
depth--;
|
|
270
|
+
}
|
|
271
|
+
pos++;
|
|
272
|
+
}
|
|
273
|
+
if (depth === 0) {
|
|
274
|
+
const closePos = pos - 1;
|
|
275
|
+
const serialized = JSON.stringify(config.dynamicPackages.client, null, 2).replace(/\n/g, '\n ');
|
|
276
|
+
const updated = code.slice(0, openPos) + serialized + code.slice(closePos + 1);
|
|
277
|
+
vm.compileFunction(updated);
|
|
278
|
+
fs.writeFileSync('mj.config.cjs', updated);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
180
282
|
}
|
|
181
283
|
}
|
|
182
284
|
} catch (e) {
|
|
@@ -212,7 +314,7 @@ fi
|
|
|
212
314
|
echo "========================================================================"
|
|
213
315
|
echo " 🎉 MemberJunction Stack is Running!"
|
|
214
316
|
echo " MJAPI: http://localhost:4000 (GraphQL & API)"
|
|
215
|
-
echo " MJExplorer: http://localhost:4200 (Web Interface)"
|
|
317
|
+
echo " MJExplorer: http://localhost:4200 (Web Interface — Zero-Config Local Dev Auth active)"
|
|
216
318
|
echo " Database: localhost:$DB_PORT (DB: $DB_DATABASE)"
|
|
217
319
|
echo "========================================================================"
|
|
218
320
|
|
|
@@ -9,6 +9,6 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
9
9
|
cd "$DIR"
|
|
10
10
|
|
|
11
11
|
echo "Pushing metadata changes to MemberJunction database..."
|
|
12
|
-
docker compose exec -T mj mj sync push --dir /work/metadata
|
|
12
|
+
docker compose exec -T mj mj sync push --dir /work/metadata "$@"
|
|
13
13
|
|
|
14
14
|
echo "Metadata sync complete!"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BuildOutputEntry, MemberPackageJson } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The entry file a package declares, relative to its directory, or null when it declares none.
|
|
4
|
+
* `main` first: it is what Node and the Angular builders resolve for every MJ package. Pure.
|
|
5
|
+
*/
|
|
6
|
+
export declare function ResolveEntryFile(pkg: MemberPackageJson): string | null;
|
|
7
|
+
/**
|
|
8
|
+
* The output folder for a declared entry file: its first path segment (`dist/esm/index.js` →
|
|
9
|
+
* `dist`). Null when the entry sits at the package root or inside `src/` — such a package ships
|
|
10
|
+
* its source, so there is no build output to compare. Pure.
|
|
11
|
+
*/
|
|
12
|
+
export declare function OutputDirOf(entryFile: string): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Reads the build state of every member package that has a `build` script and a `src/` folder.
|
|
15
|
+
*
|
|
16
|
+
* `memberNames` is passed in for the same reason {@link CollectClientPackageCensus} takes it:
|
|
17
|
+
* detection finds every Open App repo on disk, and an excluded sibling is not this workspace's
|
|
18
|
+
* concern. A package whose entry points into `src/` or sits at its root ships source and is left
|
|
19
|
+
* out — it has no output to go stale.
|
|
20
|
+
*/
|
|
21
|
+
export declare function CollectBuildOutputCensus(parentDir: string, memberNames: readonly string[]): BuildOutputEntry[];
|
|
22
|
+
//# sourceMappingURL=build-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-output.d.ts","sourceRoot":"","sources":["../../../src/lib/dev-workspace/build-output.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAoB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgCxF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAItE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5D;AAuCD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAsB9G"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-output census for `mj dev workspace doctor`.
|
|
3
|
+
*
|
|
4
|
+
* SIDE EFFECTS: READ-ONLY filesystem access — `existsSync`, `readdirSync` and `statSync` under
|
|
5
|
+
* member package directories. Never writes, never spawns a build.
|
|
6
|
+
*
|
|
7
|
+
* Linking a member package into the workspace links its SOURCE folder, so a package whose output
|
|
8
|
+
* was never built, or was built before its last source edit, still resolves at install time. It
|
|
9
|
+
* fails only when a host imports it: a missing export, or "No form is registered for …" in
|
|
10
|
+
* Explorer. This census names those packages before anything starts (#5494).
|
|
11
|
+
*
|
|
12
|
+
* @module lib/dev-workspace/build-output
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { DetectCandidates } from './detect.js';
|
|
17
|
+
/** Where a package builds to when its package.json names no entry point. */
|
|
18
|
+
const DEFAULT_OUTPUT_DIR = 'dist';
|
|
19
|
+
/** Source folder whose files the output is compared against. */
|
|
20
|
+
const SOURCE_DIR = 'src';
|
|
21
|
+
/** Directories never descended into while finding the newest file. */
|
|
22
|
+
const SKIPPED_DIRS = new Set(['node_modules', '__tests__', '__mocks__']);
|
|
23
|
+
/** Test files: editing one does not make the build output stale. */
|
|
24
|
+
const TEST_FILE_PATTERN = /\.(spec|test)\.[cm]?[jt]sx?$/;
|
|
25
|
+
/**
|
|
26
|
+
* The root export's file from an `exports` field: a string, or the `.` entry of a conditions
|
|
27
|
+
* object, itself a string or a conditions object whose `import` / `default` / `require` is one.
|
|
28
|
+
* Any other shape returns null, and the caller falls back to `main` / `module`. Pure.
|
|
29
|
+
*/
|
|
30
|
+
function rootExportPath(exportsField) {
|
|
31
|
+
if (typeof exportsField === 'string')
|
|
32
|
+
return exportsField;
|
|
33
|
+
if (exportsField === null || typeof exportsField !== 'object')
|
|
34
|
+
return null;
|
|
35
|
+
const root = exportsField['.'];
|
|
36
|
+
if (typeof root === 'string')
|
|
37
|
+
return root;
|
|
38
|
+
if (root === null || typeof root !== 'object')
|
|
39
|
+
return null;
|
|
40
|
+
for (const condition of ['import', 'default', 'require']) {
|
|
41
|
+
const target = root[condition];
|
|
42
|
+
if (typeof target === 'string')
|
|
43
|
+
return target;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The entry file a package declares, relative to its directory, or null when it declares none.
|
|
49
|
+
* `main` first: it is what Node and the Angular builders resolve for every MJ package. Pure.
|
|
50
|
+
*/
|
|
51
|
+
export function ResolveEntryFile(pkg) {
|
|
52
|
+
const entry = pkg.main ?? pkg.module ?? rootExportPath(pkg.exports);
|
|
53
|
+
if (entry === undefined || entry === null || entry.length === 0)
|
|
54
|
+
return null;
|
|
55
|
+
return path.normalize(entry);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The output folder for a declared entry file: its first path segment (`dist/esm/index.js` →
|
|
59
|
+
* `dist`). Null when the entry sits at the package root or inside `src/` — such a package ships
|
|
60
|
+
* its source, so there is no build output to compare. Pure.
|
|
61
|
+
*/
|
|
62
|
+
export function OutputDirOf(entryFile) {
|
|
63
|
+
const segments = entryFile.split(path.sep).filter((segment) => segment.length > 0 && segment !== '.');
|
|
64
|
+
if (segments.length < 2)
|
|
65
|
+
return null;
|
|
66
|
+
const first = segments[0];
|
|
67
|
+
if (first === '..' || first === SOURCE_DIR)
|
|
68
|
+
return null;
|
|
69
|
+
return first;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Newest modification time of any file under `dir`, skipping {@link SKIPPED_DIRS}, dot
|
|
73
|
+
* directories and (when `skipTests`) test files. Returns 0 for a missing or empty folder.
|
|
74
|
+
*/
|
|
75
|
+
function newestFileTime(dir, skipTests) {
|
|
76
|
+
let newest = 0;
|
|
77
|
+
const pending = [dir];
|
|
78
|
+
for (let current = pending.pop(); current !== undefined; current = pending.pop()) {
|
|
79
|
+
let entries;
|
|
80
|
+
try {
|
|
81
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
if (entry.isDirectory()) {
|
|
88
|
+
if (!SKIPPED_DIRS.has(entry.name) && !entry.name.startsWith('.'))
|
|
89
|
+
pending.push(path.join(current, entry.name));
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!entry.isFile())
|
|
93
|
+
continue;
|
|
94
|
+
if (skipTests && TEST_FILE_PATTERN.test(entry.name))
|
|
95
|
+
continue;
|
|
96
|
+
newest = Math.max(newest, statSync(path.join(current, entry.name)).mtimeMs);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return newest;
|
|
100
|
+
}
|
|
101
|
+
/** Build state of one package directory with the given output folder and optional entry file. */
|
|
102
|
+
function readBuildState(packageDir, outputDir, entryFile) {
|
|
103
|
+
const outputPath = path.join(packageDir, outputDir);
|
|
104
|
+
const builtMarker = entryFile === null ? outputPath : path.join(packageDir, entryFile);
|
|
105
|
+
if (!existsSync(builtMarker))
|
|
106
|
+
return 'missing';
|
|
107
|
+
const newestOutput = newestFileTime(outputPath, false);
|
|
108
|
+
if (newestOutput === 0)
|
|
109
|
+
return 'missing';
|
|
110
|
+
return newestFileTime(path.join(packageDir, SOURCE_DIR), true) > newestOutput ? 'stale' : 'fresh';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Reads the build state of every member package that has a `build` script and a `src/` folder.
|
|
114
|
+
*
|
|
115
|
+
* `memberNames` is passed in for the same reason {@link CollectClientPackageCensus} takes it:
|
|
116
|
+
* detection finds every Open App repo on disk, and an excluded sibling is not this workspace's
|
|
117
|
+
* concern. A package whose entry points into `src/` or sits at its root ships source and is left
|
|
118
|
+
* out — it has no output to go stale.
|
|
119
|
+
*/
|
|
120
|
+
export function CollectBuildOutputCensus(parentDir, memberNames) {
|
|
121
|
+
const inWorkspace = new Set(memberNames);
|
|
122
|
+
const entries = [];
|
|
123
|
+
for (const member of DetectCandidates(parentDir).filter((candidate) => inWorkspace.has(candidate.Name))) {
|
|
124
|
+
for (const pkg of member.Packages) {
|
|
125
|
+
const name = pkg.PackageJson.name;
|
|
126
|
+
if (name === undefined || name.length === 0 || pkg.PackageJson.scripts?.build === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
const packageDir = path.join(member.Path, pkg.RelPath);
|
|
129
|
+
if (!existsSync(path.join(packageDir, SOURCE_DIR)))
|
|
130
|
+
continue;
|
|
131
|
+
const entryFile = ResolveEntryFile(pkg.PackageJson);
|
|
132
|
+
const outputDir = entryFile === null ? DEFAULT_OUTPUT_DIR : OutputDirOf(entryFile);
|
|
133
|
+
if (outputDir === null)
|
|
134
|
+
continue;
|
|
135
|
+
entries.push({
|
|
136
|
+
Package: name,
|
|
137
|
+
Repo: member.Name,
|
|
138
|
+
RelPath: pkg.RelPath,
|
|
139
|
+
OutputDir: outputDir,
|
|
140
|
+
State: readBuildState(packageDir, outputDir, entryFile),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return entries.sort((a, b) => (a.Package < b.Package ? -1 : a.Package > b.Package ? 1 : 0));
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=build-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-output.js","sourceRoot":"","sources":["../../../src/lib/dev-workspace/build-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CandidateRepo, DevDepConflict, DuplicateClientPackage, DuplicateFamilyPackage, DuplicateProviderLink, MemberPackageJson, OpenAppClientPackage, PackageExtension, RootPackageJsonResult, ShellPeerGap,
|
|
1
|
+
import type { CandidateRepo, DevDepConflict, DuplicateClientPackage, DuplicateFamilyPackage, DuplicateProviderLink, MemberPackageJson, OpenAppClientPackage, PackageExtension, RootPackageJsonResult, ShellPeerGap, WorkspaceShell } from './types.js';
|
|
2
2
|
/** Build-scripts allowlist proven on the core-monorepo spike. */
|
|
3
3
|
export declare const ONLY_BUILT_DEPENDENCIES: readonly string[];
|
|
4
4
|
/**
|
|
@@ -65,6 +65,11 @@ export declare const PEER_DEPENDENCY_RULES: {
|
|
|
65
65
|
export declare const FALLBACK_PNPM_PIN = "pnpm@10.33.0";
|
|
66
66
|
/** The proven root-manifest devDependency baseline; fills gaps the union leaves. */
|
|
67
67
|
export declare const BASELINE_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
68
|
+
/**
|
|
69
|
+
* The parent `build` script: pnpm's own recursion in dependency order, one package at a time, so
|
|
70
|
+
* no turbo.json has to act as root over the members' own (#4913).
|
|
71
|
+
*/
|
|
72
|
+
export declare const PARENT_BUILD_SCRIPT = "pnpm -r --workspace-concurrency=1 run build";
|
|
68
73
|
/**
|
|
69
74
|
* The #3795 guard, made explicit for admitted app shells: every package an `--apps` glob
|
|
70
75
|
* enumerates must have a name no other workspace package uses, or pnpm would silently pick one.
|
|
@@ -309,9 +314,4 @@ export declare const SENTINEL_MARKER = "mj dev workspace";
|
|
|
309
314
|
* workspace must produce a byte-identical sentinel.
|
|
310
315
|
*/
|
|
311
316
|
export declare function BuildSentinel(fileNames: readonly string[], memberNames: readonly string[]): string;
|
|
312
|
-
/**
|
|
313
|
-
* Picks the parent `turbo.json`: copied verbatim from the first member (sorted by
|
|
314
|
-
* name) that carries one; a minimal proven config otherwise.
|
|
315
|
-
*/
|
|
316
|
-
export declare function PickTurboJson(members: readonly CandidateRepo[]): TurboJsonResult;
|
|
317
317
|
//# sourceMappingURL=build.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/lib/dev-workspace/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/lib/dev-workspace/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure content builders for the
|
|
2
|
+
* Pure content builders for the three generated workspace files.
|
|
3
3
|
*
|
|
4
4
|
* NO SIDE EFFECTS in this module: every function maps member metadata (already
|
|
5
5
|
* loaded by `detect.ts`) to file content strings. Writing is `write.ts`'s job.
|
|
@@ -22,10 +22,12 @@
|
|
|
22
22
|
* A package name two members both provide (every MJ-based app repo has a
|
|
23
23
|
* `mj_generatedentities`) gets consumer-scoped `link:` overrides so each
|
|
24
24
|
* member's own consumers keep its own copy — see {@link ResolveDuplicateProviderLinks}.
|
|
25
|
-
* - package.json: private root manifest, pnpm `packageManager` pin,
|
|
26
|
-
*
|
|
27
|
-
* reported). No `pnpm` block — see
|
|
28
|
-
*
|
|
25
|
+
* - package.json: private root manifest, pnpm `packageManager` pin, a `build`
|
|
26
|
+
* script ({@link PARENT_BUILD_SCRIPT}), and the devDependency union of member
|
|
27
|
+
* roots (highest version wins, every conflict reported). No `pnpm` block — see
|
|
28
|
+
* pnpm-workspace.yaml above.
|
|
29
|
+
* - No turbo.json (#4913): a member's copy at the parent makes turbo treat the
|
|
30
|
+
* parent as root, and MJ's own turbo.json then fails on its root-only keys.
|
|
29
31
|
*
|
|
30
32
|
* Why no hoist block: the 78-entry `public-hoist-pattern[]` set the manual setup
|
|
31
33
|
* carried was written for the npm-hoisted era, when a library could import a
|
|
@@ -145,11 +147,11 @@ export const BASELINE_DEV_DEPENDENCIES = {
|
|
|
145
147
|
turbo: '^2.5.0',
|
|
146
148
|
'tsc-alias': '^1.8.16',
|
|
147
149
|
};
|
|
148
|
-
/**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
/**
|
|
151
|
+
* The parent `build` script: pnpm's own recursion in dependency order, one package at a time, so
|
|
152
|
+
* no turbo.json has to act as root over the members' own (#4913).
|
|
153
|
+
*/
|
|
154
|
+
export const PARENT_BUILD_SCRIPT = 'pnpm -r --workspace-concurrency=1 run build';
|
|
153
155
|
const GENERATED_HEADER = 'Generated by `mj dev workspace` — never commit to any repo; teardown = delete.';
|
|
154
156
|
/** Quotes a YAML list entry the proven way: scoped names quoted, bare names not. */
|
|
155
157
|
function yamlListEntry(name) {
|
|
@@ -552,6 +554,13 @@ export function IndexWorkspacePackages(members) {
|
|
|
552
554
|
* contract `readJsonFile` already sets ("Unparseable JSON at <path>"). Without it a missing `name`
|
|
553
555
|
* surfaced as `Cannot read properties of undefined (reading 'length')`, which names neither the
|
|
554
556
|
* repo nor the file — and a developer may have a dozen of them.
|
|
557
|
+
*
|
|
558
|
+
* Also validates `platform`, when present, against the same three literals `runsInBrowser` below
|
|
559
|
+
* understands. `MjAppPackageEntry.platform` is a closed three-literal union, so the final
|
|
560
|
+
* `declared as MjAppPackageEntry[]` cast is a LIE unless this loop enforces it — an unvalidated
|
|
561
|
+
* typo like `"platform": "Node"` would satisfy the cast, fail every `=== 'node'`/`'browser'`/`'both'`
|
|
562
|
+
* comparison in `runsInBrowser`, and silently fall through its Node-only branch, dropping the
|
|
563
|
+
* package from the shell import set with no error (#4428).
|
|
555
564
|
*/
|
|
556
565
|
function readDeclaredEntries(member, section) {
|
|
557
566
|
const declared = member.MjAppJson?.packages?.[section];
|
|
@@ -568,33 +577,58 @@ function readDeclaredEntries(member, section) {
|
|
|
568
577
|
if (typeof entry.name !== 'string') {
|
|
569
578
|
throw new Error(`${where}[${index}] has no "name" string — a package entry must name its package`);
|
|
570
579
|
}
|
|
580
|
+
const platform = entry.platform;
|
|
581
|
+
if (platform !== undefined && !VALID_PLATFORMS.includes(platform)) {
|
|
582
|
+
throw new Error(`${where}[${index}] has an invalid "platform" value ${JSON.stringify(platform)} — must be one of ${VALID_PLATFORMS.join(', ')}`);
|
|
583
|
+
}
|
|
571
584
|
}
|
|
572
585
|
return declared;
|
|
573
586
|
}
|
|
587
|
+
/** The only valid values for a manifest package entry's `platform` field — see {@link runsInBrowser}. */
|
|
588
|
+
const VALID_PLATFORMS = ['node', 'browser', 'both'];
|
|
574
589
|
/**
|
|
575
590
|
* Every package a member declares that an app shell will be asked to import.
|
|
576
591
|
*
|
|
577
592
|
* This set mirrors the HOST's rule exactly, because the host is the only thing that decides what
|
|
578
593
|
* the shell imports. `GetClientPackagesFromManifest`
|
|
579
594
|
* (`packages/OpenApp/Engine/src/install/config-manager.ts`) builds the client dynamic-package list
|
|
580
|
-
* as `[...packages.client, ...packages.shared]`
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
* an import in the shell's generated class-registrations manifest, with no role field even present
|
|
584
|
-
* on its entry type.
|
|
595
|
+
* as `[...packages.client, ...packages.shared]` minus anything whose platform excludes the
|
|
596
|
+
* browser, and `mj codegen manifest --open-app-client-bootstrap` turns every enabled entry into an
|
|
597
|
+
* import in the shell's generated class-registrations manifest.
|
|
585
598
|
*
|
|
586
|
-
*
|
|
587
|
-
* on the premise that other roles "are imported normally"; the host
|
|
588
|
-
* is a required seven-value enum whose `components` / `module`
|
|
589
|
-
* roles. Anything narrower leaves a schema-valid package
|
|
590
|
-
* nobody — the
|
|
599
|
+
* There is deliberately no `role` FILTER here beyond the platform rule. An earlier revision kept
|
|
600
|
+
* only `role: 'bootstrap'` on the premise that other roles "are imported normally"; the host
|
|
601
|
+
* contradicts that, and `role` is a required seven-value enum whose `components` / `module`
|
|
602
|
+
* members are the documented Angular roles. Anything narrower leaves a schema-valid package
|
|
603
|
+
* imported by the shell and linked by nobody — the page-load-with-a-green-build failure this
|
|
604
|
+
* module exists to prevent (#4364).
|
|
605
|
+
*
|
|
606
|
+
* The platform rule itself IS a role test, but a targeted one: it drops only what cannot run in a
|
|
607
|
+
* browser at all. Before it existed, a Node-only actions package declared `shared` was imported
|
|
608
|
+
* into the Angular bundle and the host could not build (#4428).
|
|
591
609
|
*
|
|
592
610
|
* `packages.server[]` is NOT here, and that is not an oversight: the host routes it to
|
|
593
611
|
* `dynamicPackages.server`, a Node process that resolves importer-relative rather than from the
|
|
594
612
|
* vite root, so it is not part of the shell's resolution problem.
|
|
595
613
|
*/
|
|
596
614
|
function readShellImportedEntries(member) {
|
|
597
|
-
return [...readDeclaredEntries(member, 'client'), ...readDeclaredEntries(member, 'shared')];
|
|
615
|
+
return [...readDeclaredEntries(member, 'client'), ...readDeclaredEntries(member, 'shared')].filter(runsInBrowser);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Whether a declared package may be imported by the app shell.
|
|
619
|
+
*
|
|
620
|
+
* DELIBERATE COPY of `ResolvePackagePlatform` / `PackageRunsOnTier` in
|
|
621
|
+
* `packages/OpenApp/Engine/src/manifest/package-platform.ts`, which is canonical;
|
|
622
|
+
* `packages/DynamicPackages/src/discover.ts` keeps a third copy. Importing the engine's version
|
|
623
|
+
* here would bind this command to that package's built `dist/`. Change one, change all three. The
|
|
624
|
+
* engine's `package-platform.test.ts` holds the canonical case list; this module's spec mirrors its
|
|
625
|
+
* cases by hand, because it cannot import the engine.
|
|
626
|
+
*/
|
|
627
|
+
function runsInBrowser(pkg) {
|
|
628
|
+
// `readDeclaredEntries` has already rejected every `platform` other than absent or one of the
|
|
629
|
+
// three literals, so only `undefined` needs the role default here.
|
|
630
|
+
const platform = pkg.platform ?? (pkg.role === 'actions' ? 'node' : 'both');
|
|
631
|
+
return platform === 'both' || platform === 'browser';
|
|
598
632
|
}
|
|
599
633
|
export function CollectOpenAppClientPackages(members, workspacePackages) {
|
|
600
634
|
const collected = new Map();
|
|
@@ -962,7 +996,9 @@ export function BuildRootPackageJson(parentDirName, members) {
|
|
|
962
996
|
// which is the whole point of registering it.
|
|
963
997
|
for (const name of Object.keys(clientDependencies))
|
|
964
998
|
delete union.DevDependencies[name];
|
|
965
|
-
const pins = DeriveLockfilePins(members.flatMap((m) =>
|
|
999
|
+
const pins = DeriveLockfilePins(members.flatMap((m) => m.Lockfile !== null && m.Lockfile.Kind !== 'unsupported'
|
|
1000
|
+
? [{ Repo: m.Name, Lockfile: m.Lockfile, IncludedImporters: new Set(['.', ...m.Packages.map((p) => p.RelPath)]) }]
|
|
1001
|
+
: []), new Set(family.Names));
|
|
966
1002
|
const blocks = ResolveMemberPnpmBlocks(members);
|
|
967
1003
|
const assembled = AssembleParentOverrides(pins.Pins, blocks.Overrides, family.Names, Object.keys(blocks.PatchedDependencies));
|
|
968
1004
|
const duplicateLinks = ResolveDuplicateProviderLinks(members, family.Duplicates);
|
|
@@ -972,6 +1008,7 @@ export function BuildRootPackageJson(parentDirName, members) {
|
|
|
972
1008
|
name: workspaceName(parentDirName),
|
|
973
1009
|
private: true,
|
|
974
1010
|
packageManager: Pin,
|
|
1011
|
+
scripts: { build: PARENT_BUILD_SCRIPT },
|
|
975
1012
|
// Omitted entirely when empty, so a parent with no Open App member regenerates byte-identically.
|
|
976
1013
|
...(Object.keys(clientDependencies).length > 0 ? { dependencies: clientDependencies } : {}),
|
|
977
1014
|
devDependencies: union.DevDependencies,
|
|
@@ -1038,16 +1075,4 @@ export function BuildSentinel(fileNames, memberNames) {
|
|
|
1038
1075
|
};
|
|
1039
1076
|
return `${JSON.stringify(sentinel, null, 2)}\n`;
|
|
1040
1077
|
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Picks the parent `turbo.json`: copied verbatim from the first member (sorted by
|
|
1043
|
-
* name) that carries one; a minimal proven config otherwise.
|
|
1044
|
-
*/
|
|
1045
|
-
export function PickTurboJson(members) {
|
|
1046
|
-
for (const member of [...members].sort((a, b) => a.Name.localeCompare(b.Name))) {
|
|
1047
|
-
if (member.TurboJson !== null) {
|
|
1048
|
-
return { Content: member.TurboJson, Source: member.Name };
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
return { Content: FALLBACK_TURBO_JSON, Source: 'generator fallback' };
|
|
1052
|
-
}
|
|
1053
1078
|
//# sourceMappingURL=build.js.map
|