@bluealba/platform-cli 0.2.0 → 0.3.0-feature-platform-cli-211
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 +776 -163
- package/package.json +5 -5
- package/templates/bootstrap-service-template/package.json +2 -2
- package/templates/customization-ui-module-template/package.json +2 -2
- package/templates/customization-ui-module-template/src/root.component.tsx +2 -11
- package/templates/platform-init-template/{local → core/local}/core-docker-compose.yml +4 -4
- package/templates/platform-init-template/{local → core/local}/platform-docker-compose.yml +1 -1
- package/templates/react-ui-module-template/package.json +1 -1
- package/templates/customization-ui-module-template/src/components/ExpandedNavbarLogo.tsx +0 -62
- package/templates/customization-ui-module-template/src/components/ExtensionPoints/index.tsx +0 -28
- package/templates/customization-ui-module-template/src/components/Logo.tsx +0 -55
- package/templates/customization-ui-module-template/src/components/SplashLogo.tsx +0 -52
- package/templates/customization-ui-module-template/src/hooks/useDynamicStyleSheet.ts +0 -18
- 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/platform-init-template/{local → core/local}/.env.example +0 -0
- /package/templates/platform-init-template/{local → core/local}/environment/pae-nestjs-gateway-service.env +0 -0
- /package/templates/platform-init-template/{local → core/local}/nginx.conf +0 -0
- /package/templates/platform-init-template/{local → core/local}/ssl/cert.pem +0 -0
- /package/templates/platform-init-template/{local → core/local}/ssl/key.pem +0 -0
package/dist/index.js
CHANGED
|
@@ -61,11 +61,10 @@ import React2 from "react";
|
|
|
61
61
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
62
62
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
63
63
|
var ASCII_ART = [
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
" / | \\ "
|
|
64
|
+
"|---. .---|",
|
|
65
|
+
"| \\/ |",
|
|
66
|
+
"| /\\ |",
|
|
67
|
+
"|---' '---|"
|
|
69
68
|
];
|
|
70
69
|
var WelcomeBanner = React2.memo(function WelcomeBanner2({ version: version2 }) {
|
|
71
70
|
return /* @__PURE__ */ jsxs3(Box3, { borderStyle: "round", flexDirection: "column", paddingX: 1, paddingY: 1, children: [
|
|
@@ -125,8 +124,7 @@ function Spinner({ label }) {
|
|
|
125
124
|
import { Fzf } from "fzf";
|
|
126
125
|
|
|
127
126
|
// src/commands/create-application/create-application.command.ts
|
|
128
|
-
import { join as
|
|
129
|
-
import { cwd } from "process";
|
|
127
|
+
import { join as join10, resolve } from "path";
|
|
130
128
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
131
129
|
|
|
132
130
|
// src/commands/create-application/scaffold-application-monorepo.ts
|
|
@@ -358,10 +356,10 @@ function buildUiModuleEntry(organizationName, applicationName, applicationDispla
|
|
|
358
356
|
|
|
359
357
|
// src/commands/create-application/create-docker-compose.ts
|
|
360
358
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
361
|
-
function buildBootstrapBlock(applicationName) {
|
|
359
|
+
function buildBootstrapBlock(platformName, applicationName) {
|
|
362
360
|
return ` ${applicationName}-bootstrap-service:
|
|
363
361
|
build:
|
|
364
|
-
context:
|
|
362
|
+
context: ../../${platformName}-${applicationName}/services/${applicationName}-bootstrap-service
|
|
365
363
|
dockerfile: Dockerfile.development
|
|
366
364
|
environment:
|
|
367
365
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
@@ -371,27 +369,27 @@ function buildBootstrapBlock(applicationName) {
|
|
|
371
369
|
- GATEWAY_SERVICE_URL=\${PAE_GATEWAY_URL}
|
|
372
370
|
- SERVICE_ACCESS_SECRET=\${PAE_GATEWAY_SERVICE_ACCESS_SECRET}
|
|
373
371
|
volumes:
|
|
374
|
-
- \${PWD}
|
|
372
|
+
- \${PWD}/:/app/out
|
|
375
373
|
depends_on:
|
|
376
374
|
pae-nestjs-gateway-service:
|
|
377
375
|
condition: service_healthy
|
|
378
376
|
`;
|
|
379
377
|
}
|
|
380
|
-
function buildUiBlock(applicationName, uiPort) {
|
|
378
|
+
function buildUiBlock(platformName, applicationName, uiPort) {
|
|
381
379
|
return ` ${applicationName}-ui:
|
|
382
380
|
build:
|
|
383
|
-
context:
|
|
381
|
+
context: ../../${platformName}-${applicationName}/ui/${applicationName}-ui
|
|
384
382
|
dockerfile: Dockerfile.development
|
|
385
383
|
ports:
|
|
386
384
|
- ${uiPort}:80
|
|
387
385
|
volumes:
|
|
388
|
-
- \${PWD}
|
|
386
|
+
- \${PWD}/:/app/out
|
|
389
387
|
`;
|
|
390
388
|
}
|
|
391
|
-
function buildBackendBlock(applicationName, servicePort) {
|
|
389
|
+
function buildBackendBlock(platformName, applicationName, servicePort) {
|
|
392
390
|
return ` ${applicationName}-service:
|
|
393
391
|
build:
|
|
394
|
-
context:
|
|
392
|
+
context: ../../${platformName}-${applicationName}/services/${applicationName}-service
|
|
395
393
|
dockerfile: Dockerfile.development
|
|
396
394
|
args:
|
|
397
395
|
- BA_NPM_AUTH_TOKEN=$BA_NPM_AUTH_TOKEN
|
|
@@ -400,18 +398,18 @@ function buildBackendBlock(applicationName, servicePort) {
|
|
|
400
398
|
environment:
|
|
401
399
|
- GATEWAY_URL=\${PAE_GATEWAY_URL}
|
|
402
400
|
volumes:
|
|
403
|
-
- \${PWD}
|
|
401
|
+
- \${PWD}/:/app/out
|
|
404
402
|
`;
|
|
405
403
|
}
|
|
406
|
-
async function createDockerCompose(dockerComposePath, applicationName, hasUserInterface, hasBackendService, uiPort, servicePort, logger) {
|
|
407
|
-
const blocks = ["services:\n", buildBootstrapBlock(applicationName)];
|
|
404
|
+
async function createDockerCompose(dockerComposePath, applicationName, platformName, hasUserInterface, hasBackendService, uiPort, servicePort, logger) {
|
|
405
|
+
const blocks = ["services:\n", buildBootstrapBlock(platformName, applicationName)];
|
|
408
406
|
if (hasUserInterface) {
|
|
409
|
-
blocks.push(buildUiBlock(applicationName, uiPort));
|
|
407
|
+
blocks.push(buildUiBlock(platformName, applicationName, uiPort));
|
|
410
408
|
}
|
|
411
409
|
if (hasBackendService) {
|
|
412
|
-
blocks.push(buildBackendBlock(applicationName, servicePort));
|
|
410
|
+
blocks.push(buildBackendBlock(platformName, applicationName, servicePort));
|
|
413
411
|
}
|
|
414
|
-
const content = blocks.join("\n");
|
|
412
|
+
const content = blocks.join("\n") + "\n";
|
|
415
413
|
try {
|
|
416
414
|
await writeFile3(dockerComposePath, content, "utf-8");
|
|
417
415
|
logger.log(`Created docker-compose: ${dockerComposePath}`);
|
|
@@ -421,32 +419,18 @@ async function createDockerCompose(dockerComposePath, applicationName, hasUserIn
|
|
|
421
419
|
}
|
|
422
420
|
}
|
|
423
421
|
|
|
424
|
-
// src/commands/create-application/update-root-docker-compose.ts
|
|
425
|
-
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
426
|
-
async function updateRootDockerCompose(rootDockerComposePath, applicationName, logger) {
|
|
427
|
-
try {
|
|
428
|
-
const existing = await readFile3(rootDockerComposePath, "utf-8");
|
|
429
|
-
const includeLine = ` - path: ./${applicationName}-docker-compose.yml
|
|
430
|
-
`;
|
|
431
|
-
await writeFile4(rootDockerComposePath, existing + includeLine, "utf-8");
|
|
432
|
-
logger.log(`Updated root docker-compose: ${rootDockerComposePath}`);
|
|
433
|
-
} catch (err) {
|
|
434
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
435
|
-
logger.log(`Warning: Could not update root docker-compose \u2014 ${message}`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
422
|
// src/commands/create-application/port-allocator.ts
|
|
440
423
|
import { join as join8 } from "path";
|
|
441
|
-
import { readdir as readdir2, readFile as
|
|
442
|
-
async function getNextAvailablePort(
|
|
424
|
+
import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
|
|
425
|
+
async function getNextAvailablePort(rootDir) {
|
|
443
426
|
const allPorts = [];
|
|
427
|
+
const localDir = join8(rootDir, "core", "local");
|
|
444
428
|
try {
|
|
445
429
|
const files = await readdir2(localDir);
|
|
446
|
-
const
|
|
447
|
-
for (const file of
|
|
430
|
+
const ymlFiles = files.filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
431
|
+
for (const file of ymlFiles) {
|
|
448
432
|
try {
|
|
449
|
-
const content = await
|
|
433
|
+
const content = await readFile3(join8(localDir, file), "utf-8");
|
|
450
434
|
const regex = /^\s*-\s*"?(\d+):\d+"?\s*$/gm;
|
|
451
435
|
let match;
|
|
452
436
|
while ((match = regex.exec(content)) !== null) {
|
|
@@ -465,6 +449,71 @@ function formatError(err) {
|
|
|
465
449
|
return err instanceof Error ? err.message : String(err);
|
|
466
450
|
}
|
|
467
451
|
|
|
452
|
+
// src/utils/platform-check.ts
|
|
453
|
+
import { cwd as cwd2 } from "process";
|
|
454
|
+
import { dirname as dirname7 } from "path";
|
|
455
|
+
|
|
456
|
+
// src/utils/manifest.ts
|
|
457
|
+
import { readFile as readFile4, writeFile as writeFile4, access } from "fs/promises";
|
|
458
|
+
import { join as join9 } from "path";
|
|
459
|
+
import { cwd } from "process";
|
|
460
|
+
var MANIFEST_RELATIVE_PATH = join9("core", "product.manifest.json");
|
|
461
|
+
function manifestPath(rootDir) {
|
|
462
|
+
return join9(rootDir, MANIFEST_RELATIVE_PATH);
|
|
463
|
+
}
|
|
464
|
+
async function readManifest(rootDir = cwd()) {
|
|
465
|
+
const content = await readFile4(manifestPath(rootDir), "utf-8");
|
|
466
|
+
return JSON.parse(content);
|
|
467
|
+
}
|
|
468
|
+
async function writeManifest(manifest, rootDir = cwd()) {
|
|
469
|
+
await writeFile4(manifestPath(rootDir), JSON.stringify(manifest, null, 2), "utf-8");
|
|
470
|
+
}
|
|
471
|
+
async function manifestExists(rootDir = cwd()) {
|
|
472
|
+
try {
|
|
473
|
+
await access(manifestPath(rootDir));
|
|
474
|
+
return true;
|
|
475
|
+
} catch {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
function createInitialManifest(params) {
|
|
480
|
+
const { organizationName, platformName, platformDisplayName } = params;
|
|
481
|
+
return {
|
|
482
|
+
version: "1",
|
|
483
|
+
product: {
|
|
484
|
+
name: platformName,
|
|
485
|
+
displayName: platformDisplayName,
|
|
486
|
+
organization: organizationName,
|
|
487
|
+
scope: `@${organizationName}`
|
|
488
|
+
},
|
|
489
|
+
applications: []
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function addApplicationToManifest(manifest, app) {
|
|
493
|
+
return {
|
|
494
|
+
...manifest,
|
|
495
|
+
applications: [...manifest.applications, app]
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/utils/platform-check.ts
|
|
500
|
+
async function findRootDir(startDir = cwd2()) {
|
|
501
|
+
let dir = startDir;
|
|
502
|
+
while (true) {
|
|
503
|
+
if (await manifestExists(dir)) {
|
|
504
|
+
return dir;
|
|
505
|
+
}
|
|
506
|
+
const parent = dirname7(dir);
|
|
507
|
+
if (parent === dir) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
dir = parent;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function isPlatformInitialized() {
|
|
514
|
+
return await findRootDir() !== null;
|
|
515
|
+
}
|
|
516
|
+
|
|
468
517
|
// src/commands/create-application/create-application.command.ts
|
|
469
518
|
var CREATE_APPLICATION_COMMAND_NAME = "create-application";
|
|
470
519
|
var createApplicationCommand = {
|
|
@@ -473,18 +522,28 @@ var createApplicationCommand = {
|
|
|
473
522
|
};
|
|
474
523
|
async function createApplication(params, logger) {
|
|
475
524
|
const {
|
|
476
|
-
organizationName,
|
|
477
|
-
platformName,
|
|
478
525
|
applicationName,
|
|
479
526
|
applicationDisplayName,
|
|
480
527
|
applicationDescription,
|
|
481
528
|
hasUserInterface,
|
|
482
529
|
hasBackendService
|
|
483
530
|
} = params;
|
|
484
|
-
const rootDir =
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
531
|
+
const rootDir = await findRootDir();
|
|
532
|
+
if (!rootDir) {
|
|
533
|
+
logger.log("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
let manifest;
|
|
537
|
+
try {
|
|
538
|
+
manifest = await readManifest(rootDir);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const { organization: organizationName, name: platformName } = manifest.product;
|
|
544
|
+
const localPath = `../${platformName}-${applicationName}`;
|
|
545
|
+
const applicationDir = resolve(join10(rootDir, "core"), localPath);
|
|
546
|
+
const bootstrapServiceDir = join10(applicationDir, "services", `${applicationName}-bootstrap-service`);
|
|
488
547
|
logger.log(`Creating application monorepo "${applicationName}"...`);
|
|
489
548
|
try {
|
|
490
549
|
await scaffoldApplicationMonorepo(applicationDir, organizationName, platformName, applicationName, logger);
|
|
@@ -492,10 +551,10 @@ async function createApplication(params, logger) {
|
|
|
492
551
|
logger.log(`Error: Could not scaffold application monorepo \u2014 ${formatError(err)}`);
|
|
493
552
|
return;
|
|
494
553
|
}
|
|
495
|
-
await mkdir2(
|
|
496
|
-
await mkdir2(
|
|
554
|
+
await mkdir2(join10(applicationDir, "services"), { recursive: true });
|
|
555
|
+
await mkdir2(join10(applicationDir, "ui"), { recursive: true });
|
|
497
556
|
logger.log(`Creating bootstrap service "${applicationName}-bootstrap-service"...`);
|
|
498
|
-
const bootstrapServiceDir_var = `${applicationName}/services`;
|
|
557
|
+
const bootstrapServiceDir_var = `${platformName}-${applicationName}/services`;
|
|
499
558
|
try {
|
|
500
559
|
await scaffoldBootstrap(bootstrapServiceDir, organizationName, applicationName, bootstrapServiceDir_var, logger);
|
|
501
560
|
} catch (err) {
|
|
@@ -526,8 +585,8 @@ async function createApplication(params, logger) {
|
|
|
526
585
|
);
|
|
527
586
|
}
|
|
528
587
|
if (hasUserInterface) {
|
|
529
|
-
const uiDir =
|
|
530
|
-
const uiBaseDir = `${applicationName}/ui`;
|
|
588
|
+
const uiDir = join10(applicationDir, "ui", `${applicationName}-ui`);
|
|
589
|
+
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
531
590
|
try {
|
|
532
591
|
await scaffoldUiModule(uiDir, organizationName, platformName, applicationName, applicationDisplayName, uiBaseDir, logger);
|
|
533
592
|
} catch (err) {
|
|
@@ -536,8 +595,8 @@ async function createApplication(params, logger) {
|
|
|
536
595
|
}
|
|
537
596
|
}
|
|
538
597
|
if (hasBackendService) {
|
|
539
|
-
const serviceDir =
|
|
540
|
-
const serviceBaseDir = `${applicationName}/services`;
|
|
598
|
+
const serviceDir = join10(applicationDir, "services", `${applicationName}-service`);
|
|
599
|
+
const serviceBaseDir = `${platformName}-${applicationName}/services`;
|
|
541
600
|
try {
|
|
542
601
|
await scaffoldNestjsService(serviceDir, organizationName, applicationName, applicationDisplayName, serviceBaseDir, logger);
|
|
543
602
|
} catch (err) {
|
|
@@ -545,27 +604,39 @@ async function createApplication(params, logger) {
|
|
|
545
604
|
return;
|
|
546
605
|
}
|
|
547
606
|
}
|
|
548
|
-
const dockerComposePath =
|
|
549
|
-
const basePort = await getNextAvailablePort(
|
|
607
|
+
const dockerComposePath = join10(rootDir, "core", "local", `${applicationName}-docker-compose.yml`);
|
|
608
|
+
const basePort = await getNextAvailablePort(rootDir);
|
|
550
609
|
const uiPort = hasUserInterface ? basePort : 0;
|
|
551
610
|
const servicePort = hasBackendService ? hasUserInterface ? basePort + 1 : basePort : 0;
|
|
552
611
|
await createDockerCompose(
|
|
553
612
|
dockerComposePath,
|
|
554
613
|
applicationName,
|
|
614
|
+
platformName,
|
|
555
615
|
hasUserInterface,
|
|
556
616
|
hasBackendService,
|
|
557
617
|
uiPort,
|
|
558
618
|
servicePort,
|
|
559
619
|
logger
|
|
560
620
|
);
|
|
561
|
-
const
|
|
562
|
-
|
|
621
|
+
const updatedManifest = addApplicationToManifest(manifest, {
|
|
622
|
+
name: applicationName,
|
|
623
|
+
displayName: applicationDisplayName,
|
|
624
|
+
description: applicationDescription,
|
|
625
|
+
localPath,
|
|
626
|
+
repository: null
|
|
627
|
+
});
|
|
628
|
+
try {
|
|
629
|
+
await writeManifest(updatedManifest, rootDir);
|
|
630
|
+
logger.log(`Updated product manifest with application "${applicationName}".`);
|
|
631
|
+
} catch (err) {
|
|
632
|
+
logger.log(`Warning: Could not update product manifest \u2014 ${formatError(err)}`);
|
|
633
|
+
}
|
|
563
634
|
logger.log(`Done! Application "${applicationName}" created at ${applicationDir}`);
|
|
564
635
|
}
|
|
565
636
|
|
|
566
637
|
// src/commands/init/init.command.ts
|
|
567
|
-
import { join as
|
|
568
|
-
import { cwd as
|
|
638
|
+
import { join as join16 } from "path";
|
|
639
|
+
import { cwd as cwd3 } from "process";
|
|
569
640
|
|
|
570
641
|
// src/utils/string.ts
|
|
571
642
|
function camelize(name) {
|
|
@@ -574,9 +645,9 @@ function camelize(name) {
|
|
|
574
645
|
|
|
575
646
|
// src/commands/init/scaffold-platform.ts
|
|
576
647
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
577
|
-
import { join as
|
|
578
|
-
var templateDir =
|
|
579
|
-
|
|
648
|
+
import { join as join11, dirname as dirname8 } from "path";
|
|
649
|
+
var templateDir = join11(
|
|
650
|
+
dirname8(fileURLToPath6(import.meta.url)),
|
|
580
651
|
"..",
|
|
581
652
|
"templates",
|
|
582
653
|
"platform-init-template"
|
|
@@ -587,9 +658,9 @@ async function scaffoldPlatform(outputDir, variables, logger) {
|
|
|
587
658
|
|
|
588
659
|
// src/commands/init/scaffold-platform-bootstrap.ts
|
|
589
660
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
590
|
-
import { join as
|
|
591
|
-
var templateDir2 =
|
|
592
|
-
|
|
661
|
+
import { join as join12, dirname as dirname9 } from "path";
|
|
662
|
+
var templateDir2 = join12(
|
|
663
|
+
dirname9(fileURLToPath7(import.meta.url)),
|
|
593
664
|
"..",
|
|
594
665
|
"templates",
|
|
595
666
|
"bootstrap-service-template"
|
|
@@ -600,9 +671,9 @@ async function scaffoldPlatformBootstrap(outputDir, variables, logger) {
|
|
|
600
671
|
|
|
601
672
|
// src/commands/init/scaffold-customization-ui.ts
|
|
602
673
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
603
|
-
import { join as
|
|
604
|
-
var templateDir3 =
|
|
605
|
-
|
|
674
|
+
import { join as join13, dirname as dirname10 } from "path";
|
|
675
|
+
var templateDir3 = join13(
|
|
676
|
+
dirname10(fileURLToPath8(import.meta.url)),
|
|
606
677
|
"..",
|
|
607
678
|
"templates",
|
|
608
679
|
"customization-ui-module-template"
|
|
@@ -612,10 +683,10 @@ async function scaffoldCustomizationUi(outputDir, variables, logger) {
|
|
|
612
683
|
}
|
|
613
684
|
|
|
614
685
|
// src/commands/init/register-customization-module.ts
|
|
615
|
-
import { join as
|
|
686
|
+
import { join as join14 } from "path";
|
|
616
687
|
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
617
688
|
async function registerCustomizationModule(bootstrapServiceDir, organizationName, logger) {
|
|
618
|
-
const modulesJsonPath =
|
|
689
|
+
const modulesJsonPath = join14(bootstrapServiceDir, "src", "data", "platform", "modules.json");
|
|
619
690
|
const entry = {
|
|
620
691
|
name: `@${organizationName}/platform-customization-ui`,
|
|
621
692
|
displayName: "Platform Customization UI",
|
|
@@ -638,7 +709,7 @@ async function registerCustomizationModule(bootstrapServiceDir, organizationName
|
|
|
638
709
|
|
|
639
710
|
// src/commands/init/generate-local-env.ts
|
|
640
711
|
import { readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
641
|
-
import { join as
|
|
712
|
+
import { join as join15 } from "path";
|
|
642
713
|
|
|
643
714
|
// src/utils/random.ts
|
|
644
715
|
import { randomBytes } from "crypto";
|
|
@@ -648,9 +719,9 @@ function generateRandomSecret() {
|
|
|
648
719
|
|
|
649
720
|
// src/commands/init/generate-local-env.ts
|
|
650
721
|
async function generateLocalEnv(outputDir, logger) {
|
|
651
|
-
const examplePath =
|
|
652
|
-
const envPath =
|
|
653
|
-
logger.log("Generating local/.env with random secrets...");
|
|
722
|
+
const examplePath = join15(outputDir, "core", "local", ".env.example");
|
|
723
|
+
const envPath = join15(outputDir, "core", "local", ".env");
|
|
724
|
+
logger.log("Generating core/local/.env with random secrets...");
|
|
654
725
|
const content = await readFile6(examplePath, "utf-8");
|
|
655
726
|
const result = content.replace(/^(PAE_AUTH_JWT_SECRET|PAE_GATEWAY_SERVICE_ACCESS_SECRET|PAE_DB_PASSWORD)=$/gm, (_, key) => {
|
|
656
727
|
return `${key}=${generateRandomSecret()}`;
|
|
@@ -665,9 +736,13 @@ var initCommand = {
|
|
|
665
736
|
description: "Initialize a new platform"
|
|
666
737
|
};
|
|
667
738
|
async function init(params, logger) {
|
|
739
|
+
if (await isPlatformInitialized()) {
|
|
740
|
+
logger.log("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
668
743
|
const { organizationName, platformName, platformDisplayName } = params;
|
|
669
744
|
const platformTitle = camelize(platformName);
|
|
670
|
-
const outputDir =
|
|
745
|
+
const outputDir = cwd3();
|
|
671
746
|
logger.log(`Initializing ${platformTitle} platform for @${organizationName}...`);
|
|
672
747
|
const variables = {
|
|
673
748
|
organizationName,
|
|
@@ -686,12 +761,20 @@ async function init(params, logger) {
|
|
|
686
761
|
try {
|
|
687
762
|
await generateLocalEnv(outputDir, logger);
|
|
688
763
|
} catch (err) {
|
|
689
|
-
logger.log(`Error: Could not generate local/.env \u2014 ${formatError(err)}`);
|
|
764
|
+
logger.log(`Error: Could not generate core/local/.env \u2014 ${formatError(err)}`);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
const manifest = createInitialManifest({ organizationName, platformName, platformDisplayName });
|
|
769
|
+
await writeManifest(manifest, outputDir);
|
|
770
|
+
logger.log("Created product manifest: core/product.manifest.json");
|
|
771
|
+
} catch (err) {
|
|
772
|
+
logger.log(`Error: Could not write product manifest \u2014 ${formatError(err)}`);
|
|
690
773
|
return;
|
|
691
774
|
}
|
|
692
775
|
try {
|
|
693
776
|
await scaffoldPlatformBootstrap(
|
|
694
|
-
|
|
777
|
+
join16(outputDir, "core", "services", "platform-bootstrap-service"),
|
|
695
778
|
variables,
|
|
696
779
|
logger
|
|
697
780
|
);
|
|
@@ -701,7 +784,7 @@ async function init(params, logger) {
|
|
|
701
784
|
}
|
|
702
785
|
try {
|
|
703
786
|
await scaffoldCustomizationUi(
|
|
704
|
-
|
|
787
|
+
join16(outputDir, "core", "ui", "platform-customization-ui"),
|
|
705
788
|
variables,
|
|
706
789
|
logger
|
|
707
790
|
);
|
|
@@ -711,7 +794,7 @@ async function init(params, logger) {
|
|
|
711
794
|
}
|
|
712
795
|
try {
|
|
713
796
|
await registerCustomizationModule(
|
|
714
|
-
|
|
797
|
+
join16(outputDir, "core", "services", "platform-bootstrap-service"),
|
|
715
798
|
organizationName,
|
|
716
799
|
logger
|
|
717
800
|
);
|
|
@@ -723,8 +806,7 @@ async function init(params, logger) {
|
|
|
723
806
|
}
|
|
724
807
|
|
|
725
808
|
// src/commands/configure-idp/configure-idp.command.ts
|
|
726
|
-
import { join as
|
|
727
|
-
import { cwd as cwd3 } from "process";
|
|
809
|
+
import { join as join17 } from "path";
|
|
728
810
|
import { fetch as undiciFetch, Agent } from "undici";
|
|
729
811
|
|
|
730
812
|
// src/utils/env-reader.ts
|
|
@@ -802,7 +884,12 @@ var configureIdpCommand = {
|
|
|
802
884
|
description: "Configure an Identity Provider (IDP) in the gateway"
|
|
803
885
|
};
|
|
804
886
|
async function configureIdp(params, logger) {
|
|
805
|
-
const
|
|
887
|
+
const rootDir = await findRootDir();
|
|
888
|
+
if (!rootDir) {
|
|
889
|
+
logger.log("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const envPath = join17(rootDir, "core", "local", ".env");
|
|
806
893
|
let env;
|
|
807
894
|
try {
|
|
808
895
|
env = await readEnvFile(envPath);
|
|
@@ -813,11 +900,11 @@ async function configureIdp(params, logger) {
|
|
|
813
900
|
const gatewayUrl = env.get("PAE_GATEWAY_HOST_URL");
|
|
814
901
|
const accessSecret = env.get("PAE_GATEWAY_SERVICE_ACCESS_SECRET");
|
|
815
902
|
if (!gatewayUrl) {
|
|
816
|
-
logger.log("Error: PAE_GATEWAY_HOST_URL is not set in local/.env");
|
|
903
|
+
logger.log("Error: PAE_GATEWAY_HOST_URL is not set in core/local/.env");
|
|
817
904
|
return;
|
|
818
905
|
}
|
|
819
906
|
if (!accessSecret) {
|
|
820
|
-
logger.log("Error: PAE_GATEWAY_SERVICE_ACCESS_SECRET is not set in local/.env");
|
|
907
|
+
logger.log("Error: PAE_GATEWAY_SERVICE_ACCESS_SECRET is not set in core/local/.env");
|
|
821
908
|
return;
|
|
822
909
|
}
|
|
823
910
|
const provider = idpProviderRegistry.get(params.providerType);
|
|
@@ -853,6 +940,225 @@ async function configureIdp(params, logger) {
|
|
|
853
940
|
logger.log(`IDP provider "${params.name}" configured successfully.`);
|
|
854
941
|
}
|
|
855
942
|
|
|
943
|
+
// src/commands/create-service-module/create-service-module.command.ts
|
|
944
|
+
import { join as join19, resolve as resolve2 } from "path";
|
|
945
|
+
import { access as access2 } from "fs/promises";
|
|
946
|
+
|
|
947
|
+
// src/commands/create-service-module/scaffold-service-module.ts
|
|
948
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
949
|
+
import { join as join18, dirname as dirname11 } from "path";
|
|
950
|
+
var nestjsServiceModuleTemplateDir2 = join18(
|
|
951
|
+
dirname11(fileURLToPath9(import.meta.url)),
|
|
952
|
+
"..",
|
|
953
|
+
"templates",
|
|
954
|
+
"nestjs-service-module-template"
|
|
955
|
+
);
|
|
956
|
+
async function scaffoldServiceModule(serviceDir, organizationName, serviceName, serviceDisplayName, serviceBaseDir, logger) {
|
|
957
|
+
logger.log(`Creating NestJS service "${serviceName}"...`);
|
|
958
|
+
await applyTemplate(
|
|
959
|
+
{
|
|
960
|
+
templateDir: nestjsServiceModuleTemplateDir2,
|
|
961
|
+
outputDir: serviceDir,
|
|
962
|
+
variables: { organizationName, serviceName, serviceDisplayName, serviceBaseDir }
|
|
963
|
+
},
|
|
964
|
+
(message) => logger.log(message)
|
|
965
|
+
);
|
|
966
|
+
logger.log(`Done! Service output: ${serviceDir}`);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/commands/create-service-module/service-module-entry-builder.ts
|
|
970
|
+
function buildCustomServiceModuleEntry(organizationName, serviceName, serviceDisplayName) {
|
|
971
|
+
return {
|
|
972
|
+
name: `@${organizationName}/${serviceName}`,
|
|
973
|
+
displayName: serviceDisplayName,
|
|
974
|
+
type: "service",
|
|
975
|
+
baseUrl: `/${serviceName}`,
|
|
976
|
+
service: {
|
|
977
|
+
host: serviceName,
|
|
978
|
+
port: 80
|
|
979
|
+
},
|
|
980
|
+
dependsOn: []
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// src/commands/create-service-module/append-docker-compose.ts
|
|
985
|
+
import { readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
986
|
+
async function appendServiceToDockerCompose(dockerComposePath, serviceName, platformName, applicationName, port, logger) {
|
|
987
|
+
const block = `
|
|
988
|
+
${serviceName}:
|
|
989
|
+
build:
|
|
990
|
+
context: ../../${platformName}-${applicationName}/services/${serviceName}
|
|
991
|
+
dockerfile: Dockerfile.development
|
|
992
|
+
args:
|
|
993
|
+
- BA_NPM_AUTH_TOKEN=$BA_NPM_AUTH_TOKEN
|
|
994
|
+
ports:
|
|
995
|
+
- ${port}:80
|
|
996
|
+
environment:
|
|
997
|
+
- GATEWAY_URL=\${PAE_GATEWAY_URL}
|
|
998
|
+
volumes:
|
|
999
|
+
- \${PWD}/:/app/out
|
|
1000
|
+
`;
|
|
1001
|
+
try {
|
|
1002
|
+
const existing = await readFile8(dockerComposePath, "utf-8");
|
|
1003
|
+
await writeFile7(dockerComposePath, existing + block, "utf-8");
|
|
1004
|
+
logger.log(`Updated docker-compose: ${dockerComposePath}`);
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1007
|
+
logger.log(`Warning: Could not update docker-compose \u2014 ${message}`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/commands/create-service-module/create-service-module.command.ts
|
|
1012
|
+
var CREATE_SERVICE_MODULE_COMMAND_NAME = "create-service-module";
|
|
1013
|
+
var createServiceModuleCommand = {
|
|
1014
|
+
name: CREATE_SERVICE_MODULE_COMMAND_NAME,
|
|
1015
|
+
description: "Add a new service module to an existing application"
|
|
1016
|
+
};
|
|
1017
|
+
async function createServiceModule(params, logger) {
|
|
1018
|
+
const { applicationName, serviceName, serviceDisplayName } = params;
|
|
1019
|
+
const rootDir = await findRootDir();
|
|
1020
|
+
if (!rootDir) {
|
|
1021
|
+
logger.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
let manifest;
|
|
1025
|
+
try {
|
|
1026
|
+
manifest = await readManifest(rootDir);
|
|
1027
|
+
} catch (err) {
|
|
1028
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
const { organization: organizationName, name: platformName } = manifest.product;
|
|
1032
|
+
const appEntry = manifest.applications.find((a) => a.name === applicationName);
|
|
1033
|
+
if (!appEntry) {
|
|
1034
|
+
logger.log(`Error: The specified application "${applicationName}" is not registered in the product manifest.`);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const applicationDir = resolve2(join19(rootDir, "core"), appEntry.localPath);
|
|
1038
|
+
try {
|
|
1039
|
+
await access2(applicationDir);
|
|
1040
|
+
} catch {
|
|
1041
|
+
logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const fullServiceName = `${serviceName}-service`;
|
|
1045
|
+
const serviceDir = join19(applicationDir, "services", fullServiceName);
|
|
1046
|
+
try {
|
|
1047
|
+
await access2(serviceDir);
|
|
1048
|
+
logger.log(`Error: A service named "${fullServiceName}" already exists in application "${applicationName}".`);
|
|
1049
|
+
return;
|
|
1050
|
+
} catch {
|
|
1051
|
+
}
|
|
1052
|
+
const serviceBaseDir = `${platformName}-${applicationName}/services`;
|
|
1053
|
+
try {
|
|
1054
|
+
await scaffoldServiceModule(
|
|
1055
|
+
serviceDir,
|
|
1056
|
+
organizationName,
|
|
1057
|
+
fullServiceName,
|
|
1058
|
+
serviceDisplayName,
|
|
1059
|
+
serviceBaseDir,
|
|
1060
|
+
logger
|
|
1061
|
+
);
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
logger.log(`Error: Could not scaffold NestJS service \u2014 ${formatError(err)}`);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const bootstrapServiceDir = join19(applicationDir, "services", `${applicationName}-bootstrap-service`);
|
|
1067
|
+
const moduleEntry = buildCustomServiceModuleEntry(organizationName, fullServiceName, serviceDisplayName);
|
|
1068
|
+
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1069
|
+
const dockerComposePath = join19(rootDir, "core", "local", `${applicationName}-docker-compose.yml`);
|
|
1070
|
+
const port = await getNextAvailablePort(rootDir);
|
|
1071
|
+
await appendServiceToDockerCompose(dockerComposePath, fullServiceName, platformName, applicationName, port, logger);
|
|
1072
|
+
logger.log(`Done! Service module "${fullServiceName}" added to application "${applicationName}".`);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// src/commands/create-ui-module/create-ui-module.command.ts
|
|
1076
|
+
import { join as join20, resolve as resolve3 } from "path";
|
|
1077
|
+
import { access as access3, readdir as readdir3 } from "fs/promises";
|
|
1078
|
+
|
|
1079
|
+
// src/commands/create-ui-module/append-ui-docker-compose.ts
|
|
1080
|
+
import { readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
|
|
1081
|
+
async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger) {
|
|
1082
|
+
const block = `
|
|
1083
|
+
${applicationName}-ui:
|
|
1084
|
+
build:
|
|
1085
|
+
context: ../../${platformName}-${applicationName}/ui/${applicationName}-ui
|
|
1086
|
+
dockerfile: Dockerfile.development
|
|
1087
|
+
ports:
|
|
1088
|
+
- ${port}:80
|
|
1089
|
+
volumes:
|
|
1090
|
+
- \${PWD}/:/app/out
|
|
1091
|
+
`;
|
|
1092
|
+
try {
|
|
1093
|
+
const existing = await readFile9(dockerComposePath, "utf-8");
|
|
1094
|
+
await writeFile8(dockerComposePath, existing + block, "utf-8");
|
|
1095
|
+
logger.log(`Updated docker-compose: ${dockerComposePath}`);
|
|
1096
|
+
} catch (err) {
|
|
1097
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1098
|
+
logger.log(`Warning: Could not update docker-compose \u2014 ${message}`);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/commands/create-ui-module/create-ui-module.command.ts
|
|
1103
|
+
var CREATE_UI_MODULE_COMMAND_NAME = "create-ui-module";
|
|
1104
|
+
var createUiModuleCommand = {
|
|
1105
|
+
name: CREATE_UI_MODULE_COMMAND_NAME,
|
|
1106
|
+
description: "Add a UI module to an existing application"
|
|
1107
|
+
};
|
|
1108
|
+
async function createUiModule(params, logger) {
|
|
1109
|
+
const { applicationName, applicationDisplayName } = params;
|
|
1110
|
+
const rootDir = await findRootDir();
|
|
1111
|
+
if (!rootDir) {
|
|
1112
|
+
logger.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
let manifest;
|
|
1116
|
+
try {
|
|
1117
|
+
manifest = await readManifest(rootDir);
|
|
1118
|
+
} catch (err) {
|
|
1119
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const { organization: organizationName, name: platformName } = manifest.product;
|
|
1123
|
+
const appEntry = manifest.applications.find((a) => a.name === applicationName);
|
|
1124
|
+
if (!appEntry) {
|
|
1125
|
+
logger.log(`Error: The specified application "${applicationName}" is not registered in the product manifest.`);
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
const applicationDir = resolve3(join20(rootDir, "core"), appEntry.localPath);
|
|
1129
|
+
try {
|
|
1130
|
+
await access3(applicationDir);
|
|
1131
|
+
} catch {
|
|
1132
|
+
logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
const uiDir = join20(applicationDir, "ui");
|
|
1136
|
+
try {
|
|
1137
|
+
const uiEntries = await readdir3(uiDir);
|
|
1138
|
+
const existingUiModules = uiEntries.filter((e) => e !== ".gitkeep");
|
|
1139
|
+
if (existingUiModules.length > 0) {
|
|
1140
|
+
logger.log(`Error: Currently we support only one UI module per application. Application "${applicationName}" already has a UI module.`);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
} catch {
|
|
1144
|
+
}
|
|
1145
|
+
const uiOutputDir = join20(uiDir, `${applicationName}-ui`);
|
|
1146
|
+
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
1147
|
+
try {
|
|
1148
|
+
await scaffoldUiModule(uiOutputDir, organizationName, platformName, applicationName, applicationDisplayName, uiBaseDir, logger);
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
logger.log(`Error: Could not scaffold UI module \u2014 ${formatError(err)}`);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
const bootstrapServiceDir = join20(applicationDir, "services", `${applicationName}-bootstrap-service`);
|
|
1154
|
+
const moduleEntry = buildUiModuleEntry(organizationName, applicationName, applicationDisplayName);
|
|
1155
|
+
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1156
|
+
const dockerComposePath = join20(rootDir, "core", "local", `${applicationName}-docker-compose.yml`);
|
|
1157
|
+
const port = await getNextAvailablePort(rootDir);
|
|
1158
|
+
await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger);
|
|
1159
|
+
logger.log(`Done! UI module "${applicationName}-ui" added to application "${applicationName}".`);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
856
1162
|
// src/commands/registry.ts
|
|
857
1163
|
var CommandRegistry = class {
|
|
858
1164
|
commands = /* @__PURE__ */ new Map();
|
|
@@ -878,6 +1184,8 @@ var registry = new CommandRegistry();
|
|
|
878
1184
|
registry.register(createApplicationCommand);
|
|
879
1185
|
registry.register(initCommand);
|
|
880
1186
|
registry.register(configureIdpCommand);
|
|
1187
|
+
registry.register(createServiceModuleCommand);
|
|
1188
|
+
registry.register(createUiModuleCommand);
|
|
881
1189
|
|
|
882
1190
|
// src/app-state.ts
|
|
883
1191
|
var APP_STATE = {
|
|
@@ -890,11 +1198,6 @@ var APP_STATE = {
|
|
|
890
1198
|
// src/hooks/use-command-runner.ts
|
|
891
1199
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
892
1200
|
|
|
893
|
-
// src/controllers/ui/create-application.ui-controller.ts
|
|
894
|
-
import { readFile as readFile8 } from "fs/promises";
|
|
895
|
-
import { join as join17 } from "path";
|
|
896
|
-
import { cwd as cwd4 } from "process";
|
|
897
|
-
|
|
898
1201
|
// src/services/create-application.service.ts
|
|
899
1202
|
async function createApplicationService(params, logger) {
|
|
900
1203
|
await createApplication(params, logger);
|
|
@@ -902,22 +1205,8 @@ async function createApplicationService(params, logger) {
|
|
|
902
1205
|
|
|
903
1206
|
// src/controllers/ui/create-application.ui-controller.ts
|
|
904
1207
|
async function createApplicationUiController(ctx) {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
try {
|
|
908
|
-
const corePackageJson = JSON.parse(
|
|
909
|
-
await readFile8(join17(cwd4(), "core", "package.json"), "utf-8")
|
|
910
|
-
);
|
|
911
|
-
const scopeMatch = corePackageJson.name.match(/^@([^/]+)\//);
|
|
912
|
-
organizationName = scopeMatch?.[1];
|
|
913
|
-
const rawName = corePackageJson.name.replace(/^@[^/]+\//, "").replace(/-core$/, "");
|
|
914
|
-
platformName = rawName;
|
|
915
|
-
if (!organizationName || !platformName) {
|
|
916
|
-
throw new Error(`Could not parse organization/platform from package name: "${corePackageJson.name}"`);
|
|
917
|
-
}
|
|
918
|
-
} catch (err) {
|
|
919
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
920
|
-
ctx.log(`Error: Could not read core/package.json \u2014 ${message}`);
|
|
1208
|
+
if (!await isPlatformInitialized()) {
|
|
1209
|
+
ctx.log("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
921
1210
|
return;
|
|
922
1211
|
}
|
|
923
1212
|
const applicationName = await ctx.prompt("Application name:");
|
|
@@ -939,8 +1228,6 @@ async function createApplicationUiController(ctx) {
|
|
|
939
1228
|
}
|
|
940
1229
|
await createApplicationService(
|
|
941
1230
|
{
|
|
942
|
-
organizationName,
|
|
943
|
-
platformName,
|
|
944
1231
|
applicationName,
|
|
945
1232
|
applicationDisplayName,
|
|
946
1233
|
applicationDescription,
|
|
@@ -958,6 +1245,10 @@ async function initService(params, logger) {
|
|
|
958
1245
|
|
|
959
1246
|
// src/controllers/ui/init.ui-controller.ts
|
|
960
1247
|
async function initUiController(ctx) {
|
|
1248
|
+
if (await isPlatformInitialized()) {
|
|
1249
|
+
ctx.log("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
961
1252
|
const organizationName = await ctx.prompt("Organization name:");
|
|
962
1253
|
const platformName = await ctx.prompt("Platform name:");
|
|
963
1254
|
const platformDisplayName = await ctx.prompt("Platform display name:");
|
|
@@ -971,6 +1262,10 @@ async function configureIdpService(params, logger) {
|
|
|
971
1262
|
|
|
972
1263
|
// src/controllers/ui/configure-idp.ui-controller.ts
|
|
973
1264
|
async function configureIdpUiController(ctx) {
|
|
1265
|
+
if (!await isPlatformInitialized()) {
|
|
1266
|
+
ctx.log("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
974
1269
|
const providers = getAllProviders();
|
|
975
1270
|
const options = providers.map((p, i) => `${i + 1}: ${p.displayName}`).join(", ");
|
|
976
1271
|
const selectionInput = await ctx.prompt(`Select IDP type (${options}):`);
|
|
@@ -994,11 +1289,64 @@ async function configureIdpUiController(ctx) {
|
|
|
994
1289
|
);
|
|
995
1290
|
}
|
|
996
1291
|
|
|
1292
|
+
// src/services/create-service-module.service.ts
|
|
1293
|
+
async function createServiceModuleService(params, logger) {
|
|
1294
|
+
await createServiceModule(params, logger);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// src/controllers/ui/create-service-module.ui-controller.ts
|
|
1298
|
+
async function createServiceModuleUiController(ctx) {
|
|
1299
|
+
if (!await isPlatformInitialized()) {
|
|
1300
|
+
ctx.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
const applicationName = await ctx.prompt("Application name:");
|
|
1304
|
+
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
1305
|
+
ctx.log(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
const serviceName = await ctx.prompt("Service name:");
|
|
1309
|
+
if (!/^[a-z0-9-]+$/.test(serviceName)) {
|
|
1310
|
+
ctx.log(`Error: Service name "${serviceName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const serviceDisplayName = await ctx.prompt("Service display name:");
|
|
1314
|
+
await createServiceModuleService(
|
|
1315
|
+
{ applicationName, serviceName, serviceDisplayName },
|
|
1316
|
+
ctx
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// src/services/create-ui-module.service.ts
|
|
1321
|
+
async function createUiModuleService(params, logger) {
|
|
1322
|
+
await createUiModule(params, logger);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// src/controllers/ui/create-ui-module.ui-controller.ts
|
|
1326
|
+
async function createUiModuleUiController(ctx) {
|
|
1327
|
+
if (!await isPlatformInitialized()) {
|
|
1328
|
+
ctx.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const applicationName = await ctx.prompt("Application name:");
|
|
1332
|
+
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
1333
|
+
ctx.log(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
const applicationDisplayName = await ctx.prompt("Application display name:");
|
|
1337
|
+
await createUiModuleService(
|
|
1338
|
+
{ applicationName, applicationDisplayName },
|
|
1339
|
+
ctx
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
997
1343
|
// src/controllers/ui/registry.ts
|
|
998
1344
|
var uiControllers = /* @__PURE__ */ new Map([
|
|
999
1345
|
[CREATE_APPLICATION_COMMAND_NAME, createApplicationUiController],
|
|
1000
1346
|
[INIT_COMMAND_NAME, initUiController],
|
|
1001
|
-
[CONFIGURE_IDP_COMMAND_NAME, configureIdpUiController]
|
|
1347
|
+
[CONFIGURE_IDP_COMMAND_NAME, configureIdpUiController],
|
|
1348
|
+
[CREATE_SERVICE_MODULE_COMMAND_NAME, createServiceModuleUiController],
|
|
1349
|
+
[CREATE_UI_MODULE_COMMAND_NAME, createUiModuleUiController]
|
|
1002
1350
|
]);
|
|
1003
1351
|
|
|
1004
1352
|
// src/hooks/use-command-runner.ts
|
|
@@ -1013,7 +1361,7 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1013
1361
|
abortControllerRef.current = null;
|
|
1014
1362
|
promptResolveRef.current = null;
|
|
1015
1363
|
}, []);
|
|
1016
|
-
const
|
|
1364
|
+
const runCommand2 = useCallback(
|
|
1017
1365
|
(cmd) => {
|
|
1018
1366
|
const controller = new AbortController();
|
|
1019
1367
|
abortControllerRef.current = controller;
|
|
@@ -1027,14 +1375,14 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1027
1375
|
}
|
|
1028
1376
|
},
|
|
1029
1377
|
prompt(message) {
|
|
1030
|
-
return new Promise((
|
|
1378
|
+
return new Promise((resolve5, reject) => {
|
|
1031
1379
|
if (controller.signal.aborted) {
|
|
1032
1380
|
reject(new DOMException("Aborted", "AbortError"));
|
|
1033
1381
|
return;
|
|
1034
1382
|
}
|
|
1035
1383
|
setPromptMessage(message);
|
|
1036
1384
|
setPromptValue("");
|
|
1037
|
-
promptResolveRef.current =
|
|
1385
|
+
promptResolveRef.current = resolve5;
|
|
1038
1386
|
setState(APP_STATE.PROMPTING);
|
|
1039
1387
|
controller.signal.addEventListener(
|
|
1040
1388
|
"abort",
|
|
@@ -1066,15 +1414,15 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1066
1414
|
);
|
|
1067
1415
|
const handlePromptSubmit = useCallback(
|
|
1068
1416
|
(value) => {
|
|
1069
|
-
const
|
|
1417
|
+
const resolve5 = promptResolveRef.current;
|
|
1070
1418
|
promptResolveRef.current = null;
|
|
1071
1419
|
setState(APP_STATE.EXECUTING);
|
|
1072
|
-
|
|
1420
|
+
resolve5?.(value);
|
|
1073
1421
|
},
|
|
1074
1422
|
[setState]
|
|
1075
1423
|
);
|
|
1076
1424
|
return {
|
|
1077
|
-
runCommand,
|
|
1425
|
+
runCommand: runCommand2,
|
|
1078
1426
|
handlePromptSubmit,
|
|
1079
1427
|
abortExecution,
|
|
1080
1428
|
promptMessage,
|
|
@@ -1096,7 +1444,7 @@ function App() {
|
|
|
1096
1444
|
const appendStaticItem = useCallback2((item) => {
|
|
1097
1445
|
setStaticItems((prev) => [...prev, item]);
|
|
1098
1446
|
}, []);
|
|
1099
|
-
const { runCommand, handlePromptSubmit, abortExecution, promptMessage, promptValue, setPromptValue } = useCommandRunner({ appendStaticItem, setState });
|
|
1447
|
+
const { runCommand: runCommand2, handlePromptSubmit, abortExecution, promptMessage, promptValue, setPromptValue } = useCommandRunner({ appendStaticItem, setState });
|
|
1100
1448
|
const query = inputValue.startsWith("/") ? inputValue.slice(1) : "";
|
|
1101
1449
|
const filteredCommands = registry.search(query);
|
|
1102
1450
|
useInput(
|
|
@@ -1128,7 +1476,7 @@ function App() {
|
|
|
1128
1476
|
const cmd = filteredCommands[selectedIndex];
|
|
1129
1477
|
if (cmd) {
|
|
1130
1478
|
setInputValue("");
|
|
1131
|
-
|
|
1479
|
+
runCommand2(cmd);
|
|
1132
1480
|
}
|
|
1133
1481
|
return;
|
|
1134
1482
|
}
|
|
@@ -1164,9 +1512,9 @@ function App() {
|
|
|
1164
1512
|
if (!value.startsWith("/")) return;
|
|
1165
1513
|
const name = value.slice(1).trim();
|
|
1166
1514
|
const cmd = registry.get(name);
|
|
1167
|
-
if (cmd)
|
|
1515
|
+
if (cmd) runCommand2(cmd);
|
|
1168
1516
|
},
|
|
1169
|
-
[
|
|
1517
|
+
[runCommand2]
|
|
1170
1518
|
);
|
|
1171
1519
|
function renderActiveArea() {
|
|
1172
1520
|
switch (state) {
|
|
@@ -1202,10 +1550,11 @@ function App() {
|
|
|
1202
1550
|
}
|
|
1203
1551
|
|
|
1204
1552
|
// src/controllers/cli/create-application.cli-controller.ts
|
|
1205
|
-
import { readFile as readFile9 } from "fs/promises";
|
|
1206
|
-
import { join as join18 } from "path";
|
|
1207
|
-
import { cwd as cwd5 } from "process";
|
|
1208
1553
|
async function createApplicationCliController(args2) {
|
|
1554
|
+
if (!await isPlatformInitialized()) {
|
|
1555
|
+
console.error("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
1556
|
+
process.exit(1);
|
|
1557
|
+
}
|
|
1209
1558
|
const {
|
|
1210
1559
|
applicationName,
|
|
1211
1560
|
applicationDisplayName,
|
|
@@ -1221,28 +1570,8 @@ async function createApplicationCliController(args2) {
|
|
|
1221
1570
|
console.error(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1222
1571
|
process.exit(1);
|
|
1223
1572
|
}
|
|
1224
|
-
let organizationName;
|
|
1225
|
-
let platformName;
|
|
1226
|
-
try {
|
|
1227
|
-
const corePackageJson = JSON.parse(
|
|
1228
|
-
await readFile9(join18(cwd5(), "core", "package.json"), "utf-8")
|
|
1229
|
-
);
|
|
1230
|
-
const scopeMatch = corePackageJson.name.match(/^@([^/]+)\//);
|
|
1231
|
-
organizationName = scopeMatch?.[1];
|
|
1232
|
-
const rawName = corePackageJson.name.replace(/^@[^/]+\//, "").replace(/-core$/, "");
|
|
1233
|
-
platformName = rawName;
|
|
1234
|
-
if (!organizationName || !platformName) {
|
|
1235
|
-
throw new Error(`Could not parse organization/platform from package name: "${corePackageJson.name}"`);
|
|
1236
|
-
}
|
|
1237
|
-
} catch (err) {
|
|
1238
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1239
|
-
console.error(`Error: Could not read core/package.json \u2014 ${message}`);
|
|
1240
|
-
process.exit(1);
|
|
1241
|
-
}
|
|
1242
1573
|
await createApplicationService(
|
|
1243
1574
|
{
|
|
1244
|
-
organizationName,
|
|
1245
|
-
platformName,
|
|
1246
1575
|
applicationName,
|
|
1247
1576
|
applicationDisplayName,
|
|
1248
1577
|
applicationDescription,
|
|
@@ -1255,6 +1584,10 @@ async function createApplicationCliController(args2) {
|
|
|
1255
1584
|
|
|
1256
1585
|
// src/controllers/cli/init.cli-controller.ts
|
|
1257
1586
|
async function initCliController(args2) {
|
|
1587
|
+
if (await isPlatformInitialized()) {
|
|
1588
|
+
console.error("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
|
|
1589
|
+
process.exit(1);
|
|
1590
|
+
}
|
|
1258
1591
|
const { organizationName, platformName, platformDisplayName } = args2;
|
|
1259
1592
|
if (!organizationName || !platformName || !platformDisplayName) {
|
|
1260
1593
|
console.error("Error: organizationName, platformName, and platformDisplayName are required.");
|
|
@@ -1269,6 +1602,10 @@ async function initCliController(args2) {
|
|
|
1269
1602
|
// src/controllers/cli/configure-idp.cli-controller.ts
|
|
1270
1603
|
async function configureIdpCliController(args2) {
|
|
1271
1604
|
const logger = { log: console.log };
|
|
1605
|
+
if (!await isPlatformInitialized()) {
|
|
1606
|
+
console.error("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
1607
|
+
process.exit(1);
|
|
1608
|
+
}
|
|
1272
1609
|
const { providerType, name, issuer, clientId, clientSecret } = args2;
|
|
1273
1610
|
if (!providerType || !name || !issuer || !clientId || !clientSecret) {
|
|
1274
1611
|
logger.log("Error: Missing required arguments: providerType, name, issuer, clientId, clientSecret");
|
|
@@ -1291,22 +1628,167 @@ async function configureIdpCliController(args2) {
|
|
|
1291
1628
|
await configureIdpService({ providerType, name, issuer, clientId, clientSecret, extras }, logger);
|
|
1292
1629
|
}
|
|
1293
1630
|
|
|
1294
|
-
// src/
|
|
1631
|
+
// src/commands/local-scripts/docker-compose-orchestrator.ts
|
|
1295
1632
|
import { spawn } from "child_process";
|
|
1296
|
-
import { access } from "fs/promises";
|
|
1297
|
-
import { join as
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1633
|
+
import { access as access4 } from "fs/promises";
|
|
1634
|
+
import { join as join21 } from "path";
|
|
1635
|
+
function runDockerCompose(args2, logger, rootDir, signal) {
|
|
1636
|
+
return new Promise((resolvePromise) => {
|
|
1637
|
+
const child = spawn("docker", ["compose", ...args2], {
|
|
1638
|
+
shell: false,
|
|
1639
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1640
|
+
cwd: rootDir
|
|
1641
|
+
});
|
|
1642
|
+
const onAbort = () => {
|
|
1643
|
+
child.kill("SIGTERM");
|
|
1644
|
+
};
|
|
1645
|
+
if (signal) {
|
|
1646
|
+
if (signal.aborted) {
|
|
1647
|
+
child.kill("SIGTERM");
|
|
1648
|
+
resolvePromise();
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1652
|
+
}
|
|
1653
|
+
child.stdout.on("data", (data) => {
|
|
1654
|
+
for (const line of data.toString().split("\n")) {
|
|
1655
|
+
if (line.trim()) logger.log(line);
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
child.stderr.on("data", (data) => {
|
|
1659
|
+
for (const line of data.toString().split("\n")) {
|
|
1660
|
+
if (line.trim()) logger.log(line);
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
child.on("close", (code, sig) => {
|
|
1664
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1665
|
+
if (sig === "SIGTERM" || signal?.aborted) {
|
|
1666
|
+
logger.log("Command cancelled.");
|
|
1667
|
+
} else if (code !== 0) {
|
|
1668
|
+
logger.log(`docker compose exited with code ${code}.`);
|
|
1669
|
+
}
|
|
1670
|
+
resolvePromise();
|
|
1671
|
+
});
|
|
1672
|
+
child.on("error", (err) => {
|
|
1673
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1674
|
+
logger.log(`Failed to run docker compose: ${err.message}`);
|
|
1675
|
+
resolvePromise();
|
|
1676
|
+
});
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
async function getAppComposePaths(rootDir, manifest) {
|
|
1680
|
+
const results = [];
|
|
1681
|
+
for (const app of manifest.applications) {
|
|
1682
|
+
const composePath = join21(rootDir, "core", "local", `${app.name}-docker-compose.yml`);
|
|
1683
|
+
try {
|
|
1684
|
+
await access4(composePath);
|
|
1685
|
+
results.push({ composePath, appName: app.name });
|
|
1686
|
+
} catch {
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
return results;
|
|
1690
|
+
}
|
|
1691
|
+
async function buildAllComposeArgs(rootDir, manifest, logger) {
|
|
1692
|
+
const localDir = join21(rootDir, "core", "local");
|
|
1693
|
+
const fileArgs = [
|
|
1694
|
+
"-f",
|
|
1695
|
+
join21(localDir, "platform-docker-compose.yml"),
|
|
1696
|
+
"-f",
|
|
1697
|
+
join21(localDir, "core-docker-compose.yml")
|
|
1698
|
+
];
|
|
1699
|
+
const appEntries = await getAppComposePaths(rootDir, manifest);
|
|
1700
|
+
for (const { composePath, appName } of appEntries) {
|
|
1701
|
+
fileArgs.push("-f", composePath);
|
|
1305
1702
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1703
|
+
for (const app of manifest.applications) {
|
|
1704
|
+
if (!appEntries.find((e) => e.appName === app.name)) {
|
|
1705
|
+
logger.log(`Warning: No docker-compose found for application "${app.name}" in core/local/ \u2014 skipping.`);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return fileArgs;
|
|
1709
|
+
}
|
|
1710
|
+
async function startEnvironment(rootDir, manifest, logger, signal) {
|
|
1711
|
+
const platformName = manifest.product.name;
|
|
1712
|
+
const envFile = join21(rootDir, "core", "local", ".env");
|
|
1713
|
+
const fileArgs = await buildAllComposeArgs(rootDir, manifest, logger);
|
|
1714
|
+
const localDir = join21(rootDir, "core", "local");
|
|
1715
|
+
logger.log("Starting environment...");
|
|
1716
|
+
await runDockerCompose(
|
|
1717
|
+
[
|
|
1718
|
+
"-p",
|
|
1719
|
+
`${platformName}-platform`,
|
|
1720
|
+
"--project-directory",
|
|
1721
|
+
localDir,
|
|
1722
|
+
"--env-file",
|
|
1723
|
+
envFile,
|
|
1724
|
+
...fileArgs,
|
|
1725
|
+
"up",
|
|
1726
|
+
"-d",
|
|
1727
|
+
"--build",
|
|
1728
|
+
"--remove-orphans"
|
|
1729
|
+
],
|
|
1730
|
+
logger,
|
|
1731
|
+
rootDir,
|
|
1732
|
+
signal
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
async function stopEnvironment(rootDir, manifest, logger, signal) {
|
|
1736
|
+
const platformName = manifest.product.name;
|
|
1737
|
+
const envFile = join21(rootDir, "core", "local", ".env");
|
|
1738
|
+
const fileArgs = await buildAllComposeArgs(rootDir, manifest, logger);
|
|
1739
|
+
const localDir = join21(rootDir, "core", "local");
|
|
1740
|
+
logger.log("Stopping environment...");
|
|
1741
|
+
await runDockerCompose(
|
|
1742
|
+
[
|
|
1743
|
+
"-p",
|
|
1744
|
+
`${platformName}-platform`,
|
|
1745
|
+
"--project-directory",
|
|
1746
|
+
localDir,
|
|
1747
|
+
"--env-file",
|
|
1748
|
+
envFile,
|
|
1749
|
+
...fileArgs,
|
|
1750
|
+
"down"
|
|
1751
|
+
],
|
|
1752
|
+
logger,
|
|
1753
|
+
rootDir,
|
|
1754
|
+
signal
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
async function destroyEnvironment(rootDir, manifest, logger, signal) {
|
|
1758
|
+
const platformName = manifest.product.name;
|
|
1759
|
+
const envFile = join21(rootDir, "core", "local", ".env");
|
|
1760
|
+
const fileArgs = await buildAllComposeArgs(rootDir, manifest, logger);
|
|
1761
|
+
const localDir = join21(rootDir, "core", "local");
|
|
1762
|
+
logger.log("Destroying environment...");
|
|
1763
|
+
await runDockerCompose(
|
|
1764
|
+
[
|
|
1765
|
+
"-p",
|
|
1766
|
+
`${platformName}-platform`,
|
|
1767
|
+
"--project-directory",
|
|
1768
|
+
localDir,
|
|
1769
|
+
"--env-file",
|
|
1770
|
+
envFile,
|
|
1771
|
+
...fileArgs,
|
|
1772
|
+
"down",
|
|
1773
|
+
"-v",
|
|
1774
|
+
"--rmi",
|
|
1775
|
+
"all"
|
|
1776
|
+
],
|
|
1777
|
+
logger,
|
|
1778
|
+
rootDir,
|
|
1779
|
+
signal
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// src/commands/local-scripts/npm-orchestrator.ts
|
|
1784
|
+
import { spawn as spawn2 } from "child_process";
|
|
1785
|
+
import { access as access5 } from "fs/promises";
|
|
1786
|
+
import { join as join22, resolve as resolve4 } from "path";
|
|
1787
|
+
function runCommand(command, args2, workDir, logger, signal) {
|
|
1788
|
+
return new Promise((resolvePromise) => {
|
|
1789
|
+
const child = spawn2(command, args2, {
|
|
1790
|
+
cwd: workDir,
|
|
1791
|
+
shell: false,
|
|
1310
1792
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1311
1793
|
});
|
|
1312
1794
|
const onAbort = () => {
|
|
@@ -1315,7 +1797,7 @@ async function runNpmScript(scriptName, logger, signal) {
|
|
|
1315
1797
|
if (signal) {
|
|
1316
1798
|
if (signal.aborted) {
|
|
1317
1799
|
child.kill("SIGTERM");
|
|
1318
|
-
|
|
1800
|
+
resolvePromise();
|
|
1319
1801
|
return;
|
|
1320
1802
|
}
|
|
1321
1803
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -1333,19 +1815,72 @@ async function runNpmScript(scriptName, logger, signal) {
|
|
|
1333
1815
|
child.on("close", (code, sig) => {
|
|
1334
1816
|
signal?.removeEventListener("abort", onAbort);
|
|
1335
1817
|
if (sig === "SIGTERM" || signal?.aborted) {
|
|
1336
|
-
logger.log(
|
|
1818
|
+
logger.log("Command cancelled.");
|
|
1337
1819
|
} else if (code !== 0) {
|
|
1338
|
-
logger.log(`Command "
|
|
1820
|
+
logger.log(`Command "${command} ${args2.join(" ")}" exited with code ${code}.`);
|
|
1339
1821
|
}
|
|
1340
|
-
|
|
1822
|
+
resolvePromise();
|
|
1341
1823
|
});
|
|
1342
1824
|
child.on("error", (err) => {
|
|
1343
1825
|
signal?.removeEventListener("abort", onAbort);
|
|
1344
|
-
logger.log(`Failed to run "
|
|
1345
|
-
|
|
1826
|
+
logger.log(`Failed to run "${command} ${args2.join(" ")}": ${err.message}`);
|
|
1827
|
+
resolvePromise();
|
|
1346
1828
|
});
|
|
1347
1829
|
});
|
|
1348
1830
|
}
|
|
1831
|
+
async function dirExists(dirPath) {
|
|
1832
|
+
try {
|
|
1833
|
+
await access5(dirPath);
|
|
1834
|
+
return true;
|
|
1835
|
+
} catch {
|
|
1836
|
+
return false;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
async function installDependencies(rootDir, manifest, logger, signal) {
|
|
1840
|
+
const coreDir = join22(rootDir, "core");
|
|
1841
|
+
const appDirs = [];
|
|
1842
|
+
for (const app of manifest.applications) {
|
|
1843
|
+
const appDir = resolve4(join22(rootDir, "core"), app.localPath);
|
|
1844
|
+
if (!await dirExists(appDir)) {
|
|
1845
|
+
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping install.`);
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
appDirs.push({ name: app.name, dir: appDir });
|
|
1849
|
+
}
|
|
1850
|
+
const targets = [{ name: "core", dir: coreDir }, ...appDirs];
|
|
1851
|
+
logger.log(`Installing dependencies in parallel: ${targets.map((t) => t.name).join(", ")}...`);
|
|
1852
|
+
await Promise.all(
|
|
1853
|
+
targets.map(
|
|
1854
|
+
({ name, dir }) => runCommand("npm", ["install"], dir, logger, signal).then(() => {
|
|
1855
|
+
logger.log(`\u2713 ${name} install done`);
|
|
1856
|
+
})
|
|
1857
|
+
)
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
async function buildAll(rootDir, manifest, logger, signal) {
|
|
1861
|
+
const coreDir = join22(rootDir, "core");
|
|
1862
|
+
logger.log("Building core/...");
|
|
1863
|
+
await runCommand("npx", ["turbo", "build"], coreDir, logger, signal);
|
|
1864
|
+
if (signal?.aborted) return;
|
|
1865
|
+
const appDirs = [];
|
|
1866
|
+
for (const app of manifest.applications) {
|
|
1867
|
+
const appDir = resolve4(join22(rootDir, "core"), app.localPath);
|
|
1868
|
+
if (!await dirExists(appDir)) {
|
|
1869
|
+
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping build.`);
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
appDirs.push({ name: app.name, dir: appDir });
|
|
1873
|
+
}
|
|
1874
|
+
if (appDirs.length === 0) return;
|
|
1875
|
+
logger.log(`Building apps in parallel: ${appDirs.map((a) => a.name).join(", ")}...`);
|
|
1876
|
+
await Promise.all(
|
|
1877
|
+
appDirs.map(
|
|
1878
|
+
({ name, dir }) => runCommand("npm", ["run", "build"], dir, logger, signal).then(() => {
|
|
1879
|
+
logger.log(`\u2713 ${name} build done`);
|
|
1880
|
+
})
|
|
1881
|
+
)
|
|
1882
|
+
);
|
|
1883
|
+
}
|
|
1349
1884
|
|
|
1350
1885
|
// src/commands/local-scripts/local-script.command.ts
|
|
1351
1886
|
var INSTALL_COMMAND_NAME = "install";
|
|
@@ -1354,7 +1889,37 @@ var START_COMMAND_NAME = "start";
|
|
|
1354
1889
|
var STOP_COMMAND_NAME = "stop";
|
|
1355
1890
|
var DESTROY_COMMAND_NAME = "destroy";
|
|
1356
1891
|
async function runLocalScript(scriptName, logger, signal) {
|
|
1357
|
-
await
|
|
1892
|
+
const rootDir = await findRootDir();
|
|
1893
|
+
if (!rootDir) {
|
|
1894
|
+
logger.log(`Error: Cannot run "${scriptName}" \u2014 no platform initialized in this directory.`);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
let manifest;
|
|
1898
|
+
try {
|
|
1899
|
+
manifest = await readManifest(rootDir);
|
|
1900
|
+
} catch (err) {
|
|
1901
|
+
logger.log(`Error: Could not read product manifest \u2014 ${formatError(err)}`);
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
switch (scriptName) {
|
|
1905
|
+
case START_COMMAND_NAME:
|
|
1906
|
+
await startEnvironment(rootDir, manifest, logger, signal);
|
|
1907
|
+
break;
|
|
1908
|
+
case STOP_COMMAND_NAME:
|
|
1909
|
+
await stopEnvironment(rootDir, manifest, logger, signal);
|
|
1910
|
+
break;
|
|
1911
|
+
case DESTROY_COMMAND_NAME:
|
|
1912
|
+
await destroyEnvironment(rootDir, manifest, logger, signal);
|
|
1913
|
+
break;
|
|
1914
|
+
case INSTALL_COMMAND_NAME:
|
|
1915
|
+
await installDependencies(rootDir, manifest, logger, signal);
|
|
1916
|
+
break;
|
|
1917
|
+
case BUILD_COMMAND_NAME:
|
|
1918
|
+
await buildAll(rootDir, manifest, logger, signal);
|
|
1919
|
+
break;
|
|
1920
|
+
default:
|
|
1921
|
+
logger.log(`Error: Unknown script "${scriptName}".`);
|
|
1922
|
+
}
|
|
1358
1923
|
}
|
|
1359
1924
|
|
|
1360
1925
|
// src/services/local-script.service.ts
|
|
@@ -1405,11 +1970,59 @@ function createLocalScriptCliController(scriptName) {
|
|
|
1405
1970
|
};
|
|
1406
1971
|
}
|
|
1407
1972
|
|
|
1973
|
+
// src/controllers/cli/create-service-module.cli-controller.ts
|
|
1974
|
+
async function createServiceModuleCliController(args2) {
|
|
1975
|
+
if (!await isPlatformInitialized()) {
|
|
1976
|
+
console.error("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
1977
|
+
process.exit(1);
|
|
1978
|
+
}
|
|
1979
|
+
const { applicationName, serviceName, serviceDisplayName } = args2;
|
|
1980
|
+
if (!applicationName || !serviceName || !serviceDisplayName) {
|
|
1981
|
+
console.error("Error: applicationName, serviceName, and serviceDisplayName are required.");
|
|
1982
|
+
process.exit(1);
|
|
1983
|
+
}
|
|
1984
|
+
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
1985
|
+
console.error(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1986
|
+
process.exit(1);
|
|
1987
|
+
}
|
|
1988
|
+
if (!/^[a-z0-9-]+$/.test(serviceName)) {
|
|
1989
|
+
console.error(`Error: Service name "${serviceName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1990
|
+
process.exit(1);
|
|
1991
|
+
}
|
|
1992
|
+
await createServiceModuleService(
|
|
1993
|
+
{ applicationName, serviceName, serviceDisplayName },
|
|
1994
|
+
{ log: console.log }
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// src/controllers/cli/create-ui-module.cli-controller.ts
|
|
1999
|
+
async function createUiModuleCliController(args2) {
|
|
2000
|
+
if (!await isPlatformInitialized()) {
|
|
2001
|
+
console.error("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
2002
|
+
process.exit(1);
|
|
2003
|
+
}
|
|
2004
|
+
const { applicationName, applicationDisplayName } = args2;
|
|
2005
|
+
if (!applicationName || !applicationDisplayName) {
|
|
2006
|
+
console.error("Error: applicationName and applicationDisplayName are required.");
|
|
2007
|
+
process.exit(1);
|
|
2008
|
+
}
|
|
2009
|
+
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
2010
|
+
console.error(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
2011
|
+
process.exit(1);
|
|
2012
|
+
}
|
|
2013
|
+
await createUiModuleService(
|
|
2014
|
+
{ applicationName, applicationDisplayName },
|
|
2015
|
+
{ log: console.log }
|
|
2016
|
+
);
|
|
2017
|
+
}
|
|
2018
|
+
|
|
1408
2019
|
// src/controllers/cli/registry.ts
|
|
1409
2020
|
var cliControllers = /* @__PURE__ */ new Map([
|
|
1410
2021
|
[CREATE_APPLICATION_COMMAND_NAME, createApplicationCliController],
|
|
1411
2022
|
[INIT_COMMAND_NAME, initCliController],
|
|
1412
2023
|
[CONFIGURE_IDP_COMMAND_NAME, configureIdpCliController],
|
|
2024
|
+
[CREATE_SERVICE_MODULE_COMMAND_NAME, createServiceModuleCliController],
|
|
2025
|
+
[CREATE_UI_MODULE_COMMAND_NAME, createUiModuleCliController],
|
|
1413
2026
|
[INSTALL_COMMAND_NAME, createLocalScriptCliController(INSTALL_COMMAND_NAME)],
|
|
1414
2027
|
[BUILD_COMMAND_NAME, createLocalScriptCliController(BUILD_COMMAND_NAME)],
|
|
1415
2028
|
[START_COMMAND_NAME, createLocalScriptCliController(START_COMMAND_NAME)],
|