@podosoft/podokit 0.9.0 → 0.10.1

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 (87) hide show
  1. package/dist/add.d.ts +20 -0
  2. package/dist/add.js +121 -8
  3. package/dist/assemble.d.ts +4 -1
  4. package/dist/assemble.js +35 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +20 -4
  7. package/dist/inspect.d.ts +5 -0
  8. package/dist/inspect.js +5 -0
  9. package/dist/lockfile.d.ts +20 -1
  10. package/dist/lockfile.js +48 -6
  11. package/dist/remove.js +3 -2
  12. package/dist/templates/fullstack-nest-svelte/README.md +8 -0
  13. package/dist/templates/fullstack-nest-svelte/apps/api/Dockerfile +12 -8
  14. package/dist/templates/fullstack-nest-svelte/apps/api/src/health/health.controller.ts +2 -2
  15. package/dist/templates/fullstack-nest-svelte/apps/web/Dockerfile +12 -8
  16. package/dist/templates/fullstack-nest-svelte/apps/web/src/app.css +42 -0
  17. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/site-runtime.svelte +7 -0
  18. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/api.ts +3 -1
  19. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +4 -2
  20. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/+layout.svelte +2 -2
  21. package/dist/templates/fullstack-nest-svelte/compose.dev.yaml +4 -0
  22. package/dist/templates/fullstack-nest-svelte/dot-gitignore +6 -0
  23. package/dist/templates/fullstack-nest-svelte/infra/k3s/configmap.yaml +2 -0
  24. package/dist/templates/fullstack-nest-svelte/infra/k3s/ingress.yaml +10 -0
  25. package/dist/templates/fullstack-nest-svelte/infra/traefik/dynamic.yml +4 -0
  26. package/dist/templates/fullstack-nest-svelte/package.json +1 -1
  27. package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.controller.ts +60 -1
  28. package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.service.ts +3 -0
  29. package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +11 -1
  30. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/account-data.server.ts +42 -0
  31. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-menu.svelte +46 -0
  32. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-page.svelte +730 -0
  33. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/settings/appearance-card.svelte +371 -0
  34. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +1 -1
  35. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-runtime.svelte +39 -0
  36. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +12 -7
  37. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +32 -0
  38. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/search-indexing.ts +29 -0
  39. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/site/apply-theme.ts +124 -0
  40. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/site/themes.ts +81 -0
  41. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/site.svelte.ts +6 -0
  42. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.svelte +8 -717
  43. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +17 -9
  44. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/general-settings.svelte +0 -7
  45. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.server.ts +4 -0
  46. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/account/+page.svelte +35 -0
  47. package/dist/templates/modules/admin-dashboard/files/tests/api/site-settings.api.spec.ts +56 -0
  48. package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +10 -0
  49. package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +9 -2
  50. package/dist/templates/modules/admin-dashboard/files/tests/ui/appearance.ui.spec.ts +64 -0
  51. package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +42 -13
  52. package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +21 -2
  53. package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +1 -1
  54. package/dist/templates/modules/admin-dashboard/files/tests/ui/settings-dialogs.ui.spec.ts +5 -3
  55. package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +14 -7
  56. package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +12 -4
  57. package/dist/templates/modules/admin-dashboard/module.manifest.json +1 -1
  58. package/dist/templates/modules/api-key-auth/files/tests/api/machine.api.spec.ts +25 -0
  59. package/dist/templates/modules/auth/files/apps/api/src/health/health.controller.ts +2 -2
  60. package/dist/templates/modules/auth/files/apps/api/src/migrate.ts +22 -0
  61. package/dist/templates/modules/auth/module.manifest.json +5 -1
  62. package/dist/templates/modules/bullmq/files/infra/docker/worker.compose.example.yml +2 -1
  63. package/dist/templates/modules/bullmq/files/tests/api/jobs.api.spec.ts +38 -0
  64. package/dist/templates/modules/file-upload/files/tests/api/files.api.spec.ts +31 -0
  65. package/dist/templates/modules/job-progress/files/tests/api/progress.api.spec.ts +22 -0
  66. package/dist/templates/modules/object-storage-s3/files/tests/api/storage.api.spec.ts +28 -0
  67. package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +38 -3
  68. package/dist/templates/modules/rate-limit/files/tests/api/rate-limit.api.spec.ts +35 -0
  69. package/dist/templates/modules/rate-limit/module.manifest.json +6 -3
  70. package/dist/templates/modules/redis/files/tests/api/redis.api.spec.ts +30 -0
  71. package/dist/templates/modules/sse/files/tests/api/events.api.spec.ts +46 -0
  72. package/dist/templates/todo/apps/api/Dockerfile +12 -8
  73. package/dist/templates/todo/apps/api/src/health/health.controller.ts +2 -2
  74. package/dist/templates/todo/apps/web/Dockerfile +12 -8
  75. package/dist/templates/todo/apps/web/src/lib/components/site-runtime.svelte +7 -0
  76. package/dist/templates/todo/apps/web/src/lib/server/api.ts +3 -1
  77. package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +4 -2
  78. package/dist/templates/todo/apps/web/src/routes/+layout.svelte +2 -2
  79. package/dist/templates/todo/compose.dev.yaml +4 -0
  80. package/dist/templates/todo/dot-gitignore +6 -0
  81. package/dist/templates/todo/infra/k3s/configmap.yaml +2 -0
  82. package/dist/templates/todo/infra/k3s/ingress.yaml +10 -0
  83. package/dist/templates/todo/infra/traefik/dynamic.yml +4 -0
  84. package/dist/templates/todo/package.json +1 -1
  85. package/dist/update.js +74 -8
  86. package/package.json +1 -1
  87. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.svelte +0 -61
