@podosoft/podokit 1.3.0 → 1.4.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 (145) hide show
  1. package/dist/add.d.ts +10 -1
  2. package/dist/add.js +20 -7
  3. package/dist/api-paths.d.ts +34 -0
  4. package/dist/api-paths.js +103 -0
  5. package/dist/deploy-compose-profile.d.ts +4 -0
  6. package/dist/deploy-compose-profile.js +13 -2
  7. package/dist/deploy-compose-render.d.ts +1 -1
  8. package/dist/deploy-compose-render.js +23 -7
  9. package/dist/deploy-compose.js +2 -1
  10. package/dist/deploy-profile.d.ts +3 -0
  11. package/dist/deploy-profile.js +12 -4
  12. package/dist/deploy-render.js +54 -8
  13. package/dist/deploy.js +21 -1
  14. package/dist/dev.d.ts +14 -0
  15. package/dist/dev.js +152 -10
  16. package/dist/index.js +15 -2
  17. package/dist/lib.d.ts +2 -0
  18. package/dist/lib.js +4 -2
  19. package/dist/lockfile.d.ts +3 -0
  20. package/dist/lockfile.js +9 -0
  21. package/dist/module-api-paths.d.ts +52 -0
  22. package/dist/module-api-paths.js +189 -0
  23. package/dist/remove.d.ts +3 -0
  24. package/dist/remove.js +12 -3
  25. package/dist/templates/fullstack/AGENTS.md +17 -2
  26. package/dist/templates/fullstack/README.md +19 -5
  27. package/dist/templates/fullstack/apps/api/package.json +1 -1
  28. package/dist/templates/fullstack/apps/api/src/app.extensions.ts +7 -0
  29. package/dist/templates/fullstack/apps/api/src/app.spec.ts +20 -1
  30. package/dist/templates/fullstack/apps/api/src/app.ts +11 -0
  31. package/dist/templates/fullstack/apps/api/src/config/env.validation.ts +3 -0
  32. package/dist/templates/fullstack/apps/api/src/config/redis.connection.spec.ts +48 -2
  33. package/dist/templates/fullstack/apps/api/src/config/redis.connection.ts +45 -0
  34. package/dist/templates/fullstack/apps/api/src/config/server-options.spec.ts +46 -0
  35. package/dist/templates/fullstack/apps/api/src/config/server-options.ts +48 -0
  36. package/dist/templates/fullstack/apps/api/src/core/api-contract.spec.ts +1 -0
  37. package/dist/templates/fullstack/apps/api/src/core/api-contract.ts +9 -1
  38. package/dist/templates/fullstack/apps/api/src/core/services.spec.ts +196 -0
  39. package/dist/templates/fullstack/apps/api/src/core/services.ts +181 -2
  40. package/dist/templates/fullstack/apps/api/src/database/database.spec.ts +8 -1
  41. package/dist/templates/fullstack/apps/api/src/database/database.ts +29 -10
  42. package/dist/templates/fullstack/apps/api/src/database/pool-guard.spec.ts +188 -0
  43. package/dist/templates/fullstack/apps/api/src/database/pool-guard.ts +151 -0
  44. package/dist/templates/fullstack/apps/api/src/health/health.plugin.spec.ts +28 -4
  45. package/dist/templates/fullstack/apps/api/src/health/health.plugin.ts +10 -2
  46. package/dist/templates/fullstack/apps/api/src/main.ts +2 -1
  47. package/dist/templates/fullstack/apps/web/components.json +3 -1
  48. package/dist/templates/fullstack/apps/web/package.json +1 -1
  49. package/dist/templates/fullstack/apps/web/src/lib/components/ui/textarea/index.ts +7 -0
  50. package/dist/templates/fullstack/apps/web/src/lib/components/ui/textarea/textarea.svelte +22 -0
  51. package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.test.ts +276 -1
  52. package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.ts +194 -9
  53. package/dist/templates/fullstack/apps/web/src/lib/server/body-limits.ts +15 -0
  54. package/dist/templates/fullstack/apps/web/src/lib/server/proxy-headers.ts +12 -0
  55. package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/references/profile.md +10 -3
  56. package/dist/templates/fullstack/dot-claude/skills/podokit-elysia-endpoint/SKILL.md +5 -0
  57. package/dist/templates/fullstack/dot-claude/skills/podokit-sveltekit-route/SKILL.md +7 -1
  58. package/dist/templates/fullstack/dot-devcontainer/devcontainer.json +1 -0
  59. package/dist/templates/fullstack/{dot-env.docker → dot-env.docker.example} +7 -4
  60. package/dist/templates/fullstack/dot-env.example +10 -0
  61. package/dist/templates/fullstack/dot-gitignore +1 -0
  62. package/dist/templates/fullstack/dot-podokit/dev.json +2 -1
  63. package/dist/templates/fullstack/infra/k3s/README.md +3 -1
  64. package/dist/templates/modules/admin-dashboard/files/apps/api/src/bootstrap-admin-runtime.ts +1 -1
  65. package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +27 -9
  66. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/asset-paths.ts +11 -0
  67. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/runtime-context.test.ts +125 -0
  68. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/runtime-context.ts +107 -0
  69. package/dist/templates/modules/admin-dashboard/files/tests/api/profile-image.api.spec.ts +2 -1
  70. package/dist/templates/modules/admin-dashboard/files/tests/api/site-settings.api.spec.ts +2 -1
  71. package/dist/templates/modules/admin-dashboard/files/tests/helpers/disposable-users.ts +10 -10
  72. package/dist/templates/modules/admin-dashboard/files/tests/helpers/object-response.ts +12 -0
  73. package/dist/templates/modules/admin-dashboard/module.manifest.json +3 -2
  74. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.spec.ts +26 -0
  75. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +17 -2
  76. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.service.ts +8 -4
  77. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +2 -2
  78. package/dist/templates/modules/auth/files/apps/api/src/auth/database-outage.spec.ts +114 -0
  79. package/dist/templates/modules/auth/files/apps/api/src/auth/database-outage.ts +50 -0
  80. package/dist/templates/modules/auth/files/apps/api/src/auth/db.spec.ts +4 -1
  81. package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +11 -2
  82. package/dist/templates/modules/auth/files/apps/api/src/auth/maintenance-command.ts +5 -0
  83. package/dist/templates/modules/auth/files/apps/api/src/auth/maintenance.ts +14 -0
  84. package/dist/templates/modules/auth/files/apps/api/src/auth/postgres-pool.spec.ts +136 -0
  85. package/dist/templates/modules/auth/files/apps/api/src/auth/postgres-pool.ts +40 -0
  86. package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +3 -2
  87. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.spec.ts +121 -1
  88. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.ts +108 -10
  89. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/jobs.module.ts +3 -1
  90. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/queue.ts +31 -3
  91. package/dist/templates/modules/bullmq/module.manifest.json +5 -1
  92. package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/events-redis.module.ts +2 -4
  93. package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/redis-event.bus.spec.ts +224 -0
  94. package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/redis-event.bus.ts +124 -35
  95. package/dist/templates/modules/events-redis/module.manifest.json +4 -0
  96. package/dist/templates/modules/file-upload/files/apps/api/src/files/chunked-uploads.routes.ts +136 -0
  97. package/dist/templates/modules/file-upload/files/apps/api/src/files/chunked-uploads.spec.ts +636 -0
  98. package/dist/templates/modules/file-upload/files/apps/api/src/files/chunked-uploads.ts +673 -0
  99. package/dist/templates/modules/file-upload/files/apps/api/src/files/files.module.spec.ts +10 -4
  100. package/dist/templates/modules/file-upload/files/apps/api/src/files/files.module.ts +37 -8
  101. package/dist/templates/modules/file-upload/files/apps/api/src/files/upload-config.ts +99 -0
  102. package/dist/templates/modules/file-upload/files/apps/api/src/migrations/1792100000000-InitFileUploadSessions.ts +49 -0
  103. package/dist/templates/modules/file-upload/files/apps/web/src/lib/uploads.test.ts +456 -0
  104. package/dist/templates/modules/file-upload/files/apps/web/src/lib/uploads.ts +356 -0
  105. package/dist/templates/modules/file-upload/files/tests/api/chunked-uploads.api.spec.ts +147 -0
  106. package/dist/templates/modules/file-upload/module.manifest.json +34 -6
  107. package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/jobs-local.module.ts +1 -1
  108. package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.spec.ts +23 -0
  109. package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.ts +30 -20
  110. package/dist/templates/modules/jobs-local/module.manifest.json +3 -0
  111. package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/download-url.ts +20 -0
  112. package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/storage-local.module.spec.ts +79 -12
  113. package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/storage-local.module.ts +16 -1
  114. package/dist/templates/modules/object-storage-local/module.manifest.json +11 -2
  115. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-multipart.spec.ts +115 -0
  116. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-multipart.ts +171 -0
  117. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-object.store.spec.ts +222 -9
  118. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-object.store.ts +55 -19
  119. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-signer.spec.ts +71 -0
  120. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-signer.ts +98 -0
  121. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage-demo.spec.ts +6 -3
  122. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.config.ts +7 -2
  123. package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.service.ts +187 -44
  124. package/dist/templates/modules/object-storage-s3/module.manifest.json +10 -2
  125. package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +11 -22
  126. package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.routes.spec.ts +113 -0
  127. package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.routes.ts +122 -0
  128. package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +24 -0
  129. package/dist/templates/modules/rate-limit/module.manifest.json +7 -0
  130. package/dist/templates/modules/redis/files/apps/api/src/redis/redis-cache.store.spec.ts +14 -1
  131. package/dist/templates/modules/redis/files/apps/api/src/redis/redis-cache.store.ts +9 -12
  132. package/dist/templates/modules/redis/files/apps/api/src/redis/redis.module.ts +16 -4
  133. package/dist/templates/modules/redis/files/apps/api/src/redis/redis.service.spec.ts +233 -5
  134. package/dist/templates/modules/redis/files/apps/api/src/redis/redis.service.ts +134 -32
  135. package/dist/templates/modules/redis/module.manifest.json +4 -0
  136. package/dist/templates/modules/sse/files/apps/api/src/events/event-stream.ts +11 -4
  137. package/dist/templates/modules/sse/files/apps/api/src/events/events.service.spec.ts +33 -0
  138. package/dist/templates/modules/sse/files/apps/api/src/events/events.service.ts +7 -0
  139. package/dist/templates/todo/AGENTS.md +9 -1
  140. package/dist/templates/todo/README.md +18 -5
  141. package/dist/update.d.ts +5 -0
  142. package/dist/update.js +35 -0
  143. package/package.json +2 -2
  144. package/dist/templates/modules/auth/files/apps/api/src/auth/schema-validation.ts +0 -13
  145. package/dist/templates/modules/auth/files/apps/api/src/auth/skip-schema-validation.ts +0 -5
