@podosoft/podokit 1.0.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -6
- package/dist/add.d.ts +14 -1
- package/dist/add.js +42 -5
- package/dist/create.d.ts +4 -0
- package/dist/create.js +9 -0
- package/dist/deploy-compose-profile.d.ts +3 -0
- package/dist/deploy-compose-profile.js +74 -8
- package/dist/deploy-compose-render.d.ts +1 -0
- package/dist/deploy-compose-render.js +46 -1
- package/dist/deploy-compose.js +2 -1
- package/dist/deploy-profile.js +18 -8
- package/dist/deploy.js +157 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +41 -0
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +8 -2
- package/dist/lockfile.d.ts +6 -1
- package/dist/lockfile.js +22 -4
- package/dist/provider.d.ts +24 -0
- package/dist/provider.js +142 -0
- package/dist/remove.js +6 -3
- package/dist/templates/base/AGENTS.md +9 -3
- package/dist/templates/base/README.md +12 -8
- package/dist/templates/fullstack/AGENTS.md +24 -5
- package/dist/templates/fullstack/README.md +75 -11
- package/dist/templates/fullstack/apps/api/package.json +4 -3
- package/dist/templates/fullstack/apps/api/src/config/env.validation.ts +1 -0
- package/dist/templates/fullstack/apps/api/src/config/providers.ts +9 -0
- package/dist/templates/fullstack/apps/api/src/core/services.ts +7 -2
- package/dist/templates/fullstack/apps/api/src/database/data-source.ts +30 -8
- package/dist/templates/fullstack/apps/api/src/database/database.spec.ts +58 -0
- package/dist/templates/fullstack/apps/api/src/database/database.ts +58 -2
- package/dist/templates/fullstack/apps/api/src/database/migrate.ts +24 -2
- package/dist/templates/fullstack/apps/api/src/database/sqlite-migrator.spec.ts +50 -0
- package/dist/templates/fullstack/apps/api/src/database/sqlite-migrator.ts +134 -0
- package/dist/templates/fullstack/compose.dev.yaml +2 -2
- package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/references/profile.md +10 -3
- package/dist/templates/fullstack/dot-env.example +2 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/admin.module.ts +2 -2
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/profile-image/profile-image.service.spec.ts +10 -4
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/profile-image/profile-image.service.ts +10 -4
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.service.ts +10 -4
- package/dist/templates/modules/admin-dashboard/module.manifest.json +3 -5
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-enabled.ts +2 -5
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.migration.spec.ts +33 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.spec.ts +13 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +17 -8
- package/dist/templates/modules/audit-log/files/apps/api/src/migrations/1720300000000-InitAuditLogs.ts +5 -4
- package/dist/templates/modules/audit-log/module.manifest.json +1 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/account-issuer-migration.spec.ts +88 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/account-issuer-migration.ts +123 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-config-store.ts +2 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.module.ts +4 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.service.ts +2 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +4 -4
- package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +55 -10
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +10 -14
- package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +24 -6
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720400000000-InitAppSettings.ts +2 -1
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720500000000-InitAuthConfig.ts +4 -3
- package/dist/templates/modules/auth/files/apps/api/src/settings/app-settings.migration.spec.ts +15 -4
- package/dist/templates/modules/auth/files/apps/api/src/settings/auth-config.migration.spec.ts +30 -0
- package/dist/templates/modules/auth/module.manifest.json +6 -6
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.spec.ts +10 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.ts +107 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/jobs.module.ts +12 -11
- package/dist/templates/modules/bullmq/files/apps/api/src/main-worker.ts +5 -0
- package/dist/templates/modules/bullmq/module.manifest.json +7 -0
- package/dist/templates/modules/cache-memory/files/apps/api/src/cache-memory/cache-memory.module.spec.ts +16 -0
- package/dist/templates/modules/cache-memory/files/apps/api/src/cache-memory/cache-memory.module.ts +12 -0
- package/dist/templates/modules/cache-memory/module.manifest.json +30 -0
- package/dist/templates/modules/events-memory/files/apps/api/src/events-provider-memory/events-memory.module.ts +14 -0
- package/dist/templates/modules/events-memory/module.manifest.json +30 -0
- package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/events-redis.module.ts +16 -0
- package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/redis-event.bus.ts +75 -0
- package/dist/templates/modules/events-redis/module.manifest.json +32 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/files.module.ts +9 -5
- package/dist/templates/modules/file-upload/files/tests/api/files.api.spec.ts +1 -1
- package/dist/templates/modules/file-upload/module.manifest.json +4 -3
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/job-progress.module.ts +36 -14
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.bridge.spec.ts +9 -32
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.processor.ts +16 -11
- package/dist/templates/modules/job-progress/module.manifest.json +7 -8
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/jobs-local.module.ts +42 -0
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.spec.ts +45 -0
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.ts +212 -0
- package/dist/templates/modules/jobs-local/module.manifest.json +34 -0
- package/dist/templates/modules/mailer/files/apps/api/src/mail/mailer.ts +7 -3
- package/dist/templates/modules/mailer/module.manifest.json +2 -4
- package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/storage-local.module.spec.ts +25 -0
- package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/storage-local.module.ts +60 -0
- package/dist/templates/modules/object-storage-local/module.manifest.json +34 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-object.store.ts +50 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.module.ts +8 -0
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +4 -4
- package/dist/templates/modules/object-storage-s3/files/tests/api/storage.api.spec.ts +20 -12
- package/dist/templates/modules/object-storage-s3/module.manifest.json +4 -2
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.spec.ts +14 -1
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +18 -37
- package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +12 -0
- package/dist/templates/modules/rate-limit/module.manifest.json +8 -6
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis-cache.store.ts +82 -0
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis.module.ts +6 -0
- package/dist/templates/modules/redis/module.manifest.json +2 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/events.module.ts +5 -6
- package/dist/templates/modules/sse/files/apps/api/src/events/events.service.spec.ts +4 -4
- package/dist/templates/modules/sse/files/apps/api/src/events/events.service.ts +6 -5
- package/dist/templates/modules/sse/module.manifest.json +4 -6
- package/dist/templates/todo/AGENTS.md +25 -6
- package/dist/templates/todo/README.md +72 -14
- package/dist/templates/todo/apps/api/src/migrations/1720100000000-InitTodos.ts +11 -0
- package/dist/templates/todo/apps/api/src/todos/todo.repository.spec.ts +76 -0
- package/dist/templates/todo/apps/api/src/todos/todo.repository.ts +9 -4
- package/dist/templates/todo/tests/api/todo.api.spec.ts +32 -0
- package/dist/templates/todo/tests/ui/todo.ui.spec.ts +31 -0
- package/package.json +2 -1
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.bridge.ts +0 -32
- package/dist/templates/modules/mailer/files/apps/api/src/mail/db.ts +0 -13
- package/dist/templates/modules/sse/files/apps/api/src/events/events.transport.ts +0 -111
package/dist/deploy.js
CHANGED
|
@@ -491,6 +491,154 @@ function helmFailureFlag(runner) {
|
|
|
491
491
|
const version = checked(runner, "helm", ["version", "--short"]).trim();
|
|
492
492
|
return version.startsWith("v4.") ? "--rollback-on-failure" : "--atomic";
|
|
493
493
|
}
|
|
494
|
+
function isRecord(value) {
|
|
495
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
496
|
+
}
|
|
497
|
+
function managedStorageImage(resource, profile) {
|
|
498
|
+
const name = `${profile.target.release}-minio`;
|
|
499
|
+
if (!isRecord(resource) ||
|
|
500
|
+
resource.apiVersion !== "apps/v1" ||
|
|
501
|
+
resource.kind !== "StatefulSet" ||
|
|
502
|
+
!isRecord(resource.metadata) ||
|
|
503
|
+
resource.metadata.name !== name ||
|
|
504
|
+
resource.metadata.namespace !== profile.target.namespace ||
|
|
505
|
+
!isRecord(resource.spec) ||
|
|
506
|
+
!isRecord(resource.spec.template) ||
|
|
507
|
+
!isRecord(resource.spec.template.spec) ||
|
|
508
|
+
!Array.isArray(resource.spec.template.spec.containers)) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
const containers = resource.spec.template.spec.containers;
|
|
512
|
+
const container = containers[0];
|
|
513
|
+
if (containers.length !== 1 ||
|
|
514
|
+
!isRecord(container) ||
|
|
515
|
+
container.name !== "minio" ||
|
|
516
|
+
typeof container.image !== "string" ||
|
|
517
|
+
!container.image.trim()) {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
return container.image;
|
|
521
|
+
}
|
|
522
|
+
function jsonObjectStream(output) {
|
|
523
|
+
const documents = [];
|
|
524
|
+
let start = -1;
|
|
525
|
+
let depth = 0;
|
|
526
|
+
let quoted = false;
|
|
527
|
+
let escaped = false;
|
|
528
|
+
for (let index = 0; index < output.length; index += 1) {
|
|
529
|
+
const character = output[index];
|
|
530
|
+
if (start < 0) {
|
|
531
|
+
if (/\s/.test(character ?? ""))
|
|
532
|
+
continue;
|
|
533
|
+
if (character !== "{")
|
|
534
|
+
return null;
|
|
535
|
+
start = index;
|
|
536
|
+
}
|
|
537
|
+
if (quoted) {
|
|
538
|
+
if (escaped)
|
|
539
|
+
escaped = false;
|
|
540
|
+
else if (character === "\\")
|
|
541
|
+
escaped = true;
|
|
542
|
+
else if (character === '"')
|
|
543
|
+
quoted = false;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (character === '"')
|
|
547
|
+
quoted = true;
|
|
548
|
+
else if (character === "{")
|
|
549
|
+
depth += 1;
|
|
550
|
+
else if (character === "}")
|
|
551
|
+
depth -= 1;
|
|
552
|
+
if (depth === 0) {
|
|
553
|
+
try {
|
|
554
|
+
documents.push(JSON.parse(output.slice(start, index + 1)));
|
|
555
|
+
}
|
|
556
|
+
catch {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
start = -1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return start < 0 && documents.length > 0 ? documents : null;
|
|
563
|
+
}
|
|
564
|
+
function recordedStorageImage(profile, runner) {
|
|
565
|
+
const manifest = runner("helm", helmArgs(profile, ["get", "manifest", `${profile.target.release}-dependencies`]), { capture: true });
|
|
566
|
+
if (manifest.status !== 0 || !manifest.stdout.trim())
|
|
567
|
+
return null;
|
|
568
|
+
const temporaryRoot = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "podokit-storage-manifest-"));
|
|
569
|
+
try {
|
|
570
|
+
const path = (0, node_path_1.join)(temporaryRoot, "dependencies.yaml");
|
|
571
|
+
(0, node_fs_1.writeFileSync)(path, manifest.stdout, { mode: 0o600 });
|
|
572
|
+
// Client dry-run parses Helm's YAML without submitting a create request to Kubernetes.
|
|
573
|
+
const parsed = runner("kubectl", kubectlArgs(profile, [
|
|
574
|
+
"create", "--dry-run=client", "--validate=false", "-f", path, "-o", "json",
|
|
575
|
+
]), { capture: true });
|
|
576
|
+
if (parsed.status !== 0)
|
|
577
|
+
return null;
|
|
578
|
+
// kubectl prints consecutive JSON objects for a manifest containing multiple resources.
|
|
579
|
+
const documents = jsonObjectStream(parsed.stdout);
|
|
580
|
+
if (!documents)
|
|
581
|
+
return null;
|
|
582
|
+
const resources = [];
|
|
583
|
+
for (const document of documents) {
|
|
584
|
+
if (!isRecord(document))
|
|
585
|
+
return null;
|
|
586
|
+
if (document.kind !== "List")
|
|
587
|
+
resources.push(document);
|
|
588
|
+
else if (document.apiVersion === "v1" && Array.isArray(document.items)) {
|
|
589
|
+
resources.push(...document.items);
|
|
590
|
+
}
|
|
591
|
+
else
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
const name = `${profile.target.release}-minio`;
|
|
595
|
+
const matches = resources.filter((item) => isRecord(item) && item.kind === "StatefulSet" &&
|
|
596
|
+
isRecord(item.metadata) && item.metadata.name === name);
|
|
597
|
+
return matches.length === 1 ? managedStorageImage(matches[0], profile) : null;
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
(0, node_fs_1.rmSync)(temporaryRoot, { recursive: true, force: true });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function dependencyRollbackWarning(profile, plan, runner) {
|
|
604
|
+
if (profile.dependencies.objectStorage.mode !== "inCluster")
|
|
605
|
+
return null;
|
|
606
|
+
const name = `${profile.target.release}-minio`;
|
|
607
|
+
const current = runner("kubectl", kubectlArgs(profile, ["get", "statefulset", name, "--ignore-not-found=true", "-o", "json"]), { capture: true });
|
|
608
|
+
if (current.status !== 0)
|
|
609
|
+
return "the current managed storage image could not be inspected";
|
|
610
|
+
if (!current.stdout.trim()) {
|
|
611
|
+
return plan.dependencyRevision === null
|
|
612
|
+
? null
|
|
613
|
+
: "the existing managed storage StatefulSet is missing";
|
|
614
|
+
}
|
|
615
|
+
let resource;
|
|
616
|
+
try {
|
|
617
|
+
resource = JSON.parse(current.stdout);
|
|
618
|
+
}
|
|
619
|
+
catch {
|
|
620
|
+
return "the managed storage StatefulSet has an invalid schema";
|
|
621
|
+
}
|
|
622
|
+
const currentImage = managedStorageImage(resource, profile);
|
|
623
|
+
if (!currentImage || typeof plan.images.objectStorage !== "string") {
|
|
624
|
+
return "the current managed storage image could not be confirmed";
|
|
625
|
+
}
|
|
626
|
+
if (currentImage !== plan.images.objectStorage) {
|
|
627
|
+
return "the managed storage server image is changing";
|
|
628
|
+
}
|
|
629
|
+
// A failed revision can already contain the new image while Helm's rollback target uses the old one.
|
|
630
|
+
if (plan.dependencyRevision !== null && plan.dependencyStatus !== "deployed") {
|
|
631
|
+
return "the previous dependency release is not deployed";
|
|
632
|
+
}
|
|
633
|
+
if (plan.dependencyRevision !== null) {
|
|
634
|
+
const recordedImage = recordedStorageImage(profile, runner);
|
|
635
|
+
if (!recordedImage)
|
|
636
|
+
return "the storage image recorded by Helm could not be confirmed";
|
|
637
|
+
if (recordedImage !== currentImage)
|
|
638
|
+
return "the storage image recorded by Helm differs from the current image";
|
|
639
|
+
}
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
494
642
|
function waitForDependencies(profile, runner) {
|
|
495
643
|
const release = profile.target.release;
|
|
496
644
|
const resources = [];
|
|
@@ -603,7 +751,13 @@ async function applyDeployment(projectRoot, profileName, release, confirmation,
|
|
|
603
751
|
}
|
|
604
752
|
const runtime = (0, deploy_render_1.renderDeployment)(projectRoot, profileName, profile, release, plan.images, plan.rolloutStateDigest);
|
|
605
753
|
const failureFlag = helmFailureFlag(runner);
|
|
606
|
-
const common = ["--wait", "--timeout", "5m"
|
|
754
|
+
const common = ["--wait", "--timeout", "5m"];
|
|
755
|
+
const rollbackWarning = dependencyRollbackWarning(profile, plan, runner);
|
|
756
|
+
if (rollbackWarning) {
|
|
757
|
+
console.warn(`Disable automatic dependency rollback because ${rollbackWarning}. ` +
|
|
758
|
+
"If the upgrade fails, stop storage and restore the complete data and IAM backup before starting the previous image. " +
|
|
759
|
+
"Application upgrades retain automatic rollback.");
|
|
760
|
+
}
|
|
607
761
|
if (profile.dependencies.objectStorage.mode === "inCluster") {
|
|
608
762
|
checked(runner, "kubectl", kubectlArgs(profile, [
|
|
609
763
|
"delete",
|
|
@@ -620,6 +774,7 @@ async function applyDeployment(projectRoot, profileName, release, confirmation,
|
|
|
620
774
|
`${profile.target.release}-dependencies`,
|
|
621
775
|
runtime.dependencyChart,
|
|
622
776
|
...common,
|
|
777
|
+
...(rollbackWarning ? [] : [failureFlag]),
|
|
623
778
|
]), false);
|
|
624
779
|
waitForDependencies(profile, runner);
|
|
625
780
|
const job = migrationJobName(profile, release);
|
|
@@ -632,6 +787,7 @@ async function applyDeployment(projectRoot, profileName, release, confirmation,
|
|
|
632
787
|
profile.target.release,
|
|
633
788
|
runtime.applicationChart,
|
|
634
789
|
...common,
|
|
790
|
+
failureFlag,
|
|
635
791
|
]), false);
|
|
636
792
|
for (const kind of [
|
|
637
793
|
"api",
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -22,11 +22,14 @@ const deploy_compose_profile_1 = require("./deploy-compose-profile");
|
|
|
22
22
|
const deploy_compose_render_1 = require("./deploy-compose-render");
|
|
23
23
|
const deploy_compose_1 = require("./deploy-compose");
|
|
24
24
|
const deploy_compose_sync_1 = require("./deploy-compose-sync");
|
|
25
|
+
const provider_1 = require("./provider");
|
|
25
26
|
const HELP = `podo — PodoKit project generator
|
|
26
27
|
|
|
27
28
|
Usage:
|
|
28
29
|
podo create <name> [options]
|
|
29
30
|
podo add <module> [--adopt]
|
|
31
|
+
podo provider list Show active runtime providers
|
|
32
|
+
podo provider set <capability> <provider> [--apply]
|
|
30
33
|
podo remove <module> Un-apply a module (inverse of add)
|
|
31
34
|
podo status Show version, modules, file tiers, and local edits
|
|
32
35
|
podo diff List PodoKit-managed files you have edited
|
|
@@ -42,6 +45,7 @@ Options:
|
|
|
42
45
|
--template <t> Template to scaffold (see below)
|
|
43
46
|
--dir <path> Target directory (default: ./<name>)
|
|
44
47
|
--runtime bun Optional explicit Bun runtime selection
|
|
48
|
+
--database <provider> Database provider: postgres | sqlite
|
|
45
49
|
--name <label> Display name for a locale
|
|
46
50
|
--direction <direction> Text direction: ltr | rtl (default: ltr)
|
|
47
51
|
--profile <name> Deployment profile name
|
|
@@ -143,6 +147,9 @@ function parseArgs(argv) {
|
|
|
143
147
|
else if (arg === "--runtime") {
|
|
144
148
|
parsed.runtime = argv[++i];
|
|
145
149
|
}
|
|
150
|
+
else if (arg === "--database") {
|
|
151
|
+
parsed.database = argv[++i];
|
|
152
|
+
}
|
|
146
153
|
else if (arg === "--name") {
|
|
147
154
|
parsed.localeName = argv[++i];
|
|
148
155
|
}
|
|
@@ -335,6 +342,39 @@ async function main(argv) {
|
|
|
335
342
|
return;
|
|
336
343
|
}
|
|
337
344
|
const modulesDir = (0, node_path_1.join)(__dirname, "templates", "modules");
|
|
345
|
+
if (args.command === "provider") {
|
|
346
|
+
const action = args.positionals[1] ?? "list";
|
|
347
|
+
try {
|
|
348
|
+
if (action === "list") {
|
|
349
|
+
const providers = (0, provider_1.listProviders)(process.cwd());
|
|
350
|
+
process.stdout.write(providers.map((item) => `${item.capability.padEnd(14)} ${item.selected.padEnd(10)} (${item.available.join(", ")})`).join("\n") + "\n");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (action === "set") {
|
|
354
|
+
const capability = args.positionals[2];
|
|
355
|
+
const provider = args.positionals[3];
|
|
356
|
+
if (!capability || !provider) {
|
|
357
|
+
fail("Usage: podo provider set <capability> <provider> [--apply]");
|
|
358
|
+
}
|
|
359
|
+
const plan = args.apply
|
|
360
|
+
? (0, provider_1.applyProviderChange)(process.cwd(), capability, provider, { modulesDir })
|
|
361
|
+
: (0, provider_1.planProviderChange)(process.cwd(), capability, provider, modulesDir);
|
|
362
|
+
process.stdout.write(`${plan.capability}: ${plan.from} -> ${plan.to}${plan.changed ? "" : " (unchanged)"}\n` +
|
|
363
|
+
plan.modulesToAdd.map((module) => ` ${args.apply ? "add" : "would add"} module ${module}`).join("\n") +
|
|
364
|
+
(plan.modulesToAdd.length ? "\n" : "") +
|
|
365
|
+
plan.files.map((file) => ` ${args.apply ? "write" : "would write"} ${file}`).join("\n") +
|
|
366
|
+
(plan.files.length ? "\n" : "") +
|
|
367
|
+
plan.warnings.map((warning) => `warning: ${warning}`).join("\n") +
|
|
368
|
+
(plan.warnings.length ? "\n" : "") +
|
|
369
|
+
(args.apply ? "Applied provider configuration.\n" : "Dry-run — nothing was written. Re-run with --apply to write.\n"));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
fail(`Unknown provider command "${action}". Use list or set.`);
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
fail(err.message);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
338
378
|
if (args.command === "add") {
|
|
339
379
|
const moduleName = args.name;
|
|
340
380
|
if (!moduleName) {
|
|
@@ -695,6 +735,7 @@ async function main(argv) {
|
|
|
695
735
|
targetDir: args.dir,
|
|
696
736
|
runtime: resolved.toolchain.runtime,
|
|
697
737
|
ai: args.ai,
|
|
738
|
+
database: args.database === undefined ? undefined : args.database,
|
|
698
739
|
});
|
|
699
740
|
const relPath = (0, node_path_1.relative)(process.cwd(), result.projectDir) || ".";
|
|
700
741
|
const rel = relPath.startsWith("..") ? result.projectDir : relPath;
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { create } from "./create";
|
|
2
2
|
export type { CreateOptions, CreateResult } from "./create";
|
|
3
|
+
export { applyProviderChange, listProviders, planProviderChange, providerSource, PROVIDERS_SOURCE, } from "./provider";
|
|
4
|
+
export type { ProviderChangePlan, ProviderListItem } from "./provider";
|
|
3
5
|
export { TEMPLATES, DEFAULT_TEMPLATE, TEMPLATE_NAMES, isKnownTemplate } from "./templates";
|
|
4
6
|
export type { TemplateInfo } from "./templates";
|
|
5
7
|
export { listModules, addModule } from "./add";
|
package/dist/lib.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.verifyAnyDeployment = exports.planAnyDeployment = exports.getAnyDeploymentStatus = exports.doctorAnyDeployment = exports.describeDeploymentTarget = void 0;
|
|
3
|
+
exports.getComposeStatus = exports.doctorComposeDeployment = exports.applyComposeDeployment = exports.renderComposeDeployment = exports.parseComposeProfile = exports.initializeComposeProfile = exports.readDeploymentDriver = exports.loadKubernetesProfile = exports.loadComposeProfile = exports.loadAnyDeploymentProfile = exports.listAnyDeploymentProfiles = exports.assertAnyReleaseTag = exports.DEPLOY_DRIVERS = exports.renderDeployment = exports.loadDeploymentProfile = exports.listDeploymentProfiles = exports.initializeDeploymentProfile = exports.deploymentProfileDigest = exports.assertReleaseTag = exports.verifyDeployment = exports.rollbackDeployment = exports.planRollback = exports.planDeployment = exports.inspectClusterFingerprint = exports.getDeploymentStatus = exports.doctorDeployment = exports.applyDeployment = exports.runDevCommand = exports.resolveDevRuntime = exports.renderRuntimeCompose = exports.renderRoute = exports.readDevConfig = exports.summarize = exports.planUpdate = exports.SUPPORTED_FRAMEWORKS = exports.doctor = exports.diff = exports.status = exports.addModule = exports.listModules = exports.isKnownTemplate = exports.TEMPLATE_NAMES = exports.DEFAULT_TEMPLATE = exports.TEMPLATES = exports.PROVIDERS_SOURCE = exports.providerSource = exports.planProviderChange = exports.listProviders = exports.applyProviderChange = exports.create = void 0;
|
|
4
|
+
exports.verifyAnyDeployment = exports.planAnyDeployment = exports.getAnyDeploymentStatus = exports.doctorAnyDeployment = exports.describeDeploymentTarget = exports.verifyComposeDeployment = exports.rollbackComposeDeployment = exports.planComposeRollback = exports.planComposeDeployment = exports.inspectComposeEndpointFingerprint = void 0;
|
|
5
5
|
exports.builtinTemplatesDir = builtinTemplatesDir;
|
|
6
6
|
exports.builtinModulesDir = builtinModulesDir;
|
|
7
7
|
// Library entry point: the reusable functions behind the `podo` CLI, for other
|
|
@@ -9,6 +9,12 @@ exports.builtinModulesDir = builtinModulesDir;
|
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
10
|
var create_1 = require("./create");
|
|
11
11
|
Object.defineProperty(exports, "create", { enumerable: true, get: function () { return create_1.create; } });
|
|
12
|
+
var provider_1 = require("./provider");
|
|
13
|
+
Object.defineProperty(exports, "applyProviderChange", { enumerable: true, get: function () { return provider_1.applyProviderChange; } });
|
|
14
|
+
Object.defineProperty(exports, "listProviders", { enumerable: true, get: function () { return provider_1.listProviders; } });
|
|
15
|
+
Object.defineProperty(exports, "planProviderChange", { enumerable: true, get: function () { return provider_1.planProviderChange; } });
|
|
16
|
+
Object.defineProperty(exports, "providerSource", { enumerable: true, get: function () { return provider_1.providerSource; } });
|
|
17
|
+
Object.defineProperty(exports, "PROVIDERS_SOURCE", { enumerable: true, get: function () { return provider_1.PROVIDERS_SOURCE; } });
|
|
12
18
|
var templates_1 = require("./templates");
|
|
13
19
|
Object.defineProperty(exports, "TEMPLATES", { enumerable: true, get: function () { return templates_1.TEMPLATES; } });
|
|
14
20
|
Object.defineProperty(exports, "DEFAULT_TEMPLATE", { enumerable: true, get: function () { return templates_1.DEFAULT_TEMPLATE; } });
|
package/dist/lockfile.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { TemplateVars } from "@podosoft/podokit-template-engine";
|
|
2
|
+
import { type ProviderSelections } from "@podosoft/podokit-runtime";
|
|
2
3
|
import { type Toolchain } from "./toolchain";
|
|
3
4
|
/**
|
|
4
5
|
* The generation lockfile (`.podokit/`) records how a project was assembled so
|
|
5
6
|
* `podo update` can rebuild the same inputs and detect user edits. See
|
|
6
7
|
* ADR-0009 (update mechanism) and ADR-0010 (ownership model).
|
|
7
8
|
*/
|
|
8
|
-
export declare const LOCK_SCHEMA_VERSION =
|
|
9
|
+
export declare const LOCK_SCHEMA_VERSION = 4;
|
|
9
10
|
/** File ownership tier. See ADR-0010. */
|
|
10
11
|
export type Tier = "managed" | "assembled" | "owned";
|
|
11
12
|
export interface ManifestModule {
|
|
@@ -35,6 +36,8 @@ export interface PodokitManifest {
|
|
|
35
36
|
toolchain: Toolchain;
|
|
36
37
|
/** Render vars / prompt answers, replayed on update (copier-style). */
|
|
37
38
|
answers: Record<string, string>;
|
|
39
|
+
/** Active implementation for each provider-neutral runtime capability. */
|
|
40
|
+
providers: ProviderSelections;
|
|
38
41
|
modules: ManifestModule[];
|
|
39
42
|
/** Globs whose files are user-owned and never touched by update. */
|
|
40
43
|
ownedGlobs: string[];
|
|
@@ -68,6 +71,7 @@ export declare function readManifest(projectRoot: string): PodokitManifest | nul
|
|
|
68
71
|
export declare function writeManifest(projectRoot: string, manifest: PodokitManifest): void;
|
|
69
72
|
/** Complete replay variables for a Bun-only v1 project. */
|
|
70
73
|
export declare function manifestTemplateVars(manifest: PodokitManifest): TemplateVars;
|
|
74
|
+
export declare function providerTemplateVars(providers: ProviderSelections): TemplateVars;
|
|
71
75
|
export declare function writeFilesLock(projectRoot: string, lock: FilesLock): void;
|
|
72
76
|
/** Read the PodoKit CLI's own version to stamp into the manifest. */
|
|
73
77
|
export declare function podokitVersion(): string;
|
|
@@ -98,6 +102,7 @@ export interface InitLockOptions {
|
|
|
98
102
|
answers: Record<string, string>;
|
|
99
103
|
version?: string;
|
|
100
104
|
ownedGlobs?: string[];
|
|
105
|
+
providers?: Partial<Record<keyof ProviderSelections, string>>;
|
|
101
106
|
}
|
|
102
107
|
/** Write the initial `.podokit/` lockfile for a freshly created project. */
|
|
103
108
|
export declare function initLockfile(projectRoot: string, options: InitLockOptions): void;
|
package/dist/lockfile.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.filesLockPath = filesLockPath;
|
|
|
6
6
|
exports.readManifest = readManifest;
|
|
7
7
|
exports.writeManifest = writeManifest;
|
|
8
8
|
exports.manifestTemplateVars = manifestTemplateVars;
|
|
9
|
+
exports.providerTemplateVars = providerTemplateVars;
|
|
9
10
|
exports.writeFilesLock = writeFilesLock;
|
|
10
11
|
exports.podokitVersion = podokitVersion;
|
|
11
12
|
exports.matchGlob = matchGlob;
|
|
@@ -20,13 +21,14 @@ exports.recordModules = recordModules;
|
|
|
20
21
|
const node_fs_1 = require("node:fs");
|
|
21
22
|
const node_path_1 = require("node:path");
|
|
22
23
|
const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
|
|
24
|
+
const podokit_runtime_1 = require("@podosoft/podokit-runtime");
|
|
23
25
|
const toolchain_1 = require("./toolchain");
|
|
24
26
|
/**
|
|
25
27
|
* The generation lockfile (`.podokit/`) records how a project was assembled so
|
|
26
28
|
* `podo update` can rebuild the same inputs and detect user edits. See
|
|
27
29
|
* ADR-0009 (update mechanism) and ADR-0010 (ownership model).
|
|
28
30
|
*/
|
|
29
|
-
exports.LOCK_SCHEMA_VERSION =
|
|
31
|
+
exports.LOCK_SCHEMA_VERSION = 4;
|
|
30
32
|
const PODOKIT_DIR = ".podokit";
|
|
31
33
|
const MANIFEST_FILE = "manifest.json";
|
|
32
34
|
const FILES_LOCK_FILE = "files.lock";
|
|
@@ -87,12 +89,13 @@ function readManifest(projectRoot) {
|
|
|
87
89
|
if (!(0, node_fs_1.existsSync)(file))
|
|
88
90
|
return null;
|
|
89
91
|
const raw = JSON.parse((0, node_fs_1.readFileSync)(file, "utf8"));
|
|
90
|
-
if (raw.schemaVersion !== exports.LOCK_SCHEMA_VERSION
|
|
92
|
+
if ((raw.schemaVersion !== 3 && raw.schemaVersion !== exports.LOCK_SCHEMA_VERSION)
|
|
91
93
|
|| raw.toolchain?.runtime !== "bun"
|
|
92
94
|
|| raw.toolchain.packageManager !== "bun") {
|
|
93
95
|
throw new Error("This project was generated by PodoKit v0 and is not compatible with PodoKit v1 updates. Use @podosoft/podokit@0.17.4 for this project; create a new v1 project for Bun and Elysia.");
|
|
94
96
|
}
|
|
95
|
-
|
|
97
|
+
const providers = (0, podokit_runtime_1.resolveProviderSelections)(raw.providers);
|
|
98
|
+
return { ...raw, schemaVersion: exports.LOCK_SCHEMA_VERSION, providers };
|
|
96
99
|
}
|
|
97
100
|
function writeManifest(projectRoot, manifest) {
|
|
98
101
|
(0, node_fs_1.mkdirSync)(podokitDir(projectRoot), { recursive: true });
|
|
@@ -100,7 +103,20 @@ function writeManifest(projectRoot, manifest) {
|
|
|
100
103
|
}
|
|
101
104
|
/** Complete replay variables for a Bun-only v1 project. */
|
|
102
105
|
function manifestTemplateVars(manifest) {
|
|
103
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
...manifest.answers,
|
|
108
|
+
...providerTemplateVars(manifest.providers),
|
|
109
|
+
...(0, toolchain_1.toolchainTemplateVars)(manifest.toolchain),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function providerTemplateVars(providers) {
|
|
113
|
+
return {
|
|
114
|
+
databaseProvider: providers.database,
|
|
115
|
+
cacheProvider: providers.cache,
|
|
116
|
+
objectStorageProvider: providers["object-storage"],
|
|
117
|
+
eventsProvider: providers.events,
|
|
118
|
+
jobsProvider: providers.jobs,
|
|
119
|
+
};
|
|
104
120
|
}
|
|
105
121
|
function writeFilesLock(projectRoot, lock) {
|
|
106
122
|
(0, node_fs_1.mkdirSync)(podokitDir(projectRoot), { recursive: true });
|
|
@@ -206,12 +222,14 @@ function computeDrift(projectRoot) {
|
|
|
206
222
|
/** Write the initial `.podokit/` lockfile for a freshly created project. */
|
|
207
223
|
function initLockfile(projectRoot, options) {
|
|
208
224
|
const ownedGlobs = options.ownedGlobs ?? exports.DEFAULT_OWNED_GLOBS;
|
|
225
|
+
const providers = (0, podokit_runtime_1.resolveProviderSelections)(options.providers ?? podokit_runtime_1.SERVER_PROVIDER_SELECTIONS);
|
|
209
226
|
const manifest = {
|
|
210
227
|
schemaVersion: exports.LOCK_SCHEMA_VERSION,
|
|
211
228
|
podokitVersion: options.version ?? podokitVersion(),
|
|
212
229
|
template: options.template,
|
|
213
230
|
toolchain: options.toolchain ?? (0, toolchain_1.resolveToolchain)(),
|
|
214
231
|
answers: options.answers,
|
|
232
|
+
providers,
|
|
215
233
|
modules: [],
|
|
216
234
|
ownedGlobs,
|
|
217
235
|
managedOverrides: [],
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ProviderCapability, type ProviderSelections } from "@podosoft/podokit-runtime";
|
|
2
|
+
export declare const PROVIDERS_SOURCE = "apps/api/src/config/providers.ts";
|
|
3
|
+
export interface ProviderListItem {
|
|
4
|
+
capability: ProviderCapability;
|
|
5
|
+
selected: string;
|
|
6
|
+
available: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ProviderChangePlan {
|
|
9
|
+
capability: ProviderCapability;
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
changed: boolean;
|
|
13
|
+
files: string[];
|
|
14
|
+
modulesToAdd: string[];
|
|
15
|
+
warnings: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface ApplyProviderChangeOptions {
|
|
18
|
+
modulesDir?: string;
|
|
19
|
+
podokitVersion?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function providerSource(providers: ProviderSelections): string;
|
|
22
|
+
export declare function listProviders(projectRoot: string): ProviderListItem[];
|
|
23
|
+
export declare function planProviderChange(projectRoot: string, capabilityInput: string, provider: string, modulesDir?: string): ProviderChangePlan;
|
|
24
|
+
export declare function applyProviderChange(projectRoot: string, capabilityInput: string, provider: string, options?: ApplyProviderChangeOptions): ProviderChangePlan;
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROVIDERS_SOURCE = void 0;
|
|
4
|
+
exports.providerSource = providerSource;
|
|
5
|
+
exports.listProviders = listProviders;
|
|
6
|
+
exports.planProviderChange = planProviderChange;
|
|
7
|
+
exports.applyProviderChange = applyProviderChange;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const podokit_runtime_1 = require("@podosoft/podokit-runtime");
|
|
11
|
+
const add_1 = require("./add");
|
|
12
|
+
const lockfile_1 = require("./lockfile");
|
|
13
|
+
exports.PROVIDERS_SOURCE = "apps/api/src/config/providers.ts";
|
|
14
|
+
function requireManifest(projectRoot) {
|
|
15
|
+
const manifest = (0, lockfile_1.readManifest)(projectRoot);
|
|
16
|
+
if (!manifest) {
|
|
17
|
+
throw new Error("This does not look like a PodoKit project: .podokit/manifest.json not found.");
|
|
18
|
+
}
|
|
19
|
+
return manifest;
|
|
20
|
+
}
|
|
21
|
+
function assertProvider(capability, provider) {
|
|
22
|
+
if (!(0, podokit_runtime_1.isProviderCapability)(capability)) {
|
|
23
|
+
throw new Error(`Unknown provider capability "${capability}". Use one of: ${podokit_runtime_1.PROVIDER_CAPABILITIES.join(", ")}.`);
|
|
24
|
+
}
|
|
25
|
+
if (!(0, podokit_runtime_1.isProviderName)(capability, provider)) {
|
|
26
|
+
throw new Error(`Unknown ${capability} provider "${provider}". Use one of: ${podokit_runtime_1.PROVIDER_NAMES[capability].join(", ")}.`);
|
|
27
|
+
}
|
|
28
|
+
return capability;
|
|
29
|
+
}
|
|
30
|
+
function providerSource(providers) {
|
|
31
|
+
return `import type { ProviderSelections } from "@podosoft/podokit-runtime";\n\n` +
|
|
32
|
+
`export const PROVIDERS = {\n` +
|
|
33
|
+
` database: "${providers.database}",\n` +
|
|
34
|
+
` cache: "${providers.cache}",\n` +
|
|
35
|
+
` "object-storage": "${providers["object-storage"]}",\n` +
|
|
36
|
+
` events: "${providers.events}",\n` +
|
|
37
|
+
` jobs: "${providers.jobs}",\n` +
|
|
38
|
+
`} as const satisfies ProviderSelections;\n`;
|
|
39
|
+
}
|
|
40
|
+
function listProviders(projectRoot) {
|
|
41
|
+
const manifest = requireManifest(projectRoot);
|
|
42
|
+
return podokit_runtime_1.PROVIDER_CAPABILITIES.map((capability) => ({
|
|
43
|
+
capability,
|
|
44
|
+
selected: manifest.providers[capability],
|
|
45
|
+
available: podokit_runtime_1.PROVIDER_NAMES[capability],
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
function planProviderChange(projectRoot, capabilityInput, provider, modulesDir) {
|
|
49
|
+
const capability = assertProvider(capabilityInput, provider);
|
|
50
|
+
const manifest = requireManifest(projectRoot);
|
|
51
|
+
const from = manifest.providers[capability];
|
|
52
|
+
const changed = from !== provider;
|
|
53
|
+
const sourcePath = (0, node_path_1.join)(projectRoot, exports.PROVIDERS_SOURCE);
|
|
54
|
+
const files = changed || !(0, node_fs_1.existsSync)(sourcePath)
|
|
55
|
+
? [exports.PROVIDERS_SOURCE, ".podokit/manifest.json", ".podokit/files.lock"]
|
|
56
|
+
: [];
|
|
57
|
+
const implementation = modulesDir
|
|
58
|
+
? providerImplementationModule(projectRoot, modulesDir, capability, provider)
|
|
59
|
+
: undefined;
|
|
60
|
+
const modulesToAdd = implementation
|
|
61
|
+
&& !manifest.modules.some((module) => module.name === implementation)
|
|
62
|
+
? [implementation]
|
|
63
|
+
: [];
|
|
64
|
+
return {
|
|
65
|
+
capability,
|
|
66
|
+
from,
|
|
67
|
+
to: provider,
|
|
68
|
+
changed,
|
|
69
|
+
files,
|
|
70
|
+
modulesToAdd,
|
|
71
|
+
warnings: [
|
|
72
|
+
...(changed ? [
|
|
73
|
+
"Provider switching changes configuration and code only.",
|
|
74
|
+
"Existing data and objects are not migrated or deleted.",
|
|
75
|
+
] : []),
|
|
76
|
+
...(modulesToAdd.length
|
|
77
|
+
? [`Provider implementation module ${modulesToAdd[0]} will be added.`]
|
|
78
|
+
: []),
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function providerImplementationModule(projectRoot, modulesDir, capability, provider) {
|
|
83
|
+
for (const available of (0, add_1.listModules)(modulesDir, projectRoot)) {
|
|
84
|
+
const resolved = (0, add_1.resolveModule)(available.name, modulesDir, projectRoot);
|
|
85
|
+
if (!resolved)
|
|
86
|
+
continue;
|
|
87
|
+
if ((0, add_1.readModuleManifest)(resolved.dir).provides?.[capability] === provider) {
|
|
88
|
+
return available.name;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (capability === "database")
|
|
92
|
+
return undefined;
|
|
93
|
+
throw new Error(`No module provides the ${capability} provider "${provider}".`);
|
|
94
|
+
}
|
|
95
|
+
function applyProviderChange(projectRoot, capabilityInput, provider, options = {}) {
|
|
96
|
+
const plan = planProviderChange(projectRoot, capabilityInput, provider, options.modulesDir);
|
|
97
|
+
if (!plan.changed && plan.files.length === 0 && plan.modulesToAdd.length === 0)
|
|
98
|
+
return plan;
|
|
99
|
+
const drift = (0, lockfile_1.computeDrift)(projectRoot);
|
|
100
|
+
if (drift.drifted.includes(exports.PROVIDERS_SOURCE) || drift.missing.includes(exports.PROVIDERS_SOURCE)) {
|
|
101
|
+
throw new Error(`${exports.PROVIDERS_SOURCE} has local edits. Resolve them or run podo eject before changing providers.`);
|
|
102
|
+
}
|
|
103
|
+
for (const module of plan.modulesToAdd) {
|
|
104
|
+
if (!options.modulesDir)
|
|
105
|
+
throw new Error("A modules directory is required to add provider implementations.");
|
|
106
|
+
(0, add_1.addModule)({
|
|
107
|
+
projectRoot,
|
|
108
|
+
module,
|
|
109
|
+
modulesDir: options.modulesDir,
|
|
110
|
+
podokitVersion: options.podokitVersion,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const manifest = requireManifest(projectRoot);
|
|
114
|
+
const sourcePath = (0, node_path_1.join)(projectRoot, exports.PROVIDERS_SOURCE);
|
|
115
|
+
const manifestFile = (0, lockfile_1.manifestPath)(projectRoot);
|
|
116
|
+
const lockFile = (0, lockfile_1.filesLockPath)(projectRoot);
|
|
117
|
+
const snapshots = new Map([
|
|
118
|
+
[sourcePath, (0, node_fs_1.existsSync)(sourcePath) ? (0, node_fs_1.readFileSync)(sourcePath, "utf8") : null],
|
|
119
|
+
[manifestFile, (0, node_fs_1.readFileSync)(manifestFile, "utf8")],
|
|
120
|
+
[lockFile, (0, node_fs_1.existsSync)(lockFile) ? (0, node_fs_1.readFileSync)(lockFile, "utf8") : null],
|
|
121
|
+
]);
|
|
122
|
+
const temporary = `${sourcePath}.podokit-provider-${process.pid}`;
|
|
123
|
+
try {
|
|
124
|
+
manifest.providers[plan.capability] = plan.to;
|
|
125
|
+
manifest.answers = { ...manifest.answers, ...(0, lockfile_1.providerTemplateVars)(manifest.providers) };
|
|
126
|
+
(0, node_fs_1.writeFileSync)(temporary, providerSource(manifest.providers), { mode: 0o600 });
|
|
127
|
+
(0, node_fs_1.renameSync)(temporary, sourcePath);
|
|
128
|
+
(0, lockfile_1.writeManifest)(projectRoot, manifest);
|
|
129
|
+
(0, lockfile_1.writeFilesLock)(projectRoot, (0, lockfile_1.computeFilesLock)(projectRoot, manifest.ownedGlobs, manifest.managedOverrides ?? []));
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
(0, node_fs_1.rmSync)(temporary, { force: true });
|
|
133
|
+
for (const [path, content] of snapshots) {
|
|
134
|
+
if (content === null)
|
|
135
|
+
(0, node_fs_1.rmSync)(path, { force: true });
|
|
136
|
+
else
|
|
137
|
+
(0, node_fs_1.writeFileSync)(path, content);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
return plan;
|
|
142
|
+
}
|
package/dist/remove.js
CHANGED
|
@@ -6,7 +6,6 @@ const node_path_1 = require("node:path");
|
|
|
6
6
|
const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
|
|
7
7
|
const add_1 = require("./add");
|
|
8
8
|
const lockfile_1 = require("./lockfile");
|
|
9
|
-
const toolchain_1 = require("./toolchain");
|
|
10
9
|
function readModuleManifest(moduleDir, vars) {
|
|
11
10
|
const source = (0, node_fs_1.readFileSync)((0, node_path_1.join)(moduleDir, "module.manifest.json"), "utf8");
|
|
12
11
|
return JSON.parse(vars ? (0, podokit_template_engine_1.renderTokens)(source, vars) : source);
|
|
@@ -51,7 +50,7 @@ function removeModule(options) {
|
|
|
51
50
|
}
|
|
52
51
|
const vars = {
|
|
53
52
|
projectName: projectName(projectRoot),
|
|
54
|
-
...(0,
|
|
53
|
+
...(0, lockfile_1.manifestTemplateVars)(manifest),
|
|
55
54
|
};
|
|
56
55
|
const targetManifest = readModuleManifest(moduleDir, vars);
|
|
57
56
|
// Resolve the manifests of the OTHER installed modules once — used for the
|
|
@@ -64,7 +63,11 @@ function removeModule(options) {
|
|
|
64
63
|
})
|
|
65
64
|
.filter((m) => m !== null);
|
|
66
65
|
// Guard: refuse if another installed module requires this one.
|
|
67
|
-
const
|
|
66
|
+
const selectedCapabilities = Object.entries(targetManifest.provides ?? {})
|
|
67
|
+
.filter(([capability, provider]) => manifest.providers[capability] === provider)
|
|
68
|
+
.map(([capability]) => capability);
|
|
69
|
+
const dependents = others.filter((other) => (other.manifest.requires ?? []).includes(module)
|
|
70
|
+
|| (other.manifest.requiresCapabilities ?? []).some((capability) => selectedCapabilities.includes(capability))).map((other) => other.name);
|
|
68
71
|
if (dependents.length) {
|
|
69
72
|
throw new Error(`Cannot remove "${module}": required by ${dependents.join(", ")}. Remove ${dependents.length > 1 ? "those" : "that"} first.`);
|
|
70
73
|
}
|
|
@@ -5,19 +5,25 @@ by [PodoKit](https://github.com/podosoft-dev/podokit).
|
|
|
5
5
|
|
|
6
6
|
## Project overview
|
|
7
7
|
|
|
8
|
-
A minimal Bun 1.4 workspace starter
|
|
9
|
-
|
|
8
|
+
A minimal Bun 1.4 workspace starter with placeholder API/web scripts. Build your
|
|
9
|
+
own foundation here. Modules requiring the fullstack service registry, provider
|
|
10
|
+
consumers, or injection points cannot be added directly to this skeleton.
|
|
11
|
+
|
|
12
|
+
For a simple working full-stack app, create `fullstack` or `todo` with SQLite,
|
|
13
|
+
memory cache/events, local files, and local jobs. Those templates support local
|
|
14
|
+
providers independently of template choice and need one API process. See
|
|
15
|
+
[Getting Started](https://github.com/podosoft-dev/podokit/blob/main/docs/getting-started.md).
|
|
10
16
|
|
|
11
17
|
## Commands
|
|
12
18
|
|
|
13
19
|
```bash
|
|
14
20
|
{{packageManager}} install
|
|
15
|
-
{{rootRun}} dev # runs workspace dev scripts
|
|
16
21
|
{{rootRun}} build
|
|
17
22
|
{{rootRun}} lint # type-check
|
|
18
23
|
{{rootRun}} test
|
|
19
24
|
```
|
|
20
25
|
|
|
26
|
+
The generated scripts are placeholders; implement them before running the app.
|
|
21
27
|
## Code style
|
|
22
28
|
|
|
23
29
|
- TypeScript `strict`. **No `any`** (use `unknown` + narrowing), no `@ts-ignore`.
|