@podosoft/podokit 1.5.0 → 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.
Files changed (211) hide show
  1. package/README.md +2 -1
  2. package/dist/app-proxy-timeouts.d.ts +27 -0
  3. package/dist/app-proxy-timeouts.js +180 -0
  4. package/dist/assemble.d.ts +5 -2
  5. package/dist/assemble.js +14 -4
  6. package/dist/deploy-checks.d.ts +44 -0
  7. package/dist/deploy-checks.js +267 -0
  8. package/dist/deploy-compose-profile.d.ts +6 -2
  9. package/dist/deploy-compose-profile.js +15 -6
  10. package/dist/deploy-compose-render.d.ts +12 -4
  11. package/dist/deploy-compose-render.js +47 -18
  12. package/dist/deploy-compose.d.ts +2 -5
  13. package/dist/deploy-compose.js +7 -2
  14. package/dist/deploy-profile.d.ts +6 -2
  15. package/dist/deploy-profile.js +11 -7
  16. package/dist/deploy-render.d.ts +4 -1
  17. package/dist/deploy-render.js +27 -21
  18. package/dist/deploy-schema.d.ts +56 -2
  19. package/dist/deploy-schema.js +130 -14
  20. package/dist/deploy.d.ts +2 -5
  21. package/dist/deploy.js +5 -2
  22. package/dist/dev.d.ts +21 -0
  23. package/dist/dev.js +78 -3
  24. package/dist/eject.js +2 -0
  25. package/dist/index.js +85 -5
  26. package/dist/lib.d.ts +2 -0
  27. package/dist/lib.js +5 -2
  28. package/dist/lockfile.d.ts +24 -0
  29. package/dist/lockfile.js +74 -4
  30. package/dist/provider.js +10 -5
  31. package/dist/remove.js +1 -1
  32. package/dist/template-files.d.ts +42 -0
  33. package/dist/template-files.js +102 -0
  34. package/dist/templates/fullstack/AGENTS.md +28 -4
  35. package/dist/templates/fullstack/README.md +8 -7
  36. package/dist/templates/fullstack/apps/api/package.json +4 -4
  37. package/dist/templates/fullstack/apps/api/scripts/build.extensions.mjs +6 -0
  38. package/dist/templates/fullstack/apps/api/scripts/build.mjs +65 -16
  39. package/dist/templates/fullstack/apps/api/scripts/run-migrations.mjs +7 -3
  40. package/dist/templates/fullstack/apps/api/scripts/verify-api-contract.ts +5 -1
  41. package/dist/templates/fullstack/apps/api/src/app.spec.ts +22 -0
  42. package/dist/templates/fullstack/apps/api/src/app.ts +41 -25
  43. package/dist/templates/fullstack/apps/api/src/build.spec.ts +118 -0
  44. package/dist/templates/fullstack/apps/api/src/config/env.extensions.ts +20 -0
  45. package/dist/templates/fullstack/apps/api/src/config/env.validation.spec.ts +76 -0
  46. package/dist/templates/fullstack/apps/api/src/config/env.validation.ts +18 -3
  47. package/dist/templates/fullstack/apps/api/src/config/provider-selection.spec.ts +53 -0
  48. package/dist/templates/fullstack/apps/api/src/config/provider-selection.ts +79 -0
  49. package/dist/templates/fullstack/apps/api/src/config/providers.ts +7 -3
  50. package/dist/templates/fullstack/apps/api/src/core/api-contract.extensions.ts +6 -0
  51. package/dist/templates/fullstack/apps/api/src/core/api-contract.spec.ts +29 -0
  52. package/dist/templates/fullstack/apps/api/src/core/api-contract.ts +39 -2
  53. package/dist/templates/fullstack/apps/api/src/core/services.ts +13 -0
  54. package/dist/templates/fullstack/apps/api/src/database/data-source.spec.ts +27 -0
  55. package/dist/templates/fullstack/apps/api/src/database/data-source.ts +7 -10
  56. package/dist/templates/fullstack/apps/api/src/database/database.spec.ts +12 -0
  57. package/dist/templates/fullstack/apps/api/src/database/database.ts +2 -3
  58. package/dist/templates/fullstack/apps/api/src/database/entities.ts +12 -0
  59. package/dist/templates/fullstack/apps/api/src/database/migrate.ts +12 -2
  60. package/dist/templates/fullstack/apps/api/src/database/migration-files.spec.ts +116 -0
  61. package/dist/templates/fullstack/apps/api/src/database/migration-files.ts +134 -0
  62. package/dist/templates/fullstack/apps/api/src/database/migration-readiness.spec.ts +36 -0
  63. package/dist/templates/fullstack/apps/api/src/database/migration-readiness.ts +25 -0
  64. package/dist/templates/fullstack/apps/api/src/database/run-migrations.spec.ts +12 -1
  65. package/dist/templates/fullstack/apps/api/src/database/sqlite-migrator.ts +2 -36
  66. package/dist/templates/fullstack/apps/api/src/main.ts +2 -2
  67. package/dist/templates/fullstack/apps/api/tsconfig.json +2 -0
  68. package/dist/templates/fullstack/apps/web/package.json +1 -1
  69. package/dist/templates/fullstack/apps/web/src/app.css +3 -0
  70. package/dist/templates/fullstack/apps/web/src/app.extensions.css +11 -0
  71. package/dist/templates/fullstack/apps/web/src/lib/components/site-runtime.svelte +3 -1
  72. package/dist/templates/fullstack/apps/web/src/lib/server/api.test.ts +19 -0
  73. package/dist/templates/fullstack/apps/web/src/lib/server/api.ts +4 -1
  74. package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.test.ts +79 -0
  75. package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.ts +55 -1
  76. package/dist/templates/fullstack/apps/web/src/lib/server/proxy-timeouts.ts +2 -1
  77. package/dist/templates/fullstack/apps/web/tsconfig.json +1 -1
  78. package/dist/templates/fullstack/apps/web/vite.config.test.ts +33 -0
  79. package/dist/templates/fullstack/apps/web/vite.config.ts +55 -13
  80. package/dist/templates/fullstack/apps/web/vite.extensions.ts +23 -0
  81. package/dist/templates/fullstack/compose.dev.yaml +7 -4
  82. package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/SKILL.md +2 -2
  83. package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/references/profile.md +12 -3
  84. package/dist/templates/fullstack/dot-claude/skills/podokit-update/SKILL.md +5 -2
  85. package/dist/templates/fullstack/dot-env.docker.example +5 -2
  86. package/dist/templates/fullstack/dot-env.example +4 -0
  87. package/dist/templates/fullstack/dot-podokit/dev.json +1 -1
  88. package/dist/templates/fullstack/infra/k3s/configmap.yaml +1 -0
  89. package/dist/templates/fullstack/tests/README.md +40 -1
  90. package/dist/templates/fullstack/tests/helpers/app-paths.ts +18 -0
  91. package/dist/templates/fullstack/tests/helpers/paths.ts +33 -0
  92. package/dist/templates/fullstack/tests/package.json +2 -2
  93. package/dist/templates/fullstack/tests/playwright.config.ts +7 -3
  94. package/dist/templates/fullstack/tests/playwright.extensions.ts +88 -1
  95. package/dist/templates/fullstack/tests/ui/smoke.ui.spec.ts +5 -4
  96. package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.test.ts +37 -1
  97. package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +15 -6
  98. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flag-settings.test.ts +31 -0
  99. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flag-settings.ts +31 -0
  100. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-flags.ts +12 -0
  101. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/app-nav.ts +11 -0
  102. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/nav.test.ts +39 -0
  103. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/nav.ts +35 -0
  104. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/registry.svelte.ts +15 -0
  105. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-page.svelte +11 -1
  106. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/admin-sidebar.svelte +2 -23
  107. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +7 -2
  108. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.test.ts +30 -0
  109. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/impersonation-banner.svelte +3 -2
  110. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/settings/app-flags-settings.svelte +65 -0
  111. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +32 -23
  112. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.test.ts +27 -0
  113. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-footer.svelte +4 -1
  114. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-runtime.svelte +3 -1
  115. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/en.json +6 -0
  116. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/ko.json +6 -0
  117. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/app-hooks.ts +11 -0
  118. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/non-indexable-paths.ts +8 -0
  119. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.test.ts +31 -0
  120. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.ts +5 -1
  121. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(admin)/admin/settings/+page.svelte +1 -1
  122. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.server.ts +2 -1
  123. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.svelte +1 -5
  124. package/dist/templates/modules/admin-dashboard/files/dot-claude/skills/podokit-data-table/SKILL.md +2 -0
  125. package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +1 -1
  126. package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +159 -170
  127. package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +72 -81
  128. package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +1 -6
  129. package/dist/templates/modules/admin-dashboard/files/tests/api/profile-image.api.spec.ts +1 -1
  130. package/dist/templates/modules/admin-dashboard/files/tests/api/site-settings.api.spec.ts +43 -53
  131. package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +3 -1
  132. package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +24 -6
  133. package/dist/templates/modules/admin-dashboard/files/tests/helpers/app-accounts.ts +24 -0
  134. package/dist/templates/modules/admin-dashboard/files/tests/helpers/devices.ts +41 -11
  135. package/dist/templates/modules/admin-dashboard/files/tests/helpers/disposable-users.ts +5 -3
  136. package/dist/templates/modules/admin-dashboard/files/tests/helpers/mailpit.ts +3 -9
  137. package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +2 -4
  138. package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +11 -5
  139. package/dist/templates/modules/admin-dashboard/files/tests/ui/account-flows.ui.spec.ts +4 -5
  140. package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +55 -55
  141. package/dist/templates/modules/admin-dashboard/files/tests/ui/app-flags.ui.spec.ts +63 -0
  142. package/dist/templates/modules/admin-dashboard/files/tests/ui/appearance.ui.spec.ts +12 -16
  143. package/dist/templates/modules/admin-dashboard/files/tests/ui/audit.ui.spec.ts +3 -2
  144. package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +32 -35
  145. package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +4 -2
  146. package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +1 -2
  147. package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +1 -2
  148. package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +82 -91
  149. package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +6 -2
  150. package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +16 -20
  151. package/dist/templates/modules/admin-dashboard/files/tests/ui/impersonation.ui.spec.ts +6 -3
  152. package/dist/templates/modules/admin-dashboard/files/tests/ui/login-methods.ui.spec.ts +2 -3
  153. package/dist/templates/modules/admin-dashboard/files/tests/ui/oidc.ui.spec.ts +2 -4
  154. package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +5 -2
  155. package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +7 -2
  156. package/dist/templates/modules/admin-dashboard/files/tests/ui/same-origin-requests.ui.spec.ts +89 -0
  157. package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +5 -4
  158. package/dist/templates/modules/admin-dashboard/files/tests/ui/settings-dialogs.ui.spec.ts +67 -73
  159. package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +84 -93
  160. package/dist/templates/modules/admin-dashboard/files/tests/ui/table-toolbar.ui.spec.ts +8 -7
  161. package/dist/templates/modules/admin-dashboard/files/tests/ui/theme-mode.ui.spec.ts +29 -0
  162. package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +51 -54
  163. package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +10 -9
  164. package/dist/templates/modules/admin-dashboard/module.manifest.json +7 -2
  165. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +5 -1
  166. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.spec.ts +17 -0
  167. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +18 -0
  168. package/dist/templates/modules/audit-log/module.manifest.json +15 -3
  169. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-app-name.spec.ts +21 -0
  170. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-config-store.ts +13 -1
  171. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-environment.ts +9 -2
  172. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +19 -3
  173. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.module.ts +2 -0
  174. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.runtime.spec.ts +10 -0
  175. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +7 -5
  176. package/dist/templates/modules/auth/files/apps/api/src/auth/schema-recheck.spec.ts +86 -0
  177. package/dist/templates/modules/auth/files/apps/api/src/auth/schema-recheck.ts +44 -0
  178. package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +2 -1
  179. package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +3 -1
  180. package/dist/templates/modules/auth/files/apps/api/src/settings/app-flags.ts +9 -0
  181. package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.spec.ts +43 -0
  182. package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +30 -5
  183. package/dist/templates/modules/auth/module.manifest.json +8 -2
  184. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.spec.ts +57 -1
  185. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.ts +93 -9
  186. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.spec.ts +40 -0
  187. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +52 -5
  188. package/dist/templates/modules/bullmq/files/apps/api/src/main-worker.ts +9 -7
  189. package/dist/templates/modules/bullmq/files/apps/api/src/worker.extensions.ts +13 -0
  190. package/dist/templates/modules/bullmq/module.manifest.json +5 -1
  191. package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.worker.spec.ts +2 -0
  192. package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.spec.ts +87 -1
  193. package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.ts +93 -3
  194. package/dist/templates/modules/jobs-local/module.manifest.json +3 -0
  195. package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.spec.ts +16 -0
  196. package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.ts +15 -10
  197. package/dist/templates/modules/mailer/module.manifest.json +1 -1
  198. package/dist/templates/modules/object-storage-local/files/tests/api/storage-local.api.spec.ts +1 -1
  199. package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -4
  200. package/dist/templates/modules/object-storage-s3/files/tests/api/storage.api.spec.ts +1 -1
  201. package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +45 -20
  202. package/dist/templates/modules/sse/files/tests/api/events.api.spec.ts +1 -1
  203. package/dist/templates/todo/AGENTS.md +5 -1
  204. package/dist/templates/todo/README.md +6 -4
  205. package/dist/text-diff.d.ts +12 -0
  206. package/dist/text-diff.js +111 -0
  207. package/dist/update.d.ts +61 -3
  208. package/dist/update.js +152 -27
  209. package/package.json +2 -2
  210. package/dist/templates/modules/admin-dashboard/files/tests/api/exclusive.api.spec.ts +0 -75
  211. package/dist/templates/modules/admin-dashboard/files/tests/helpers/exclusive.ts +0 -80
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";
package/dist/lib.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.addedModuleReason = exports.SUPPORTED_FRAMEWORKS = exports.doctor = exports.diff = exports.status = exports.describeApiPaths = 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 = exports.getComposeStatus = exports.doctorComposeDeployment = void 0;
3
+ 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.templateDiff = exports.restore = exports.summarize = exports.planUpdate = exports.addedModuleReason = exports.SUPPORTED_FRAMEWORKS = exports.doctor = exports.diff = exports.status = exports.describeApiPaths = 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 = exports.getComposeStatus = exports.doctorComposeDeployment = exports.applyComposeDeployment = exports.renderComposeDeployment = 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
@@ -34,6 +34,9 @@ var update_1 = require("./update");
34
34
  Object.defineProperty(exports, "addedModuleReason", { enumerable: true, get: function () { return update_1.addedModuleReason; } });