@@ -38,6 +38,22 @@ function user(userId: string, roles: string[] = [], organizationId?: string): Ev
38
38
  return { userId, roles, ...(organizationId ? { organizationId } : {}) };
39
39
  }
40
40
 
41
+ /** A memory bus that can report a transport interruption, as the Redis bus does after reconnecting. */
42
+ class InterruptibleBus extends MemoryEventBus {
43
+ readonly listeners = new Set<() => void>();
44
+
45
+ onInterruption(listener: () => void): () => void {
46
+ this.listeners.add(listener);
47
+ return () => {
48
+ this.listeners.delete(listener);
49
+ };
50
+ }
51
+
52
+ interrupt(): void {
53
+ for (const listener of this.listeners) listener();
54
+ }
55
+ }
56
+
41
57
  const services: EventsService[] = [];
42
58
 
43
59
  async function service(
@@ -268,6 +284,23 @@ describe("per-connection safety", () => {
268
284
  expect(decoded(await stream.next())).toEqual({ topic: "t", data: 6 });
269
285
  });
270
286
 
287
+ it("tells every open stream to resync after a transport interruption", async () => {
288
+ const bus = new InterruptibleBus();
289
+ const events = await service({}, bus);
290
+ const topical = await open(events, user("a"), ["t"]);
291
+ const everything = await open(events, user("b"));
292
+ await events.publishAsync({ topic: "t", data: 1 });
293
+
294
+ bus.interrupt();
295
+ expect(await topical.next()).toEqual({ kind: "resync" });
296
+ expect(await everything.next()).toEqual({ kind: "resync" });
297
+ await events.publishAsync({ topic: "t", data: 2 });
298
+ expect(decoded(await topical.next())).toEqual({ topic: "t", data: 2 });
299
+
300
+ await events.close();
301
+ expect(bus.listeners.size).toBe(0);
302
+ });
303
+
271
304
  it("reuses one heartbeat timer per connection and releases it on disconnect", async () => {
272
305
  const scheduler = fakeScheduler();
273
306
  const events = await service({ scheduler });
@@ -44,6 +44,7 @@ export class EventsService {
44
44
  private readonly everyTopic = new Set<EventStream>();
45
45
  private unregisterReadiness?: () => void;
46
46
  private unsubscribe?: Unsubscribe;
47
+ private stopResync?: () => void;
47
48
 
48
49
  constructor(
49
50
  private readonly transport: EventBus,
@@ -74,6 +75,11 @@ export class EventsService {
74
75
  }
75
76
  this.deliver(envelope);
76
77
  });
78
+ // Events published while the transport was down never arrive, so open
79
+ // streams are told to reload once it is back.
80
+ this.stopResync = this.transport.onInterruption?.(() => {
81
+ for (const stream of this.streams) stream.resync();
82
+ });
77
83
  this.unregisterReadiness = this.readiness.register("events", () => this.transport.ready());
78
84
  }
79
85
 
@@ -133,6 +139,7 @@ export class EventsService {
133
139
 
134
140
  async close(): Promise<void> {
135
141
  this.unregisterReadiness?.();
142
+ this.stopResync?.();
136
143
  await this.unsubscribe?.();
137
144
  for (const stream of [...this.streams]) stream.close();
138
145
  }
@@ -42,6 +42,14 @@ Merge added module settings from `.env.example` into an existing `.env`.
42
42
 
43
43
  - **Ports: web = 5001, api = 5002.** The web app reaches the API only through its
44
44
  SvelteKit server proxy (`/api/*`), never with a direct browser `fetch`.
45
+ - The proxy (`apps/web/src/lib/server/backend-proxy.ts`) is managed and passes only
46
+ allowlisted headers. List extra request and response header names in the
47
+ app-owned `apps/web/src/lib/server/proxy-headers.ts`
48
+ (`APP_FORWARDED_REQUEST_HEADERS`, `APP_RELAYED_RESPONSE_HEADERS`); never edit the
49
+ managed lists. Cap a route's request body in the app-owned
50
+ `apps/web/src/lib/server/body-limits.ts` (`APP_BODY_LIMITS`, 413
51
+ `PAYLOAD_TOO_LARGE`). `BODY_SIZE_LIMIT` is the ceiling for every web route, so
52
+ uploads larger than it must use a direct API path (`apiPaths`) instead.
45
53
  - Build: `{{rootRun}} build` · Type-check/lint: `{{rootRun}} lint` · Unit tests: `{{rootRun}} test`
46
54
  - Per workspace: `{{apiRun}} build` / `{{webRun}} build`.
47
55
  - Application migrations (PostgreSQL or SQLite): `{{apiRun}} migration:run`.
@@ -92,7 +100,7 @@ Useful commands:
92
100
  - `podo remove <module>` — un-apply a module (inverse of add; refuses if another module needs it, keeps files you edited).
93
101
  - `podo status` / `podo diff` — see your local edits vs. what PodoKit generated.
94
102
  - `podo update` — pull in template/module improvements (3-way merges your edits; never touches owned files).
95
- - `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; 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`.
103
+ - `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`.
96
104
  - `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`.
97
105
 
98
106
  ## Commit conventions
@@ -63,8 +63,17 @@ the same topology, with one route and no project-specific host port.
63
63
 
64
64
  When the API exposes WebSocket endpoints, add their exact paths to
65
65
  `.podokit/dev.json` under `webSocketPaths`. The shared gateway sends only those paths
66
- directly to the API and leaves every other path on the web app. Add `publicUrl` to the
67
- same file when an HTTPS tunnel preserves its public `Host` header.
66
+ directly to the API and leaves every other path on the web app. Routes that accept
67
+ bodies larger than the web server's `BODY_SIZE_LIMIT`, such as file uploads, go under
68
+ `apiPaths` as prefixes like `/api/files/uploads/`; the gateway removes `/api` and
69
+ sends them to the API without the web proxy. `podo add` adds the paths a module
70
+ declares, such as `file-upload`'s, to this file and to every deployment profile. Add `publicUrl` to the same file when
71
+ an HTTPS tunnel preserves its public `Host` header.
72
+
73
+ The containers read `.env.docker`, which Git ignores. `dev` commands create it from
74
+ the committed `.env.docker.example` when it is missing, as in a fresh clone, and fill
75
+ each empty `*_SECRET` value with a random one; `dev env` does only that. Keep shared
76
+ defaults in the example and credentials only in `.env.docker`.
68
77
 
69
78
  In a second terminal, apply the included Todo migration and use the lifecycle helpers:
70
79
 
@@ -80,7 +89,9 @@ In a second terminal, apply the included Todo migration and use the lifecycle he
80
89
  `dev watch` reads `.podokit/manifest.json` and automatically activates `cache`,
81
90
  `storage`, and `queue` when installed modules require Redis, Silo, or a worker.
82
91
  `dev up` uses the same shared gateway and module profiles without keeping Compose
83
- Watch attached.
92
+ Watch attached. It rebuilds the images from the current source first, because Compose
93
+ Watch syncs edits into running containers only: a container recreated from an older
94
+ image would lose them. Pass `--no-build` to start from the existing images.
84
95
  You can still activate an additional Compose profile explicitly:
85
96
 
86
97
  ```bash
@@ -88,7 +99,8 @@ You can still activate an additional Compose profile explicitly:
88
99
  ```
89
100
 
90
101
  Explicit profile flags are preserved for other lifecycle commands. `dev down`
91
- activates all profiles while removing this project's stack and route. If it is
102
+ first stops this project's running `dev watch`, so the next `dev watch` starts
103
+ cleanly, then activates all profiles while removing this project's stack and route. If it is
92
104
  the final registered
93
105
  project, it also removes the shared gateway and network. Source changes are synced
94
106
  through Compose Watch, including Vite HMR on the same portless browser origin.
@@ -149,5 +161,6 @@ PodoKit [deployment guide](https://github.com/podosoft-dev/podokit/blob/main/doc
149
161
 
150
162
  For a Docker Compose deployment with API WebSockets, list their exact paths under
151
163
  `exposure.webSocketPaths`. The driver keeps one published port, routes those paths
152
- to API, and sends every other request to web. If an external reverse proxy supplies
164
+ to API, and sends every other request to web. Large-upload routes listed under
165
+ `exposure.apiPaths` reach the API the same way, with `/api` removed. If an external reverse proxy supplies
153
166
  forwarding headers, list only its direct CIDR blocks in `exposure.trustedProxyCidrs`.
package/dist/update.d.ts CHANGED
@@ -2,6 +2,7 @@ import { type TemplateVars } from "@podosoft/podokit-template-engine";
2
2
  import { type Tier } from "./lockfile";
3
3
  import type { ProviderCapability } from "@podosoft/podokit-runtime";
4
4
  import type { Toolchain } from "./toolchain";
5
+ import { type ApiPathResult } from "./module-api-paths";
5
6
  /**
6
7
  * `podo update` planner. For now it produces a dry-run plan only: it assembles
7
8
  * the new-version tree from the installed CLI's templates and compares it to the
@@ -36,6 +37,8 @@ export interface UpdatePlan {
36
37
  /** Modules the update adds, ordered ahead of the modules that need them. */
37
38
  addedModules: AddedModule[];
38
39
  changes: FileChange[];
40
+ /** Direct API paths that modules newly declare, per configuration file. */
41
+ apiPaths: ApiPathResult;
39
42
  }
40
43
  /**
41
44
  * Build the update plan. `templatesDir` is the installed CLI's template set (the
@@ -77,6 +80,8 @@ export interface ApplyResult {
77
80
  merged: string[];
78
81
  /** Files written with conflict markers for manual resolution. */
79
82
  conflicts: string[];
83
+ /** Direct API paths added to `.podokit/dev.json` and deployment profiles. */
84
+ apiPaths: ApiPathResult;
80
85
  }
81
86
  /**
82
87
  * Apply an update to the working copy. Clean updates and additions are written,
package/dist/update.js CHANGED
@@ -14,6 +14,7 @@ const lockfile_1 = require("./lockfile");
14
14
  const inspect_1 = require("./inspect");
15
15
  const add_1 = require("./add");
16
16
  const module_migrations_1 = require("./module-migrations");
17
+ const module_api_paths_1 = require("./module-api-paths");
17
18
  function treeText(tree, path) {
18
19
  const file = tree.get(path);
19
20
  if (!file)
@@ -90,6 +91,18 @@ function targetModules(projectRoot, templatesDir, modules, providers) {
90
91
  }
91
92
  return { modules: ordered, added };
92
93
  }
94
+ function targetApiPaths(projectRoot, templatesDir, modules) {
95
+ const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
96
+ const declared = new Map();
97
+ const pending = [];
98
+ for (const module of modules) {
99
+ const resolved = (0, add_1.resolveModule)(module.packageName ?? module.name, modulesDir, projectRoot);
100
+ const paths = resolved ? ((0, add_1.readModuleManifest)(resolved.dir).apiPaths ?? []) : [];
101
+ declared.set(module.name, paths);
102
+ pending.push(...(0, module_api_paths_1.pendingApiPaths)(module, paths));
103
+ }
104
+ return { declared, pending };
105
+ }
93
106
  function targetManagedOverrides(projectRoot, templatesDir, modules, current = []) {
94
107
  const overrides = new Set(current);
95
108
  const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
@@ -222,6 +235,16 @@ function planUpdate(projectRoot, templatesDir, options = {}) {
222
235
  note: edited ? "you edited this; update would need a merge" : "clean update",
223
236
  });
224
237
  }
238
+ // A configuration file the update creates, such as a missing
239
+ // .podokit/dev.json, receives the paths after it is written.
240
+ const created = new Set(changes.filter((change) => change.action === "add").map((change) => change.path));
241
+ const apiPaths = options.onlyPaths
242
+ ? { changed: [], skipped: [] }
243
+ : (0, module_api_paths_1.addApiPaths)(projectRoot, targetApiPaths(projectRoot, templatesDir, modules).pending, {
244
+ dryRun: true,
245
+ read: (file) => diskContent(projectRoot, file)?.toString("utf8")
246
+ ?? (created.has(file) ? treeText(newTree, file) : null),
247
+ });
225
248
  return {
226
249
  fromVersion: manifest.podokitVersion,
227
250
  toVersion: (0, lockfile_1.podokitVersion)(),
@@ -229,6 +252,7 @@ function planUpdate(projectRoot, templatesDir, options = {}) {
229
252
  modules: modules.map((module) => module.name),
230
253
  addedModules: added,
231
254
  changes,
255
+ apiPaths,
232
256
  };
233
257
  }
234
258
  function assertRecordedExternalModules(moduleRoot, templatesDir, modules) {
@@ -420,6 +444,7 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
420
444
  removed: [],
421
445
  merged: [],
422
446
  conflicts: [],
447
+ apiPaths: { changed: [], skipped: [] },
423
448
  };
424
449
  for (const change of plan.changes) {
425
450
  const newText = treeText(newTree, change.path);
@@ -447,6 +472,9 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
447
472
  result.merged.push(change.path);
448
473
  }
449
474
  }
475
+ const apiPaths = options.onlyPaths ? undefined : targetApiPaths(projectRoot, templatesDir, modules);
476
+ if (apiPaths)
477
+ result.apiPaths = (0, module_api_paths_1.addApiPaths)(projectRoot, apiPaths.pending);
450
478
  // Keep the assembled target as the managed baseline. A merged working file
451
479
  // intentionally remains drifted so a future update performs another 3-way
452
480
  // merge instead of treating the user's lines as clean generated output.
@@ -466,6 +494,12 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
466
494
  ...(appliedMigrations.get(module.name) ?? []),
467
495
  ]),
468
496
  ];
497
+ const appliedApiPaths = [
498
+ ...new Set([
499
+ ...(module.appliedApiPaths ?? []),
500
+ ...(apiPaths?.declared.get(module.name) ?? []),
501
+ ]),
502
+ ];
469
503
  return {
470
504
  ...module,
471
505
  ...(resolved?.packageName
@@ -475,6 +509,7 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
475
509
  }
476
510
  : {}),
477
511
  ...(applied.length ? { appliedMigrations: applied } : {}),
512
+ ...(appliedApiPaths.length ? { appliedApiPaths } : {}),
478
513
  };
479
514
  });
480
515
  (0, lockfile_1.writeManifest)(projectRoot, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podosoft/podokit",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "An opinionated Bun and Elysia full-stack TypeScript starter toolkit and CLI with SvelteKit, TailwindCSS, shadcn-svelte, Docker, and k3s.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -45,7 +45,7 @@
45
45
  "test": "vitest run"
46
46
  },
47
47
  "dependencies": {
48
- "@podosoft/podokit-runtime": "^0.3.0",
48
+ "@podosoft/podokit-runtime": "^0.4.0",
49
49
  "@podosoft/podokit-template-engine": "^0.4.2"
50
50
  }
51
51
  }
@@ -1,13 +0,0 @@
1
- // Better Auth validates the database schema when an instance starts, and fails
2
- // authentication requests while the schema does not match. Servers keep that check.
3
- // The migration runner builds an instance only to read its options, before the
4
- // schema it is about to change exists or matches, so it opts out for its process.
5
- let validateSchema = true;
6
-
7
- export function skipAuthSchemaValidation(): void {
8
- validateSchema = false;
9
- }
10
-
11
- export function authSchemaValidation(): boolean {
12
- return validateSchema;
13
- }
@@ -1,5 +0,0 @@
1
- // Imported first by the migration runner, before ./auth creates the Better Auth
2
- // instance whose startup check would report the schema those migrations change.
3
- import { skipAuthSchemaValidation } from "./schema-validation";
4
-
5
- skipAuthSchemaValidation();