@podosoft/podokit 1.4.2 → 1.6.0
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 +2 -1
- package/dist/app-proxy-timeouts.d.ts +27 -0
- package/dist/app-proxy-timeouts.js +180 -0
- package/dist/assemble.d.ts +5 -2
- package/dist/assemble.js +14 -4
- package/dist/deploy-checks.d.ts +44 -0
- package/dist/deploy-checks.js +267 -0
- package/dist/deploy-compose-profile.d.ts +6 -2
- package/dist/deploy-compose-profile.js +15 -6
- package/dist/deploy-compose-render.d.ts +12 -4
- package/dist/deploy-compose-render.js +47 -17
- package/dist/deploy-compose.d.ts +2 -5
- package/dist/deploy-compose.js +7 -2
- package/dist/deploy-profile.d.ts +6 -2
- package/dist/deploy-profile.js +11 -7
- package/dist/deploy-render.d.ts +4 -1
- package/dist/deploy-render.js +28 -20
- package/dist/deploy-schema.d.ts +61 -0
- package/dist/deploy-schema.js +157 -1
- package/dist/deploy.d.ts +2 -5
- package/dist/deploy.js +5 -2
- package/dist/dev.d.ts +21 -0
- package/dist/dev.js +78 -3
- package/dist/eject.js +2 -0
- package/dist/index.js +85 -5
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +5 -2
- package/dist/lockfile.d.ts +24 -0
- package/dist/lockfile.js +77 -5
- package/dist/provider.d.ts +1 -0
- package/dist/provider.js +12 -4
- package/dist/remove.js +1 -1
- package/dist/template-files.d.ts +42 -0
- package/dist/template-files.js +102 -0
- package/dist/templates/fullstack/AGENTS.md +44 -7
- package/dist/templates/fullstack/README.md +8 -7
- package/dist/templates/fullstack/apps/api/package.json +4 -4
- package/dist/templates/fullstack/apps/api/scripts/build.extensions.mjs +6 -0
- package/dist/templates/fullstack/apps/api/scripts/build.mjs +65 -16
- package/dist/templates/fullstack/apps/api/scripts/run-migrations.mjs +7 -3
- package/dist/templates/fullstack/apps/api/scripts/verify-api-contract.ts +5 -1
- package/dist/templates/fullstack/apps/api/src/app.extensions.ts +7 -4
- package/dist/templates/fullstack/apps/api/src/app.spec.ts +22 -0
- package/dist/templates/fullstack/apps/api/src/app.ts +47 -28
- package/dist/templates/fullstack/apps/api/src/build.spec.ts +118 -0
- package/dist/templates/fullstack/apps/api/src/config/env.extensions.ts +20 -0
- package/dist/templates/fullstack/apps/api/src/config/env.validation.spec.ts +76 -0
- package/dist/templates/fullstack/apps/api/src/config/env.validation.ts +46 -3
- package/dist/templates/fullstack/apps/api/src/config/provider-selection.spec.ts +53 -0
- package/dist/templates/fullstack/apps/api/src/config/provider-selection.ts +79 -0
- package/dist/templates/fullstack/apps/api/src/config/providers.ts +8 -2
- package/dist/templates/fullstack/apps/api/src/config/server-options.spec.ts +17 -6
- package/dist/templates/fullstack/apps/api/src/config/server-options.ts +5 -8
- package/dist/templates/fullstack/apps/api/src/core/api-contract.extensions.ts +6 -0
- package/dist/templates/fullstack/apps/api/src/core/api-contract.spec.ts +29 -0
- package/dist/templates/fullstack/apps/api/src/core/api-contract.ts +39 -2
- package/dist/templates/fullstack/apps/api/src/core/client-address.spec.ts +82 -0
- package/dist/templates/fullstack/apps/api/src/core/client-address.ts +68 -0
- package/dist/templates/fullstack/apps/api/src/core/services.ts +43 -0
- package/dist/templates/fullstack/apps/api/src/database/data-source.spec.ts +27 -0
- package/dist/templates/fullstack/apps/api/src/database/data-source.ts +7 -10
- package/dist/templates/fullstack/apps/api/src/database/database.spec.ts +12 -0
- package/dist/templates/fullstack/apps/api/src/database/database.ts +2 -3
- package/dist/templates/fullstack/apps/api/src/database/entities.ts +12 -0
- package/dist/templates/fullstack/apps/api/src/database/migrate.ts +12 -2
- package/dist/templates/fullstack/apps/api/src/database/migration-files.spec.ts +116 -0
- package/dist/templates/fullstack/apps/api/src/database/migration-files.ts +134 -0
- package/dist/templates/fullstack/apps/api/src/database/migration-readiness.spec.ts +36 -0
- package/dist/templates/fullstack/apps/api/src/database/migration-readiness.ts +25 -0
- package/dist/templates/fullstack/apps/api/src/database/run-migrations.spec.ts +12 -1
- package/dist/templates/fullstack/apps/api/src/database/sqlite-migrator.ts +2 -36
- package/dist/templates/fullstack/apps/api/src/main.ts +2 -2
- package/dist/templates/fullstack/apps/api/tsconfig.json +2 -0
- package/dist/templates/fullstack/apps/web/Dockerfile +4 -0
- package/dist/templates/fullstack/apps/web/package.json +1 -1
- package/dist/templates/fullstack/apps/web/src/app.css +3 -0
- package/dist/templates/fullstack/apps/web/src/app.extensions.css +11 -0
- package/dist/templates/fullstack/apps/web/src/lib/components/site-runtime.svelte +3 -1
- package/dist/templates/fullstack/apps/web/src/lib/server/api.test.ts +19 -0
- package/dist/templates/fullstack/apps/web/src/lib/server/api.ts +4 -1
- package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.test.ts +370 -36
- package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.ts +250 -41
- package/dist/templates/fullstack/apps/web/src/lib/server/body-limits.ts +5 -2
- package/dist/templates/fullstack/apps/web/src/lib/server/proxy-timeouts.ts +23 -0
- package/dist/templates/fullstack/apps/web/tsconfig.json +1 -1
- package/dist/templates/fullstack/apps/web/vite.config.test.ts +33 -0
- package/dist/templates/fullstack/apps/web/vite.config.ts +55 -13
- package/dist/templates/fullstack/apps/web/vite.extensions.ts +23 -0
- package/dist/templates/fullstack/compose.dev.yaml +7 -4
- package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/SKILL.md +2 -2
- package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/references/profile.md +12 -3
- package/dist/templates/fullstack/dot-claude/skills/podokit-update/SKILL.md +5 -2
- package/dist/templates/fullstack/dot-env.docker.example +7 -2
- package/dist/templates/fullstack/dot-env.example +21 -1
- package/dist/templates/fullstack/dot-podokit/dev.json +1 -1
- package/dist/templates/fullstack/infra/k3s/configmap.yaml +4 -0
- package/dist/templates/fullstack/tests/README.md +40 -1
- package/dist/templates/fullstack/tests/helpers/app-paths.ts +18 -0
- package/dist/templates/fullstack/tests/helpers/paths.ts +33 -0
- package/dist/templates/fullstack/tests/package.json +2 -2
- package/dist/templates/fullstack/tests/playwright.config.ts +7 -3
- package/dist/templates/fullstack/tests/playwright.extensions.ts +88 -1
- package/dist/templates/fullstack/tests/ui/smoke.ui.spec.ts +5 -4
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/account-sessions/account-sessions.routes.spec.ts +152 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/account-sessions/account-sessions.routes.ts +112 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/admin-sessions/admin-sessions.revoker.ts +24 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/admin-sessions/admin-sessions.service.spec.ts +18 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/admin-sessions/admin-sessions.service.ts +36 -13
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/admin.module.ts +13 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.test.ts +37 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +15 -6
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/account-sessions.test.ts +33 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/account-sessions.ts +34 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flag-settings.test.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flag-settings.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flags.ts +12 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-nav.ts +11 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/nav.test.ts +39 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/nav.ts +35 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/registry.svelte.ts +15 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-page.svelte +73 -39
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/admin-sidebar.svelte +2 -23
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +7 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.test.ts +30 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/impersonation-banner.svelte +3 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/settings/app-flags-settings.svelte +65 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +32 -23
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.test.ts +27 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-footer.svelte +4 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-runtime.svelte +3 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-toolbar.svelte +3 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/en.json +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/ko.json +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/app-hooks.ts +11 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/non-indexable-paths.ts +8 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.test.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.ts +5 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(admin)/admin/settings/+page.svelte +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.server.ts +2 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.svelte +1 -5
- package/dist/templates/modules/admin-dashboard/files/dot-claude/skills/podokit-data-table/SKILL.md +2 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/account-sessions.api.spec.ts +55 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +159 -170
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +72 -81
- package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +1 -6
- package/dist/templates/modules/admin-dashboard/files/tests/api/profile-image.api.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/site-settings.api.spec.ts +43 -53
- package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +29 -1
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +24 -6
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/app-accounts.ts +24 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/devices.ts +55 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/disposable-users.ts +5 -3
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/mailpit.ts +3 -9
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +2 -4
- package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +11 -5
- package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +3 -10
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account-flows.ui.spec.ts +4 -5
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account-sessions.ui.spec.ts +46 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +62 -59
- package/dist/templates/modules/admin-dashboard/files/tests/ui/app-flags.ui.spec.ts +63 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/appearance.ui.spec.ts +12 -16
- package/dist/templates/modules/admin-dashboard/files/tests/ui/audit.ui.spec.ts +3 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +32 -35
- package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +4 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +1 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +1 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +82 -91
- package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +6 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +16 -20
- package/dist/templates/modules/admin-dashboard/files/tests/ui/impersonation.ui.spec.ts +6 -3
- package/dist/templates/modules/admin-dashboard/files/tests/ui/login-methods.ui.spec.ts +2 -3
- package/dist/templates/modules/admin-dashboard/files/tests/ui/oidc.ui.spec.ts +2 -4
- package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +5 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +7 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/same-origin-requests.ui.spec.ts +89 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +5 -4
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings-dialogs.ui.spec.ts +67 -73
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +84 -93
- package/dist/templates/modules/admin-dashboard/files/tests/ui/table-toolbar.ui.spec.ts +33 -7
- package/dist/templates/modules/admin-dashboard/files/tests/ui/theme-mode.ui.spec.ts +29 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +51 -54
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +10 -9
- package/dist/templates/modules/admin-dashboard/module.manifest.json +7 -2
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-hook.ts +2 -1
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +5 -1
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.spec.ts +33 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +20 -1
- package/dist/templates/modules/audit-log/module.manifest.json +15 -3
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-app-name.spec.ts +21 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-config-store.ts +13 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-environment.ts +9 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +19 -3
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.module.ts +6 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.runtime.spec.ts +10 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.service.spec.ts +54 -6
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.service.ts +26 -4
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +15 -8
- package/dist/templates/modules/auth/files/apps/api/src/auth/revoke-other-sessions.spec.ts +56 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/revoke-other-sessions.ts +45 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/schema-recheck.spec.ts +86 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/schema-recheck.ts +44 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +2 -1
- package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +3 -1
- package/dist/templates/modules/auth/files/apps/api/src/settings/app-flags.ts +9 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.spec.ts +43 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +30 -5
- package/dist/templates/modules/auth/module.manifest.json +8 -2
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.spec.ts +57 -1
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.ts +93 -9
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.spec.ts +40 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +52 -5
- package/dist/templates/modules/bullmq/files/apps/api/src/main-worker.ts +9 -7
- package/dist/templates/modules/bullmq/files/apps/api/src/worker.extensions.ts +13 -0
- package/dist/templates/modules/bullmq/module.manifest.json +5 -1
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.worker.spec.ts +2 -0
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.spec.ts +87 -1
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.ts +93 -3
- package/dist/templates/modules/jobs-local/module.manifest.json +3 -0
- package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.spec.ts +16 -0
- package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.ts +15 -10
- package/dist/templates/modules/mailer/module.manifest.json +1 -1
- package/dist/templates/modules/object-storage-local/files/tests/api/storage-local.api.spec.ts +1 -1
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -4
- package/dist/templates/modules/object-storage-s3/files/tests/api/storage.api.spec.ts +1 -1
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.config.spec.ts +2 -59
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.config.ts +0 -65
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.identity.spec.ts +82 -21
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.identity.ts +54 -15
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.spec.ts +43 -5
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +10 -2
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.routes.spec.ts +61 -3
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.routes.ts +48 -15
- package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +63 -27
- package/dist/templates/modules/rate-limit/module.manifest.json +6 -9
- package/dist/templates/modules/sse/files/tests/api/events.api.spec.ts +1 -1
- package/dist/templates/todo/AGENTS.md +46 -6
- package/dist/templates/todo/README.md +6 -4
- package/dist/text-diff.d.ts +12 -0
- package/dist/text-diff.js +111 -0
- package/dist/update.d.ts +61 -3
- package/dist/update.js +152 -27
- package/package.json +2 -2
- package/dist/templates/modules/admin-dashboard/files/tests/api/exclusive.api.spec.ts +0 -75
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/exclusive.ts +0 -80
package/dist/deploy-schema.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AppProxyTimeout, AppProxyTimeouts } from "./app-proxy-timeouts";
|
|
1
2
|
/**
|
|
2
3
|
* Validation primitives shared by every deployment driver.
|
|
3
4
|
*
|
|
@@ -30,6 +31,23 @@ export declare const SENSITIVE_KEY: RegExp;
|
|
|
30
31
|
export declare const STABLE_SEMVER_TAG_PATTERN = "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$";
|
|
31
32
|
/** Keys PodoKit derives itself, so a profile may not set them. */
|
|
32
33
|
export declare const MANAGED_RUNTIME_CONFIG_KEYS: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Keys that decide which forwarded address web and API record. They follow from
|
|
36
|
+
* the proxies in front of the deployment, so a profile states that one number and
|
|
37
|
+
* PodoKit renders the four values that agree with it.
|
|
38
|
+
*/
|
|
39
|
+
export declare const CLIENT_ADDRESS_KEYS: string[];
|
|
40
|
+
export declare function parseEdgeProxies(exposure: Record<string, unknown>, fallback: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* What the web server and the API trust, given how many proxies each request to
|
|
43
|
+
* the web server has passed. Behind at least one, the web server takes its origin
|
|
44
|
+
* from X-Forwarded-Proto and X-Forwarded-Host and the client from the
|
|
45
|
+
* X-Forwarded-For entry the outermost trusted proxy wrote (XFF_DEPTH); with none
|
|
46
|
+
* it trusts no forwarded header. It forwards that one address to the API, so the
|
|
47
|
+
* API always trusts one hop: the web proxy, the Compose gateway, or an Ingress
|
|
48
|
+
* that trusts no proxy in front of it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function proxyTrustConfig(webProxyHops: number, trustForwardedOrigin?: boolean): Record<string, string>;
|
|
33
51
|
export declare function containsUrlCredentials(value: string): boolean;
|
|
34
52
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
35
53
|
export declare function assertOnlyKeys(value: Record<string, unknown>, allowed: string[], field: string): void;
|
|
@@ -50,6 +68,49 @@ export declare function validateImage(value: string, field: string): void;
|
|
|
50
68
|
export declare function validateImageRepository(value: string, field: string): void;
|
|
51
69
|
export declare function parseReleaseProfile(release: Record<string, unknown>): ReleaseProfile;
|
|
52
70
|
export declare function parseRuntimeConfig(runtimeConfig: Record<string, unknown>): Record<string, string>;
|
|
71
|
+
/**
|
|
72
|
+
* An application-owned PostgreSQL pool each API or worker process opens beside
|
|
73
|
+
* PodoKit's own: `sizeKey` names the runtimeConfig key that caps it, and
|
|
74
|
+
* `extraConnections` counts connections it opens beyond that cap, such as one to
|
|
75
|
+
* check a server that stopped answering.
|
|
76
|
+
*/
|
|
77
|
+
export interface AppConnectionPool {
|
|
78
|
+
sizeKey: string;
|
|
79
|
+
extraConnections: number;
|
|
80
|
+
}
|
|
81
|
+
/** What the connection budget needs to know about the database, beyond its mode. */
|
|
82
|
+
export interface PostgresBudgetProfile {
|
|
83
|
+
/** The server's max_connections; PodoKit sets it on a database it runs. */
|
|
84
|
+
maxConnections?: number;
|
|
85
|
+
appPools?: AppConnectionPool[];
|
|
86
|
+
}
|
|
87
|
+
export declare function parsePostgresBudget(value: Record<string, unknown>, field: string, runtimeConfig: Record<string, string>): PostgresBudgetProfile;
|
|
88
|
+
/** Bun closes silent connections on a 4-second tick, up to 4 seconds before their idle timeout. */
|
|
89
|
+
export declare const IDLE_TICK_SECONDS = 4;
|
|
90
|
+
export declare const MAX_IDLE_TIMEOUT_SECONDS = 255;
|
|
91
|
+
export declare const IDLE_TIMEOUT_KEYS: readonly ["CONNECTION_IDLE_TIMEOUT", "API_IDLE_TIMEOUT"];
|
|
92
|
+
/** The longest the /api proxy waits for response headers, and what sets it. */
|
|
93
|
+
export interface LongestProxyWait {
|
|
94
|
+
/** Milliseconds, or null for a route without a deadline. */
|
|
95
|
+
timeoutMs: number | null;
|
|
96
|
+
source: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* BACKEND_PROXY_TIMEOUT_MS, or the application's longest APP_PROXY_TIMEOUTS
|
|
100
|
+
* entry when that waits longer; an entry without a deadline wins outright.
|
|
101
|
+
*/
|
|
102
|
+
export declare function longestProxyWait(runtimeConfig: Record<string, string>, entries: AppProxyTimeout[]): LongestProxyWait;
|
|
103
|
+
export declare function describeProxyWait(wait: LongestProxyWait): string;
|
|
104
|
+
/** Whether an idle timeout keeps a silent connection open for the whole wait. */
|
|
105
|
+
export declare function idleTimeoutCovers(seconds: number, wait: LongestProxyWait): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* The idle timeouts of the web server (CONNECTION_IDLE_TIMEOUT) and the API
|
|
108
|
+
* (API_IDLE_TIMEOUT), both run by Bun. Each is the profile's own value, or else
|
|
109
|
+
* the fewest seconds that keep a silent connection open past the /api proxy's
|
|
110
|
+
* longest wait: 35 for the default 30000 ms, and 0 (off) when an application
|
|
111
|
+
* route has no deadline.
|
|
112
|
+
*/
|
|
113
|
+
export declare function idleTimeoutConfig(runtimeConfig: Record<string, string>, proxyTimeouts: AppProxyTimeouts): Record<string, string>;
|
|
53
114
|
export declare function validateHttpsOrigin(value: string): void;
|
|
54
115
|
export declare function parseVerification(verification: Record<string, unknown>): VerificationProfile;
|
|
55
116
|
export declare function profileDirectory(projectRoot: string): string;
|
package/dist/deploy-schema.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MANAGED_RUNTIME_CONFIG_KEYS = exports.STABLE_SEMVER_TAG_PATTERN = exports.SENSITIVE_KEY = exports.DNS_LABEL = exports.PROFILE_NAME = void 0;
|
|
3
|
+
exports.IDLE_TIMEOUT_KEYS = exports.MAX_IDLE_TIMEOUT_SECONDS = exports.IDLE_TICK_SECONDS = exports.CLIENT_ADDRESS_KEYS = exports.MANAGED_RUNTIME_CONFIG_KEYS = exports.STABLE_SEMVER_TAG_PATTERN = exports.SENSITIVE_KEY = exports.DNS_LABEL = exports.PROFILE_NAME = void 0;
|
|
4
|
+
exports.parseEdgeProxies = parseEdgeProxies;
|
|
5
|
+
exports.proxyTrustConfig = proxyTrustConfig;
|
|
4
6
|
exports.containsUrlCredentials = containsUrlCredentials;
|
|
5
7
|
exports.isRecord = isRecord;
|
|
6
8
|
exports.assertOnlyKeys = assertOnlyKeys;
|
|
@@ -17,6 +19,11 @@ exports.validateImage = validateImage;
|
|
|
17
19
|
exports.validateImageRepository = validateImageRepository;
|
|
18
20
|
exports.parseReleaseProfile = parseReleaseProfile;
|
|
19
21
|
exports.parseRuntimeConfig = parseRuntimeConfig;
|
|
22
|
+
exports.parsePostgresBudget = parsePostgresBudget;
|
|
23
|
+
exports.longestProxyWait = longestProxyWait;
|
|
24
|
+
exports.describeProxyWait = describeProxyWait;
|
|
25
|
+
exports.idleTimeoutCovers = idleTimeoutCovers;
|
|
26
|
+
exports.idleTimeoutConfig = idleTimeoutConfig;
|
|
20
27
|
exports.validateHttpsOrigin = validateHttpsOrigin;
|
|
21
28
|
exports.parseVerification = parseVerification;
|
|
22
29
|
exports.profileDirectory = profileDirectory;
|
|
@@ -35,6 +42,43 @@ exports.MANAGED_RUNTIME_CONFIG_KEYS = [
|
|
|
35
42
|
"PROTOCOL_HEADER",
|
|
36
43
|
"HOST_HEADER",
|
|
37
44
|
];
|
|
45
|
+
/**
|
|
46
|
+
* Keys that decide which forwarded address web and API record. They follow from
|
|
47
|
+
* the proxies in front of the deployment, so a profile states that one number and
|
|
48
|
+
* PodoKit renders the four values that agree with it.
|
|
49
|
+
*/
|
|
50
|
+
exports.CLIENT_ADDRESS_KEYS = ["ADDRESS_HEADER", "XFF_DEPTH", "TRUSTED_PROXY_HOPS", "TRUSTED_PROXY_HEADER"];
|
|
51
|
+
/** Edge proxies are counted, not configured; more than this is a typo. */
|
|
52
|
+
const MAX_EDGE_PROXIES = 8;
|
|
53
|
+
function parseEdgeProxies(exposure, fallback) {
|
|
54
|
+
const value = exposure.edgeProxies ?? fallback;
|
|
55
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > MAX_EDGE_PROXIES) {
|
|
56
|
+
throw new Error(`Deployment profile field "exposure.edgeProxies" must be an integer from 0 to ${MAX_EDGE_PROXIES}.`);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* What the web server and the API trust, given how many proxies each request to
|
|
62
|
+
* the web server has passed. Behind at least one, the web server takes its origin
|
|
63
|
+
* from X-Forwarded-Proto and X-Forwarded-Host and the client from the
|
|
64
|
+
* X-Forwarded-For entry the outermost trusted proxy wrote (XFF_DEPTH); with none
|
|
65
|
+
* it trusts no forwarded header. It forwards that one address to the API, so the
|
|
66
|
+
* API always trusts one hop: the web proxy, the Compose gateway, or an Ingress
|
|
67
|
+
* that trusts no proxy in front of it.
|
|
68
|
+
*/
|
|
69
|
+
function proxyTrustConfig(webProxyHops, trustForwardedOrigin = webProxyHops > 0) {
|
|
70
|
+
return {
|
|
71
|
+
...(trustForwardedOrigin ? { PROTOCOL_HEADER: "x-forwarded-proto", HOST_HEADER: "x-forwarded-host" } : {}),
|
|
72
|
+
...(webProxyHops > 0 ? { ADDRESS_HEADER: "x-forwarded-for", XFF_DEPTH: String(webProxyHops) } : {}),
|
|
73
|
+
TRUSTED_PROXY_HOPS: "1",
|
|
74
|
+
TRUSTED_PROXY_HEADER: "x-forwarded-for",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Keys the generated API no longer reads; it refuses to start while they are set. */
|
|
78
|
+
const RENAMED_RUNTIME_CONFIG_KEYS = {
|
|
79
|
+
RATE_LIMIT_TRUSTED_PROXY_HOPS: "TRUSTED_PROXY_HOPS",
|
|
80
|
+
RATE_LIMIT_PROXY_HEADER: "TRUSTED_PROXY_HEADER",
|
|
81
|
+
};
|
|
38
82
|
function containsUrlCredentials(value) {
|
|
39
83
|
if (!value.includes("://"))
|
|
40
84
|
return false;
|
|
@@ -183,6 +227,14 @@ function parseRuntimeConfig(runtimeConfig) {
|
|
|
183
227
|
if (exports.MANAGED_RUNTIME_CONFIG_KEYS.includes(key)) {
|
|
184
228
|
throw new Error(`Deployment runtimeConfig key "${key}" is managed by PodoKit.`);
|
|
185
229
|
}
|
|
230
|
+
if (exports.CLIENT_ADDRESS_KEYS.includes(key)) {
|
|
231
|
+
throw new Error(`Deployment runtimeConfig key "${key}" is derived from exposure.edgeProxies, the number of reverse ` +
|
|
232
|
+
"proxies in front of the deployment; remove it and set exposure.edgeProxies instead.");
|
|
233
|
+
}
|
|
234
|
+
const renamed = RENAMED_RUNTIME_CONFIG_KEYS[key];
|
|
235
|
+
if (renamed) {
|
|
236
|
+
throw new Error(`Deployment runtimeConfig key "${key}" was renamed to "${renamed}".`);
|
|
237
|
+
}
|
|
186
238
|
if (typeof entry !== "string") {
|
|
187
239
|
throw new Error(`Deployment runtimeConfig value "${key}" must be a string.`);
|
|
188
240
|
}
|
|
@@ -193,6 +245,110 @@ function parseRuntimeConfig(runtimeConfig) {
|
|
|
193
245
|
}
|
|
194
246
|
return parsed;
|
|
195
247
|
}
|
|
248
|
+
/** PostgreSQL's own ceiling for max_connections. */
|
|
249
|
+
const MAX_POSTGRES_CONNECTIONS = 262_143;
|
|
250
|
+
/** Pools whose sizes the connection budget already reads. */
|
|
251
|
+
const PODOKIT_POOL_KEYS = ["DB_POOL_MAX", "DB_AUTH_POOL_MAX"];
|
|
252
|
+
function parsePostgresBudget(value, field, runtimeConfig) {
|
|
253
|
+
const parsed = {};
|
|
254
|
+
if (value.maxConnections !== undefined) {
|
|
255
|
+
const maxConnections = value.maxConnections;
|
|
256
|
+
if (typeof maxConnections !== "number" ||
|
|
257
|
+
!Number.isInteger(maxConnections) ||
|
|
258
|
+
maxConnections < 4 ||
|
|
259
|
+
maxConnections > MAX_POSTGRES_CONNECTIONS) {
|
|
260
|
+
throw new Error(`Deployment profile field "${field}.maxConnections" must be an integer from 4 to ${MAX_POSTGRES_CONNECTIONS}.`);
|
|
261
|
+
}
|
|
262
|
+
parsed.maxConnections = maxConnections;
|
|
263
|
+
}
|
|
264
|
+
if (value.appPools !== undefined) {
|
|
265
|
+
if (!Array.isArray(value.appPools)) {
|
|
266
|
+
throw new Error(`Deployment profile field "${field}.appPools" must be an array.`);
|
|
267
|
+
}
|
|
268
|
+
const seen = new Set();
|
|
269
|
+
parsed.appPools = value.appPools.map((entry, index) => {
|
|
270
|
+
const entryField = `${field}.appPools[${index}]`;
|
|
271
|
+
if (!isRecord(entry))
|
|
272
|
+
throw new Error(`Deployment profile field "${entryField}" must be an object.`);
|
|
273
|
+
assertOnlyKeys(entry, ["sizeKey", "extraConnections"], entryField);
|
|
274
|
+
const sizeKey = requiredString(entry, "sizeKey");
|
|
275
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(sizeKey) || PODOKIT_POOL_KEYS.includes(sizeKey) || seen.has(sizeKey)) {
|
|
276
|
+
throw new Error(`Deployment profile field "${entryField}.sizeKey" must name another pool's runtimeConfig key, once; ` +
|
|
277
|
+
`${PODOKIT_POOL_KEYS.join(" and ")} are counted already.`);
|
|
278
|
+
}
|
|
279
|
+
seen.add(sizeKey);
|
|
280
|
+
if (!/^[1-9][0-9]*$/.test(runtimeConfig[sizeKey] ?? "")) {
|
|
281
|
+
throw new Error(`Deployment profile field "${entryField}.sizeKey" names ${sizeKey}, which runtimeConfig must set to a positive integer.`);
|
|
282
|
+
}
|
|
283
|
+
const extraConnections = entry.extraConnections ?? 0;
|
|
284
|
+
if (typeof extraConnections !== "number" || !Number.isInteger(extraConnections) || extraConnections < 0) {
|
|
285
|
+
throw new Error(`Deployment profile field "${entryField}.extraConnections" must be a non-negative integer.`);
|
|
286
|
+
}
|
|
287
|
+
return { sizeKey, extraConnections };
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return parsed;
|
|
291
|
+
}
|
|
292
|
+
/** Bun closes silent connections on a 4-second tick, up to 4 seconds before their idle timeout. */
|
|
293
|
+
exports.IDLE_TICK_SECONDS = 4;
|
|
294
|
+
exports.MAX_IDLE_TIMEOUT_SECONDS = 255;
|
|
295
|
+
const DEFAULT_BACKEND_PROXY_TIMEOUT_MS = 30_000;
|
|
296
|
+
exports.IDLE_TIMEOUT_KEYS = ["CONNECTION_IDLE_TIMEOUT", "API_IDLE_TIMEOUT"];
|
|
297
|
+
/**
|
|
298
|
+
* BACKEND_PROXY_TIMEOUT_MS, or the application's longest APP_PROXY_TIMEOUTS
|
|
299
|
+
* entry when that waits longer; an entry without a deadline wins outright.
|
|
300
|
+
*/
|
|
301
|
+
function longestProxyWait(runtimeConfig, entries) {
|
|
302
|
+
const proxyTimeout = runtimeConfig.BACKEND_PROXY_TIMEOUT_MS?.trim();
|
|
303
|
+
let longest = {
|
|
304
|
+
timeoutMs: proxyTimeout && /^\d+$/.test(proxyTimeout) ? Number(proxyTimeout) : DEFAULT_BACKEND_PROXY_TIMEOUT_MS,
|
|
305
|
+
source: "BACKEND_PROXY_TIMEOUT_MS",
|
|
306
|
+
};
|
|
307
|
+
for (const entry of entries) {
|
|
308
|
+
if (longest.timeoutMs !== null && (entry.timeoutMs === null || entry.timeoutMs > longest.timeoutMs)) {
|
|
309
|
+
longest = { timeoutMs: entry.timeoutMs, source: `APP_PROXY_TIMEOUTS entry ${entry.pathPrefix}` };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return longest;
|
|
313
|
+
}
|
|
314
|
+
function describeProxyWait(wait) {
|
|
315
|
+
return `${wait.timeoutMs === null ? "no deadline" : `${wait.timeoutMs} ms`}, ${wait.source}`;
|
|
316
|
+
}
|
|
317
|
+
/** Whether an idle timeout keeps a silent connection open for the whole wait. */
|
|
318
|
+
function idleTimeoutCovers(seconds, wait) {
|
|
319
|
+
return seconds === 0 || (wait.timeoutMs !== null && (seconds - exports.IDLE_TICK_SECONDS) * 1000 > wait.timeoutMs);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* The idle timeouts of the web server (CONNECTION_IDLE_TIMEOUT) and the API
|
|
323
|
+
* (API_IDLE_TIMEOUT), both run by Bun. Each is the profile's own value, or else
|
|
324
|
+
* the fewest seconds that keep a silent connection open past the /api proxy's
|
|
325
|
+
* longest wait: 35 for the default 30000 ms, and 0 (off) when an application
|
|
326
|
+
* route has no deadline.
|
|
327
|
+
*/
|
|
328
|
+
function idleTimeoutConfig(runtimeConfig, proxyTimeouts) {
|
|
329
|
+
const config = {};
|
|
330
|
+
for (const name of exports.IDLE_TIMEOUT_KEYS) {
|
|
331
|
+
const configured = runtimeConfig[name];
|
|
332
|
+
if (configured !== undefined) {
|
|
333
|
+
config[name] = configured;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if ("error" in proxyTimeouts)
|
|
337
|
+
throw new Error(proxyTimeouts.error);
|
|
338
|
+
const wait = longestProxyWait(runtimeConfig, proxyTimeouts.entries);
|
|
339
|
+
if (wait.timeoutMs === null) {
|
|
340
|
+
config[name] = "0";
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const derived = Math.floor(wait.timeoutMs / 1000) + exports.IDLE_TICK_SECONDS + 1;
|
|
344
|
+
if (derived > exports.MAX_IDLE_TIMEOUT_SECONDS) {
|
|
345
|
+
throw new Error(`The /api proxy waits up to ${wait.timeoutMs} ms (${wait.source}), longer than the longest idle timeout ` +
|
|
346
|
+
`(${exports.MAX_IDLE_TIMEOUT_SECONDS} seconds); set ${name} to "0" to turn it off.`);
|
|
347
|
+
}
|
|
348
|
+
config[name] = String(derived);
|
|
349
|
+
}
|
|
350
|
+
return config;
|
|
351
|
+
}
|
|
196
352
|
function validateHttpsOrigin(value) {
|
|
197
353
|
let parsed;
|
|
198
354
|
try {
|
package/dist/deploy.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import { type DeployFinding } from "./deploy-checks";
|
|
1
2
|
import type { CommandRunner } from "./dev";
|
|
2
3
|
import { type DeploymentImages } from "./deploy-render";
|
|
3
|
-
export
|
|
4
|
-
code: string;
|
|
5
|
-
ok: boolean;
|
|
6
|
-
message: string;
|
|
7
|
-
}
|
|
4
|
+
export type DeploymentDoctorFinding = DeployFinding;
|
|
8
5
|
export interface DeploymentPlanAction {
|
|
9
6
|
order: number;
|
|
10
7
|
kind: "dependencies" | "migration" | "application" | "verification" | "rollback";
|
package/dist/deploy.js
CHANGED
|
@@ -14,6 +14,8 @@ const node_fs_1 = require("node:fs");
|
|
|
14
14
|
const node_os_1 = require("node:os");
|
|
15
15
|
const node_path_1 = require("node:path");
|
|
16
16
|
const api_paths_1 = require("./api-paths");
|
|
17
|
+
const app_proxy_timeouts_1 = require("./app-proxy-timeouts");
|
|
18
|
+
const deploy_checks_1 = require("./deploy-checks");
|
|
17
19
|
const deploy_profile_1 = require("./deploy-profile");
|
|
18
20
|
const deploy_render_1 = require("./deploy-render");
|
|
19
21
|
function defaultRunner(command, args, options) {
|
|
@@ -143,7 +145,7 @@ function apiSecretRequirements(profile) {
|
|
|
143
145
|
}
|
|
144
146
|
function doctorDeployment(projectRoot, profileName, runner = defaultRunner) {
|
|
145
147
|
const profile = (0, deploy_profile_1.loadDeploymentProfile)(projectRoot, profileName);
|
|
146
|
-
const findings =
|
|
148
|
+
const findings = (0, deploy_checks_1.profileFindings)(projectRoot, profile);
|
|
147
149
|
const fingerprint = inspectClusterFingerprint(profile.target.context, runner);
|
|
148
150
|
findings.push({
|
|
149
151
|
code: "cluster-fingerprint",
|
|
@@ -496,11 +498,12 @@ function planDeployment(projectRoot, profileName, release, runner = defaultRunne
|
|
|
496
498
|
warnings: [
|
|
497
499
|
"Application rollback does not reverse database migrations.",
|
|
498
500
|
"PersistentVolumeClaims and Kubernetes Secrets are never deleted by apply or rollback.",
|
|
501
|
+
...[(0, deploy_checks_1.connectionBudgetWarning)(profile, (0, deploy_checks_1.installedModules)(projectRoot))].filter((warning) => warning !== null),
|
|
499
502
|
],
|
|
500
503
|
};
|
|
501
504
|
const temporaryRoot = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "podokit-deploy-plan-"));
|
|
502
505
|
try {
|
|
503
|
-
const runtime = (0, deploy_render_1.renderDeployment)(temporaryRoot, profileName, profile, release, images, state.rolloutStateDigest);
|
|
506
|
+
const runtime = (0, deploy_render_1.renderDeployment)(temporaryRoot, profileName, profile, release, images, state.rolloutStateDigest, (0, app_proxy_timeouts_1.readAppProxyTimeouts)(projectRoot));
|
|
504
507
|
return { ...base, planHash: planDigest(base, runtime) };
|
|
505
508
|
}
|
|
506
509
|
finally {
|
package/dist/dev.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export interface DevConfig {
|
|
|
9
9
|
export interface DevRuntime {
|
|
10
10
|
alias: string;
|
|
11
11
|
config: DevConfig;
|
|
12
|
+
/** Browser origins of the development stack, for CORS_ORIGIN. */
|
|
13
|
+
origins: string[];
|
|
14
|
+
/** BETTER_AUTH_URL for the containers. */
|
|
15
|
+
authUrl: string;
|
|
12
16
|
projectRoot: string;
|
|
13
17
|
routeId: string;
|
|
14
18
|
runtimeComposePath: string;
|
|
@@ -22,8 +26,25 @@ export type CommandRunner = (command: string, args: string[], options: {
|
|
|
22
26
|
capture: boolean;
|
|
23
27
|
}) => CommandResult;
|
|
24
28
|
export declare function readDevConfig(projectRoot: string): DevConfig;
|
|
29
|
+
/**
|
|
30
|
+
* The origins a browser reaches the development stack on: the gateway
|
|
31
|
+
* hostname, the HTTPS tunnel origin when one is declared, and plain localhost
|
|
32
|
+
* for the compose file's own proxy.
|
|
33
|
+
*/
|
|
34
|
+
export declare function devOrigins(config: Pick<DevConfig, "hostname" | "publicUrl">): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Template variables for files that name the development host. They follow
|
|
37
|
+
* `.podokit/dev.json` when it exists and is valid, so a renamed host renders
|
|
38
|
+
* the same way it is routed; otherwise the project name's default.
|
|
39
|
+
*/
|
|
40
|
+
export declare function devTemplateVars(projectName: string, projectRoot?: string): Record<string, string>;
|
|
25
41
|
export declare function resolveDevRuntime(projectRoot: string): DevRuntime;
|
|
26
42
|
export declare function installedDevProfiles(projectRoot: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* The compose overlay `podo dev` adds. Besides the gateway network it sets
|
|
45
|
+
* CORS_ORIGIN and BETTER_AUTH_URL for web and api from `.podokit/dev.json`, so
|
|
46
|
+
* the browser origins follow the routed hostname whatever `.env.docker` says.
|
|
47
|
+
*/
|
|
27
48
|
export declare function renderRuntimeCompose(runtime: DevRuntime): string;
|
|
28
49
|
export declare function renderRoute(runtime: DevRuntime): string;
|
|
29
50
|
/**
|
package/dist/dev.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readDevConfig = readDevConfig;
|
|
4
|
+
exports.devOrigins = devOrigins;
|
|
5
|
+
exports.devTemplateVars = devTemplateVars;
|
|
4
6
|
exports.resolveDevRuntime = resolveDevRuntime;
|
|
5
7
|
exports.installedDevProfiles = installedDevProfiles;
|
|
6
8
|
exports.renderRuntimeCompose = renderRuntimeCompose;
|
|
@@ -106,6 +108,68 @@ function readDevConfig(projectRoot) {
|
|
|
106
108
|
apiPaths: (0, api_paths_1.parseApiPaths)(config.apiPaths ?? [], `${DEV_CONFIG}.apiPaths`, webSocketPaths),
|
|
107
109
|
};
|
|
108
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The origins a browser reaches the development stack on: the gateway
|
|
113
|
+
* hostname, the HTTPS tunnel origin when one is declared, and plain localhost
|
|
114
|
+
* for the compose file's own proxy.
|
|
115
|
+
*/
|
|
116
|
+
function devOrigins(config) {
|
|
117
|
+
return [
|
|
118
|
+
...new Set([
|
|
119
|
+
`http://${config.hostname}`,
|
|
120
|
+
...(config.publicUrl ? [new URL(config.publicUrl).origin] : []),
|
|
121
|
+
"http://localhost",
|
|
122
|
+
]),
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Template variables for files that name the development host. They follow
|
|
127
|
+
* `.podokit/dev.json` when it exists and is valid, so a renamed host renders
|
|
128
|
+
* the same way it is routed; otherwise the project name's default.
|
|
129
|
+
*/
|
|
130
|
+
function devTemplateVars(projectName, projectRoot) {
|
|
131
|
+
let config = { hostname: `${projectName}.localhost` };
|
|
132
|
+
const path = projectRoot ? (0, node_path_1.join)(projectRoot, DEV_CONFIG) : undefined;
|
|
133
|
+
if (path && (0, node_fs_1.existsSync)(path)) {
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
136
|
+
if (typeof parsed.hostname === "string") {
|
|
137
|
+
validateHostname(parsed.hostname);
|
|
138
|
+
if (parsed.publicUrl)
|
|
139
|
+
validatePublicUrl(parsed.publicUrl);
|
|
140
|
+
config = { hostname: parsed.hostname, publicUrl: parsed.publicUrl };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// podo dev reports an unreadable dev.json; rendering keeps the default.
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
devHostname: config.hostname,
|
|
149
|
+
devCorsOrigin: devOrigins(config).join(","),
|
|
150
|
+
devAuthUrl: `http://${config.hostname}`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* BETTER_AUTH_URL for the containers: the gateway origin, or the origin named
|
|
155
|
+
* by PODOKIT_DEV_AUTH_URL for an OAuth round trip through `publicUrl`.
|
|
156
|
+
*/
|
|
157
|
+
function devAuthUrl(config, origins) {
|
|
158
|
+
const requested = process.env.PODOKIT_DEV_AUTH_URL?.trim();
|
|
159
|
+
if (!requested)
|
|
160
|
+
return `http://${config.hostname}`;
|
|
161
|
+
let origin;
|
|
162
|
+
try {
|
|
163
|
+
origin = new URL(requested).origin;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
origin = "";
|
|
167
|
+
}
|
|
168
|
+
if (!origins.includes(origin) || origin === "http://localhost") {
|
|
169
|
+
throw new Error(`PODOKIT_DEV_AUTH_URL must be http://${config.hostname}${config.publicUrl ? ` or ${new URL(config.publicUrl).origin}` : ""} (from ${DEV_CONFIG}).`);
|
|
170
|
+
}
|
|
171
|
+
return origin;
|
|
172
|
+
}
|
|
109
173
|
function stableId(projectRoot) {
|
|
110
174
|
return (0, node_crypto_1.createHash)("sha256").update(projectRoot).digest("hex").slice(0, 10);
|
|
111
175
|
}
|
|
@@ -118,9 +182,12 @@ function resolveDevRuntime(projectRoot) {
|
|
|
118
182
|
const config = readDevConfig(root);
|
|
119
183
|
const id = stableId(root);
|
|
120
184
|
const projectSlug = slug(config.hostname.replace(/\.localhost$/, ""));
|
|
185
|
+
const origins = devOrigins(config);
|
|
121
186
|
return {
|
|
122
187
|
alias: `podokit-${projectSlug}-${id}`,
|
|
123
188
|
config,
|
|
189
|
+
origins,
|
|
190
|
+
authUrl: devAuthUrl(config, origins),
|
|
124
191
|
projectRoot: root,
|
|
125
192
|
routeId: `route-${projectSlug}-${id}`,
|
|
126
193
|
runtimeComposePath: (0, node_path_1.join)(root, RUNTIME_COMPOSE),
|
|
@@ -143,11 +210,19 @@ function routesToApi(config) {
|
|
|
143
210
|
function anyOf(matchers) {
|
|
144
211
|
return matchers.length > 1 ? `(${matchers.join(" || ")})` : (matchers[0] ?? "");
|
|
145
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* The compose overlay `podo dev` adds. Besides the gateway network it sets
|
|
215
|
+
* CORS_ORIGIN and BETTER_AUTH_URL for web and api from `.podokit/dev.json`, so
|
|
216
|
+
* the browser origins follow the routed hostname whatever `.env.docker` says.
|
|
217
|
+
*/
|
|
146
218
|
function renderRuntimeCompose(runtime) {
|
|
147
|
-
const
|
|
148
|
-
|
|
219
|
+
const environment = ` environment:\n` +
|
|
220
|
+
` CORS_ORIGIN: ${JSON.stringify(runtime.origins.join(","))}\n` +
|
|
221
|
+
` BETTER_AUTH_URL: ${JSON.stringify(runtime.authUrl)}\n`;
|
|
222
|
+
const apiNetworks = routesToApi(runtime.config)
|
|
223
|
+
? ` networks:\n ${GATEWAY_NETWORK}:\n aliases: [${runtime.alias}-api]\n`
|
|
149
224
|
: "";
|
|
150
|
-
return `# Generated by podo dev. Do not edit.\nservices:\n traefik:\n profiles: [podokit-legacy-proxy]\n web:\n networks:\n ${GATEWAY_NETWORK}:\n aliases: [${runtime.alias}]\n${
|
|
225
|
+
return `# Generated by podo dev. Do not edit.\nservices:\n traefik:\n profiles: [podokit-legacy-proxy]\n web:\n${environment} networks:\n ${GATEWAY_NETWORK}:\n aliases: [${runtime.alias}]\n api:\n${environment}${apiNetworks}networks:\n ${GATEWAY_NETWORK}:\n external: true\n name: ${GATEWAY_NETWORK}\n`;
|
|
151
226
|
}
|
|
152
227
|
function renderRoute(runtime) {
|
|
153
228
|
const { alias, config, routeId } = runtime;
|
package/dist/eject.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -12,12 +12,14 @@ const remove_1 = require("./remove");
|
|
|
12
12
|
const inspect_1 = require("./inspect");
|
|
13
13
|
const update_1 = require("./update");
|
|
14
14
|
const eject_1 = require("./eject");
|
|
15
|
+
const template_files_1 = require("./template-files");
|
|
15
16
|
const dev_1 = require("./dev");
|
|
16
17
|
const locale_1 = require("./locale");
|
|
17
18
|
const deploy_1 = require("./deploy");
|
|
18
19
|
const deploy_profile_1 = require("./deploy-profile");
|
|
19
20
|
const deploy_render_1 = require("./deploy-render");
|
|
20
21
|
const deploy_driver_1 = require("./deploy-driver");
|
|
22
|
+
const deploy_checks_1 = require("./deploy-checks");
|
|
21
23
|
const deploy_compose_profile_1 = require("./deploy-compose-profile");
|
|
22
24
|
const deploy_compose_render_1 = require("./deploy-compose-render");
|
|
23
25
|
const deploy_compose_1 = require("./deploy-compose");
|
|
@@ -33,7 +35,7 @@ Usage:
|
|
|
33
35
|
podo provider set <capability> <provider> [--apply]
|
|
34
36
|
podo remove <module> Un-apply a module (inverse of add)
|
|
35
37
|
podo status Show version, modules, file tiers, and local edits
|
|
36
|
-
podo diff
|
|
38
|
+
podo diff [path...] List edited managed files, or diff files against the template
|
|
37
39
|
podo doctor Check framework versions against supported ranges
|
|
38
40
|
podo dev <action> [...] Run the shared, portless container development gateway
|
|
39
41
|
podo deploy <action> Plan, apply, verify, or roll back a deployment
|
|
@@ -41,6 +43,7 @@ Usage:
|
|
|
41
43
|
podo locale <command> Add, validate, activate, or list JSON locales
|
|
42
44
|
podo update [--apply] Preview (or apply) what a version update would change
|
|
43
45
|
podo eject <path...> Take ownership of managed files (update skips them)
|
|
46
|
+
podo restore <path...> Rewrite managed or ejected files from the template
|
|
44
47
|
|
|
45
48
|
Options:
|
|
46
49
|
--template <t> Template to scaffold (see below)
|
|
@@ -220,6 +223,26 @@ function parseArgs(argv) {
|
|
|
220
223
|
parsed.positionals = positionals;
|
|
221
224
|
return parsed;
|
|
222
225
|
}
|
|
226
|
+
/** Explain owned files whose template changed, with the upstream diff when the previous templates are given. */
|
|
227
|
+
function writeOwnedTemplateNotes(plan, templatesDir, from) {
|
|
228
|
+
if (plan.changes.some((change) => change.action === "review")) {
|
|
229
|
+
if (from) {
|
|
230
|
+
process.stdout.write("\nTemplate changes to files you own (not applied):\n");
|
|
231
|
+
for (const change of (0, update_1.upstreamChanges)(process.cwd(), templatesDir, plan, from)) {
|
|
232
|
+
process.stdout.write(change.diff || `${change.path}: new in this version's template.\n`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
process.stdout.write("\nreview: the template changed for a file you own. podo update never writes it; " +
|
|
237
|
+
"podo diff <path> compares it with the new template, and --from <previous templates> shows what changed upstream.\n");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (plan.ownedWithoutTemplate.length) {
|
|
241
|
+
process.stdout.write(`\n${plan.ownedWithoutTemplate.length} owned file(s) differ from this version's templates, and the lock does not record ` +
|
|
242
|
+
"the template they came from. podo diff <path> shows the difference; applying this update records the template " +
|
|
243
|
+
"so the next update reports only real template changes.\n");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
223
246
|
function writeApiPaths(heading, result, verb) {
|
|
224
247
|
const lines = (0, module_api_paths_1.describeApiPaths)(result, verb);
|
|
225
248
|
if (lines.length)
|
|
@@ -258,7 +281,7 @@ async function runComposeDeploy(action, args, profileName) {
|
|
|
258
281
|
const findings = (0, deploy_compose_1.doctorComposeDeployment)(cwd, profileName, undefined, args.release);
|
|
259
282
|
process.stdout.write(args.json
|
|
260
283
|
? `${JSON.stringify(findings, null, 2)}\n`
|
|
261
|
-
: `${findings.map(
|
|
284
|
+
: `${findings.map(deploy_checks_1.formatFinding).join("\n")}\n`);
|
|
262
285
|
if (findings.some((finding) => !finding.ok))
|
|
263
286
|
process.exitCode = 1;
|
|
264
287
|
return;
|
|
@@ -461,7 +484,33 @@ async function main(argv) {
|
|
|
461
484
|
return;
|
|
462
485
|
}
|
|
463
486
|
if (args.command === "diff") {
|
|
487
|
+
const targets = args.positionals.slice(1);
|
|
464
488
|
try {
|
|
489
|
+
if (targets.length) {
|
|
490
|
+
const result = (0, template_files_1.templateDiff)(process.cwd(), (0, node_path_1.join)(__dirname, "templates"), targets);
|
|
491
|
+
if (result.projectVersion !== result.templateVersion) {
|
|
492
|
+
process.stdout.write(`Comparing with PodoKit ${result.templateVersion} templates; this project is on ${result.projectVersion}.\n`);
|
|
493
|
+
}
|
|
494
|
+
for (const file of result.files) {
|
|
495
|
+
const tier = file.tier ? ` (${file.tier})` : "";
|
|
496
|
+
if (file.status === "not-generated") {
|
|
497
|
+
process.stdout.write(`${file.path}: PodoKit does not generate this file.\n`);
|
|
498
|
+
}
|
|
499
|
+
else if (file.status === "missing") {
|
|
500
|
+
process.stdout.write(`${file.path}${tier}: missing; podo restore ${file.path} writes the template's version.\n`);
|
|
501
|
+
}
|
|
502
|
+
else if (file.status === "same") {
|
|
503
|
+
process.stdout.write(`${file.path}${tier}: same as the template.\n`);
|
|
504
|
+
}
|
|
505
|
+
else if (file.binary) {
|
|
506
|
+
process.stdout.write(`${file.path}${tier}: binary file differs from the template.\n`);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
process.stdout.write(file.diff);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
465
514
|
const { drifted, missing } = (0, inspect_1.diff)(process.cwd());
|
|
466
515
|
if (!drifted.length && !missing.length) {
|
|
467
516
|
process.stdout.write("No local edits to PodoKit-managed files.\n");
|
|
@@ -539,7 +588,7 @@ async function main(argv) {
|
|
|
539
588
|
const findings = (0, deploy_1.doctorDeployment)(process.cwd(), profileName);
|
|
540
589
|
process.stdout.write(args.json
|
|
541
590
|
? `${JSON.stringify(findings, null, 2)}\n`
|
|
542
|
-
: `${findings.map(
|
|
591
|
+
: `${findings.map(deploy_checks_1.formatFinding).join("\n")}\n`);
|
|
543
592
|
if (findings.some((finding) => !finding.ok))
|
|
544
593
|
process.exitCode = 1;
|
|
545
594
|
return;
|
|
@@ -561,7 +610,9 @@ async function main(argv) {
|
|
|
561
610
|
const plan = (0, deploy_1.planDeployment)(process.cwd(), profileName, args.release);
|
|
562
611
|
process.stdout.write(args.json
|
|
563
612
|
? `${JSON.stringify(plan, null, 2)}\n`
|
|
564
|
-
: `${plan.actions.map((entry) => `${entry.order}. ${entry.description}`).join("\n")}\
|
|
613
|
+
: `${plan.actions.map((entry) => `${entry.order}. ${entry.description}`).join("\n")}\n` +
|
|
614
|
+
`${plan.warnings.map((warning) => `warning: ${warning}`).join("\n")}\n` +
|
|
615
|
+
`Plan hash: ${plan.planHash}\n`);
|
|
565
616
|
return;
|
|
566
617
|
}
|
|
567
618
|
if (action === "apply") {
|
|
@@ -672,12 +723,23 @@ async function main(argv) {
|
|
|
672
723
|
result.conflicts.map((f) => ` ${f}`).join("\n") +
|
|
673
724
|
"\n");
|
|
674
725
|
}
|
|
726
|
+
if (result.review.length) {
|
|
727
|
+
process.stdout.write("\nThe template changed for these files you own; they were left as they are. " +
|
|
728
|
+
"Compare each with podo diff <path>:\n" +
|
|
729
|
+
result.review.map((f) => ` ${f}`).join("\n") +
|
|
730
|
+
"\n");
|
|
731
|
+
}
|
|
675
732
|
return;
|
|
676
733
|
}
|
|
677
734
|
const plan = (0, update_1.planUpdate)(process.cwd(), templatesDir);
|
|
678
735
|
const counts = (0, update_1.summarize)(plan);
|
|
679
736
|
const shown = plan.changes.filter((c) => c.action !== "up-to-date" && c.action !== "skip");
|
|
680
737
|
process.stdout.write(`podo update ${plan.fromVersion} -> ${plan.toVersion} (template: ${plan.template}; modules: ${plan.modules.join(", ") || "none"})\n\n`);
|
|
738
|
+
if (plan.moduleDowngrades.length) {
|
|
739
|
+
process.stdout.write("WARN older external modules are installed than this project applied:\n" +
|
|
740
|
+
`${(0, update_1.describeDowngrades)(plan.moduleDowngrades)}\n` +
|
|
741
|
+
"--apply refuses until you install the recorded versions; the plan below would downgrade them.\n\n");
|
|
742
|
+
}
|
|
681
743
|
if (plan.addedModules.length) {
|
|
682
744
|
process.stdout.write("Modules to add:\n" +
|
|
683
745
|
plan.addedModules.map((module) => ` ${module.name} (${(0, update_1.addedModuleReason)(module)})`).join("\n") +
|
|
@@ -696,9 +758,10 @@ async function main(argv) {
|
|
|
696
758
|
process.stdout.write(` ${c.action.padEnd(9)} ${path} (${c.note})\n`);
|
|
697
759
|
}
|
|
698
760
|
process.stdout.write(`${shown.length ? "\n" : ""}${counts.update} update, ${counts.add} add, ${counts.move} move, ` +
|
|
699
|
-
`${counts.remove} remove, ${counts.conflict} conflict. ` +
|
|
761
|
+
`${counts.remove} remove, ${counts.conflict} conflict, ${counts.review} review. ` +
|
|
700
762
|
`Dry-run — nothing was written. Re-run with --apply to write (use --from <dir> for a 3-way merge).\n`);
|
|
701
763
|
}
|
|
764
|
+
writeOwnedTemplateNotes(plan, templatesDir, args.from);
|
|
702
765
|
}
|
|
703
766
|
catch (err) {
|
|
704
767
|
fail(err.message);
|
|
@@ -727,6 +790,23 @@ async function main(argv) {
|
|
|
727
790
|
}
|
|
728
791
|
return;
|
|
729
792
|
}
|
|
793
|
+
if (args.command === "restore") {
|
|
794
|
+
const targets = args.positionals.slice(1);
|
|
795
|
+
if (!targets.length) {
|
|
796
|
+
fail("Usage: podo restore <path...>");
|
|
797
|
+
}
|
|
798
|
+
try {
|
|
799
|
+
const result = (0, template_files_1.restore)(process.cwd(), (0, node_path_1.join)(__dirname, "templates"), targets);
|
|
800
|
+
process.stdout.write(`Restored from the template: ${result.restored.join(", ")}\n`);
|
|
801
|
+
if (result.remanaged.length) {
|
|
802
|
+
process.stdout.write(`Managed by PodoKit again: ${result.remanaged.join(", ")}\n`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
catch (err) {
|
|
806
|
+
fail(err.message);
|
|
807
|
+
}
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
730
810
|
if (args.command !== "create") {
|
|
731
811
|
fail(`Unknown command "${args.command}". Run "podo --help".`);
|
|
732
812
|
}
|
package/dist/lib.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export { describeApiPaths } from "./module-api-paths";
|
|
|
11
11
|
export { status, diff, doctor, SUPPORTED_FRAMEWORKS } from "./inspect";
|
|
12
12
|
export type { StatusReport, DoctorFinding } from "./inspect";
|
|
13
13
|
export { addedModuleReason, planUpdate, summarize } from "./update";
|
|
14
|
+
export { restore, templateDiff } from "./template-files";
|
|
15
|
+
export type { RestoreResult, TemplateDiffResult, TemplateFileDiff } from "./template-files";
|
|
14
16
|
export type { AddedModule, UpdatePlan, FileChange } from "./update";
|
|
15
17
|
export { readDevConfig, renderRoute, renderRuntimeCompose, resolveDevRuntime, runDevCommand } from "./dev";
|
|
16
18
|
export type { CommandRunner, DevConfig, DevRuntime } from "./dev";
|