35
35
  Object.defineProperty(exports, "planUpdate", { enumerable: true, get: function () { return update_1.planUpdate; } });
36
36
  Object.defineProperty(exports, "summarize", { enumerable: true, get: function () { return update_1.summarize; } });
37
+ var template_files_1 = require("./template-files");
38
+ Object.defineProperty(exports, "restore", { enumerable: true, get: function () { return template_files_1.restore; } });
39
+ Object.defineProperty(exports, "templateDiff", { enumerable: true, get: function () { return template_files_1.templateDiff; } });
37
40
  var dev_1 = require("./dev");
38
41
  Object.defineProperty(exports, "readDevConfig", { enumerable: true, get: function () { return dev_1.readDevConfig; } });
39
42
  Object.defineProperty(exports, "renderRoute", { enumerable: true, get: function () { return dev_1.renderRoute; } });
@@ -51,6 +51,12 @@ export interface FileEntry {
51
51
  tier: Tier;
52
52
  /** Hash of the exact bytes PodoKit last wrote; user-edit detector. */
53
53
  outHash: string;
54
+ /**
55
+ * Owned files only: hash of what the template generated for the path when
56
+ * PodoKit last rendered it, so `podo update` can tell the application that
57
+ * the template changed without touching the file.
58
+ */
59
+ templateHash?: string;
54
60
  }