package/dist/update.js CHANGED
@@ -9,6 +9,7 @@ const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
9
9
  const assemble_1 = require("./assemble");
10
10
  const lockfile_1 = require("./lockfile");
11
11
  const inspect_1 = require("./inspect");
12
+ const add_1 = require("./add");
12
13
  function treeText(tree, path) {
13
14
  const file = tree.get(path);
14
15
  if (!file)
@@ -32,7 +33,8 @@ function planUpdate(projectRoot, templatesDir) {
32
33
  templatesDir,
33
34
  template: manifest.template,
34
35
  answers: manifest.answers,
35
- modules: manifest.modules.map((m) => m.name),
36
+ modules: manifest.modules,
37
+ projectRoot,
36
38
  });
37
39
  const changes = [];
38
40
  const paths = new Set([...newTree.keys(), ...Object.keys(lock.files)]);
@@ -92,6 +94,40 @@ function writeFile(projectRoot, path, content) {
92
94
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(abs), { recursive: true });
93
95
  (0, node_fs_1.writeFileSync)(abs, content);
94
96
  }
97
+ /**
98
+ * Refresh update metadata without teaching the lock that a clean 3-way merge is
99
+ * PodoKit's new baseline. Managed files keep the newly assembled template hash,
100
+ * so the user's merged lines are still recognised as edits on the next update.
101
+ * Files that are neither in the assembled tree nor explicitly owned stay out of
102
+ * the lock instead of being accidentally adopted as managed files.
103
+ */
104
+ function updatedFilesLock(projectRoot, newTree, previous, ownedGlobs) {
105
+ const next = (0, lockfile_1.computeFilesLock)(projectRoot, ownedGlobs);
106
+ for (const [path, entry] of Object.entries(next.files)) {
107
+ const newText = treeText(newTree, path);
108
+ if (newText !== null) {
109
+ if (entry.tier !== "owned") {
110
+ entry.tier = previous.files[path]?.tier ?? entry.tier;
111
+ entry.outHash = (0, podokit_template_engine_1.hashContent)(newText);
112
+ }
113
+ continue;
114
+ }
115
+ if (entry.tier === "owned")
116
+ continue;
117
+ const oldEntry = previous.files[path];
118
+ if (oldEntry && oldEntry.tier !== "owned") {
119
+ // Removed upstream but kept on disk because it was edited: continue to
120
+ // report the removal conflict until the user deletes or ejects it.
121
+ entry.tier = oldEntry.tier;
122
+ entry.outHash = oldEntry.outHash;
123
+ }
124
+ else {
125
+ // A file created by the application is not implicitly PodoKit-managed.
126
+ delete next.files[path];
127
+ }
128
+ }
129
+ return next;
130
+ }
95
131
  /**
96
132
  * Apply an update to the working copy. Clean updates and additions are written,
97
133
  * upstream removals deleted, and user-edited files 3-way merged against the old
@@ -101,13 +137,26 @@ function writeFile(projectRoot, path, content) {
101
137
  */
102
138
  function applyUpdate(projectRoot, templatesDir, options = {}) {
103
139
  const manifest = (0, lockfile_1.readManifest)(projectRoot);
104
- if (!manifest)
140
+ const previousLock = (0, lockfile_1.readFilesLock)(projectRoot);
141
+ if (!manifest || !previousLock)
105
142
  throw new inspect_1.NotAProjectError();
106
143
  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 });
