@bluealba/platform-cli 0.3.0 → 0.3.1-feature-platform-cli-prefix-221
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +627 -307
- package/package.json +5 -5
- package/templates/bootstrap-service-template/package.json +2 -2
- package/templates/customization-ui-module-template/Dockerfile +4 -4
- package/templates/customization-ui-module-template/Dockerfile.development +1 -1
- package/templates/customization-ui-module-template/package.json +5 -5
- package/templates/customization-ui-module-template/tsconfig.json +1 -1
- package/templates/customization-ui-module-template/webpack.config.js +2 -2
- package/templates/platform-init-template/{local → {{platformName}}-core/local}/.env.example +1 -1
- package/templates/platform-init-template/{local → {{platformName}}-core/local}/platform-docker-compose.yml +1 -1
- package/templates/platform-init-template/{local/core-docker-compose.yml → {{platformName}}-core/local/{{platformName}}-core-docker-compose.yml} +7 -7
- package/templates/react-ui-module-template/Dockerfile +3 -3
- package/templates/react-ui-module-template/Dockerfile.development +1 -1
- package/templates/react-ui-module-template/package.json +2 -2
- package/templates/react-ui-module-template/src/Icon.tsx +1 -1
- package/templates/platform-init-template/local/docker-compose.yml +0 -3
- package/templates/platform-init-template/local/package.json +0 -18
- package/templates/platform-init-template/local/scripts/build.sh +0 -18
- package/templates/platform-init-template/local/scripts/install.sh +0 -18
- /package/templates/customization-ui-module-template/src/{platform-customization-ui.tsx → {{platformName}}-customization-ui.tsx} +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/.changeset/config.json +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/.nvmrc +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/.syncpackrc +0 -0
- /package/templates/platform-init-template/{local → {{platformName}}-core/local}/environment/pae-nestjs-gateway-service.env +0 -0
- /package/templates/platform-init-template/{local → {{platformName}}-core/local}/nginx.conf +0 -0
- /package/templates/platform-init-template/{local → {{platformName}}-core/local}/ssl/cert.pem +0 -0
- /package/templates/platform-init-template/{local → {{platformName}}-core/local}/ssl/key.pem +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/package.json +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/packages-versions.json +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/scripts/preinstall.mjs +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/services/.gitkeep +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/turbo.json +0 -0
- /package/templates/platform-init-template/{core → {{platformName}}-core}/ui/.gitkeep +0 -0
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { render } from "ink";
|
|
|
6
6
|
// src/app.tsx
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { useState as useState3, useCallback as useCallback2 } from "react";
|
|
9
|
-
import { Box as Box4, Text as
|
|
9
|
+
import { Box as Box4, Text as Text7, useApp, useInput } from "ink";
|
|
10
10
|
|
|
11
11
|
// src/components/prompt.tsx
|
|
12
12
|
import { Box, Text } from "ink";
|
|
@@ -54,53 +54,76 @@ var CommandPalette = React.memo(function CommandPalette2({ commands, selectedInd
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
// src/components/scrollback-history.tsx
|
|
57
|
-
import { Static, Text as
|
|
57
|
+
import { Static, Text as Text5 } from "ink";
|
|
58
58
|
|
|
59
59
|
// src/components/welcome-banner.tsx
|
|
60
|
+
import React3 from "react";
|
|
61
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
62
|
+
|
|
63
|
+
// src/components/working-directory.tsx
|
|
60
64
|
import React2 from "react";
|
|
61
|
-
import {
|
|
65
|
+
import { Text as Text3 } from "ink";
|
|
66
|
+
import { cwd } from "process";
|
|
67
|
+
import { homedir } from "os";
|
|
62
68
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
69
|
+
function shortenPath(fullPath) {
|
|
70
|
+
const home = homedir();
|
|
71
|
+
return fullPath.startsWith(home) ? `~${fullPath.slice(home.length)}` : fullPath;
|
|
72
|
+
}
|
|
73
|
+
var WorkingDirectory = React2.memo(function WorkingDirectory2() {
|
|
74
|
+
const dir = shortenPath(cwd());
|
|
75
|
+
return /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
76
|
+
" ",
|
|
77
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "cwd:" }),
|
|
78
|
+
" ",
|
|
79
|
+
dir
|
|
80
|
+
] });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// src/components/welcome-banner.tsx
|
|
84
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
63
85
|
var ASCII_ART = [
|
|
64
86
|
"|---. .---|",
|
|
65
87
|
"| \\/ |",
|
|
66
88
|
"| /\\ |",
|
|
67
89
|
"|---' '---|"
|
|
68
90
|
];
|
|
69
|
-
var WelcomeBanner =
|
|
70
|
-
return /* @__PURE__ */
|
|
71
|
-
/* @__PURE__ */
|
|
91
|
+
var WelcomeBanner = React3.memo(function WelcomeBanner2({ version: version2 }) {
|
|
92
|
+
return /* @__PURE__ */ jsxs4(Box3, { borderStyle: "round", flexDirection: "column", paddingX: 1, paddingY: 1, children: [
|
|
93
|
+
/* @__PURE__ */ jsxs4(Text4, { bold: true, color: "cyan", children: [
|
|
72
94
|
"Blue Alba Platform CLI v",
|
|
73
95
|
version2
|
|
74
96
|
] }),
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
/* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ jsxs4(Box3, { marginTop: 1, children: [
|
|
98
|
+
/* @__PURE__ */ jsx4(Box3, { flexDirection: "column", marginRight: 2, children: ASCII_ART.map((line, i) => /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: line }, i)) }),
|
|
99
|
+
/* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", justifyContent: "center", marginRight: 2, children: [
|
|
100
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: "Welcome to the" }),
|
|
101
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: "Blue Alba Platform!" })
|
|
80
102
|
] }),
|
|
81
|
-
/* @__PURE__ */
|
|
82
|
-
/* @__PURE__ */
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
/* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ jsx4(Box3, { flexDirection: "column", children: ASCII_ART.map((_, i) => /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "\u2502" }, i)) }),
|
|
104
|
+
/* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginLeft: 2, children: [
|
|
105
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: "Tips for getting started" }),
|
|
106
|
+
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
85
107
|
"Run ",
|
|
86
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", children: "/init" }),
|
|
87
109
|
" to start building a platform"
|
|
88
110
|
] })
|
|
89
111
|
] })
|
|
90
|
-
] })
|
|
112
|
+
] }),
|
|
113
|
+
/* @__PURE__ */ jsx4(WorkingDirectory, {})
|
|
91
114
|
] });
|
|
92
115
|
});
|
|
93
116
|
|
|
94
117
|
// src/components/scrollback-history.tsx
|
|
95
|
-
import { jsx as
|
|
118
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
96
119
|
function ScrollbackHistory({ items, version: version2 }) {
|
|
97
|
-
return /* @__PURE__ */
|
|
120
|
+
return /* @__PURE__ */ jsx5(Static, { items, children: (item) => item.kind === "banner" ? /* @__PURE__ */ jsx5(WelcomeBanner, { version: version2 }, item.id) : /* @__PURE__ */ jsx5(Text5, { children: item.line }, item.id) });
|
|
98
121
|
}
|
|
99
122
|
|
|
100
123
|
// src/components/spinner.tsx
|
|
101
124
|
import { useState, useEffect } from "react";
|
|
102
|
-
import { Text as
|
|
103
|
-
import { jsx as
|
|
125
|
+
import { Text as Text6 } from "ink";
|
|
126
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
104
127
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
105
128
|
var INTERVAL_MS = 80;
|
|
106
129
|
function Spinner({ label }) {
|
|
@@ -111,12 +134,12 @@ function Spinner({ label }) {
|
|
|
111
134
|
}, INTERVAL_MS);
|
|
112
135
|
return () => clearInterval(id);
|
|
113
136
|
}, []);
|
|
114
|
-
return /* @__PURE__ */
|
|
115
|
-
/* @__PURE__ */
|
|
137
|
+
return /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
138
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "cyan", children: [
|
|
116
139
|
FRAMES[frame],
|
|
117
140
|
" "
|
|
118
141
|
] }),
|
|
119
|
-
label && /* @__PURE__ */
|
|
142
|
+
label && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: label })
|
|
120
143
|
] });
|
|
121
144
|
}
|
|
122
145
|
|
|
@@ -124,8 +147,7 @@ function Spinner({ label }) {
|
|
|
124
147
|
import { Fzf } from "fzf";
|
|
125
148
|
|
|
126
149
|
// src/commands/create-application/create-application.command.ts
|
|
127
|
-
import { join as
|
|
128
|
-
import { cwd as cwd2 } from "process";
|
|
150
|
+
import { join as join11, resolve } from "path";
|
|
129
151
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
130
152
|
|
|
131
153
|
// src/commands/create-application/scaffold-application-monorepo.ts
|
|
@@ -290,8 +312,8 @@ var nestjsServiceModuleTemplateDir = join6(
|
|
|
290
312
|
"templates",
|
|
291
313
|
"nestjs-service-module-template"
|
|
292
314
|
);
|
|
293
|
-
async function scaffoldNestjsService(serviceDir, organizationName, applicationName, applicationDisplayName, serviceBaseDir, logger) {
|
|
294
|
-
const serviceName = `${applicationName}-service`;
|
|
315
|
+
async function scaffoldNestjsService(serviceDir, organizationName, platformName, applicationName, applicationDisplayName, serviceBaseDir, logger) {
|
|
316
|
+
const serviceName = `${platformName}-${applicationName}-service`;
|
|
295
317
|
const serviceDisplayName = `${applicationDisplayName} Service`;
|
|
296
318
|
logger.log(`Creating NestJS service "${serviceName}"...`);
|
|
297
319
|
await applyTemplate(
|
|
@@ -322,33 +344,35 @@ async function addModuleEntry(bootstrapServiceDir, applicationName, entry, logge
|
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
// src/commands/create-application/module-entry-builders.ts
|
|
325
|
-
function buildServiceModuleEntry(organizationName, applicationName, applicationDisplayName) {
|
|
347
|
+
function buildServiceModuleEntry(organizationName, platformName, applicationName, applicationDisplayName) {
|
|
348
|
+
const serviceName = `${platformName}-${applicationName}-service`;
|
|
326
349
|
return {
|
|
327
|
-
name: `@${organizationName}/${
|
|
350
|
+
name: `@${organizationName}/${serviceName}`,
|
|
328
351
|
displayName: `${applicationDisplayName} Service`,
|
|
329
352
|
type: "service",
|
|
330
|
-
baseUrl: `/${
|
|
353
|
+
baseUrl: `/${serviceName}`,
|
|
331
354
|
service: {
|
|
332
|
-
host:
|
|
355
|
+
host: serviceName,
|
|
333
356
|
port: 80
|
|
334
357
|
},
|
|
335
358
|
dependsOn: []
|
|
336
359
|
};
|
|
337
360
|
}
|
|
338
|
-
function buildUiModuleEntry(organizationName, applicationName, applicationDisplayName) {
|
|
361
|
+
function buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName) {
|
|
362
|
+
const uiName = `${platformName}-${applicationName}-ui`;
|
|
339
363
|
return {
|
|
340
|
-
name: `@${organizationName}/${
|
|
364
|
+
name: `@${organizationName}/${uiName}`,
|
|
341
365
|
displayName: applicationDisplayName,
|
|
342
366
|
type: "app",
|
|
343
|
-
baseUrl: `/${
|
|
367
|
+
baseUrl: `/${uiName}`,
|
|
344
368
|
service: {
|
|
345
|
-
host:
|
|
369
|
+
host: uiName,
|
|
346
370
|
port: 80
|
|
347
371
|
},
|
|
348
372
|
ui: {
|
|
349
373
|
route: `/${applicationName}`,
|
|
350
374
|
mountAtSelector: "#pae-shell-ui-content",
|
|
351
|
-
bundleFile: `${organizationName}-${
|
|
375
|
+
bundleFile: `${organizationName}-${uiName}.js`,
|
|
352
376
|
customProps: {}
|
|
353
377
|
},
|
|
354
378
|
dependsOn: [`@bluealba/pae-shell-ui`]
|
|
@@ -357,10 +381,11 @@ function buildUiModuleEntry(organizationName, applicationName, applicationDispla
|
|
|
357
381
|
|
|
358
382
|
// src/commands/create-application/create-docker-compose.ts
|
|
359
383
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
360
|
-
function buildBootstrapBlock(applicationName) {
|
|
361
|
-
|
|
384
|
+
function buildBootstrapBlock(platformName, applicationName) {
|
|
385
|
+
const bootstrapName = `${platformName}-${applicationName}-bootstrap-service`;
|
|
386
|
+
return ` ${bootstrapName}:
|
|
362
387
|
build:
|
|
363
|
-
context:
|
|
388
|
+
context: ../../${platformName}-${applicationName}/services/${bootstrapName}
|
|
364
389
|
dockerfile: Dockerfile.development
|
|
365
390
|
environment:
|
|
366
391
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
@@ -370,27 +395,29 @@ function buildBootstrapBlock(applicationName) {
|
|
|
370
395
|
- GATEWAY_SERVICE_URL=\${PAE_GATEWAY_URL}
|
|
371
396
|
- SERVICE_ACCESS_SECRET=\${PAE_GATEWAY_SERVICE_ACCESS_SECRET}
|
|
372
397
|
volumes:
|
|
373
|
-
- \${PWD}
|
|
398
|
+
- \${PWD}/:/app/out
|
|
374
399
|
depends_on:
|
|
375
400
|
pae-nestjs-gateway-service:
|
|
376
401
|
condition: service_healthy
|
|
377
402
|
`;
|
|
378
403
|
}
|
|
379
|
-
function buildUiBlock(applicationName, uiPort) {
|
|
380
|
-
|
|
404
|
+
function buildUiBlock(platformName, applicationName, uiPort) {
|
|
405
|
+
const uiName = `${platformName}-${applicationName}-ui`;
|
|
406
|
+
return ` ${uiName}:
|
|
381
407
|
build:
|
|
382
|
-
context:
|
|
408
|
+
context: ../../${platformName}-${applicationName}/ui/${uiName}
|
|
383
409
|
dockerfile: Dockerfile.development
|
|
384
410
|
ports:
|
|
385
411
|
- ${uiPort}:80
|
|
386
412
|
volumes:
|
|
387
|
-
- \${PWD}
|
|
413
|
+
- \${PWD}/:/app/out
|
|
388
414
|
`;
|
|
389
415
|
}
|
|
390
|
-
function buildBackendBlock(applicationName, servicePort) {
|
|
391
|
-
|
|
416
|
+
function buildBackendBlock(platformName, applicationName, servicePort) {
|
|
417
|
+
const serviceName = `${platformName}-${applicationName}-service`;
|
|
418
|
+
return ` ${serviceName}:
|
|
392
419
|
build:
|
|
393
|
-
context:
|
|
420
|
+
context: ../../${platformName}-${applicationName}/services/${serviceName}
|
|
394
421
|
dockerfile: Dockerfile.development
|
|
395
422
|
args:
|
|
396
423
|
- BA_NPM_AUTH_TOKEN=$BA_NPM_AUTH_TOKEN
|
|
@@ -399,18 +426,18 @@ function buildBackendBlock(applicationName, servicePort) {
|
|
|
399
426
|
environment:
|
|
400
427
|
- GATEWAY_URL=\${PAE_GATEWAY_URL}
|
|
401
428
|
volumes:
|
|
402
|
-
- \${PWD}
|
|
429
|
+
- \${PWD}/:/app/out
|
|
403
430
|
`;
|
|
404
431
|
}
|
|
405
|
-
async function createDockerCompose(dockerComposePath, applicationName, hasUserInterface, hasBackendService, uiPort, servicePort, logger) {
|
|
406
|
-
const blocks = ["services:\n", buildBootstrapBlock(applicationName)];
|
|
432
|
+
async function createDockerCompose(dockerComposePath, applicationName, platformName, hasUserInterface, hasBackendService, uiPort, servicePort, logger) {
|
|
433
|
+
const blocks = ["services:\n", buildBootstrapBlock(platformName, applicationName)];
|
|
407
434
|
if (hasUserInterface) {
|
|
408
|
-
blocks.push(buildUiBlock(applicationName, uiPort));
|
|
435
|
+
blocks.push(buildUiBlock(platformName, applicationName, uiPort));
|
|
409
436
|
}
|
|
410
437
|
if (hasBackendService) {
|
|
411
|
-
blocks.push(buildBackendBlock(applicationName, servicePort));
|
|
438
|
+
blocks.push(buildBackendBlock(platformName, applicationName, servicePort));
|
|
412
439
|
}
|
|
413
|
-
const content = blocks.join("\n");
|
|
440
|
+
const content = blocks.join("\n") + "\n";
|
|
414
441
|
try {
|
|
415
442
|
await writeFile3(dockerComposePath, content, "utf-8");
|
|
416
443
|
logger.log(`Created docker-compose: ${dockerComposePath}`);
|
|
@@ -420,32 +447,17 @@ async function createDockerCompose(dockerComposePath, applicationName, hasUserIn
|
|
|
420
447
|
}
|
|
421
448
|
}
|
|
422
449
|
|
|
423
|
-
// src/commands/create-application/update-root-docker-compose.ts
|
|
424
|
-
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
425
|
-
async function updateRootDockerCompose(rootDockerComposePath, applicationName, logger) {
|
|
426
|
-
try {
|
|
427
|
-
const existing = await readFile3(rootDockerComposePath, "utf-8");
|
|
428
|
-
const includeLine = ` - path: ./${applicationName}-docker-compose.yml
|
|
429
|
-
`;
|
|
430
|
-
await writeFile4(rootDockerComposePath, existing + includeLine, "utf-8");
|
|
431
|
-
logger.log(`Updated root docker-compose: ${rootDockerComposePath}`);
|
|
432
|
-
} catch (err) {
|
|
433
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
434
|
-
logger.log(`Warning: Could not update root docker-compose \u2014 ${message}`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
450
|
// src/commands/create-application/port-allocator.ts
|
|
439
451
|
import { join as join8 } from "path";
|
|
440
|
-
import { readdir as readdir2, readFile as
|
|
452
|
+
import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
|
|
441
453
|
async function getNextAvailablePort(localDir) {
|
|
442
454
|
const allPorts = [];
|
|
443
455
|
try {
|
|
444
456
|
const files = await readdir2(localDir);
|
|
445
|
-
const
|
|
446
|
-
for (const file of
|
|
457
|
+
const ymlFiles = files.filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
458
|
+
for (const file of ymlFiles) {
|
|
447
459
|
try {
|
|
448
|
-
const content = await
|
|
460
|
+
const content = await readFile3(join8(localDir, file), "utf-8");
|
|
449
461
|
const regex = /^\s*-\s*"?(\d+):\d+"?\s*$/gm;
|
|
450
462
|
let match;
|
|
451
463
|
while ((match = regex.exec(content)) !== null) {
|
|
@@ -464,18 +476,95 @@ function formatError(err) {
|
|
|
464
476
|
return err instanceof Error ? err.message : String(err);
|
|
465
477
|
}
|
|
466
478
|
|
|
467
|
-
// src/utils/platform-
|
|
468
|
-
import { access } from "fs/promises";
|
|
469
|
-
import { join as join9 } from "path";
|
|
470
|
-
import { cwd } from "process";
|
|
471
|
-
async function
|
|
479
|
+
// src/utils/platform-layout.ts
|
|
480
|
+
import { access, readdir as readdir3 } from "fs/promises";
|
|
481
|
+
import { join as join9, dirname as dirname7 } from "path";
|
|
482
|
+
import { cwd as cwd2 } from "process";
|
|
483
|
+
async function findCoreDirIn(dir) {
|
|
484
|
+
let entries;
|
|
472
485
|
try {
|
|
473
|
-
await
|
|
474
|
-
await access(join9(cwd(), "local"));
|
|
475
|
-
return true;
|
|
486
|
+
entries = await readdir3(dir, { withFileTypes: true });
|
|
476
487
|
} catch {
|
|
477
|
-
return
|
|
488
|
+
return null;
|
|
478
489
|
}
|
|
490
|
+
const dirs = entries.filter((e) => e.isDirectory());
|
|
491
|
+
for (const entry of dirs) {
|
|
492
|
+
if (entry.name.endsWith("-core")) {
|
|
493
|
+
try {
|
|
494
|
+
await access(join9(dir, entry.name, "product.manifest.json"));
|
|
495
|
+
return entry.name;
|
|
496
|
+
} catch {
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const coreEntry = dirs.find((e) => e.name === "core");
|
|
501
|
+
if (coreEntry) {
|
|
502
|
+
try {
|
|
503
|
+
await access(join9(dir, "core", "product.manifest.json"));
|
|
504
|
+
return "core";
|
|
505
|
+
} catch {
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
async function findPlatformLayout(startDir = cwd2()) {
|
|
511
|
+
let dir = startDir;
|
|
512
|
+
while (true) {
|
|
513
|
+
const coreDirName = await findCoreDirIn(dir);
|
|
514
|
+
if (coreDirName) {
|
|
515
|
+
const coreDir = join9(dir, coreDirName);
|
|
516
|
+
return {
|
|
517
|
+
rootDir: dir,
|
|
518
|
+
coreDir,
|
|
519
|
+
coreDirName,
|
|
520
|
+
localDir: join9(coreDir, "local")
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
const parent = dirname7(dir);
|
|
524
|
+
if (parent === dir) {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
dir = parent;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/utils/platform-check.ts
|
|
532
|
+
async function isPlatformInitialized() {
|
|
533
|
+
return await findPlatformLayout() !== null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// src/utils/manifest.ts
|
|
537
|
+
import { readFile as readFile4, writeFile as writeFile4, access as access2 } from "fs/promises";
|
|
538
|
+
import { join as join10 } from "path";
|
|
539
|
+
import { cwd as cwd3 } from "process";
|
|
540
|
+
function manifestPath(rootDir, coreDirName = "core") {
|
|
541
|
+
return join10(rootDir, coreDirName, "product.manifest.json");
|
|
542
|
+
}
|
|
543
|
+
async function readManifest(rootDir = cwd3(), coreDirName = "core") {
|
|
544
|
+
const content = await readFile4(manifestPath(rootDir, coreDirName), "utf-8");
|
|
545
|
+
return JSON.parse(content);
|
|
546
|
+
}
|
|
547
|
+
async function writeManifest(manifest, rootDir = cwd3(), coreDirName = "core") {
|
|
548
|
+
await writeFile4(manifestPath(rootDir, coreDirName), JSON.stringify(manifest, null, 2), "utf-8");
|
|
549
|
+
}
|
|
550
|
+
function createInitialManifest(params) {
|
|
551
|
+
const { organizationName, platformName, platformDisplayName } = params;
|
|
552
|
+
return {
|
|
553
|
+
version: "1",
|
|
554
|
+
product: {
|
|
555
|
+
name: platformName,
|
|
556
|
+
displayName: platformDisplayName,
|
|
557
|
+
organization: organizationName,
|
|
558
|
+
scope: `@${organizationName}`
|
|
559
|
+
},
|
|
560
|
+
applications: []
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function addApplicationToManifest(manifest, app) {
|
|
564
|
+
return {
|
|
565
|
+
...manifest,
|
|
566
|
+
applications: [...manifest.applications, app]
|
|
567
|
+
};
|
|
479
568
|
}
|
|
480
569
|
|
|
481
570
|
// src/commands/create-application/create-application.command.ts
|
|
@@ -486,22 +575,30 @@ var createApplicationCommand = {
|
|
|
486
575
|
};
|
|
487
576
|
async function createApplication(params, logger) {
|
|
488
577
|
const {
|
|
489
|
-
organizationName,
|
|
490
|
-
platformName,
|
|
491
578
|
applicationName,
|
|
492
579
|
applicationDisplayName,
|
|
493
580
|
applicationDescription,
|
|
494
581
|
hasUserInterface,
|
|
495
582
|
hasBackendService
|
|
496
583
|
} = params;
|
|
497
|
-
|
|
584
|
+
const layout = await findPlatformLayout();
|
|
585
|
+
if (!layout) {
|
|
498
586
|
logger.log("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
499
587
|
return;
|
|
500
588
|
}
|
|
501
|
-
const rootDir =
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
589
|
+
const { rootDir, coreDirName, localDir } = layout;
|
|
590
|
+
let manifest;
|
|
591
|
+
try {
|
|
592
|
+
manifest = await readManifest(rootDir, coreDirName);
|
|
593
|
+
} catch (err) {
|
|
594
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const { organization: organizationName, name: platformName } = manifest.product;
|
|
598
|
+
const localPath = `../${platformName}-${applicationName}`;
|
|
599
|
+
const applicationDir = resolve(join11(rootDir, coreDirName), localPath);
|
|
600
|
+
const bootstrapServiceName = `${platformName}-${applicationName}-bootstrap-service`;
|
|
601
|
+
const bootstrapServiceDir = join11(applicationDir, "services", bootstrapServiceName);
|
|
505
602
|
logger.log(`Creating application monorepo "${applicationName}"...`);
|
|
506
603
|
try {
|
|
507
604
|
await scaffoldApplicationMonorepo(applicationDir, organizationName, platformName, applicationName, logger);
|
|
@@ -509,12 +606,12 @@ async function createApplication(params, logger) {
|
|
|
509
606
|
logger.log(`Error: Could not scaffold application monorepo \u2014 ${formatError(err)}`);
|
|
510
607
|
return;
|
|
511
608
|
}
|
|
512
|
-
await mkdir2(
|
|
513
|
-
await mkdir2(
|
|
514
|
-
logger.log(`Creating bootstrap service "${
|
|
515
|
-
const
|
|
609
|
+
await mkdir2(join11(applicationDir, "services"), { recursive: true });
|
|
610
|
+
await mkdir2(join11(applicationDir, "ui"), { recursive: true });
|
|
611
|
+
logger.log(`Creating bootstrap service "${bootstrapServiceName}"...`);
|
|
612
|
+
const bootstrapServiceBaseDir = `${platformName}-${applicationName}/services`;
|
|
516
613
|
try {
|
|
517
|
-
await scaffoldBootstrap(bootstrapServiceDir, organizationName, applicationName
|
|
614
|
+
await scaffoldBootstrap(bootstrapServiceDir, organizationName, `${platformName}-${applicationName}`, bootstrapServiceBaseDir, logger);
|
|
518
615
|
} catch (err) {
|
|
519
616
|
logger.log(`Error: Could not scaffold bootstrap service \u2014 ${formatError(err)}`);
|
|
520
617
|
return;
|
|
@@ -530,7 +627,7 @@ async function createApplication(params, logger) {
|
|
|
530
627
|
await addModuleEntry(
|
|
531
628
|
bootstrapServiceDir,
|
|
532
629
|
applicationName,
|
|
533
|
-
buildUiModuleEntry(organizationName, applicationName, applicationDisplayName),
|
|
630
|
+
buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName),
|
|
534
631
|
logger
|
|
535
632
|
);
|
|
536
633
|
}
|
|
@@ -538,13 +635,14 @@ async function createApplication(params, logger) {
|
|
|
538
635
|
await addModuleEntry(
|
|
539
636
|
bootstrapServiceDir,
|
|
540
637
|
applicationName,
|
|
541
|
-
buildServiceModuleEntry(organizationName, applicationName, applicationDisplayName),
|
|
638
|
+
buildServiceModuleEntry(organizationName, platformName, applicationName, applicationDisplayName),
|
|
542
639
|
logger
|
|
543
640
|
);
|
|
544
641
|
}
|
|
545
642
|
if (hasUserInterface) {
|
|
546
|
-
const
|
|
547
|
-
const
|
|
643
|
+
const uiName = `${platformName}-${applicationName}-ui`;
|
|
644
|
+
const uiDir = join11(applicationDir, "ui", uiName);
|
|
645
|
+
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
548
646
|
try {
|
|
549
647
|
await scaffoldUiModule(uiDir, organizationName, platformName, applicationName, applicationDisplayName, uiBaseDir, logger);
|
|
550
648
|
} catch (err) {
|
|
@@ -553,36 +651,49 @@ async function createApplication(params, logger) {
|
|
|
553
651
|
}
|
|
554
652
|
}
|
|
555
653
|
if (hasBackendService) {
|
|
556
|
-
const
|
|
557
|
-
const
|
|
654
|
+
const serviceName = `${platformName}-${applicationName}-service`;
|
|
655
|
+
const serviceDir = join11(applicationDir, "services", serviceName);
|
|
656
|
+
const serviceBaseDir = `${platformName}-${applicationName}/services`;
|
|
558
657
|
try {
|
|
559
|
-
await scaffoldNestjsService(serviceDir, organizationName, applicationName, applicationDisplayName, serviceBaseDir, logger);
|
|
658
|
+
await scaffoldNestjsService(serviceDir, organizationName, platformName, applicationName, applicationDisplayName, serviceBaseDir, logger);
|
|
560
659
|
} catch (err) {
|
|
561
660
|
logger.log(`Error: Could not scaffold NestJS service \u2014 ${formatError(err)}`);
|
|
562
661
|
return;
|
|
563
662
|
}
|
|
564
663
|
}
|
|
565
|
-
const dockerComposePath =
|
|
664
|
+
const dockerComposePath = join11(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
566
665
|
const basePort = await getNextAvailablePort(localDir);
|
|
567
666
|
const uiPort = hasUserInterface ? basePort : 0;
|
|
568
667
|
const servicePort = hasBackendService ? hasUserInterface ? basePort + 1 : basePort : 0;
|
|
569
668
|
await createDockerCompose(
|
|
570
669
|
dockerComposePath,
|
|
571
670
|
applicationName,
|
|
671
|
+
platformName,
|
|
572
672
|
hasUserInterface,
|
|
573
673
|
hasBackendService,
|
|
574
674
|
uiPort,
|
|
575
675
|
servicePort,
|
|
576
676
|
logger
|
|
577
677
|
);
|
|
578
|
-
const
|
|
579
|
-
|
|
678
|
+
const updatedManifest = addApplicationToManifest(manifest, {
|
|
679
|
+
name: applicationName,
|
|
680
|
+
displayName: applicationDisplayName,
|
|
681
|
+
description: applicationDescription,
|
|
682
|
+
localPath,
|
|
683
|
+
repository: null
|
|
684
|
+
});
|
|
685
|
+
try {
|
|
686
|
+
await writeManifest(updatedManifest, rootDir, coreDirName);
|
|
687
|
+
logger.log(`Updated product manifest with application "${applicationName}".`);
|
|
688
|
+
} catch (err) {
|
|
689
|
+
logger.log(`Warning: Could not update product manifest \u2014 ${formatError(err)}`);
|
|
690
|
+
}
|
|
580
691
|
logger.log(`Done! Application "${applicationName}" created at ${applicationDir}`);
|
|
581
692
|
}
|
|
582
693
|
|
|
583
694
|
// src/commands/init/init.command.ts
|
|
584
|
-
import { join as
|
|
585
|
-
import { cwd as
|
|
695
|
+
import { join as join17 } from "path";
|
|
696
|
+
import { cwd as cwd4 } from "process";
|
|
586
697
|
|
|
587
698
|
// src/utils/string.ts
|
|
588
699
|
function camelize(name) {
|
|
@@ -591,9 +702,9 @@ function camelize(name) {
|
|
|
591
702
|
|
|
592
703
|
// src/commands/init/scaffold-platform.ts
|
|
593
704
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
594
|
-
import { join as
|
|
595
|
-
var templateDir =
|
|
596
|
-
|
|
705
|
+
import { join as join12, dirname as dirname8 } from "path";
|
|
706
|
+
var templateDir = join12(
|
|
707
|
+
dirname8(fileURLToPath6(import.meta.url)),
|
|
597
708
|
"..",
|
|
598
709
|
"templates",
|
|
599
710
|
"platform-init-template"
|
|
@@ -604,9 +715,9 @@ async function scaffoldPlatform(outputDir, variables, logger) {
|
|
|
604
715
|
|
|
605
716
|
// src/commands/init/scaffold-platform-bootstrap.ts
|
|
606
717
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
607
|
-
import { join as
|
|
608
|
-
var templateDir2 =
|
|
609
|
-
|
|
718
|
+
import { join as join13, dirname as dirname9 } from "path";
|
|
719
|
+
var templateDir2 = join13(
|
|
720
|
+
dirname9(fileURLToPath7(import.meta.url)),
|
|
610
721
|
"..",
|
|
611
722
|
"templates",
|
|
612
723
|
"bootstrap-service-template"
|
|
@@ -617,9 +728,9 @@ async function scaffoldPlatformBootstrap(outputDir, variables, logger) {
|
|
|
617
728
|
|
|
618
729
|
// src/commands/init/scaffold-customization-ui.ts
|
|
619
730
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
620
|
-
import { join as
|
|
621
|
-
var templateDir3 =
|
|
622
|
-
|
|
731
|
+
import { join as join14, dirname as dirname10 } from "path";
|
|
732
|
+
var templateDir3 = join14(
|
|
733
|
+
dirname10(fileURLToPath8(import.meta.url)),
|
|
623
734
|
"..",
|
|
624
735
|
"templates",
|
|
625
736
|
"customization-ui-module-template"
|
|
@@ -629,19 +740,20 @@ async function scaffoldCustomizationUi(outputDir, variables, logger) {
|
|
|
629
740
|
}
|
|
630
741
|
|
|
631
742
|
// src/commands/init/register-customization-module.ts
|
|
632
|
-
import { join as
|
|
743
|
+
import { join as join15 } from "path";
|
|
633
744
|
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
634
|
-
async function registerCustomizationModule(bootstrapServiceDir, organizationName, logger) {
|
|
635
|
-
const modulesJsonPath =
|
|
745
|
+
async function registerCustomizationModule(bootstrapServiceDir, organizationName, logger, platformName = "platform") {
|
|
746
|
+
const modulesJsonPath = join15(bootstrapServiceDir, "src", "data", "platform", "modules.json");
|
|
747
|
+
const customizationUiName = `${platformName}-customization-ui`;
|
|
636
748
|
const entry = {
|
|
637
|
-
name: `@${organizationName}
|
|
749
|
+
name: `@${organizationName}/${customizationUiName}`,
|
|
638
750
|
displayName: "Platform Customization UI",
|
|
639
751
|
type: "app",
|
|
640
|
-
baseUrl:
|
|
641
|
-
service: { host:
|
|
752
|
+
baseUrl: `/${customizationUiName}`,
|
|
753
|
+
service: { host: customizationUiName, port: 80 },
|
|
642
754
|
ui: {
|
|
643
755
|
route: "/",
|
|
644
|
-
bundleFile:
|
|
756
|
+
bundleFile: `${customizationUiName}.js`,
|
|
645
757
|
isPlatformCustomization: true,
|
|
646
758
|
customProps: {}
|
|
647
759
|
},
|
|
@@ -655,7 +767,7 @@ async function registerCustomizationModule(bootstrapServiceDir, organizationName
|
|
|
655
767
|
|
|
656
768
|
// src/commands/init/generate-local-env.ts
|
|
657
769
|
import { readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
658
|
-
import { join as
|
|
770
|
+
import { join as join16 } from "path";
|
|
659
771
|
|
|
660
772
|
// src/utils/random.ts
|
|
661
773
|
import { randomBytes } from "crypto";
|
|
@@ -664,10 +776,10 @@ function generateRandomSecret() {
|
|
|
664
776
|
}
|
|
665
777
|
|
|
666
778
|
// src/commands/init/generate-local-env.ts
|
|
667
|
-
async function generateLocalEnv(outputDir, logger) {
|
|
668
|
-
const examplePath =
|
|
669
|
-
const envPath =
|
|
670
|
-
logger.log(
|
|
779
|
+
async function generateLocalEnv(outputDir, logger, coreDirName = "core") {
|
|
780
|
+
const examplePath = join16(outputDir, coreDirName, "local", ".env.example");
|
|
781
|
+
const envPath = join16(outputDir, coreDirName, "local", ".env");
|
|
782
|
+
logger.log(`Generating ${coreDirName}/local/.env with random secrets...`);
|
|
671
783
|
const content = await readFile6(examplePath, "utf-8");
|
|
672
784
|
const result = content.replace(/^(PAE_AUTH_JWT_SECRET|PAE_GATEWAY_SERVICE_ACCESS_SECRET|PAE_DB_PASSWORD)=$/gm, (_, key) => {
|
|
673
785
|
return `${key}=${generateRandomSecret()}`;
|
|
@@ -688,15 +800,18 @@ async function init(params, logger) {
|
|
|
688
800
|
}
|
|
689
801
|
const { organizationName, platformName, platformDisplayName } = params;
|
|
690
802
|
const platformTitle = camelize(platformName);
|
|
691
|
-
const outputDir =
|
|
803
|
+
const outputDir = cwd4();
|
|
692
804
|
logger.log(`Initializing ${platformTitle} platform for @${organizationName}...`);
|
|
805
|
+
const coreDirName = `${platformName}-core`;
|
|
806
|
+
const bootstrapServiceName = `${platformName}-bootstrap-service`;
|
|
807
|
+
const customizationUiName = `${platformName}-customization-ui`;
|
|
693
808
|
const variables = {
|
|
694
809
|
organizationName,
|
|
695
810
|
platformName,
|
|
696
811
|
platformTitle,
|
|
697
812
|
platformDisplayName,
|
|
698
|
-
bootstrapName:
|
|
699
|
-
bootstrapServiceDir:
|
|
813
|
+
bootstrapName: platformName,
|
|
814
|
+
bootstrapServiceDir: `${coreDirName}/services`
|
|
700
815
|
};
|
|
701
816
|
try {
|
|
702
817
|
await scaffoldPlatform(outputDir, variables, logger);
|
|
@@ -705,14 +820,22 @@ async function init(params, logger) {
|
|
|
705
820
|
return;
|
|
706
821
|
}
|
|
707
822
|
try {
|
|
708
|
-
await generateLocalEnv(outputDir, logger);
|
|
823
|
+
await generateLocalEnv(outputDir, logger, coreDirName);
|
|
709
824
|
} catch (err) {
|
|
710
|
-
logger.log(`Error: Could not generate local/.env \u2014 ${formatError(err)}`);
|
|
825
|
+
logger.log(`Error: Could not generate ${coreDirName}/local/.env \u2014 ${formatError(err)}`);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
try {
|
|
829
|
+
const manifest = createInitialManifest({ organizationName, platformName, platformDisplayName });
|
|
830
|
+
await writeManifest(manifest, outputDir, coreDirName);
|
|
831
|
+
logger.log(`Created product manifest: ${coreDirName}/product.manifest.json`);
|
|
832
|
+
} catch (err) {
|
|
833
|
+
logger.log(`Error: Could not write product manifest \u2014 ${formatError(err)}`);
|
|
711
834
|
return;
|
|
712
835
|
}
|
|
713
836
|
try {
|
|
714
837
|
await scaffoldPlatformBootstrap(
|
|
715
|
-
|
|
838
|
+
join17(outputDir, coreDirName, "services", bootstrapServiceName),
|
|
716
839
|
variables,
|
|
717
840
|
logger
|
|
718
841
|
);
|
|
@@ -722,7 +845,7 @@ async function init(params, logger) {
|
|
|
722
845
|
}
|
|
723
846
|
try {
|
|
724
847
|
await scaffoldCustomizationUi(
|
|
725
|
-
|
|
848
|
+
join17(outputDir, coreDirName, "ui", customizationUiName),
|
|
726
849
|
variables,
|
|
727
850
|
logger
|
|
728
851
|
);
|
|
@@ -732,9 +855,10 @@ async function init(params, logger) {
|
|
|
732
855
|
}
|
|
733
856
|
try {
|
|
734
857
|
await registerCustomizationModule(
|
|
735
|
-
|
|
858
|
+
join17(outputDir, coreDirName, "services", bootstrapServiceName),
|
|
736
859
|
organizationName,
|
|
737
|
-
logger
|
|
860
|
+
logger,
|
|
861
|
+
platformName
|
|
738
862
|
);
|
|
739
863
|
} catch (err) {
|
|
740
864
|
logger.log(`Error: Could not register customization module \u2014 ${formatError(err)}`);
|
|
@@ -744,8 +868,7 @@ async function init(params, logger) {
|
|
|
744
868
|
}
|
|
745
869
|
|
|
746
870
|
// src/commands/configure-idp/configure-idp.command.ts
|
|
747
|
-
import { join as
|
|
748
|
-
import { cwd as cwd4 } from "process";
|
|
871
|
+
import { join as join18 } from "path";
|
|
749
872
|
import { fetch as undiciFetch, Agent } from "undici";
|
|
750
873
|
|
|
751
874
|
// src/utils/env-reader.ts
|
|
@@ -823,11 +946,12 @@ var configureIdpCommand = {
|
|
|
823
946
|
description: "Configure an Identity Provider (IDP) in the gateway"
|
|
824
947
|
};
|
|
825
948
|
async function configureIdp(params, logger) {
|
|
826
|
-
|
|
949
|
+
const layout = await findPlatformLayout();
|
|
950
|
+
if (!layout) {
|
|
827
951
|
logger.log("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
828
952
|
return;
|
|
829
953
|
}
|
|
830
|
-
const envPath =
|
|
954
|
+
const envPath = join18(layout.localDir, ".env");
|
|
831
955
|
let env;
|
|
832
956
|
try {
|
|
833
957
|
env = await readEnvFile(envPath);
|
|
@@ -838,11 +962,11 @@ async function configureIdp(params, logger) {
|
|
|
838
962
|
const gatewayUrl = env.get("PAE_GATEWAY_HOST_URL");
|
|
839
963
|
const accessSecret = env.get("PAE_GATEWAY_SERVICE_ACCESS_SECRET");
|
|
840
964
|
if (!gatewayUrl) {
|
|
841
|
-
logger.log("Error: PAE_GATEWAY_HOST_URL is not set in local/.env");
|
|
965
|
+
logger.log("Error: PAE_GATEWAY_HOST_URL is not set in core/local/.env");
|
|
842
966
|
return;
|
|
843
967
|
}
|
|
844
968
|
if (!accessSecret) {
|
|
845
|
-
logger.log("Error: PAE_GATEWAY_SERVICE_ACCESS_SECRET is not set in local/.env");
|
|
969
|
+
logger.log("Error: PAE_GATEWAY_SERVICE_ACCESS_SECRET is not set in core/local/.env");
|
|
846
970
|
return;
|
|
847
971
|
}
|
|
848
972
|
const provider = idpProviderRegistry.get(params.providerType);
|
|
@@ -879,15 +1003,14 @@ async function configureIdp(params, logger) {
|
|
|
879
1003
|
}
|
|
880
1004
|
|
|
881
1005
|
// src/commands/create-service-module/create-service-module.command.ts
|
|
882
|
-
import { join as
|
|
883
|
-
import {
|
|
884
|
-
import { access as access2, readFile as readFile9 } from "fs/promises";
|
|
1006
|
+
import { join as join20, resolve as resolve2 } from "path";
|
|
1007
|
+
import { access as access3 } from "fs/promises";
|
|
885
1008
|
|
|
886
1009
|
// src/commands/create-service-module/scaffold-service-module.ts
|
|
887
1010
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
888
|
-
import { join as
|
|
889
|
-
var nestjsServiceModuleTemplateDir2 =
|
|
890
|
-
|
|
1011
|
+
import { join as join19, dirname as dirname11 } from "path";
|
|
1012
|
+
var nestjsServiceModuleTemplateDir2 = join19(
|
|
1013
|
+
dirname11(fileURLToPath9(import.meta.url)),
|
|
891
1014
|
"..",
|
|
892
1015
|
"templates",
|
|
893
1016
|
"nestjs-service-module-template"
|
|
@@ -922,11 +1045,11 @@ function buildCustomServiceModuleEntry(organizationName, serviceName, serviceDis
|
|
|
922
1045
|
|
|
923
1046
|
// src/commands/create-service-module/append-docker-compose.ts
|
|
924
1047
|
import { readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
925
|
-
async function appendServiceToDockerCompose(dockerComposePath, serviceName, applicationName, port, logger) {
|
|
1048
|
+
async function appendServiceToDockerCompose(dockerComposePath, serviceName, platformName, applicationName, port, logger) {
|
|
926
1049
|
const block = `
|
|
927
1050
|
${serviceName}:
|
|
928
1051
|
build:
|
|
929
|
-
context:
|
|
1052
|
+
context: ../../${platformName}-${applicationName}/services/${serviceName}
|
|
930
1053
|
dockerfile: Dockerfile.development
|
|
931
1054
|
args:
|
|
932
1055
|
- BA_NPM_AUTH_TOKEN=$BA_NPM_AUTH_TOKEN
|
|
@@ -935,7 +1058,7 @@ async function appendServiceToDockerCompose(dockerComposePath, serviceName, appl
|
|
|
935
1058
|
environment:
|
|
936
1059
|
- GATEWAY_URL=\${PAE_GATEWAY_URL}
|
|
937
1060
|
volumes:
|
|
938
|
-
- \${PWD}
|
|
1061
|
+
- \${PWD}/:/app/out
|
|
939
1062
|
`;
|
|
940
1063
|
try {
|
|
941
1064
|
const existing = await readFile8(dockerComposePath, "utf-8");
|
|
@@ -955,41 +1078,41 @@ var createServiceModuleCommand = {
|
|
|
955
1078
|
};
|
|
956
1079
|
async function createServiceModule(params, logger) {
|
|
957
1080
|
const { applicationName, serviceName, serviceDisplayName } = params;
|
|
958
|
-
|
|
1081
|
+
const layout = await findPlatformLayout();
|
|
1082
|
+
if (!layout) {
|
|
959
1083
|
logger.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
960
1084
|
return;
|
|
961
1085
|
}
|
|
962
|
-
const rootDir =
|
|
963
|
-
|
|
1086
|
+
const { rootDir, coreDirName, localDir } = layout;
|
|
1087
|
+
let manifest;
|
|
964
1088
|
try {
|
|
965
|
-
await
|
|
1089
|
+
manifest = await readManifest(rootDir, coreDirName);
|
|
1090
|
+
} catch (err) {
|
|
1091
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
const { organization: organizationName, name: platformName } = manifest.product;
|
|
1095
|
+
const appEntry = manifest.applications.find((a) => a.name === applicationName);
|
|
1096
|
+
if (!appEntry) {
|
|
1097
|
+
logger.log(`Error: The specified application "${applicationName}" is not registered in the product manifest.`);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
const applicationDir = resolve2(join20(rootDir, coreDirName), appEntry.localPath);
|
|
1101
|
+
try {
|
|
1102
|
+
await access3(applicationDir);
|
|
966
1103
|
} catch {
|
|
967
1104
|
logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
|
|
968
1105
|
return;
|
|
969
1106
|
}
|
|
970
|
-
const fullServiceName = `${serviceName}-service`;
|
|
971
|
-
const serviceDir =
|
|
1107
|
+
const fullServiceName = `${platformName}-${serviceName}-service`;
|
|
1108
|
+
const serviceDir = join20(applicationDir, "services", fullServiceName);
|
|
972
1109
|
try {
|
|
973
|
-
await
|
|
1110
|
+
await access3(serviceDir);
|
|
974
1111
|
logger.log(`Error: A service named "${fullServiceName}" already exists in application "${applicationName}".`);
|
|
975
1112
|
return;
|
|
976
1113
|
} catch {
|
|
977
1114
|
}
|
|
978
|
-
|
|
979
|
-
try {
|
|
980
|
-
const corePackageJson = JSON.parse(
|
|
981
|
-
await readFile9(join19(rootDir, "core", "package.json"), "utf-8")
|
|
982
|
-
);
|
|
983
|
-
const scopeMatch = corePackageJson.name.match(/^@([^/]+)\//);
|
|
984
|
-
organizationName = scopeMatch?.[1];
|
|
985
|
-
if (!organizationName) {
|
|
986
|
-
throw new Error(`Could not parse organization from package name: "${corePackageJson.name}"`);
|
|
987
|
-
}
|
|
988
|
-
} catch (err) {
|
|
989
|
-
logger.log(`Error: Could not read core/package.json \u2014 ${formatError(err)}`);
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
const serviceBaseDir = `${applicationName}/services`;
|
|
1115
|
+
const serviceBaseDir = `${platformName}-${applicationName}/services`;
|
|
993
1116
|
try {
|
|
994
1117
|
await scaffoldServiceModule(
|
|
995
1118
|
serviceDir,
|
|
@@ -1003,36 +1126,35 @@ async function createServiceModule(params, logger) {
|
|
|
1003
1126
|
logger.log(`Error: Could not scaffold NestJS service \u2014 ${formatError(err)}`);
|
|
1004
1127
|
return;
|
|
1005
1128
|
}
|
|
1006
|
-
const bootstrapServiceDir =
|
|
1129
|
+
const bootstrapServiceDir = join20(applicationDir, "services", `${platformName}-${applicationName}-bootstrap-service`);
|
|
1007
1130
|
const moduleEntry = buildCustomServiceModuleEntry(organizationName, fullServiceName, serviceDisplayName);
|
|
1008
1131
|
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1009
|
-
const
|
|
1010
|
-
const dockerComposePath = join19(localDir, `${applicationName}-docker-compose.yml`);
|
|
1132
|
+
const dockerComposePath = join20(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
1011
1133
|
const port = await getNextAvailablePort(localDir);
|
|
1012
|
-
await appendServiceToDockerCompose(dockerComposePath, fullServiceName, applicationName, port, logger);
|
|
1134
|
+
await appendServiceToDockerCompose(dockerComposePath, fullServiceName, platformName, applicationName, port, logger);
|
|
1013
1135
|
logger.log(`Done! Service module "${fullServiceName}" added to application "${applicationName}".`);
|
|
1014
1136
|
}
|
|
1015
1137
|
|
|
1016
1138
|
// src/commands/create-ui-module/create-ui-module.command.ts
|
|
1017
|
-
import { join as
|
|
1018
|
-
import {
|
|
1019
|
-
import { access as access3, readdir as readdir3, readFile as readFile11 } from "fs/promises";
|
|
1139
|
+
import { join as join21, resolve as resolve3 } from "path";
|
|
1140
|
+
import { access as access4, readdir as readdir4 } from "fs/promises";
|
|
1020
1141
|
|
|
1021
1142
|
// src/commands/create-ui-module/append-ui-docker-compose.ts
|
|
1022
|
-
import { readFile as
|
|
1023
|
-
async function appendUiToDockerCompose(dockerComposePath, applicationName, port, logger) {
|
|
1143
|
+
import { readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
|
|
1144
|
+
async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger) {
|
|
1145
|
+
const uiName = `${platformName}-${applicationName}-ui`;
|
|
1024
1146
|
const block = `
|
|
1025
|
-
${
|
|
1147
|
+
${uiName}:
|
|
1026
1148
|
build:
|
|
1027
|
-
context:
|
|
1149
|
+
context: ../../${platformName}-${applicationName}/ui/${uiName}
|
|
1028
1150
|
dockerfile: Dockerfile.development
|
|
1029
1151
|
ports:
|
|
1030
1152
|
- ${port}:80
|
|
1031
1153
|
volumes:
|
|
1032
|
-
- \${PWD}
|
|
1154
|
+
- \${PWD}/:/app/out
|
|
1033
1155
|
`;
|
|
1034
1156
|
try {
|
|
1035
|
-
const existing = await
|
|
1157
|
+
const existing = await readFile9(dockerComposePath, "utf-8");
|
|
1036
1158
|
await writeFile8(dockerComposePath, existing + block, "utf-8");
|
|
1037
1159
|
logger.log(`Updated docker-compose: ${dockerComposePath}`);
|
|
1038
1160
|
} catch (err) {
|
|
@@ -1049,21 +1171,35 @@ var createUiModuleCommand = {
|
|
|
1049
1171
|
};
|
|
1050
1172
|
async function createUiModule(params, logger) {
|
|
1051
1173
|
const { applicationName, applicationDisplayName } = params;
|
|
1052
|
-
|
|
1174
|
+
const layout = await findPlatformLayout();
|
|
1175
|
+
if (!layout) {
|
|
1053
1176
|
logger.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
1054
1177
|
return;
|
|
1055
1178
|
}
|
|
1056
|
-
const rootDir =
|
|
1057
|
-
|
|
1179
|
+
const { rootDir, coreDirName, localDir } = layout;
|
|
1180
|
+
let manifest;
|
|
1058
1181
|
try {
|
|
1059
|
-
await
|
|
1182
|
+
manifest = await readManifest(rootDir, coreDirName);
|
|
1183
|
+
} catch (err) {
|
|
1184
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const { organization: organizationName, name: platformName } = manifest.product;
|
|
1188
|
+
const appEntry = manifest.applications.find((a) => a.name === applicationName);
|
|
1189
|
+
if (!appEntry) {
|
|
1190
|
+
logger.log(`Error: The specified application "${applicationName}" is not registered in the product manifest.`);
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
const applicationDir = resolve3(join21(rootDir, coreDirName), appEntry.localPath);
|
|
1194
|
+
try {
|
|
1195
|
+
await access4(applicationDir);
|
|
1060
1196
|
} catch {
|
|
1061
1197
|
logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
|
|
1062
1198
|
return;
|
|
1063
1199
|
}
|
|
1064
|
-
const uiDir =
|
|
1200
|
+
const uiDir = join21(applicationDir, "ui");
|
|
1065
1201
|
try {
|
|
1066
|
-
const uiEntries = await
|
|
1202
|
+
const uiEntries = await readdir4(uiDir);
|
|
1067
1203
|
const existingUiModules = uiEntries.filter((e) => e !== ".gitkeep");
|
|
1068
1204
|
if (existingUiModules.length > 0) {
|
|
1069
1205
|
logger.log(`Error: Currently we support only one UI module per application. Application "${applicationName}" already has a UI module.`);
|
|
@@ -1071,39 +1207,22 @@ async function createUiModule(params, logger) {
|
|
|
1071
1207
|
}
|
|
1072
1208
|
} catch {
|
|
1073
1209
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const corePackageJson = JSON.parse(
|
|
1078
|
-
await readFile11(join20(rootDir, "core", "package.json"), "utf-8")
|
|
1079
|
-
);
|
|
1080
|
-
const scopeMatch = corePackageJson.name.match(/^@([^/]+)\//);
|
|
1081
|
-
organizationName = scopeMatch?.[1];
|
|
1082
|
-
const rawName = corePackageJson.name.replace(/^@[^/]+\//, "").replace(/-core$/, "");
|
|
1083
|
-
platformName = rawName;
|
|
1084
|
-
if (!organizationName || !platformName) {
|
|
1085
|
-
throw new Error(`Could not parse organization/platform from package name: "${corePackageJson.name}"`);
|
|
1086
|
-
}
|
|
1087
|
-
} catch (err) {
|
|
1088
|
-
logger.log(`Error: Could not read core/package.json \u2014 ${formatError(err)}`);
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
const uiOutputDir = join20(uiDir, `${applicationName}-ui`);
|
|
1092
|
-
const uiBaseDir = `${applicationName}/ui`;
|
|
1210
|
+
const uiName = `${platformName}-${applicationName}-ui`;
|
|
1211
|
+
const uiOutputDir = join21(uiDir, uiName);
|
|
1212
|
+
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
1093
1213
|
try {
|
|
1094
1214
|
await scaffoldUiModule(uiOutputDir, organizationName, platformName, applicationName, applicationDisplayName, uiBaseDir, logger);
|
|
1095
1215
|
} catch (err) {
|
|
1096
1216
|
logger.log(`Error: Could not scaffold UI module \u2014 ${formatError(err)}`);
|
|
1097
1217
|
return;
|
|
1098
1218
|
}
|
|
1099
|
-
const bootstrapServiceDir =
|
|
1100
|
-
const moduleEntry = buildUiModuleEntry(organizationName, applicationName, applicationDisplayName);
|
|
1219
|
+
const bootstrapServiceDir = join21(applicationDir, "services", `${platformName}-${applicationName}-bootstrap-service`);
|
|
1220
|
+
const moduleEntry = buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName);
|
|
1101
1221
|
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1102
|
-
const
|
|
1103
|
-
const dockerComposePath = join20(localDir, `${applicationName}-docker-compose.yml`);
|
|
1222
|
+
const dockerComposePath = join21(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
1104
1223
|
const port = await getNextAvailablePort(localDir);
|
|
1105
|
-
await appendUiToDockerCompose(dockerComposePath, applicationName, port, logger);
|
|
1106
|
-
logger.log(`Done! UI module "${
|
|
1224
|
+
await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger);
|
|
1225
|
+
logger.log(`Done! UI module "${uiName}" added to application "${applicationName}".`);
|
|
1107
1226
|
}
|
|
1108
1227
|
|
|
1109
1228
|
// src/commands/registry.ts
|
|
@@ -1145,11 +1264,6 @@ var APP_STATE = {
|
|
|
1145
1264
|
// src/hooks/use-command-runner.ts
|
|
1146
1265
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
1147
1266
|
|
|
1148
|
-
// src/controllers/ui/create-application.ui-controller.ts
|
|
1149
|
-
import { readFile as readFile12 } from "fs/promises";
|
|
1150
|
-
import { join as join21 } from "path";
|
|
1151
|
-
import { cwd as cwd7 } from "process";
|
|
1152
|
-
|
|
1153
1267
|
// src/services/create-application.service.ts
|
|
1154
1268
|
async function createApplicationService(params, logger) {
|
|
1155
1269
|
await createApplication(params, logger);
|
|
@@ -1161,24 +1275,6 @@ async function createApplicationUiController(ctx) {
|
|
|
1161
1275
|
ctx.log("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
1162
1276
|
return;
|
|
1163
1277
|
}
|
|
1164
|
-
let organizationName;
|
|
1165
|
-
let platformName;
|
|
1166
|
-
try {
|
|
1167
|
-
const corePackageJson = JSON.parse(
|
|
1168
|
-
await readFile12(join21(cwd7(), "core", "package.json"), "utf-8")
|
|
1169
|
-
);
|
|
1170
|
-
const scopeMatch = corePackageJson.name.match(/^@([^/]+)\//);
|
|
1171
|
-
organizationName = scopeMatch?.[1];
|
|
1172
|
-
const rawName = corePackageJson.name.replace(/^@[^/]+\//, "").replace(/-core$/, "");
|
|
1173
|
-
platformName = rawName;
|
|
1174
|
-
if (!organizationName || !platformName) {
|
|
1175
|
-
throw new Error(`Could not parse organization/platform from package name: "${corePackageJson.name}"`);
|
|
1176
|
-
}
|
|
1177
|
-
} catch (err) {
|
|
1178
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1179
|
-
ctx.log(`Error: Could not read core/package.json \u2014 ${message}`);
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
1278
|
const applicationName = await ctx.prompt("Application name:");
|
|
1183
1279
|
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
1184
1280
|
ctx.log(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
@@ -1198,8 +1294,6 @@ async function createApplicationUiController(ctx) {
|
|
|
1198
1294
|
}
|
|
1199
1295
|
await createApplicationService(
|
|
1200
1296
|
{
|
|
1201
|
-
organizationName,
|
|
1202
|
-
platformName,
|
|
1203
1297
|
applicationName,
|
|
1204
1298
|
applicationDisplayName,
|
|
1205
1299
|
applicationDescription,
|
|
@@ -1333,7 +1427,7 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1333
1427
|
abortControllerRef.current = null;
|
|
1334
1428
|
promptResolveRef.current = null;
|
|
1335
1429
|
}, []);
|
|
1336
|
-
const
|
|
1430
|
+
const runCommand2 = useCallback(
|
|
1337
1431
|
(cmd) => {
|
|
1338
1432
|
const controller = new AbortController();
|
|
1339
1433
|
abortControllerRef.current = controller;
|
|
@@ -1347,14 +1441,14 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1347
1441
|
}
|
|
1348
1442
|
},
|
|
1349
1443
|
prompt(message) {
|
|
1350
|
-
return new Promise((
|
|
1444
|
+
return new Promise((resolve5, reject) => {
|
|
1351
1445
|
if (controller.signal.aborted) {
|
|
1352
1446
|
reject(new DOMException("Aborted", "AbortError"));
|
|
1353
1447
|
return;
|
|
1354
1448
|
}
|
|
1355
1449
|
setPromptMessage(message);
|
|
1356
1450
|
setPromptValue("");
|
|
1357
|
-
promptResolveRef.current =
|
|
1451
|
+
promptResolveRef.current = resolve5;
|
|
1358
1452
|
setState(APP_STATE.PROMPTING);
|
|
1359
1453
|
controller.signal.addEventListener(
|
|
1360
1454
|
"abort",
|
|
@@ -1386,15 +1480,15 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1386
1480
|
);
|
|
1387
1481
|
const handlePromptSubmit = useCallback(
|
|
1388
1482
|
(value) => {
|
|
1389
|
-
const
|
|
1483
|
+
const resolve5 = promptResolveRef.current;
|
|
1390
1484
|
promptResolveRef.current = null;
|
|
1391
1485
|
setState(APP_STATE.EXECUTING);
|
|
1392
|
-
|
|
1486
|
+
resolve5?.(value);
|
|
1393
1487
|
},
|
|
1394
1488
|
[setState]
|
|
1395
1489
|
);
|
|
1396
1490
|
return {
|
|
1397
|
-
runCommand,
|
|
1491
|
+
runCommand: runCommand2,
|
|
1398
1492
|
handlePromptSubmit,
|
|
1399
1493
|
abortExecution,
|
|
1400
1494
|
promptMessage,
|
|
@@ -1404,7 +1498,7 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1404
1498
|
}
|
|
1405
1499
|
|
|
1406
1500
|
// src/app.tsx
|
|
1407
|
-
import { jsx as
|
|
1501
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1408
1502
|
var require2 = createRequire(import.meta.url);
|
|
1409
1503
|
var { version } = require2("../package.json");
|
|
1410
1504
|
function App() {
|
|
@@ -1416,7 +1510,7 @@ function App() {
|
|
|
1416
1510
|
const appendStaticItem = useCallback2((item) => {
|
|
1417
1511
|
setStaticItems((prev) => [...prev, item]);
|
|
1418
1512
|
}, []);
|
|
1419
|
-
const { runCommand, handlePromptSubmit, abortExecution, promptMessage, promptValue, setPromptValue } = useCommandRunner({ appendStaticItem, setState });
|
|
1513
|
+
const { runCommand: runCommand2, handlePromptSubmit, abortExecution, promptMessage, promptValue, setPromptValue } = useCommandRunner({ appendStaticItem, setState });
|
|
1420
1514
|
const query = inputValue.startsWith("/") ? inputValue.slice(1) : "";
|
|
1421
1515
|
const filteredCommands = registry.search(query);
|
|
1422
1516
|
useInput(
|
|
@@ -1448,7 +1542,7 @@ function App() {
|
|
|
1448
1542
|
const cmd = filteredCommands[selectedIndex];
|
|
1449
1543
|
if (cmd) {
|
|
1450
1544
|
setInputValue("");
|
|
1451
|
-
|
|
1545
|
+
runCommand2(cmd);
|
|
1452
1546
|
}
|
|
1453
1547
|
return;
|
|
1454
1548
|
}
|
|
@@ -1484,24 +1578,24 @@ function App() {
|
|
|
1484
1578
|
if (!value.startsWith("/")) return;
|
|
1485
1579
|
const name = value.slice(1).trim();
|
|
1486
1580
|
const cmd = registry.get(name);
|
|
1487
|
-
if (cmd)
|
|
1581
|
+
if (cmd) runCommand2(cmd);
|
|
1488
1582
|
},
|
|
1489
|
-
[
|
|
1583
|
+
[runCommand2]
|
|
1490
1584
|
);
|
|
1491
1585
|
function renderActiveArea() {
|
|
1492
1586
|
switch (state) {
|
|
1493
1587
|
case APP_STATE.EXECUTING:
|
|
1494
|
-
return /* @__PURE__ */
|
|
1495
|
-
/* @__PURE__ */
|
|
1496
|
-
/* @__PURE__ */
|
|
1588
|
+
return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "row", gap: 1, children: [
|
|
1589
|
+
/* @__PURE__ */ jsx7(Spinner, { label: "Running\u2026" }),
|
|
1590
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "(esc to cancel)" })
|
|
1497
1591
|
] });
|
|
1498
1592
|
case APP_STATE.PROMPTING:
|
|
1499
|
-
return /* @__PURE__ */
|
|
1500
|
-
/* @__PURE__ */
|
|
1501
|
-
/* @__PURE__ */
|
|
1593
|
+
return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "column", children: [
|
|
1594
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: promptMessage }),
|
|
1595
|
+
/* @__PURE__ */ jsx7(Prompt, { value: promptValue, onChange: setPromptValue, onSubmit: handlePromptSubmit })
|
|
1502
1596
|
] });
|
|
1503
1597
|
default:
|
|
1504
|
-
return /* @__PURE__ */
|
|
1598
|
+
return /* @__PURE__ */ jsx7(
|
|
1505
1599
|
Prompt,
|
|
1506
1600
|
{
|
|
1507
1601
|
value: inputValue,
|
|
@@ -1514,17 +1608,14 @@ function App() {
|
|
|
1514
1608
|
);
|
|
1515
1609
|
}
|
|
1516
1610
|
}
|
|
1517
|
-
return /* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
1611
|
+
return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "column", children: [
|
|
1612
|
+
/* @__PURE__ */ jsx7(ScrollbackHistory, { items: staticItems, version }),
|
|
1519
1613
|
renderActiveArea(),
|
|
1520
|
-
state === APP_STATE.PALETTE && /* @__PURE__ */
|
|
1614
|
+
state === APP_STATE.PALETTE && /* @__PURE__ */ jsx7(CommandPalette, { commands: filteredCommands, selectedIndex })
|
|
1521
1615
|
] });
|
|
1522
1616
|
}
|
|
1523
1617
|
|
|
1524
1618
|
// src/controllers/cli/create-application.cli-controller.ts
|
|
1525
|
-
import { readFile as readFile13 } from "fs/promises";
|
|
1526
|
-
import { join as join22 } from "path";
|
|
1527
|
-
import { cwd as cwd8 } from "process";
|
|
1528
1619
|
async function createApplicationCliController(args2) {
|
|
1529
1620
|
if (!await isPlatformInitialized()) {
|
|
1530
1621
|
console.error("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
@@ -1545,28 +1636,8 @@ async function createApplicationCliController(args2) {
|
|
|
1545
1636
|
console.error(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1546
1637
|
process.exit(1);
|
|
1547
1638
|
}
|
|
1548
|
-
let organizationName;
|
|
1549
|
-
let platformName;
|
|
1550
|
-
try {
|
|
1551
|
-
const corePackageJson = JSON.parse(
|
|
1552
|
-
await readFile13(join22(cwd8(), "core", "package.json"), "utf-8")
|
|
1553
|
-
);
|
|
1554
|
-
const scopeMatch = corePackageJson.name.match(/^@([^/]+)\//);
|
|
1555
|
-
organizationName = scopeMatch?.[1];
|
|
1556
|
-
const rawName = corePackageJson.name.replace(/^@[^/]+\//, "").replace(/-core$/, "");
|
|
1557
|
-
platformName = rawName;
|
|
1558
|
-
if (!organizationName || !platformName) {
|
|
1559
|
-
throw new Error(`Could not parse organization/platform from package name: "${corePackageJson.name}"`);
|
|
1560
|
-
}
|
|
1561
|
-
} catch (err) {
|
|
1562
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1563
|
-
console.error(`Error: Could not read core/package.json \u2014 ${message}`);
|
|
1564
|
-
process.exit(1);
|
|
1565
|
-
}
|
|
1566
1639
|
await createApplicationService(
|
|
1567
1640
|
{
|
|
1568
|
-
organizationName,
|
|
1569
|
-
platformName,
|
|
1570
1641
|
applicationName,
|
|
1571
1642
|
applicationDisplayName,
|
|
1572
1643
|
applicationDescription,
|
|
@@ -1623,22 +1694,187 @@ async function configureIdpCliController(args2) {
|
|
|
1623
1694
|
await configureIdpService({ providerType, name, issuer, clientId, clientSecret, extras }, logger);
|
|
1624
1695
|
}
|
|
1625
1696
|
|
|
1626
|
-
// src/
|
|
1697
|
+
// src/commands/local-scripts/docker-compose-orchestrator.ts
|
|
1627
1698
|
import { spawn } from "child_process";
|
|
1628
|
-
import { access as
|
|
1629
|
-
import { join as
|
|
1630
|
-
|
|
1631
|
-
|
|
1699
|
+
import { access as access5 } from "fs/promises";
|
|
1700
|
+
import { join as join22 } from "path";
|
|
1701
|
+
function runDockerCompose(args2, logger, rootDir, signal) {
|
|
1702
|
+
return new Promise((resolvePromise) => {
|
|
1703
|
+
const child = spawn("docker", ["compose", ...args2], {
|
|
1704
|
+
shell: false,
|
|
1705
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1706
|
+
cwd: rootDir
|
|
1707
|
+
});
|
|
1708
|
+
const onAbort = () => {
|
|
1709
|
+
child.kill("SIGTERM");
|
|
1710
|
+
};
|
|
1711
|
+
if (signal) {
|
|
1712
|
+
if (signal.aborted) {
|
|
1713
|
+
child.kill("SIGTERM");
|
|
1714
|
+
resolvePromise();
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1718
|
+
}
|
|
1719
|
+
child.stdout.on("data", (data) => {
|
|
1720
|
+
for (const line of data.toString().split("\n")) {
|
|
1721
|
+
if (line.trim()) logger.log(line);
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
child.stderr.on("data", (data) => {
|
|
1725
|
+
for (const line of data.toString().split("\n")) {
|
|
1726
|
+
if (line.trim()) logger.log(line);
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
child.on("close", (code, sig) => {
|
|
1730
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1731
|
+
if (sig === "SIGTERM" || signal?.aborted) {
|
|
1732
|
+
logger.log("Command cancelled.");
|
|
1733
|
+
} else if (code !== 0) {
|
|
1734
|
+
logger.log(`docker compose exited with code ${code}.`);
|
|
1735
|
+
}
|
|
1736
|
+
resolvePromise();
|
|
1737
|
+
});
|
|
1738
|
+
child.on("error", (err) => {
|
|
1739
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1740
|
+
logger.log(`Failed to run docker compose: ${err.message}`);
|
|
1741
|
+
resolvePromise();
|
|
1742
|
+
});
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
async function getAppComposePaths(localDir, platformName, manifest) {
|
|
1746
|
+
const results = [];
|
|
1747
|
+
for (const app of manifest.applications) {
|
|
1748
|
+
const prefixedPath = join22(localDir, `${platformName}-${app.name}-docker-compose.yml`);
|
|
1749
|
+
const unprefixedPath = join22(localDir, `${app.name}-docker-compose.yml`);
|
|
1750
|
+
let resolved = null;
|
|
1751
|
+
try {
|
|
1752
|
+
await access5(prefixedPath);
|
|
1753
|
+
resolved = prefixedPath;
|
|
1754
|
+
} catch {
|
|
1755
|
+
try {
|
|
1756
|
+
await access5(unprefixedPath);
|
|
1757
|
+
resolved = unprefixedPath;
|
|
1758
|
+
} catch {
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
if (resolved) {
|
|
1762
|
+
results.push({ composePath: resolved, appName: app.name });
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return results;
|
|
1766
|
+
}
|
|
1767
|
+
async function buildAllComposeArgs(layout, manifest, logger) {
|
|
1768
|
+
const { rootDir, coreDirName, localDir } = layout;
|
|
1769
|
+
const platformName = manifest.product.name;
|
|
1770
|
+
const prefixedCoreCompose = join22(localDir, `${coreDirName}-docker-compose.yml`);
|
|
1771
|
+
const unprefixedCoreCompose = join22(localDir, "core-docker-compose.yml");
|
|
1772
|
+
let coreComposePath;
|
|
1632
1773
|
try {
|
|
1633
|
-
await
|
|
1774
|
+
await access5(prefixedCoreCompose);
|
|
1775
|
+
coreComposePath = prefixedCoreCompose;
|
|
1634
1776
|
} catch {
|
|
1635
|
-
|
|
1636
|
-
|
|
1777
|
+
coreComposePath = unprefixedCoreCompose;
|
|
1778
|
+
}
|
|
1779
|
+
const fileArgs = [
|
|
1780
|
+
"-f",
|
|
1781
|
+
join22(localDir, "platform-docker-compose.yml"),
|
|
1782
|
+
"-f",
|
|
1783
|
+
coreComposePath
|
|
1784
|
+
];
|
|
1785
|
+
const appEntries = await getAppComposePaths(localDir, platformName, manifest);
|
|
1786
|
+
for (const { composePath } of appEntries) {
|
|
1787
|
+
fileArgs.push("-f", composePath);
|
|
1788
|
+
}
|
|
1789
|
+
for (const app of manifest.applications) {
|
|
1790
|
+
if (!appEntries.find((e) => e.appName === app.name)) {
|
|
1791
|
+
logger.log(`Warning: No docker-compose found for application "${app.name}" in ${coreDirName}/local/ \u2014 skipping.`);
|
|
1792
|
+
}
|
|
1637
1793
|
}
|
|
1638
|
-
return
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1794
|
+
return fileArgs;
|
|
1795
|
+
}
|
|
1796
|
+
async function startEnvironment(layout, manifest, logger, signal) {
|
|
1797
|
+
const { rootDir, localDir } = layout;
|
|
1798
|
+
const platformName = manifest.product.name;
|
|
1799
|
+
const envFile = join22(localDir, ".env");
|
|
1800
|
+
const fileArgs = await buildAllComposeArgs(layout, manifest, logger);
|
|
1801
|
+
logger.log("Starting environment...");
|
|
1802
|
+
await runDockerCompose(
|
|
1803
|
+
[
|
|
1804
|
+
"-p",
|
|
1805
|
+
`${platformName}-platform`,
|
|
1806
|
+
"--project-directory",
|
|
1807
|
+
localDir,
|
|
1808
|
+
"--env-file",
|
|
1809
|
+
envFile,
|
|
1810
|
+
...fileArgs,
|
|
1811
|
+
"up",
|
|
1812
|
+
"-d",
|
|
1813
|
+
"--build",
|
|
1814
|
+
"--remove-orphans"
|
|
1815
|
+
],
|
|
1816
|
+
logger,
|
|
1817
|
+
rootDir,
|
|
1818
|
+
signal
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
async function stopEnvironment(layout, manifest, logger, signal) {
|
|
1822
|
+
const { rootDir, localDir } = layout;
|
|
1823
|
+
const platformName = manifest.product.name;
|
|
1824
|
+
const envFile = join22(localDir, ".env");
|
|
1825
|
+
const fileArgs = await buildAllComposeArgs(layout, manifest, logger);
|
|
1826
|
+
logger.log("Stopping environment...");
|
|
1827
|
+
await runDockerCompose(
|
|
1828
|
+
[
|
|
1829
|
+
"-p",
|
|
1830
|
+
`${platformName}-platform`,
|
|
1831
|
+
"--project-directory",
|
|
1832
|
+
localDir,
|
|
1833
|
+
"--env-file",
|
|
1834
|
+
envFile,
|
|
1835
|
+
...fileArgs,
|
|
1836
|
+
"down"
|
|
1837
|
+
],
|
|
1838
|
+
logger,
|
|
1839
|
+
rootDir,
|
|
1840
|
+
signal
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
async function destroyEnvironment(layout, manifest, logger, signal) {
|
|
1844
|
+
const { rootDir, localDir } = layout;
|
|
1845
|
+
const platformName = manifest.product.name;
|
|
1846
|
+
const envFile = join22(localDir, ".env");
|
|
1847
|
+
const fileArgs = await buildAllComposeArgs(layout, manifest, logger);
|
|
1848
|
+
logger.log("Destroying environment...");
|
|
1849
|
+
await runDockerCompose(
|
|
1850
|
+
[
|
|
1851
|
+
"-p",
|
|
1852
|
+
`${platformName}-platform`,
|
|
1853
|
+
"--project-directory",
|
|
1854
|
+
localDir,
|
|
1855
|
+
"--env-file",
|
|
1856
|
+
envFile,
|
|
1857
|
+
...fileArgs,
|
|
1858
|
+
"down",
|
|
1859
|
+
"-v",
|
|
1860
|
+
"--rmi",
|
|
1861
|
+
"all"
|
|
1862
|
+
],
|
|
1863
|
+
logger,
|
|
1864
|
+
rootDir,
|
|
1865
|
+
signal
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// src/commands/local-scripts/npm-orchestrator.ts
|
|
1870
|
+
import { spawn as spawn2 } from "child_process";
|
|
1871
|
+
import { access as access6 } from "fs/promises";
|
|
1872
|
+
import { resolve as resolve4, join as join23 } from "path";
|
|
1873
|
+
function runCommand(command, args2, workDir, logger, signal) {
|
|
1874
|
+
return new Promise((resolvePromise) => {
|
|
1875
|
+
const child = spawn2(command, args2, {
|
|
1876
|
+
cwd: workDir,
|
|
1877
|
+
shell: false,
|
|
1642
1878
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1643
1879
|
});
|
|
1644
1880
|
const onAbort = () => {
|
|
@@ -1647,7 +1883,7 @@ async function runNpmScript(scriptName, logger, signal) {
|
|
|
1647
1883
|
if (signal) {
|
|
1648
1884
|
if (signal.aborted) {
|
|
1649
1885
|
child.kill("SIGTERM");
|
|
1650
|
-
|
|
1886
|
+
resolvePromise();
|
|
1651
1887
|
return;
|
|
1652
1888
|
}
|
|
1653
1889
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -1665,19 +1901,72 @@ async function runNpmScript(scriptName, logger, signal) {
|
|
|
1665
1901
|
child.on("close", (code, sig) => {
|
|
1666
1902
|
signal?.removeEventListener("abort", onAbort);
|
|
1667
1903
|
if (sig === "SIGTERM" || signal?.aborted) {
|
|
1668
|
-
logger.log(
|
|
1904
|
+
logger.log("Command cancelled.");
|
|
1669
1905
|
} else if (code !== 0) {
|
|
1670
|
-
logger.log(`Command "
|
|
1906
|
+
logger.log(`Command "${command} ${args2.join(" ")}" exited with code ${code}.`);
|
|
1671
1907
|
}
|
|
1672
|
-
|
|
1908
|
+
resolvePromise();
|
|
1673
1909
|
});
|
|
1674
1910
|
child.on("error", (err) => {
|
|
1675
1911
|
signal?.removeEventListener("abort", onAbort);
|
|
1676
|
-
logger.log(`Failed to run "
|
|
1677
|
-
|
|
1912
|
+
logger.log(`Failed to run "${command} ${args2.join(" ")}": ${err.message}`);
|
|
1913
|
+
resolvePromise();
|
|
1678
1914
|
});
|
|
1679
1915
|
});
|
|
1680
1916
|
}
|
|
1917
|
+
async function dirExists(dirPath) {
|
|
1918
|
+
try {
|
|
1919
|
+
await access6(dirPath);
|
|
1920
|
+
return true;
|
|
1921
|
+
} catch {
|
|
1922
|
+
return false;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
async function installDependencies(layout, manifest, logger, signal) {
|
|
1926
|
+
const { coreDir, coreDirName } = layout;
|
|
1927
|
+
const appDirs = [];
|
|
1928
|
+
for (const app of manifest.applications) {
|
|
1929
|
+
const appDir = resolve4(join23(coreDir), app.localPath);
|
|
1930
|
+
if (!await dirExists(appDir)) {
|
|
1931
|
+
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping install.`);
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
appDirs.push({ name: app.name, dir: appDir });
|
|
1935
|
+
}
|
|
1936
|
+
const targets = [{ name: coreDirName, dir: coreDir }, ...appDirs];
|
|
1937
|
+
logger.log(`Installing dependencies in parallel: ${targets.map((t) => t.name).join(", ")}...`);
|
|
1938
|
+
await Promise.all(
|
|
1939
|
+
targets.map(
|
|
1940
|
+
({ name, dir }) => runCommand("npm", ["install"], dir, logger, signal).then(() => {
|
|
1941
|
+
logger.log(`\u2713 ${name} install done`);
|
|
1942
|
+
})
|
|
1943
|
+
)
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1946
|
+
async function buildAll(layout, manifest, logger, signal) {
|
|
1947
|
+
const { coreDir, coreDirName } = layout;
|
|
1948
|
+
logger.log(`Building ${coreDirName}/...`);
|
|
1949
|
+
await runCommand("npx", ["turbo", "build"], coreDir, logger, signal);
|
|
1950
|
+
if (signal?.aborted) return;
|
|
1951
|
+
const appDirs = [];
|
|
1952
|
+
for (const app of manifest.applications) {
|
|
1953
|
+
const appDir = resolve4(join23(coreDir), app.localPath);
|
|
1954
|
+
if (!await dirExists(appDir)) {
|
|
1955
|
+
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping build.`);
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
appDirs.push({ name: app.name, dir: appDir });
|
|
1959
|
+
}
|
|
1960
|
+
if (appDirs.length === 0) return;
|
|
1961
|
+
logger.log(`Building apps in parallel: ${appDirs.map((a) => a.name).join(", ")}...`);
|
|
1962
|
+
await Promise.all(
|
|
1963
|
+
appDirs.map(
|
|
1964
|
+
({ name, dir }) => runCommand("npm", ["run", "build"], dir, logger, signal).then(() => {
|
|
1965
|
+
logger.log(`\u2713 ${name} build done`);
|
|
1966
|
+
})
|
|
1967
|
+
)
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1681
1970
|
|
|
1682
1971
|
// src/commands/local-scripts/local-script.command.ts
|
|
1683
1972
|
var INSTALL_COMMAND_NAME = "install";
|
|
@@ -1686,7 +1975,38 @@ var START_COMMAND_NAME = "start";
|
|
|
1686
1975
|
var STOP_COMMAND_NAME = "stop";
|
|
1687
1976
|
var DESTROY_COMMAND_NAME = "destroy";
|
|
1688
1977
|
async function runLocalScript(scriptName, logger, signal) {
|
|
1689
|
-
await
|
|
1978
|
+
const layout = await findPlatformLayout();
|
|
1979
|
+
if (!layout) {
|
|
1980
|
+
logger.log(`Error: Cannot run "${scriptName}" \u2014 no platform initialized in this directory.`);
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
const { rootDir, coreDirName } = layout;
|
|
1984
|
+
let manifest;
|
|
1985
|
+
try {
|
|
1986
|
+
manifest = await readManifest(rootDir, coreDirName);
|
|
1987
|
+
} catch (err) {
|
|
1988
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
switch (scriptName) {
|
|
1992
|
+
case START_COMMAND_NAME:
|
|
1993
|
+
await startEnvironment(layout, manifest, logger, signal);
|
|
1994
|
+
break;
|
|
1995
|
+
case STOP_COMMAND_NAME:
|
|
1996
|
+
await stopEnvironment(layout, manifest, logger, signal);
|
|
1997
|
+
break;
|
|
1998
|
+
case DESTROY_COMMAND_NAME:
|
|
1999
|
+
await destroyEnvironment(layout, manifest, logger, signal);
|
|
2000
|
+
break;
|
|
2001
|
+
case INSTALL_COMMAND_NAME:
|
|
2002
|
+
await installDependencies(layout, manifest, logger, signal);
|
|
2003
|
+
break;
|
|
2004
|
+
case BUILD_COMMAND_NAME:
|
|
2005
|
+
await buildAll(layout, manifest, logger, signal);
|
|
2006
|
+
break;
|
|
2007
|
+
default:
|
|
2008
|
+
logger.log(`Error: Unknown script "${scriptName}".`);
|
|
2009
|
+
}
|
|
1690
2010
|
}
|
|
1691
2011
|
|
|
1692
2012
|
// src/services/local-script.service.ts
|
|
@@ -1812,10 +2132,10 @@ function parseKeyValueArgs(args2) {
|
|
|
1812
2132
|
}
|
|
1813
2133
|
|
|
1814
2134
|
// src/index.tsx
|
|
1815
|
-
import { jsx as
|
|
2135
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1816
2136
|
var args = process.argv.slice(2);
|
|
1817
2137
|
if (args.length === 0) {
|
|
1818
|
-
render(/* @__PURE__ */
|
|
2138
|
+
render(/* @__PURE__ */ jsx8(App, {}));
|
|
1819
2139
|
} else {
|
|
1820
2140
|
const commandName = args[0];
|
|
1821
2141
|
const params = parseKeyValueArgs(args.slice(1));
|