55
61
  export interface FilesLock {
56
62
  schemaVersion: number;
@@ -68,6 +74,12 @@ export interface RecordModulesLockSnapshot {
68
74
  }
69
75
  /** Default user-owned globs seeded into a new project. See ADR-0010. */
70
76
  export declare const DEFAULT_OWNED_GLOBS: string[];
77
+ /**
78
+ * PodoKit's own agent skills stay managed inside the owned `.claude/**` and
79
+ * `.agents/**` trees, so updates keep their commands and APIs current. Skills an
80
+ * application adds under other names stay owned.
81
+ */
82
+ export declare const DEFAULT_MANAGED_OVERRIDES: string[];
71
83
  export declare function manifestPath(projectRoot: string): string;
72
84
  export declare function filesLockPath(projectRoot: string): string;
73
85
  export declare function readManifest(projectRoot: string): PodokitManifest | null;
@@ -93,6 +105,18 @@ export declare function isLocalEnvFile(relPath: string): boolean;
93
105
  export declare function walkFiles(projectRoot: string, prefix?: string): string[];
94
106
  /** Recompute the files.lock by hashing and classifying every project file. */
95
107
  export declare function computeFilesLock(projectRoot: string, ownedGlobs: string[], managedOverrides?: string[]): FilesLock;
108
+ /**
109
+ * The template content an earlier lock knew for a path: the recorded template
110
+ * hash of an owned file, or the generated baseline of a managed one.
111
+ */
112
+ export declare function recordedTemplateHash(entry: FileEntry | undefined): string | undefined;
113
+ /**
114
+ * Carry the template hashes of an earlier lock into a recomputed one for files
115
+ * that are owned now. Paths in `changed` were just rewritten from a different
116
+ * template, such as the files a module added to or removed from, so their old
117
+ * hash would be wrong and is dropped.
118
+ */
119
+ export declare function keepTemplateHashes(next: FilesLock, previous: FilesLock | null, changed?: Iterable<string>): FilesLock;
96
120
  export declare function readFilesLock(projectRoot: string): FilesLock | null;
97
121
  export interface Drift {
98
122
  /** Managed/assembled files whose content differs from what PodoKit wrote. */
package/dist/lockfile.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_OWNED_GLOBS = exports.LOCK_SCHEMA_VERSION = void 0;
3
+ exports.DEFAULT_MANAGED_OVERRIDES = exports.DEFAULT_OWNED_GLOBS = exports.LOCK_SCHEMA_VERSION = void 0;
4
4
  exports.manifestPath = manifestPath;
5
5
  exports.filesLockPath = filesLockPath;
6
6
  exports.readManifest = readManifest;
@@ -14,6 +14,8 @@ exports.classifyTier = classifyTier;
14
14
  exports.isLocalEnvFile = isLocalEnvFile;
15
15
  exports.walkFiles = walkFiles;
16
16
  exports.computeFilesLock = computeFilesLock;
17
+ exports.recordedTemplateHash = recordedTemplateHash;
18
+ exports.keepTemplateHashes = keepTemplateHashes;
17
19
  exports.readFilesLock = readFilesLock;
18
20
  exports.computeDrift = computeDrift;
19
21
  exports.initLockfile = initLockfile;
@@ -55,11 +57,23 @@ exports.DEFAULT_OWNED_GLOBS = [
55
57
  "apps/web/src/lib/i18n/catalogs/app/**",
56
58
  // Owned Elysia extension slot — register application services and plugins here.
57
59
  "apps/api/src/app.extensions.ts",
60
+ // Owned application environment variables, validated with PodoKit's.
61
+ "apps/api/src/config/env.extensions.ts",
62
+ // Owned TypeORM entities the migration data source loads.
63
+ "apps/api/src/database/entities.ts",
64
+ // Owned application operations that `bun run contract` requires.
65
+ "apps/api/src/core/api-contract.extensions.ts",
66
+ // Owned entry points the API build bundles next to PodoKit's.
67
+ "apps/api/scripts/build.extensions.mjs",
58
68
  // Owned additions to the managed /api proxy's header lists, body limits, and
59
69
  // response deadlines.
60
70
  "apps/web/src/lib/server/proxy-headers.ts",
61
71
  "apps/web/src/lib/server/body-limits.ts",
62
72
  "apps/web/src/lib/server/proxy-timeouts.ts",
73
+ // Owned additions to the managed Vite and SvelteKit configuration.
74
+ "apps/web/vite.extensions.ts",
75
+ // Owned styles the managed app.css imports last.
76
+ "apps/web/src/app.extensions.css",
63
77
  // Owned fallback page SvelteKit serves when the root layout or a hook fails.
64
78
  "apps/web/src/error.html",
65
79
  // AI agent guidance — yours to customize; update never touches it.
@@ -81,6 +95,27 @@ exports.DEFAULT_OWNED_GLOBS = [
81
95
  ".podokit/dev.json",
82
96
  // Optional application-owned Playwright device/browser projects.
83
97
  "tests/playwright.projects.cjs",
98
+ // Application screens the managed e2e specs visit.
99
+ "tests/helpers/app-paths.ts",
100
+ // Project documents, ignore rules, environment examples, and example
101
+ // manifests every application rewrites. PodoKit seeds them once, `podo add`
102
+ // still appends module environment lines and wiring, and `podo update`
103
+ // reports their template changes for review instead of merging them.
104
+ "README.md",
105
+ "tests/README.md",
106
+ ".gitignore",
107
+ ".env.example",
108
+ ".env.docker.example",
109
+ "infra/k3s/**",
110
+ ];
111
+ /**
112
+ * PodoKit's own agent skills stay managed inside the owned `.claude/**` and
113
+ * `.agents/**` trees, so updates keep their commands and APIs current. Skills an
114
+ * application adds under other names stay owned.
115
+ */
116
+ exports.DEFAULT_MANAGED_OVERRIDES = [
117
+ ".claude/skills/podokit-*/**",
118
+ ".agents/skills/podokit-*/**",
84
119
  ];
85
120
  const TEXT_EXT = /\.(json|md|ts|tsx|js|mjs|cjs|css|html|yml|yaml|txt|env|svelte|gitignore|example)$/i;
86
121
  function podokitDir(projectRoot) {
@@ -209,6 +244,34 @@ function computeFilesLock(projectRoot, ownedGlobs, managedOverrides = []) {
209
244
  }
210
245
  return { schemaVersion: exports.LOCK_SCHEMA_VERSION, files };
211
246
  }
247
+ /**
248
+ * The template content an earlier lock knew for a path: the recorded template
249
+ * hash of an owned file, or the generated baseline of a managed one.
250
+ */
251
+ function recordedTemplateHash(entry) {
252
+ if (!entry)
253
+ return undefined;
254
+ return entry.tier === "owned" ? entry.templateHash : entry.outHash;
255
+ }
256
+ /**
257
+ * Carry the template hashes of an earlier lock into a recomputed one for files
258
+ * that are owned now. Paths in `changed` were just rewritten from a different
259
+ * template, such as the files a module added to or removed from, so their old
260
+ * hash would be wrong and is dropped.
261
+ */
262
+ function keepTemplateHashes(next, previous, changed = []) {
263
+ if (!previous)
264
+ return next;
265
+ const skip = new Set(changed);
266
+ for (const [path, entry] of Object.entries(next.files)) {
267
+ if (entry.tier !== "owned" || entry.templateHash || skip.has(path))
268
+ continue;
269
+ const templateHash = recordedTemplateHash(previous.files[path]);
270
+ if (templateHash)
271
+ entry.templateHash = templateHash;
272
+ }
273
+ return next;
274
+ }
212
275
  function readFilesLock(projectRoot) {
213
276
  const file = filesLockPath(projectRoot);
214
277
  if (!(0, node_fs_1.existsSync)(file))
@@ -246,6 +309,7 @@ function computeDrift(projectRoot) {
246
309
  /** Write the initial `.podokit/` lockfile for a freshly created project. */
247
310
  function initLockfile(projectRoot, options) {
248
311
  const ownedGlobs = options.ownedGlobs ?? exports.DEFAULT_OWNED_GLOBS;
312
+ const managedOverrides = [...exports.DEFAULT_MANAGED_OVERRIDES];
249
313
  const providers = (0, podokit_runtime_1.resolveProviderSelections)(options.providers ?? podokit_runtime_1.SERVER_PROVIDER_SELECTIONS);
250
314
  const manifest = {
251
315
  schemaVersion: exports.LOCK_SCHEMA_VERSION,
@@ -256,10 +320,16 @@ function initLockfile(projectRoot, options) {
256
320
  providers,
257
321
  modules: [],
258
322
  ownedGlobs,
259
- managedOverrides: [],
323
+ managedOverrides,
260
324
  };
261
325
  writeManifest(projectRoot, manifest);
262
- writeFilesLock(projectRoot, computeFilesLock(projectRoot, ownedGlobs));
326
+ const lock = computeFilesLock(projectRoot, ownedGlobs, managedOverrides);
327
+ // Create wrote every file straight from the template.
328
+ for (const entry of Object.values(lock.files)) {
329
+ if (entry.tier === "owned")
330
+ entry.templateHash = entry.outHash;
331
+ }
332
+ writeFilesLock(projectRoot, lock);
263
333
  }
264
334
  /** Union two glob lists, preserving order and dropping duplicates. Used to fold
265
335
  * module-declared and ejected owned paths into the project's `ownedGlobs`, which
@@ -359,5 +429,5 @@ insertBefore) {
359
429
  continue;
360
430
  files[path] = current;
361
431
  }
362
- writeFilesLock(projectRoot, { schemaVersion: exports.LOCK_SCHEMA_VERSION, files });
432
+ writeFilesLock(projectRoot, keepTemplateHashes({ schemaVersion: exports.LOCK_SCHEMA_VERSION, files }, lockSnapshot.previous, modulePaths));
363
433
  }
package/dist/provider.js CHANGED
@@ -29,16 +29,20 @@ function assertProvider(capability, provider) {
29
29
  }
30
30
  /** The managed provider source; it must render byte for byte like the template's copy. */
31
31
  function providerSource(providers) {
32
- return `import type { ProviderSelections } from "@podosoft/podokit-runtime";\n\n` +
33
- `// Typed with every provider name, not the selected ones: the module of a\n` +
32
+ return `import type { ProviderSelections } from "@podosoft/podokit-runtime";\n` +
33
+ `import { providersFromEnvironment } from "./provider-selection";\n\n` +
34
+ `// The providers selected when the project was generated or by \`podo provider\n` +
35
+ `// set\`. Typed with every provider name, not the selected ones: the module of a\n` +
34
36
  `// provider that stays installed after a switch compares against its own name.\n` +
35
- `export const PROVIDERS: Readonly<ProviderSelections> = {\n` +
37
+ `// A <CAPABILITY>_PROVIDER variable replaces one at startup; see\n` +
38
+ `// provider-selection.ts.\n` +
39
+ `export const PROVIDERS: Readonly<ProviderSelections> = providersFromEnvironment({\n` +
36
40
  ` database: "${providers.database}",\n` +
37
41
  ` cache: "${providers.cache}",\n` +
38
42
  ` "object-storage": "${providers["object-storage"]}",\n` +
39
43
  ` events: "${providers.events}",\n` +
40
44
  ` jobs: "${providers.jobs}",\n` +
41
- `};\n`;
45
+ `});\n`;
42
46
  }
43
47
  function listProviders(projectRoot) {
44
48
  const manifest = requireManifest(projectRoot);
@@ -122,6 +126,7 @@ function applyProviderChange(projectRoot, capabilityInput, provider, options = {
122
126
  [manifestFile, (0, node_fs_1.readFileSync)(manifestFile, "utf8")],
123
127
  [lockFile, (0, node_fs_1.existsSync)(lockFile) ? (0, node_fs_1.readFileSync)(lockFile, "utf8") : null],
124
128
  ]);
129
+ const previousLock = (0, lockfile_1.readFilesLock)(projectRoot);
125
130
  const temporary = `${sourcePath}.podokit-provider-${process.pid}`;
126
131
  try {
127
132
  manifest.providers[plan.capability] = plan.to;
@@ -129,7 +134,7 @@ function applyProviderChange(projectRoot, capabilityInput, provider, options = {
129
134
  (0, node_fs_1.writeFileSync)(temporary, providerSource(manifest.providers), { mode: 0o600 });
130
135
  (0, node_fs_1.renameSync)(temporary, sourcePath);
131
136
  (0, lockfile_1.writeManifest)(projectRoot, manifest);
132
- (0, lockfile_1.writeFilesLock)(projectRoot, (0, lockfile_1.computeFilesLock)(projectRoot, manifest.ownedGlobs, manifest.managedOverrides ?? []));
137
+ (0, lockfile_1.writeFilesLock)(projectRoot, (0, lockfile_1.keepTemplateHashes)((0, lockfile_1.computeFilesLock)(projectRoot, manifest.ownedGlobs, manifest.managedOverrides ?? []), previousLock));
133
138
  }
134
139
  catch (error) {
135
140
  (0, node_fs_1.rmSync)(temporary, { force: true });
package/dist/remove.js CHANGED
@@ -135,7 +135,7 @@ function removeModule(options) {
135
135
  sharedOwnedGlobs.has(glob) ||
136
136
  keptEdited.some((path) => (0, lockfile_1.matchGlob)(path, glob)));
137
137
  (0, lockfile_1.writeManifest)(projectRoot, manifest);
138
- (0, lockfile_1.writeFilesLock)(projectRoot, (0, lockfile_1.computeFilesLock)(projectRoot, manifest.ownedGlobs, manifest.managedOverrides));
138
+ (0, lockfile_1.writeFilesLock)(projectRoot, (0, lockfile_1.keepTemplateHashes)((0, lockfile_1.computeFilesLock)(projectRoot, manifest.ownedGlobs, manifest.managedOverrides), lock, [...unwired, ".env.example"]));
139
139
  return { module, removed, keptEdited, keptShared, unwired, apiPaths };
140
140
  }
141
141
  /** Remove now-empty directories left behind by a deleted file, up to the root. */
@@ -0,0 +1,42 @@
1
+ import { type Tier } from "./lockfile";
2
+ /**
3
+ * `podo diff <path>` and `podo restore <path>`: compare a working file with
4
+ * what the installed CLI's templates generate for it, and put a managed or
5
+ * ejected file back to that content.
6
+ */
7
+ export type TemplateFileStatus = "same" | "differs" | "missing" | "not-generated";
8
+ export interface TemplateFileDiff {
9
+ path: string;
10
+ /** Ownership tier the lock records, or null for a file the lock does not track. */
11
+ tier: Tier | null;
12
+ status: TemplateFileStatus;
13
+ /** Unified diff from the template to the working file; empty unless it differs. */
14
+ diff: string;
15
+ /** Whether either side is binary, so no line diff is shown. */
16
+ binary: boolean;
17
+ }
18
+ export interface TemplateDiffResult {
19
+ /** PodoKit version the project was generated or last updated with. */
20
+ projectVersion: string;
21
+ /** Version of the CLI whose templates were rendered. */
22
+ templateVersion: string;
23
+ files: TemplateFileDiff[];
24
+ }
25
+ /** Normalize a user-supplied path to the lock's POSIX project-relative form. */
26
+ export declare function projectPath(path: string): string;
27
+ export declare function templateDiff(projectRoot: string, templatesDir: string, paths: string[]): TemplateDiffResult;
28
+ export interface RestoreResult {
29
+ /** Files rewritten from the template. */
30
+ restored: string[];
31
+ /** Files that were ejected and are managed again. */
32
+ remanaged: string[];
33
+ }
34
+ /**
35
+ * Rewrite managed or assembled files with the template's content and record
36
+ * that content as the lock baseline. A file that `podo eject` made owned is
37
+ * handed back to PodoKit: its exact path leaves the manifest's owned paths, so
38
+ * future updates manage it again. Files owned because a template or module
39
+ * declares them owned, such as routes, are refused. Nothing is written unless
40
+ * every path can be restored.
41
+ */
42
+ export declare function restore(projectRoot: string, templatesDir: string, paths: string[]): RestoreResult;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectPath = projectPath;
4
+ exports.templateDiff = templateDiff;
5
+ exports.restore = restore;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
9
+ const lockfile_1 = require("./lockfile");
10
+ const update_1 = require("./update");
11
+ const text_diff_1 = require("./text-diff");
12
+ /** Normalize a user-supplied path to the lock's POSIX project-relative form. */
13
+ function projectPath(path) {
14
+ const normalized = (0, node_path_1.normalize)(path).split(node_path_1.sep).join("/").replace(/^\.\//, "");
15
+ if (normalized.startsWith("../") || normalized === ".." || normalized.startsWith("/")) {
16
+ throw new Error(`${path} is outside the project.`);
17
+ }
18
+ return normalized;
19
+ }
20
+ function isUtf8Text(content) {
21
+ return !content.includes(0) && Buffer.from(content.toString("utf8"), "utf8").equals(content);
22
+ }
23
+ function fileBuffer(file) {
24
+ return typeof file.content === "string" ? Buffer.from(file.content, "utf8") : file.content;
25
+ }
26
+ function templateDiff(projectRoot, templatesDir, paths) {
27
+ const target = (0, update_1.targetProject)(projectRoot, templatesDir);
28
+ const templateVersion = (0, lockfile_1.podokitVersion)();
29
+ const files = paths.map((input) => {
30
+ const path = projectPath(input);
31
+ const tier = target.lock.files[path]?.tier ?? null;
32
+ const generated = target.tree.get(path);
33
+ const abs = (0, node_path_1.join)(projectRoot, path);
34
+ if (!generated) {
35
+ return { path, tier, status: "not-generated", diff: "", binary: false };
36
+ }
37
+ if (!(0, node_fs_1.existsSync)(abs))
38
+ return { path, tier, status: "missing", diff: "", binary: false };
39
+ const templateBytes = fileBuffer(generated);
40
+ const diskBytes = (0, node_fs_1.readFileSync)(abs);
41
+ if (templateBytes.equals(diskBytes)) {
42
+ return { path, tier, status: "same", diff: "", binary: false };
43
+ }
44
+ const binary = !isUtf8Text(templateBytes) || !isUtf8Text(diskBytes);
45
+ return {
46
+ path,
47
+ tier,
48
+ status: "differs",
49
+ binary,
50
+ diff: binary
51
+ ? ""
52
+ : (0, text_diff_1.unifiedDiff)(templateBytes.toString("utf8"), diskBytes.toString("utf8"), {
53
+ fromLabel: `podokit-${templateVersion}/${path}`,
54
+ toLabel: path,
55
+ }),
56
+ };
57
+ });
58
+ return { projectVersion: target.manifest.podokitVersion, templateVersion, files };
59
+ }
60
+ /**
61
+ * Rewrite managed or assembled files with the template's content and record
62
+ * that content as the lock baseline. A file that `podo eject` made owned is
63
+ * handed back to PodoKit: its exact path leaves the manifest's owned paths, so
64
+ * future updates manage it again. Files owned because a template or module
65
+ * declares them owned, such as routes, are refused. Nothing is written unless
66
+ * every path can be restored.
67
+ */
68
+ function restore(projectRoot, templatesDir, paths) {
69
+ const target = (0, update_1.targetProject)(projectRoot, templatesDir);
70
+ const cliVersion = (0, lockfile_1.podokitVersion)();
71
+ if (target.manifest.podokitVersion !== cliVersion) {
72
+ throw new Error(`This project is on PodoKit ${target.manifest.podokitVersion} and this CLI renders ${cliVersion}. ` +
73
+ "Run podo update --apply first, or restore with the CLI version the project is on.");
74
+ }
75
+ const declared = new Set((0, update_1.declaredOwnedGlobs)(projectRoot, templatesDir, target.modules));
76
+ const requested = [...new Set(paths.map(projectPath))];
77
+ const ejected = requested.filter((path) => target.manifest.ownedGlobs.includes(path) && !declared.has(path));
78
+ const ownedGlobs = target.manifest.ownedGlobs.filter((glob) => !ejected.includes(glob));
79
+ const classifyGlobs = [...new Set([...declared, ...ownedGlobs])];
80
+ const writes = [];
81
+ for (const path of requested) {
82
+ const file = target.tree.get(path);
83
+ if (!file)
84
+ throw new Error(`PodoKit does not generate ${path}, so there is nothing to restore.`);
85
+ const tier = (0, lockfile_1.classifyTier)(path, fileBuffer(file), classifyGlobs, target.managedOverrides);
86
+ if (tier === "owned") {
87
+ throw new Error(`${path} is owned by the application through the template or a module, so PodoKit never manages it. ` +
88
+ `Run podo diff ${path} to see the template's version.`);
89
+ }
90
+ writes.push({ path, file, tier });
91
+ }
92
+ for (const { path, file, tier } of writes) {
93
+ const abs = (0, node_path_1.join)(projectRoot, path);
94
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(abs), { recursive: true });
95
+ (0, node_fs_1.writeFileSync)(abs, file.content);
96
+ target.lock.files[path] = { tier, outHash: (0, podokit_template_engine_1.hashContent)(fileBuffer(file)) };
97
+ }
98
+ (0, lockfile_1.writeFilesLock)(projectRoot, target.lock);
99
+ if (ejected.length)
100
+ (0, lockfile_1.writeManifest)(projectRoot, { ...target.manifest, ownedGlobs });
101
+ return { restored: writes.map((write) => write.path), remanaged: ejected };
102
+ }
@@ -54,7 +54,20 @@ Merge added module settings from `.env.example` into an existing `.env`.
54
54
  `apps/web/src/lib/server/proxy-timeouts.ts` (`APP_PROXY_TIMEOUTS`, `null` for
55
55
  none); never edit the managed deadline. Both lists take path prefixes with
56
56
  `:name` parameters, such as `/api/hosts/:hostId/file-transfers`, and the most
57
- specific prefix wins.
57
+ specific prefix wins. The proxy and `serverApiClient` send the request's public
58
+ origin to the API in `x-forwarded-host`/`x-forwarded-proto` only when
59
+ `CORS_ORIGIN` lists it; an API route may use them to choose among its configured
60
+ origins, never as a new origin.
61
+ - `apps/web/src/app.css` is managed. Put application styles (theme variable
62
+ overrides, `@theme` entries, fonts, base styles, and `@source` for packages
63
+ whose built components use Tailwind classes) in the app-owned
64
+ `apps/web/src/app.extensions.css`, which `app.css` imports last.
65
+ - `apps/web/vite.config.ts` is managed. Put Vite options (warmup, `optimizeDeps`,
66
+ `define`, plugins, allowed hosts) in `appViteConfig` and SvelteKit options
67
+ (`alias`, `csrf`) in `appSvelteKitConfig` of the app-owned
68
+ `apps/web/vite.extensions.ts`. `bun run dev` proxies the exact
69
+ `webSocketPaths` of `.podokit/dev.json` to the API like the `podo dev` gateway,
70
+ so do not add a WebSocket proxy of your own.
58
71
  - Server-side API calls (`serverApiClient`) give up after `BACKEND_TIMEOUT_MS`
59
72
  (2000), and the proxy answers 504 `BACKEND_TIMEOUT` when the API sends no
60
73
  response headers within `BACKEND_PROXY_TIMEOUT_MS` (30000). Once the API's
@@ -86,7 +99,17 @@ Merge added module settings from `.env.example` into an existing `.env`.
86
99
  Better Auth schema before the application migrations. `{{apiRun}} migration:run` runs
87
100
  only the application migrations and never creates Better Auth tables;
88
101
  `{{apiRun}} migration:revert` reverts the latest application migration.
89
- - e2e (Playwright, ships in `tests/`): `{{rootRun}} test:e2e`.
102
+ - Name a migration `apps/api/src/migrations/<timestamp>-<Name>.ts` with exactly one exported
103
+ migration class. The build lists compiled migrations in `dist/migrations/migrations.json` and
104
+ fails when one is lost; `{{apiRun}} migrate:list` prints what a build would run.
105
+ - e2e (Playwright, ships in `tests/`): `{{rootRun}} test:e2e`. Run the whole suite on more
106
+ workers with `E2E_WORKERS=2`, never `--workers`. Tag a test `{ tag: "@exclusive" }` when
107
+ it changes state other tests rely on (a server-wide setting, a seeded account's profile);
108
+ tagged tests run alone after the rest. Give each test its own mail and SMS recipients and
109
+ never empty the shared Mailpit inbox or SMS sink. See `tests/README.md`. The managed specs
110
+ read the app's first screen, public pages, and seeded accounts from the owned
111
+ `tests/helpers/app-paths.ts` (and `app-accounts.ts` with admin-dashboard): change those
112
+ instead of editing or ejecting a managed spec, and test the app's own screens in specs of its own.
90
113
  - Playwright officially runs on Node, so `bunx playwright` respects its Node
91
114
  shebang. Node LTS is a test-tool dependency only; API, web, worker, migrations,
92
115
  builds, and unit tests run on Bun.
@@ -121,7 +144,7 @@ changes need an e2e/unit test.
121
144
  This project is managed by the `podo` CLI. Files have an ownership tier recorded
122
145
  in `.podokit/files.lock` — respect it:
123
146
 
124
- - **owned** (your code: `apps/web/src/routes/**`, your components/services, `apps/api/src/app.extensions.ts`, and this `AGENTS.md`) — edit freely. Add application-owned services or module descriptors through `app.extensions.ts` instead of changing toolkit registrations. Set Bun server options (`maxRequestBodySize`, `idleTimeout`) with its `apiServerOptions` export rather than editing `main.ts`; `API_MAX_REQUEST_BODY_SIZE` and `API_IDLE_TIMEOUT` apply when the export leaves them out.
147
+ - **owned** (your code: `apps/web/src/routes/**`, your components/services, `apps/api/src/app.extensions.ts`, and this `AGENTS.md`) — edit freely. Add application-owned services or module descriptors through `app.extensions.ts` instead of changing toolkit registrations. Set Bun server options (`maxRequestBodySize`, `idleTimeout`) with its `apiServerOptions` export rather than editing `main.ts`; `API_MAX_REQUEST_BODY_SIZE` and `API_IDLE_TIMEOUT` apply when the export leaves them out. Declare application environment variables in the owned `apps/api/src/config/env.extensions.ts` (`appEnvShape`, `refineAppEnv`) instead of editing `env.validation.ts`, and TypeORM entities in `APP_ENTITIES` of the owned `apps/api/src/database/entities.ts` instead of editing `data-source.ts`. List the operations your API must keep in `APP_DOCUMENTED_ROUTES` of the owned `apps/api/src/core/api-contract.extensions.ts`; `{{apiRun}} contract` checks they are registered and documented. With `auth`, declare app on/off settings in `APP_FLAG_DEFAULTS` of the owned `apps/api/src/settings/app-flags.ts` instead of editing `flag-defaults.ts`.
125
148
  - **assembled** (e.g. `apps/api/src/app.ts`, `auth.ts`) — contain `// podokit:begin:… / …:end` fenced regions that the toolkit recomputes. Edit outside the fences only; **never hand-edit inside the fences**.
126
149
  - **managed** (bootstrap/config like `main.ts`) — the toolkit may update these; avoid gratuitous edits.
127
150
 
@@ -140,7 +163,8 @@ Useful commands:
140
163
  - `podo add <module>` — add a feature (auth, admin-dashboard, redis, bullmq, …). It wires itself into the app; run `podo add` with no argument to list modules.
141
164
  - `podo remove <module>` — un-apply a module (inverse of add; refuses if another module needs it, keeps files you edited).
142
165
  - `podo provider list` / `podo provider set <capability> <provider> [--apply]` — inspect or switch database, cache, object storage, events, and jobs. Set is dry-run by default and never migrates or deletes provider data. Local providers require one API process.
143
- - `podo status` / `podo diff` — see your local edits vs. what PodoKit generated.
166
+ - `podo status` / `podo diff` — see your local edits vs. what PodoKit generated; `podo diff <path>` shows a file's line diff against the template, owned files included.
167
+ - `podo restore <path>` — rewrite a managed file from the template, or hand an ejected file back to PodoKit.
144
168
  - `podo update` — pull in template/module improvements (3-way merges your edits; never touches owned files).
145
169
  - `podo deploy` — plan, apply, verify, or roll back an immutable release on Kubernetes or one Docker host. Production API WebSocket paths belong in the deployment profile's exact `exposure.webSocketPaths` allowlist, and API routes that take bodies above the web server's `BODY_SIZE_LIMIT` (file uploads) in `exposure.apiPaths` (and `.podokit/dev.json` `apiPaths` for `podo dev`; `podo add` and `podo deploy init` fill in the paths modules declare), which reach the API without the web proxy, so the API must check their size, session, and `Origin` itself; Compose trusts forwarding headers only from `exposure.trustedProxyCidrs`. Tag `vX.Y.Z` to build and push the images (`.github/workflows/release.yml`; check its runner — GitHub-hosted minutes are free for public repositories only). Use `.agents/skills/podokit-deploy`.
146
170
  - `podo deploy sync` — copy local build output into a running Compose deployment and restart it, for iterating without a release. Not a release: no image is published and the next `apply` discards it. Use `.agents/skills/podokit-deploy-fast`.
@@ -55,8 +55,9 @@ cp .env.example .env
55
55
  ```
56
56
 
57
57
  Open the URL printed by the command; a new project defaults to
58
- **http://{{projectName}}.localhost**. The committed `.podokit/dev.json` is the
59
- source of truth if you change the hostname. The first running PodoKit project starts
58
+ **http://{{devHostname}}**. The committed `.podokit/dev.json` is the
59
+ source of truth if you change the hostname: `podo dev` sets `CORS_ORIGIN` and
60
+ `BETTER_AUTH_URL` for the web and API containers from it. The first running PodoKit project starts
60
61
  one user-level Traefik gateway on `127.0.0.1:80`; additional projects reuse it and
61
62
  route by that hostname. Even a single app uses
62
63
  the same topology, with one route and no project-specific host port.
@@ -188,11 +189,11 @@ HTTP, fix its public origin at build time instead:
188
189
  docker build --build-arg PODOKIT_BUILD_ORIGIN=https://example.com -f apps/web/Dockerfile -t {{projectName}}-web .
189
190
  ```
190
191
 
191
- For a Docker Compose deployment behind a trusted reverse proxy, set
192
- `PROTOCOL_HEADER=x-forwarded-proto` and `HOST_HEADER=x-forwarded-host` in the
193
- deployment profile runtime configuration and list the proxy's direct CIDR blocks in
194
- `exposure.trustedProxyCidrs`. Never trust client-controlled forwarded headers on a
195
- directly exposed container.
192
+ Set `exposure.edgeProxies` in the deployment profile to the number of reverse
193
+ proxies in front of the deployment; PodoKit derives which forwarded headers web and
194
+ API trust from it, and trusts none when it is `0`. With a Compose gateway, also list
195
+ the proxy's direct CIDR blocks in `exposure.trustedProxyCidrs`. Never count a proxy
196
+ that clients can bypass.
196
197
 
197
198
  If the production API exposes WebSocket endpoints, list their exact paths under
198
199
  `exposure.webSocketPaths` in the deployment profile. The Compose driver then keeps a
@@ -10,14 +10,15 @@
10
10
  "test": "bun test src",
11
11
  "contract": "bun scripts/verify-api-contract.ts",
12
12
  "migrate:all": "bun scripts/run-migrations.mjs",
13
- "typeorm": "bunx typeorm-ts-node-commonjs -d src/database/data-source.ts",
13
+ "migrate:list": "bun scripts/run-migrations.mjs --list",
14
+ "typeorm": "bunx --bun typeorm -d src/database/data-source.ts",
14
15
  "migration:run": "bun src/database/migrate.ts",
15
16
  "migration:revert": "bun src/database/migrate.ts --revert",
16
17
  "migration:generate": "bun run typeorm -- migration:generate"
17
18
  },
18
19
  "dependencies": {
19
- "@podosoft/podokit-contracts": "^0.5.0",
20
- "@podosoft/podokit-runtime": "^0.4.1",
20
+ "@podosoft/podokit-contracts": "^0.6.0",
21
+ "@podosoft/podokit-runtime": "^0.5.0",
21
22
  "@elysia/cors": "^1.4.2",
22
23
  "@elysia/openapi": "^1.4.15",
23
24
  "dotenv": "^16.4.5",
@@ -30,7 +31,6 @@
30
31
  "devDependencies": {
31
32
  "@types/bun": "^1.3.9",
32
33
  "@types/node": "^20.17.0",
33
- "ts-node": "^10.9.2",
34
34
  "typescript": "^6.0.3"
35
35
  }
36
36
  }
@@ -0,0 +1,6 @@
1
+ // This owned file is never overwritten by `podo update`. List entry points the
2
+ // API build bundles next to PodoKit's, such as a maintenance command run from
3
+ // the image. Each must exist. For example:
4
+ //
5
+ // export const appBuildTargets = [{ entrypoint: "scripts/repair-data.ts", outdir: "dist/scripts" }];
6
+ export const appBuildTargets = [];