@podosoft/podokit 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/add.d.ts +2 -0
- package/dist/add.js +5 -1
- package/dist/assemble.d.ts +14 -0
- package/dist/assemble.js +60 -0
- package/dist/create.d.ts +2 -0
- package/dist/create.js +7 -0
- package/dist/eject.d.ts +11 -0
- package/dist/eject.js +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +122 -1
- package/dist/inspect.d.ts +43 -0
- package/dist/inspect.js +82 -0
- package/dist/lockfile.d.ts +80 -0
- package/dist/lockfile.js +193 -0
- package/dist/templates/base/dot-env.example +11 -6
- package/dist/templates/fullstack-nest-svelte/README.md +2 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/package.json +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +6 -3
- package/dist/templates/fullstack-nest-svelte/apps/api/src/common/app-exception.ts +4 -12
- package/dist/templates/fullstack-nest-svelte/apps/api/src/config/env.validation.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/api/src/main.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/package.json +2 -2
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-content.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-item.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/dot-env.example +10 -5
- package/dist/templates/fullstack-nest-svelte/infra/docker/docker-compose.yml +37 -0
- package/dist/templates/fullstack-nest-svelte/infra/docker/sms-sink.mjs +40 -0
- package/dist/templates/fullstack-nest-svelte/tests/README.md +1 -1
- package/dist/templates/fullstack-nest-svelte/tests/package.json +2 -1
- package/dist/templates/fullstack-nest-svelte/tests/playwright.config.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/app.d.ts +3 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/app-sidebar.svelte +7 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +149 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-pagination.svelte +21 -22
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-toolbar.svelte +93 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +224 -8
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/guards.ts +22 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts +19 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/accept-invitation/[id]/+page.svelte +36 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte +21 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.svelte +360 -49
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.server.ts +2 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +82 -37
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.server.ts +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.svelte +264 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.server.ts +3 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +79 -45
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.server.ts +26 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +322 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.server.ts +3 -5
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +254 -108
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/forgot-password/+page.svelte +8 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.server.ts +14 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +127 -5
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +18 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/verify-email/+page.svelte +63 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/oauth2/consent/+page.svelte +54 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +51 -2
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +80 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +264 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +120 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +10 -10
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +2 -2
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/mailpit.ts +49 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +35 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/totp.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +12 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +77 -6
- package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/data-table.ui.spec.ts +32 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +60 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +42 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +1 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/oidc.ui.spec.ts +57 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +52 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +36 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +9 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +34 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/table-toolbar.ui.spec.ts +32 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users-crud.ui.spec.ts +28 -12
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +35 -3
- package/dist/templates/modules/admin-dashboard/module.manifest.json +13 -7
- package/dist/templates/modules/api-key-auth/module.manifest.json +3 -3
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-enabled.ts +16 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-hook.ts +4 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.interceptor.ts +2 -0
- package/dist/templates/modules/audit-log/module.manifest.json +15 -9
- package/dist/templates/modules/auth/files/apps/api/src/account/account.controller.ts +78 -13
- package/dist/templates/modules/auth/files/apps/api/src/account/account.module.ts +2 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +73 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +197 -52
- package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +12 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +56 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/org-permissions.ts +22 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/permissions.ts +23 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.controller.ts +33 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.entity.ts +26 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.module.ts +12 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +118 -0
- package/dist/templates/modules/auth/files/apps/api/src/mail/mailer.ts +65 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720400000000-InitAppSettings.ts +38 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720500000000-InitAuthConfig.ts +27 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/app-setting.entity.ts +16 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +25 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/settings.module.ts +11 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/settings.service.ts +57 -0
- package/dist/templates/modules/auth/files/apps/api/src/sms/sms.ts +23 -0
- package/dist/templates/modules/auth/module.manifest.json +54 -20
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +6 -3
- package/dist/templates/modules/bullmq/module.manifest.json +4 -4
- package/dist/templates/modules/file-upload/module.manifest.json +3 -3
- package/dist/templates/modules/job-progress/module.manifest.json +9 -9
- package/dist/templates/modules/logging/module.manifest.json +3 -3
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -3
- package/dist/templates/modules/object-storage-s3/module.manifest.json +5 -5
- package/dist/templates/modules/rate-limit/module.manifest.json +2 -2
- package/dist/templates/modules/redis/module.manifest.json +4 -4
- package/dist/templates/modules/sse/module.manifest.json +4 -4
- package/dist/templates/todo/README.md +2 -2
- package/dist/templates/todo/apps/api/package.json +1 -0
- package/dist/templates/todo/apps/api/src/app.module.ts +6 -3
- package/dist/templates/todo/apps/api/src/common/app-exception.ts +4 -12
- package/dist/templates/todo/apps/api/src/config/env.validation.ts +1 -1
- package/dist/templates/todo/apps/api/src/main.ts +1 -1
- package/dist/templates/todo/apps/web/package.json +2 -2
- package/dist/templates/todo/apps/web/src/lib/components/ui/switch/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
- package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +1 -1
- package/dist/templates/todo/dot-env.example +10 -5
- package/dist/templates/todo/infra/docker/docker-compose.yml +4 -0
- package/dist/templates/todo/tests/README.md +1 -1
- package/dist/templates/todo/tests/package.json +2 -1
- package/dist/templates/todo/tests/playwright.config.ts +1 -1
- package/dist/update.d.ts +49 -0
- package/dist/update.js +158 -0
- package/package.json +2 -2
|
@@ -22,7 +22,7 @@ async function bootstrap(): Promise<void> {
|
|
|
22
22
|
.build();
|
|
23
23
|
SwaggerModule.setup("api-docs", app, SwaggerModule.createDocument(app, config));
|
|
24
24
|
|
|
25
|
-
const port = Number(process.env.PORT ??
|
|
25
|
+
const port = Number(process.env.PORT ?? 5002);
|
|
26
26
|
await app.listen(port);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"dev": "vite dev --port
|
|
7
|
+
"dev": "vite dev --port 5001",
|
|
8
8
|
"build": "vite build",
|
|
9
9
|
"preview": "vite preview",
|
|
10
10
|
"lint": "svelte-check --tsconfig ./tsconfig.json",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@internationalized/date": "^3.12.2",
|
|
15
15
|
"@lucide/svelte": "^1.23.0",
|
|
16
|
-
"@podosoft/podokit-api-client": "^0.
|
|
16
|
+
"@podosoft/podokit-api-client": "^0.5.0",
|
|
17
17
|
"bits-ui": "^2.18.1",
|
|
18
18
|
"clsx": "^2.1.1",
|
|
19
19
|
"mode-watcher": "^1.1.0",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Switch as SwitchPrimitive } from "bits-ui";
|
|
3
|
+
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
checked = $bindable(false),
|
|
8
|
+
class: className,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<SwitchPrimitive.Root
|
|
14
|
+
bind:ref
|
|
15
|
+
data-slot="switch"
|
|
16
|
+
class={cn(
|
|
17
|
+
"peer focus-visible:border-ring focus-visible:ring-ring/50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
bind:checked
|
|
21
|
+
{...restProps}
|
|
22
|
+
>
|
|
23
|
+
<SwitchPrimitive.Thumb
|
|
24
|
+
data-slot="switch-thumb"
|
|
25
|
+
class={cn(
|
|
26
|
+
"bg-background pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
|
27
|
+
)}
|
|
28
|
+
/>
|
|
29
|
+
</SwitchPrimitive.Root>
|
|
@@ -5,7 +5,7 @@ const FORWARDED_HEADERS = ["authorization", "cookie", "content-type", "accept",
|
|
|
5
5
|
const RELAYED_RESPONSE_HEADERS = ["content-type", "location", "cache-control"];
|
|
6
6
|
|
|
7
7
|
export function backendBaseUrl(): string {
|
|
8
|
-
return process.env.BACKEND_INTERNAL_URL ?? "http://localhost:
|
|
8
|
+
return process.env.BACKEND_INTERNAL_URL ?? "http://localhost:5002";
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// Normalize the resolved client IP for the forwarded header. better-auth stores
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copy to .env and adjust. Never commit real secrets.
|
|
2
|
+
|
|
3
|
+
# ── Required (runtime) ───────────────────────────────────────────────
|
|
2
4
|
NODE_ENV=development
|
|
3
|
-
PORT=
|
|
5
|
+
PORT=5002
|
|
4
6
|
|
|
5
7
|
POSTGRES_HOST=localhost
|
|
6
8
|
POSTGRES_PORT=5432
|
|
@@ -8,9 +10,12 @@ POSTGRES_USER=podokit
|
|
|
8
10
|
POSTGRES_PASSWORD=podokit
|
|
9
11
|
POSTGRES_DB=podokit
|
|
10
12
|
|
|
13
|
+
# Web -> API boundary (server-side only; not exposed to the browser)
|
|
14
|
+
BACKEND_INTERNAL_URL=http://localhost:5002
|
|
15
|
+
CORS_ORIGIN=http://localhost:5001
|
|
16
|
+
|
|
17
|
+
# ── Optional / development ───────────────────────────────────────────
|
|
18
|
+
# Redis — only needed by cache/queue modules. Start it with:
|
|
19
|
+
# docker compose --profile cache up
|
|
11
20
|
REDIS_HOST=localhost
|
|
12
21
|
REDIS_PORT=6379
|
|
13
|
-
|
|
14
|
-
# Web -> API boundary (server-side only; not exposed to the browser)
|
|
15
|
-
BACKEND_INTERNAL_URL=http://localhost:3000
|
|
16
|
-
CORS_ORIGIN=http://localhost:5173
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
services:
|
|
2
|
+
# --- Runtime (always started by `docker compose up`) ---
|
|
2
3
|
postgres:
|
|
3
4
|
image: postgres:16-alpine
|
|
4
5
|
environment:
|
|
@@ -15,8 +16,11 @@ services:
|
|
|
15
16
|
timeout: 5s
|
|
16
17
|
retries: 5
|
|
17
18
|
|
|
19
|
+
# --- Runtime, but only needed by cache/queue modules. Start with:
|
|
20
|
+
# docker compose --profile cache up
|
|
18
21
|
redis:
|
|
19
22
|
image: redis:7-alpine
|
|
23
|
+
profiles: ["cache"]
|
|
20
24
|
ports:
|
|
21
25
|
- "${REDIS_PORT:-6379}:6379"
|
|
22
26
|
healthcheck:
|
|
@@ -10,6 +10,6 @@ npm run test:e2e:report # open the HTML report
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
The suite runs against a live stack (web on `E2E_BASE_URL`, default
|
|
13
|
-
`http://localhost:
|
|
13
|
+
`http://localhost:5001`, proxying `/api/*` to the API). Start PostgreSQL, run the
|
|
14
14
|
auth migration, and start the API + web first. Files ending in `*.api.spec.ts` run
|
|
15
15
|
in the request-only `api` project; `*.ui.spec.ts` run in the chromium `ui` project.
|
|
@@ -4,7 +4,7 @@ import { defineConfig, devices } from "@playwright/test";
|
|
|
4
4
|
// the API). Start it first (see docs/testing.md) — locally via the dev harness,
|
|
5
5
|
// in CI via scripts/e2e-ci.mjs. Two projects: `api` (request-only, backend e2e)
|
|
6
6
|
// and `ui` (chromium, pages). Files are routed by suffix (*.api/*.ui.spec.ts).
|
|
7
|
-
const baseURL = process.env.E2E_BASE_URL ?? "http://localhost:
|
|
7
|
+
const baseURL = process.env.E2E_BASE_URL ?? "http://localhost:5001";
|
|
8
8
|
|
|
9
9
|
export default defineConfig({
|
|
10
10
|
testDir: ".",
|
package/dist/update.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Tier } from "./lockfile";
|
|
2
|
+
/**
|
|
3
|
+
* `podo update` planner. For now it produces a dry-run plan only: it assembles
|
|
4
|
+
* the new-version tree from the installed CLI's templates and compares it to the
|
|
5
|
+
* working copy, classifying each file. Applying the plan (writing files, 3-way
|
|
6
|
+
* merging) is a later step (see ADR-0009).
|
|
7
|
+
*/
|
|
8
|
+
export type Action = "update" | "merge" | "conflict" | "add" | "remove" | "skip" | "up-to-date";
|
|
9
|
+
export interface FileChange {
|
|
10
|
+
path: string;
|
|
11
|
+
tier: Tier;
|
|
12
|
+
action: Action;
|
|
13
|
+
/** Why the action was chosen (for the report). */
|
|
14
|
+
note: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UpdatePlan {
|
|
17
|
+
fromVersion: string;
|
|
18
|
+
toVersion: string;
|
|
19
|
+
template: string;
|
|
20
|
+
modules: string[];
|
|
21
|
+
changes: FileChange[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build the update plan. `templatesDir` is the installed CLI's template set (the
|
|
25
|
+
* new version); the lock records what PodoKit last wrote (to detect user edits).
|
|
26
|
+
*/
|
|
27
|
+
export declare function planUpdate(projectRoot: string, templatesDir: string): UpdatePlan;
|
|
28
|
+
export interface ApplyOptions {
|
|
29
|
+
/** Templates for the version the project is currently on, for 3-way merges. */
|
|
30
|
+
oldTemplatesDir?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ApplyResult {
|
|
33
|
+
written: string[];
|
|
34
|
+
removed: string[];
|
|
35
|
+
/** Files 3-way merged cleanly. */
|
|
36
|
+
merged: string[];
|
|
37
|
+
/** Files written with conflict markers for manual resolution. */
|
|
38
|
+
conflicts: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Apply an update to the working copy. Clean updates and additions are written,
|
|
42
|
+
* upstream removals deleted, and user-edited files 3-way merged against the old
|
|
43
|
+
* version (when `oldTemplatesDir` is given) or written with git-style conflict
|
|
44
|
+
* markers otherwise. Owned files are never touched. Refreshes the lockfile and
|
|
45
|
+
* stamps the new version.
|
|
46
|
+
*/
|
|
47
|
+
export declare function applyUpdate(projectRoot: string, templatesDir: string, options?: ApplyOptions): ApplyResult;
|
|
48
|
+
/** Count actions for a one-line summary. */
|
|
49
|
+
export declare function summarize(plan: UpdatePlan): Record<Action, number>;
|
package/dist/update.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.planUpdate = planUpdate;
|
|
4
|
+
exports.applyUpdate = applyUpdate;
|
|
5
|
+
exports.summarize = summarize;
|
|
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 assemble_1 = require("./assemble");
|
|
10
|
+
const lockfile_1 = require("./lockfile");
|
|
11
|
+
const inspect_1 = require("./inspect");
|
|
12
|
+
function treeText(tree, path) {
|
|
13
|
+
const file = tree.get(path);
|
|
14
|
+
if (!file)
|
|
15
|
+
return null;
|
|
16
|
+
return typeof file.content === "string" ? file.content : file.content.toString("utf8");
|
|
17
|
+
}
|
|
18
|
+
function diskContent(projectRoot, path) {
|
|
19
|
+
const abs = (0, node_path_1.join)(projectRoot, path);
|
|
20
|
+
return (0, node_fs_1.existsSync)(abs) ? (0, node_fs_1.readFileSync)(abs) : null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build the update plan. `templatesDir` is the installed CLI's template set (the
|
|
24
|
+
* new version); the lock records what PodoKit last wrote (to detect user edits).
|
|
25
|
+
*/
|
|
26
|
+
function planUpdate(projectRoot, templatesDir) {
|
|
27
|
+
const manifest = (0, lockfile_1.readManifest)(projectRoot);
|
|
28
|
+
const lock = (0, lockfile_1.readFilesLock)(projectRoot);
|
|
29
|
+
if (!manifest || !lock)
|
|
30
|
+
throw new inspect_1.NotAProjectError();
|
|
31
|
+
const newTree = (0, assemble_1.assembleProject)({
|
|
32
|
+
templatesDir,
|
|
33
|
+
template: manifest.template,
|
|
34
|
+
answers: manifest.answers,
|
|
35
|
+
modules: manifest.modules.map((m) => m.name),
|
|
36
|
+
});
|
|
37
|
+
const changes = [];
|
|
38
|
+
const paths = new Set([...newTree.keys(), ...Object.keys(lock.files)]);
|
|
39
|
+
for (const path of [...paths].sort()) {
|
|
40
|
+
const locked = lock.files[path];
|
|
41
|
+
const newText = treeText(newTree, path);
|
|
42
|
+
const disk = diskContent(projectRoot, path);
|
|
43
|
+
// tier: prefer the lock; else classify the new file.
|
|
44
|
+
const tier = locked?.tier ??
|
|
45
|
+
(newText !== null ? (0, lockfile_1.classifyTier)(path, newText, manifest.ownedGlobs) : "managed");
|
|
46
|
+
if (tier === "owned") {
|
|
47
|
+
changes.push({ path, tier, action: "skip", note: "owned — never modified" });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
// File removed upstream.
|
|
51
|
+
if (newText === null) {
|
|
52
|
+
if (disk === null)
|
|
53
|
+
continue; // already gone
|
|
54
|
+
const edited = locked && (0, podokit_template_engine_1.hashContent)(disk) !== locked.outHash;
|
|
55
|
+
changes.push({
|
|
56
|
+
path,
|
|
57
|
+
tier,
|
|
58
|
+
action: edited ? "conflict" : "remove",
|
|
59
|
+
note: edited ? "removed upstream but edited locally" : "removed upstream",
|
|
60
|
+
});
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const newHash = (0, podokit_template_engine_1.hashContent)(newText);
|
|
64
|
+
// New file added upstream.
|
|
65
|
+
if (disk === null) {
|
|
66
|
+
changes.push({ path, tier, action: "add", note: "new in this version" });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const diskHash = (0, podokit_template_engine_1.hashContent)(disk);
|
|
70
|
+
if (diskHash === newHash) {
|
|
71
|
+
changes.push({ path, tier, action: "up-to-date", note: "already current" });
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const edited = !locked || diskHash !== locked.outHash;
|
|
75
|
+
changes.push({
|
|
76
|
+
path,
|
|
77
|
+
tier,
|
|
78
|
+
action: edited ? "conflict" : "update",
|
|
79
|
+
note: edited ? "you edited this; update would need a merge" : "clean update",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
fromVersion: manifest.podokitVersion,
|
|
84
|
+
toVersion: (0, lockfile_1.podokitVersion)(),
|
|
85
|
+
template: manifest.template,
|
|
86
|
+
modules: manifest.modules.map((m) => m.name),
|
|
87
|
+
changes,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function writeFile(projectRoot, path, content) {
|
|
91
|
+
const abs = (0, node_path_1.join)(projectRoot, path);
|
|
92
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(abs), { recursive: true });
|
|
93
|
+
(0, node_fs_1.writeFileSync)(abs, content);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Apply an update to the working copy. Clean updates and additions are written,
|
|
97
|
+
* upstream removals deleted, and user-edited files 3-way merged against the old
|
|
98
|
+
* version (when `oldTemplatesDir` is given) or written with git-style conflict
|
|
99
|
+
* markers otherwise. Owned files are never touched. Refreshes the lockfile and
|
|
100
|
+
* stamps the new version.
|
|
101
|
+
*/
|
|
102
|
+
function applyUpdate(projectRoot, templatesDir, options = {}) {
|
|
103
|
+
const manifest = (0, lockfile_1.readManifest)(projectRoot);
|
|
104
|
+
if (!manifest)
|
|
105
|
+
throw new inspect_1.NotAProjectError();
|
|
106
|
+
const plan = planUpdate(projectRoot, templatesDir);
|
|
107
|
+
const modules = manifest.modules.map((m) => m.name);
|
|
108
|
+
const newTree = (0, assemble_1.assembleProject)({ templatesDir, template: manifest.template, answers: manifest.answers, modules });
|
|
109
|
+
const oldTree = options.oldTemplatesDir
|
|
110
|
+
? (0, assemble_1.assembleProject)({ templatesDir: options.oldTemplatesDir, template: manifest.template, answers: manifest.answers, modules })
|
|
111
|
+
: null;
|
|
112
|
+
const result = { written: [], removed: [], merged: [], conflicts: [] };
|
|
113
|
+
for (const change of plan.changes) {
|
|
114
|
+
const newText = treeText(newTree, change.path);
|
|
115
|
+
if (change.action === "update" || change.action === "add") {
|
|
116
|
+
writeFile(projectRoot, change.path, newText ?? "");
|
|
117
|
+
result.written.push(change.path);
|
|
118
|
+
}
|
|
119
|
+
else if (change.action === "remove") {
|
|
120
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(projectRoot, change.path), { force: true });
|
|
121
|
+
result.removed.push(change.path);
|
|
122
|
+
}
|
|
123
|
+
else if (change.action === "conflict" && newText !== null) {
|
|
124
|
+
const disk = (0, node_fs_1.readFileSync)((0, node_path_1.join)(projectRoot, change.path), "utf8");
|
|
125
|
+
const base = oldTree ? treeText(oldTree, change.path) : null;
|
|
126
|
+
if (base === null) {
|
|
127
|
+
// No old version to merge against — never clobber the user's edits.
|
|
128
|
+
result.conflicts.push(change.path);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const merge = (0, podokit_template_engine_1.threeWayMerge)(base, disk, newText, { current: "current", next: "podokit" });
|
|
132
|
+
writeFile(projectRoot, change.path, merge.merged);
|
|
133
|
+
if (merge.conflicts > 0)
|
|
134
|
+
result.conflicts.push(change.path);
|
|
135
|
+
else
|
|
136
|
+
result.merged.push(change.path);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Refresh the lock to match what we just wrote and stamp the new version.
|
|
140
|
+
(0, lockfile_1.writeFilesLock)(projectRoot, (0, lockfile_1.computeFilesLock)(projectRoot, manifest.ownedGlobs));
|
|
141
|
+
(0, lockfile_1.writeManifest)(projectRoot, { ...manifest, podokitVersion: (0, lockfile_1.podokitVersion)() });
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/** Count actions for a one-line summary. */
|
|
145
|
+
function summarize(plan) {
|
|
146
|
+
const counts = {
|
|
147
|
+
update: 0,
|
|
148
|
+
merge: 0,
|
|
149
|
+
conflict: 0,
|
|
150
|
+
add: 0,
|
|
151
|
+
remove: 0,
|
|
152
|
+
skip: 0,
|
|
153
|
+
"up-to-date": 0,
|
|
154
|
+
};
|
|
155
|
+
for (const change of plan.changes)
|
|
156
|
+
counts[change.action] += 1;
|
|
157
|
+
return counts;
|
|
158
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podosoft/podokit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "An opinionated but extensible starter toolkit and CLI for full-stack TypeScript apps with NestJS, SvelteKit, TailwindCSS, shadcn-svelte, Docker, and k3s.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,6 @@
|
|
|
36
36
|
"test": "vitest run"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@podosoft/podokit-template-engine": "^0.
|
|
39
|
+
"@podosoft/podokit-template-engine": "^0.4.0"
|
|
40
40
|
}
|
|
41
41
|
}
|