144
+ const modules = manifest.modules;
145
+ const newTree = (0, assemble_1.assembleProject)({
146
+ templatesDir,
147
+ template: manifest.template,
148
+ answers: manifest.answers,
149
+ modules,
150
+ projectRoot,
151
+ });
109
152
  const oldTree = options.oldTemplatesDir
110
- ? (0, assemble_1.assembleProject)({ templatesDir: options.oldTemplatesDir, template: manifest.template, answers: manifest.answers, modules })
153
+ ? (0, assemble_1.assembleProject)({
154
+ templatesDir: options.oldTemplatesDir,
155
+ template: manifest.template,
156
+ answers: manifest.answers,
157
+ modules,
158
+ projectRoot,
159
+ })
111
160
  : null;
112
161
  const result = { written: [], removed: [], merged: [], conflicts: [] };
113
162
  for (const change of plan.changes) {
@@ -136,9 +185,26 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
136
185
  result.merged.push(change.path);
137
186
  }
138
187
  }
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)() });
188
+ // Keep the assembled target as the managed baseline. A merged working file
189
+ // intentionally remains drifted so a future update performs another 3-way
190
+ // merge instead of treating the user's lines as clean generated output.
191
+ (0, lockfile_1.writeFilesLock)(projectRoot, updatedFilesLock(projectRoot, newTree, previousLock, manifest.ownedGlobs));
192
+ const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
193
+ const refreshedModules = manifest.modules.map((module) => {
194
+ const resolved = (0, add_1.resolveModule)(module.packageName ?? module.name, modulesDir, projectRoot);
195
+ return resolved?.packageName
196
+ ? {
197
+ ...module,
198
+ packageName: resolved.packageName,
199
+ moduleVersion: resolved.moduleVersion,
200
+ }
201
+ : module;
202
+ });
203
+ (0, lockfile_1.writeManifest)(projectRoot, {
204
+ ...manifest,
205
+ modules: refreshedModules,
206
+ podokitVersion: (0, lockfile_1.podokitVersion)(),
207
+ });
142
208
  return result;
143
209
  }
144
210
  /** Count actions for a one-line summary. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podosoft/podokit",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
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": {
@@ -1,61 +0,0 @@
1
- <script lang="ts">
2
- import "../app.css";
3
- import { ModeWatcher } from "mode-watcher";
4
- import { setI18nContext } from "$lib/i18n";
5
- import { messages, resolveLocale } from "$lib/i18n/messages";
6
- import { site } from "$lib/site.svelte";
7
-
8
- let { children, data } = $props();
9
- const locale = $derived(resolveLocale(data.locale));
10
- setI18nContext({
11
- get t() {
12
- return messages[locale];
13
- },
14
- get locale() {
15
- return locale;
16
- },
17
- });
18
-
19
- // Seed the reactive site branding; the general-settings form patches it live.
20
- $effect.pre(() => site.init(data.site));
21
- // Prefer live store values (patched after a save); fall back to the server load
22
- // so SSR renders the saved branding too. Empty falls back to the app.html default.
23
- const branding = $derived({
24
- name: site.value.name ?? data.site?.name ?? "",
25
- description: site.value.description ?? data.site?.description ?? "",
26
- brandColor: site.value.brandColor ?? data.site?.brandColor ?? "",
27
- hasFavicon: site.value.hasFavicon || Boolean(data.site?.hasFavicon),
28
- faviconVersion: site.value.faviconVersion ?? data.site?.faviconVersion ?? "",
29
- });
30
- // Apply the site name to the browser tab live (avoids a duplicate <title> vs
31
- // the app.html default; the default project name stays as the SSR fallback).
32
- $effect(() => {
33
- if (branding.name) document.title = branding.name;
34
- });
35
- // Apply the brand color as the theme's primary accent live. Clearing it in
36
- // Settings restores the stylesheet default (remove the inline override).
37
- $effect(() => {
38
- const root = document.documentElement;
39
- if (branding.brandColor) {
40
- root.style.setProperty("--primary", branding.brandColor);
41
- root.style.setProperty("--sidebar-primary", branding.brandColor);
42
- root.style.setProperty("--ring", branding.brandColor);
43
- } else {
44
- root.style.removeProperty("--primary");
45
- root.style.removeProperty("--sidebar-primary");
46
- root.style.removeProperty("--ring");
47
- }
48
- });
49
- </script>
50
-
51
- <svelte:head>
52
- {#if branding.description}
53
- <meta name="description" content={branding.description} />
54
- {/if}
55
- {#if branding.hasFavicon}
56
- <link rel="icon" href={`/api/site/favicon?v=${branding.faviconVersion}`} />
57
- {/if}
58
- </svelte:head>
59
-
60
- <ModeWatcher />
61
- {@render children()}