@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.
- package/dist/add.d.ts +10 -1
- package/dist/add.js +20 -7
- package/dist/api-paths.d.ts +34 -0
- package/dist/api-paths.js +103 -0
- package/dist/deploy-compose-profile.d.ts +4 -0
- package/dist/deploy-compose-profile.js +13 -2
- package/dist/deploy-compose-render.d.ts +1 -1
- package/dist/deploy-compose-render.js +23 -7
- package/dist/deploy-compose.js +2 -1
- package/dist/deploy-profile.d.ts +3 -0
- package/dist/deploy-profile.js +12 -4
- package/dist/deploy-render.js +54 -8
- package/dist/deploy.js +21 -1
- package/dist/dev.d.ts +14 -0
- package/dist/dev.js +152 -10
- package/dist/index.js +15 -2
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +4 -2
- package/dist/lockfile.d.ts +3 -0
- package/dist/lockfile.js +9 -0
- package/dist/module-api-paths.d.ts +52 -0
- package/dist/module-api-paths.js +189 -0
- package/dist/remove.d.ts +3 -0
- package/dist/remove.js +12 -3
- package/dist/templates/fullstack/AGENTS.md +17 -2
- package/dist/templates/fullstack/README.md +19 -5
- package/dist/templates/fullstack/apps/api/package.json +1 -1
- package/dist/templates/fullstack/apps/api/src/app.extensions.ts +7 -0
- package/dist/templates/fullstack/apps/api/src/app.spec.ts +20 -1
- package/dist/templates/fullstack/apps/api/src/app.ts +11 -0
- package/dist/templates/fullstack/apps/api/src/config/env.validation.ts +3 -0
- package/dist/templates/fullstack/apps/api/src/config/redis.connection.spec.ts +48 -2
- package/dist/templates/fullstack/apps/api/src/config/redis.connection.ts +45 -0
- package/dist/templates/fullstack/apps/api/src/config/server-options.spec.ts +46 -0
- package/dist/templates/fullstack/apps/api/src/config/server-options.ts +48 -0
- package/dist/templates/fullstack/apps/api/src/core/api-contract.spec.ts +1 -0
- package/dist/templates/fullstack/apps/api/src/core/api-contract.ts +9 -1
- package/dist/templates/fullstack/apps/api/src/core/services.spec.ts +196 -0
- package/dist/templates/fullstack/apps/api/src/core/services.ts +181 -2
- package/dist/templates/fullstack/apps/api/src/database/database.spec.ts +8 -1
- package/dist/templates/fullstack/apps/api/src/database/database.ts +29 -10
- package/dist/templates/fullstack/apps/api/src/database/pool-guard.spec.ts +188 -0
- package/dist/templates/fullstack/apps/api/src/database/pool-guard.ts +151 -0
- package/dist/templates/fullstack/apps/api/src/health/health.plugin.spec.ts +28 -4
- package/dist/templates/fullstack/apps/api/src/health/health.plugin.ts +10 -2
- package/dist/templates/fullstack/apps/api/src/main.ts +2 -1
- package/dist/templates/fullstack/apps/web/components.json +3 -1
- package/dist/templates/fullstack/apps/web/package.json +1 -1
- package/dist/templates/fullstack/apps/web/src/lib/components/ui/textarea/index.ts +7 -0
- package/dist/templates/fullstack/apps/web/src/lib/components/ui/textarea/textarea.svelte +22 -0
- package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.test.ts +276 -1
- package/dist/templates/fullstack/apps/web/src/lib/server/backend-proxy.ts +194 -9
- package/dist/templates/fullstack/apps/web/src/lib/server/body-limits.ts +15 -0
- package/dist/templates/fullstack/apps/web/src/lib/server/proxy-headers.ts +12 -0
- package/dist/templates/fullstack/dot-agents/skills/podokit-deploy/references/profile.md +10 -3
- package/dist/templates/fullstack/dot-claude/skills/podokit-elysia-endpoint/SKILL.md +5 -0
- package/dist/templates/fullstack/dot-claude/skills/podokit-sveltekit-route/SKILL.md +7 -1
- package/dist/templates/fullstack/dot-devcontainer/devcontainer.json +1 -0
- package/dist/templates/fullstack/{dot-env.docker → dot-env.docker.example} +7 -4
- package/dist/templates/fullstack/dot-env.example +10 -0
- package/dist/templates/fullstack/dot-gitignore +1 -0
- package/dist/templates/fullstack/dot-podokit/dev.json +2 -1
- package/dist/templates/fullstack/infra/k3s/README.md +3 -1
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/bootstrap-admin-runtime.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +27 -9
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/asset-paths.ts +11 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/runtime-context.test.ts +125 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/runtime-context.ts +107 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/profile-image.api.spec.ts +2 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/site-settings.api.spec.ts +2 -1
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/disposable-users.ts +10 -10
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/object-response.ts +12 -0
- package/dist/templates/modules/admin-dashboard/module.manifest.json +3 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.spec.ts +26 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +17 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.service.ts +8 -4
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +2 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/database-outage.spec.ts +114 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/database-outage.ts +50 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/db.spec.ts +4 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +11 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/maintenance-command.ts +5 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/maintenance.ts +14 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/postgres-pool.spec.ts +136 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/postgres-pool.ts +40 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +3 -2
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.spec.ts +121 -1
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/bullmq-job.queue.ts +108 -10
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/jobs.module.ts +3 -1
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/queue.ts +31 -3
- package/dist/templates/modules/bullmq/module.manifest.json +5 -1
- package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/events-redis.module.ts +2 -4
- package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/redis-event.bus.spec.ts +224 -0
- package/dist/templates/modules/events-redis/files/apps/api/src/events-provider-redis/redis-event.bus.ts +124 -35
- package/dist/templates/modules/events-redis/module.manifest.json +4 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/chunked-uploads.routes.ts +136 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/chunked-uploads.spec.ts +636 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/chunked-uploads.ts +673 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/files.module.spec.ts +10 -4
- package/dist/templates/modules/file-upload/files/apps/api/src/files/files.module.ts +37 -8
- package/dist/templates/modules/file-upload/files/apps/api/src/files/upload-config.ts +99 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/migrations/1792100000000-InitFileUploadSessions.ts +49 -0
- package/dist/templates/modules/file-upload/files/apps/web/src/lib/uploads.test.ts +456 -0
- package/dist/templates/modules/file-upload/files/apps/web/src/lib/uploads.ts +356 -0
- package/dist/templates/modules/file-upload/files/tests/api/chunked-uploads.api.spec.ts +147 -0
- package/dist/templates/modules/file-upload/module.manifest.json +34 -6
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/jobs-local.module.ts +1 -1
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.spec.ts +23 -0
- package/dist/templates/modules/jobs-local/files/apps/api/src/jobs-local/local-job.queue.ts +30 -20
- package/dist/templates/modules/jobs-local/module.manifest.json +3 -0
- package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/download-url.ts +20 -0
- package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/storage-local.module.spec.ts +79 -12
- package/dist/templates/modules/object-storage-local/files/apps/api/src/storage-local/storage-local.module.ts +16 -1
- package/dist/templates/modules/object-storage-local/module.manifest.json +11 -2
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-multipart.spec.ts +115 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-multipart.ts +171 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-object.store.spec.ts +222 -9
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-object.store.ts +55 -19
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-signer.spec.ts +71 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/s3-signer.ts +98 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage-demo.spec.ts +6 -3
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.config.ts +7 -2
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.service.ts +187 -44
- package/dist/templates/modules/object-storage-s3/module.manifest.json +10 -2
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +11 -22
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.routes.spec.ts +113 -0
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.routes.ts +122 -0
- package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +24 -0
- package/dist/templates/modules/rate-limit/module.manifest.json +7 -0
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis-cache.store.spec.ts +14 -1
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis-cache.store.ts +9 -12
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis.module.ts +16 -4
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis.service.spec.ts +233 -5
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis.service.ts +134 -32
- package/dist/templates/modules/redis/module.manifest.json +4 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/event-stream.ts +11 -4
- package/dist/templates/modules/sse/files/apps/api/src/events/events.service.spec.ts +33 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/events.service.ts +7 -0
- package/dist/templates/todo/AGENTS.md +9 -1
- package/dist/templates/todo/README.md +18 -5
- package/dist/update.d.ts +5 -0
- package/dist/update.js +35 -0
- package/package.json +2 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/schema-validation.ts +0 -13
- 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.
|
|
67
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
+
"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.
|
|
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
|
-
}
|