@podosoft/podokit 0.11.2 → 0.12.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.
- package/README.md +7 -3
- package/dist/dev.d.ts +26 -0
- package/dist/dev.js +297 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +82 -2
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +7 -1
- package/dist/locale.d.ts +22 -0
- package/dist/locale.js +184 -0
- package/dist/lockfile.js +4 -0
- package/dist/templates/fullstack-nest-svelte/README.md +42 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/README.md +7 -5
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/catalogs/app/en.json +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/catalogs/app/ko.json +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/locales/en.json +6 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/locales/ko.json +6 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/vite.config.ts +3 -9
- package/dist/templates/fullstack-nest-svelte/compose.dev.yaml +6 -11
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-locale/SKILL.md +63 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-locale/agents/openai.yaml +4 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-locale/references/catalog-format.md +49 -0
- package/dist/templates/fullstack-nest-svelte/dot-env.docker +2 -2
- package/dist/templates/fullstack-nest-svelte/dot-gitignore +3 -0
- package/dist/templates/fullstack-nest-svelte/dot-podokit/dev.json +4 -0
- package/dist/templates/fullstack-nest-svelte/tests/README.md +10 -4
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.controller.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +5 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/auth-errors.ts +3 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/auth-redirect.ts +27 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-menu.svelte +8 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/en.json +564 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/ko.json +564 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +13 -12
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +90 -722
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/site.svelte.ts +8 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +4 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.svelte +2 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +4 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +8 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/general-settings.svelte +15 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +4 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +32 -35
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +10 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.server.ts +11 -7
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +78 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +28 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +15 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +59 -6
- package/dist/templates/modules/admin-dashboard/files/tests/ui/login-methods.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings-dialogs.ui.spec.ts +5 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +2 -2
- package/dist/templates/modules/admin-dashboard/module.manifest.json +4 -0
- package/dist/templates/modules/auth/files/apps/api/scripts/configure-auth.mjs +22 -11
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +6 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +50 -25
- package/dist/templates/modules/auth/files/dot-claude/skills/podokit-configure-auth/SKILL.md +1 -1
- package/dist/templates/modules/auth/files/dot-claude/skills/podokit-configure-auth/references/google.md +44 -0
- package/dist/templates/todo/README.md +42 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/README.md +7 -5
- package/dist/templates/todo/apps/web/src/lib/i18n/catalogs/app/en.json +1 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/catalogs/app/ko.json +1 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/locales/en.json +6 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/locales/ko.json +6 -0
- package/dist/templates/todo/apps/web/vite.config.ts +3 -9
- package/dist/templates/todo/compose.dev.yaml +6 -11
- package/dist/templates/todo/dot-claude/skills/podokit-add-locale/SKILL.md +63 -0
- package/dist/templates/todo/dot-claude/skills/podokit-add-locale/agents/openai.yaml +4 -0
- package/dist/templates/todo/dot-claude/skills/podokit-add-locale/references/catalog-format.md +49 -0
- package/dist/templates/todo/dot-env.docker +2 -2
- package/dist/templates/todo/dot-gitignore +3 -0
- package/dist/templates/todo/dot-podokit/dev.json +4 -0
- package/dist/templates/todo/tests/README.md +10 -4
- package/dist/update.js +42 -6
- package/package.json +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/en.ts +0 -10
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/ko.ts +0 -11
- package/dist/templates/todo/apps/web/src/lib/i18n/en.ts +0 -10
- package/dist/templates/todo/apps/web/src/lib/i18n/ko.ts +0 -11
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# JSON Catalog Format
|
|
2
|
+
|
|
3
|
+
## Directory Layout
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
apps/web/src/lib/i18n/
|
|
7
|
+
├── locales/
|
|
8
|
+
│ ├── en.json
|
|
9
|
+
│ ├── ko.json
|
|
10
|
+
│ └── pt-BR.json
|
|
11
|
+
└── catalogs/
|
|
12
|
+
├── admin-dashboard/ # PodoKit-managed module messages
|
|
13
|
+
├── blog/ # module-managed messages when installed
|
|
14
|
+
└── app/ # app-owned product messages and overrides
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Each locale definition has this shape:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"code": "pt-BR",
|
|
22
|
+
"name": "Português (Brasil)",
|
|
23
|
+
"direction": "ltr",
|
|
24
|
+
"enabled": false
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`code` must be a canonical BCP 47 tag and must match the filename. `direction` is `ltr` or `rtl`. Newly added locales stay inactive until explicitly activated.
|
|
29
|
+
|
|
30
|
+
## Catalog Composition
|
|
31
|
+
|
|
32
|
+
Catalogs are nested JSON objects with strings at their leaves:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"common": {
|
|
37
|
+
"save": "Save",
|
|
38
|
+
"welcome": "Welcome, {name}"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The runtime lazily loads every `catalogs/*/<locale>.json` file. Module catalogs are merged first in stable name order; the `app` catalog is merged last, so the consuming app may intentionally override module copy without modifying managed files.
|
|
44
|
+
|
|
45
|
+
English defines the complete key and value-type contract. Other locales may omit keys, but translated values must retain the English type and all `{placeholder}` names. JSON objects and arrays are supported; executable expressions are not.
|
|
46
|
+
|
|
47
|
+
## Development Reloading
|
|
48
|
+
|
|
49
|
+
Catalog imports use Vite's `import.meta.glob`. Changes to an existing JSON dependency participate in Vite HMR and must appear without restarting the development container. A new file changes the glob membership and may cause Vite to reload the page automatically. If neither occurs, confirm that the app source directory, including `apps/web/src`, is mounted or synced by the development compose configuration.
|
|
@@ -6,11 +6,11 @@ POSTGRES_USER=podokit
|
|
|
6
6
|
POSTGRES_PASSWORD=podokit
|
|
7
7
|
POSTGRES_DB=podokit
|
|
8
8
|
BACKEND_INTERNAL_URL=http://api:5002
|
|
9
|
-
CORS_ORIGIN=http://
|
|
9
|
+
CORS_ORIGIN=http://{{projectName}}.localhost,http://localhost
|
|
10
10
|
REDIS_HOST=redis
|
|
11
11
|
REDIS_PORT=6379
|
|
12
12
|
BETTER_AUTH_SECRET=change-me-in-container-min-32-characters
|
|
13
|
-
BETTER_AUTH_URL=http://
|
|
13
|
+
BETTER_AUTH_URL=http://{{projectName}}.localhost
|
|
14
14
|
ADMIN_EMAILS=admin@example.com
|
|
15
15
|
AUDIT_LOG_ENABLED=true
|
|
16
16
|
RATE_LIMIT_TTL=60
|
|
@@ -9,7 +9,13 @@ npm run test:e2e:ui # pages (chromium)
|
|
|
9
9
|
npm run test:e2e:report # open the HTML report
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
The suite runs against a live stack (web on `E2E_BASE_URL`,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
The suite runs against a live stack (web on `E2E_BASE_URL`, proxying `/api/*` to
|
|
13
|
+
the API). With the recommended containerized loop, start `podo dev watch` and run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
E2E_BASE_URL=http://{{projectName}}.localhost npm run test:e2e
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The default `http://localhost:5001` is for the alternative `npm run dev`
|
|
20
|
+
host-process loop. Files ending in `*.api.spec.ts` run in the request-only `api`
|
|
21
|
+
project; `*.ui.spec.ts` run in the chromium `ui` project.
|
package/dist/update.js
CHANGED
|
@@ -32,6 +32,18 @@ function targetManagedOverrides(projectRoot, templatesDir, modules, current = []
|
|
|
32
32
|
}
|
|
33
33
|
return [...overrides];
|
|
34
34
|
}
|
|
35
|
+
function targetOwnedGlobs(projectRoot, templatesDir, modules, current) {
|
|
36
|
+
const owned = new Set([...lockfile_1.DEFAULT_OWNED_GLOBS, ...current]);
|
|
37
|
+
const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
|
|
38
|
+
for (const module of modules) {
|
|
39
|
+
const resolved = (0, add_1.resolveModule)(module.packageName ?? module.name, modulesDir, projectRoot);
|
|
40
|
+
if (!resolved)
|
|
41
|
+
continue;
|
|
42
|
+
for (const glob of (0, add_1.readModuleManifest)(resolved.dir).ownedGlobs ?? [])
|
|
43
|
+
owned.add(glob);
|
|
44
|
+
}
|
|
45
|
+
return [...owned];
|
|
46
|
+
}
|
|
35
47
|
/**
|
|
36
48
|
* Build the update plan. `templatesDir` is the installed CLI's template set (the
|
|
37
49
|
* new version); the lock records what PodoKit last wrote (to detect user edits).
|
|
@@ -49,6 +61,7 @@ function planUpdate(projectRoot, templatesDir) {
|
|
|
49
61
|
projectRoot,
|
|
50
62
|
});
|
|
51
63
|
const managedOverrides = targetManagedOverrides(projectRoot, templatesDir, manifest.modules, manifest.managedOverrides);
|
|
64
|
+
const ownedGlobs = targetOwnedGlobs(projectRoot, templatesDir, manifest.modules, manifest.ownedGlobs);
|
|
52
65
|
const changes = [];
|
|
53
66
|
const paths = new Set([...newTree.keys(), ...Object.keys(lock.files)]);
|
|
54
67
|
for (const path of [...paths].sort()) {
|
|
@@ -58,12 +71,15 @@ function planUpdate(projectRoot, templatesDir) {
|
|
|
58
71
|
// A newer module can explicitly take responsibility for a path that older
|
|
59
72
|
// projects classified under a broad owned glob (notably generated skills).
|
|
60
73
|
const managedByTarget = managedOverrides.some((glob) => (0, lockfile_1.matchGlob)(path, glob));
|
|
74
|
+
const ownedByTarget = ownedGlobs.some((glob) => (0, lockfile_1.matchGlob)(path, glob));
|
|
61
75
|
const tier = managedByTarget
|
|
62
76
|
? "managed"
|
|
63
|
-
:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
: ownedByTarget
|
|
78
|
+
? "owned"
|
|
79
|
+
: locked?.tier ??
|
|
80
|
+
(newText !== null
|
|
81
|
+
? (0, lockfile_1.classifyTier)(path, newText, ownedGlobs, managedOverrides)
|
|
82
|
+
: "managed");
|
|
67
83
|
if (tier === "owned") {
|
|
68
84
|
changes.push({ path, tier, action: "skip", note: "owned — never modified" });
|
|
69
85
|
continue;
|
|
@@ -108,6 +124,20 @@ function planUpdate(projectRoot, templatesDir) {
|
|
|
108
124
|
changes,
|
|
109
125
|
};
|
|
110
126
|
}
|
|
127
|
+
function assertPreviousExternalModulesInstalled(projectRoot, templatesDir, modules) {
|
|
128
|
+
const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
|
|
129
|
+
for (const module of modules) {
|
|
130
|
+
if (!module.packageName || !module.moduleVersion)
|
|
131
|
+
continue;
|
|
132
|
+
const resolved = (0, add_1.resolveModule)(module.packageName, modulesDir, projectRoot);
|
|
133
|
+
if (!resolved?.moduleVersion || resolved.moduleVersion === module.moduleVersion)
|
|
134
|
+
continue;
|
|
135
|
+
throw new Error(`Cannot use --from while external module "${module.packageName}" is installed at ` +
|
|
136
|
+
`${resolved.moduleVersion} but the project records ${module.moduleVersion}. Restore ` +
|
|
137
|
+
`${module.packageName}@${module.moduleVersion}, update PodoKit with --from, then upgrade ` +
|
|
138
|
+
"external modules separately.");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
111
141
|
function writeFile(projectRoot, path, content) {
|
|
112
142
|
const abs = (0, node_path_1.join)(projectRoot, path);
|
|
113
143
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(abs), { recursive: true });
|
|
@@ -171,7 +201,12 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
|
|
|
171
201
|
projectRoot,
|
|
172
202
|
});
|
|
173
203
|
const managedOverrides = targetManagedOverrides(projectRoot, templatesDir, modules, manifest.managedOverrides);
|
|
174
|
-
const
|
|
204
|
+
const ownedGlobs = targetOwnedGlobs(projectRoot, templatesDir, modules, manifest.ownedGlobs);
|
|
205
|
+
const needsMergeBase = plan.changes.some((change) => change.action === "conflict");
|
|
206
|
+
if (options.oldTemplatesDir && needsMergeBase) {
|
|
207
|
+
assertPreviousExternalModulesInstalled(projectRoot, templatesDir, modules);
|
|
208
|
+
}
|
|
209
|
+
const oldTree = options.oldTemplatesDir && needsMergeBase
|
|
175
210
|
? (0, assemble_1.assembleProject)({
|
|
176
211
|
templatesDir: options.oldTemplatesDir,
|
|
177
212
|
template: manifest.template,
|
|
@@ -210,7 +245,7 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
|
|
|
210
245
|
// Keep the assembled target as the managed baseline. A merged working file
|
|
211
246
|
// intentionally remains drifted so a future update performs another 3-way
|
|
212
247
|
// merge instead of treating the user's lines as clean generated output.
|
|
213
|
-
(0, lockfile_1.writeFilesLock)(projectRoot, updatedFilesLock(projectRoot, newTree, previousLock,
|
|
248
|
+
(0, lockfile_1.writeFilesLock)(projectRoot, updatedFilesLock(projectRoot, newTree, previousLock, ownedGlobs, managedOverrides));
|
|
214
249
|
const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
|
|
215
250
|
const refreshedModules = manifest.modules.map((module) => {
|
|
216
251
|
const resolved = (0, add_1.resolveModule)(module.packageName ?? module.name, modulesDir, projectRoot);
|
|
@@ -224,6 +259,7 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
|
|
|
224
259
|
});
|
|
225
260
|
(0, lockfile_1.writeManifest)(projectRoot, {
|
|
226
261
|
...manifest,
|
|
262
|
+
ownedGlobs,
|
|
227
263
|
managedOverrides,
|
|
228
264
|
modules: refreshedModules,
|
|
229
265
|
podokitVersion: (0, lockfile_1.podokitVersion)(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podosoft/podokit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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,10 +0,0 @@
|
|
|
1
|
-
import type { BaseTranslation } from "typesafe-i18n";
|
|
2
|
-
|
|
3
|
-
// Base locale. Run `npx typesafe-i18n` to generate the typed runtime,
|
|
4
|
-
// then access strings as `$LL.appTitle()` in components.
|
|
5
|
-
const en = {
|
|
6
|
-
appTitle: "{{projectName}}",
|
|
7
|
-
checkHealth: "Check API health",
|
|
8
|
-
} satisfies BaseTranslation;
|
|
9
|
-
|
|
10
|
-
export default en;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { BaseTranslation } from "typesafe-i18n";
|
|
2
|
-
|
|
3
|
-
// Uses BaseTranslation until you generate typed runtimes with `npx typesafe-i18n`.
|
|
4
|
-
// After generating, switch to `import type { Translation } from "../i18n-types"`
|
|
5
|
-
// so this locale is checked against the base locale's keys.
|
|
6
|
-
const ko = {
|
|
7
|
-
appTitle: "{{projectName}}",
|
|
8
|
-
checkHealth: "API 상태 확인",
|
|
9
|
-
} satisfies BaseTranslation;
|
|
10
|
-
|
|
11
|
-
export default ko;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { BaseTranslation } from "typesafe-i18n";
|
|
2
|
-
|
|
3
|
-
// Base locale. Run `npx typesafe-i18n` to generate the typed runtime,
|
|
4
|
-
// then access strings as `$LL.appTitle()` in components.
|
|
5
|
-
const en = {
|
|
6
|
-
appTitle: "{{projectName}}",
|
|
7
|
-
checkHealth: "Check API health",
|
|
8
|
-
} satisfies BaseTranslation;
|
|
9
|
-
|
|
10
|
-
export default en;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { BaseTranslation } from "typesafe-i18n";
|
|
2
|
-
|
|
3
|
-
// Uses BaseTranslation until you generate typed runtimes with `npx typesafe-i18n`.
|
|
4
|
-
// After generating, switch to `import type { Translation } from "../i18n-types"`
|
|
5
|
-
// so this locale is checked against the base locale's keys.
|
|
6
|
-
const ko = {
|
|
7
|
-
appTitle: "{{projectName}}",
|
|
8
|
-
checkHealth: "API 상태 확인",
|
|
9
|
-
} satisfies BaseTranslation;
|
|
10
|
-
|
|
11
|
-
export default ko;
|