@askrjs/cli 0.0.2 → 0.0.4
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 +46 -7
- package/dist/add.d.ts +16 -0
- package/dist/add.js +558 -1
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +43 -16
- package/dist/create.d.ts +5 -0
- package/dist/create.js +104 -26
- package/dist/directory-swap-DWoHtx7C.js +35 -0
- package/dist/discovery-Difb7Y4G.js +323 -0
- package/dist/generate.d.ts +5 -0
- package/dist/generate.js +461 -0
- package/dist/is-direct-execution-Cdlr-ZUl.js +0 -2
- package/dist/openapi.d.ts +25 -0
- package/dist/openapi.js +49 -16
- package/dist/planner-VAj7qlxr.js +285 -0
- package/dist/range-YUs9eimn.js +125 -0
- package/dist/registry-CyrRHRG5.js +209 -0
- package/dist/skills/askr-dashboard-charts/SKILL.md +61 -44
- package/dist/skills/askr-dashboard-charts/agents/openai.yaml +2 -2
- package/dist/skills/askr-ssr-ssg/SKILL.md +3 -1
- package/dist/{skills-Bh0mEhIx.js → skills-B7CbWur9.js} +89 -26
- package/dist/skills.d.ts +18 -0
- package/dist/skills.js +1 -1
- package/dist/specification-DXnDOC-0.js +49 -0
- package/dist/ssg-config.d.ts +41 -0
- package/dist/ssg-config.js +1 -0
- package/dist/ssg.d.ts +61 -0
- package/dist/ssg.js +360 -24
- package/dist/templates/full-stack/AGENTS.md +0 -1
- package/dist/templates/full-stack/README.md +0 -1
- package/dist/templates/full-stack/index.html +0 -1
- package/dist/templates/full-stack/package.json +5 -3
- package/dist/templates/full-stack/server.ts +5 -9
- package/dist/templates/full-stack/src/action-authorizations.ts +2 -2
- package/dist/templates/full-stack/src/actions/create-message.ts +5 -5
- package/dist/templates/full-stack/src/i18n.ts +4 -4
- package/dist/templates/full-stack/src/main.tsx +4 -5
- package/dist/templates/full-stack/src/pages/home.tsx +5 -5
- package/dist/templates/full-stack/src/pages/layout.tsx +15 -5
- package/dist/templates/full-stack/src/pages/not-found.tsx +5 -2
- package/dist/templates/full-stack/src/routes.tsx +11 -12
- package/dist/templates/full-stack/src/schemas.ts +1 -2
- package/dist/templates/full-stack/src/server/action-registry.ts +5 -10
- package/dist/templates/full-stack/src/server/actions/create-message.ts +4 -4
- package/dist/templates/full-stack/src/server/app.ts +67 -57
- package/dist/templates/full-stack/src/server/dependencies.ts +10 -10
- package/dist/templates/full-stack/src/server/entry-server.ts +3 -3
- package/dist/templates/full-stack/src/telemetry.ts +1 -2
- package/dist/templates/full-stack/tests/actions/create-message.test.ts +7 -8
- package/dist/templates/full-stack/tsconfig.json +1 -2
- package/dist/templates/full-stack/vite.config.ts +5 -6
- package/dist/templates/spa/AGENTS.md +1 -1
- package/dist/templates/spa/package.json +3 -4
- package/dist/templates/spa/src/adapters/operations-client.ts +7 -2
- package/dist/templates/spa/src/pages/app/_routes.tsx +2 -0
- package/dist/templates/spa/src/pages/app/admin-home.tsx +21 -3
- package/dist/templates/spa/src/pages/public/_routes.tsx +2 -0
- package/dist/templates/spa/src/styles.css +1 -1
- package/dist/templates/spa/tsconfig.json +2 -1
- package/dist/templates/spa/vitest.config.ts +0 -4
- package/dist/templates/ssg/README.md +1 -1
- package/dist/templates/ssg/package.json +1 -1
- package/dist/templates/ssg/ssg.config.ts +7 -0
- package/dist/templates/ssg/tests/ssg-config.test.ts +5 -3
- package/dist/templates/ssg/tsconfig.json +2 -1
- package/dist/templates/ssg/vitest.config.ts +0 -4
- package/dist/templates/ssr/package.json +2 -2
- package/dist/templates/ssr/server.ts +4 -10
- package/dist/templates/ssr/src/entry-server.tsx +6 -4
- package/dist/templates/startkit/package.json +2 -2
- package/dist/templates/startkit/src/lib/mock-data.ts +17 -39
- package/dist/templates/startkit/tsconfig.json +2 -1
- package/dist/update.d.ts +41 -0
- package/dist/update.js +282 -2
- package/dist/writer-D8qe_7ud.js +226 -0
- package/package.json +23 -22
- package/dist/add-DqhqRzBQ.js +0 -555
- package/dist/add-DqhqRzBQ.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/create.js.map +0 -1
- package/dist/is-direct-execution-Cdlr-ZUl.js.map +0 -1
- package/dist/openapi.js.map +0 -1
- package/dist/skills-Bh0mEhIx.js.map +0 -1
- package/dist/ssg.js.map +0 -1
- package/dist/templates/full-stack/src/vite-server.d.ts +0 -6
- package/dist/templates/spa/src/styles.d.ts +0 -1
- package/dist/templates/ssg/src/vite-env.d.ts +0 -5
- package/dist/templates/ssr/src/vite-server.d.ts +0 -7
- package/dist/templates/startkit/src/vite-env.d.ts +0 -5
- package/dist/update-DyW1na5V.js +0 -1140
- package/dist/update-DyW1na5V.js.map +0 -1
- package/skills/askr-accessibility/SKILL.md +0 -68
- package/skills/askr-accessibility/agents/openai.yaml +0 -4
- package/skills/askr-agent-execution/SKILL.md +0 -85
- package/skills/askr-agent-execution/agents/openai.yaml +0 -4
- package/skills/askr-agent-workflows/SKILL.md +0 -57
- package/skills/askr-agent-workflows/agents/openai.yaml +0 -4
- package/skills/askr-api-integration/SKILL.md +0 -79
- package/skills/askr-api-integration/agents/openai.yaml +0 -4
- package/skills/askr-app-builder/SKILL.md +0 -99
- package/skills/askr-app-builder/agents/openai.yaml +0 -4
- package/skills/askr-auth-access/SKILL.md +0 -74
- package/skills/askr-auth-access/agents/openai.yaml +0 -4
- package/skills/askr-cli-vite/SKILL.md +0 -76
- package/skills/askr-cli-vite/agents/openai.yaml +0 -4
- package/skills/askr-dashboard-charts/SKILL.md +0 -82
- package/skills/askr-dashboard-charts/agents/openai.yaml +0 -4
- package/skills/askr-design-system/SKILL.md +0 -86
- package/skills/askr-design-system/agents/openai.yaml +0 -4
- package/skills/askr-env-config/SKILL.md +0 -74
- package/skills/askr-env-config/agents/openai.yaml +0 -4
- package/skills/askr-error-loading-empty/SKILL.md +0 -89
- package/skills/askr-error-loading-empty/agents/openai.yaml +0 -4
- package/skills/askr-file-upload-artifacts/SKILL.md +0 -77
- package/skills/askr-file-upload-artifacts/agents/openai.yaml +0 -4
- package/skills/askr-forms-tables-crud/SKILL.md +0 -98
- package/skills/askr-forms-tables-crud/agents/openai.yaml +0 -4
- package/skills/askr-mental-model/SKILL.md +0 -109
- package/skills/askr-mental-model/agents/openai.yaml +0 -4
- package/skills/askr-migration-react/SKILL.md +0 -81
- package/skills/askr-migration-react/agents/openai.yaml +0 -4
- package/skills/askr-observability-debugging/SKILL.md +0 -83
- package/skills/askr-observability-debugging/agents/openai.yaml +0 -4
- package/skills/askr-project-structure/SKILL.md +0 -79
- package/skills/askr-project-structure/agents/openai.yaml +0 -4
- package/skills/askr-query-mutation/SKILL.md +0 -115
- package/skills/askr-query-mutation/agents/openai.yaml +0 -4
- package/skills/askr-realtime-streaming/SKILL.md +0 -76
- package/skills/askr-realtime-streaming/agents/openai.yaml +0 -4
- package/skills/askr-resources-data/SKILL.md +0 -100
- package/skills/askr-resources-data/agents/openai.yaml +0 -4
- package/skills/askr-routing-layouts/SKILL.md +0 -107
- package/skills/askr-routing-layouts/agents/openai.yaml +0 -4
- package/skills/askr-runtime-reactivity/SKILL.md +0 -92
- package/skills/askr-runtime-reactivity/agents/openai.yaml +0 -4
- package/skills/askr-ssr-ssg/SKILL.md +0 -76
- package/skills/askr-ssr-ssg/agents/openai.yaml +0 -4
- package/skills/askr-testing-determinism/SKILL.md +0 -75
- package/skills/askr-testing-determinism/agents/openai.yaml +0 -4
- package/skills/askr-theming/SKILL.md +0 -86
- package/skills/askr-theming/agents/openai.yaml +0 -4
- package/skills/askr-ui-composition/SKILL.md +0 -92
- package/skills/askr-ui-composition/agents/openai.yaml +0 -4
- package/templates/full-stack/AGENTS.md +0 -12
- package/templates/full-stack/README.md +0 -18
- package/templates/full-stack/gitignore.template +0 -4
- package/templates/full-stack/index.html +0 -14
- package/templates/full-stack/package.json +0 -37
- package/templates/full-stack/server.ts +0 -13
- package/templates/full-stack/src/action-authorizations.ts +0 -10
- package/templates/full-stack/src/actions/create-message.ts +0 -10
- package/templates/full-stack/src/i18n.ts +0 -8
- package/templates/full-stack/src/main.tsx +0 -12
- package/templates/full-stack/src/pages/home.tsx +0 -25
- package/templates/full-stack/src/pages/layout.tsx +0 -33
- package/templates/full-stack/src/pages/not-found.tsx +0 -4
- package/templates/full-stack/src/routes.tsx +0 -20
- package/templates/full-stack/src/schemas.ts +0 -11
- package/templates/full-stack/src/server/action-registry.ts +0 -12
- package/templates/full-stack/src/server/actions/create-message.ts +0 -14
- package/templates/full-stack/src/server/app.ts +0 -66
- package/templates/full-stack/src/server/dependencies.ts +0 -55
- package/templates/full-stack/src/server/entry-server.ts +0 -8
- package/templates/full-stack/src/telemetry.ts +0 -4
- package/templates/full-stack/src/vite-server.d.ts +0 -6
- package/templates/full-stack/tests/actions/create-message.test.ts +0 -13
- package/templates/full-stack/tsconfig.json +0 -16
- package/templates/full-stack/vite.config.ts +0 -11
- package/templates/spa/AGENTS.md +0 -60
- package/templates/spa/README.md +0 -96
- package/templates/spa/gitignore.template +0 -16
- package/templates/spa/index.html +0 -13
- package/templates/spa/package.json +0 -35
- package/templates/spa/src/adapters/operations-client.ts +0 -90
- package/templates/spa/src/components/shared/metric-card.tsx +0 -27
- package/templates/spa/src/components/shared/status-badge.tsx +0 -24
- package/templates/spa/src/features/operations/operations.query.ts +0 -9
- package/templates/spa/src/main.tsx +0 -10
- package/templates/spa/src/pages/_layout.tsx +0 -9
- package/templates/spa/src/pages/_routes.tsx +0 -27
- package/templates/spa/src/pages/app/_layout.tsx +0 -94
- package/templates/spa/src/pages/app/_routes.tsx +0 -10
- package/templates/spa/src/pages/app/admin-home.tsx +0 -152
- package/templates/spa/src/pages/app/agent-runs.tsx +0 -93
- package/templates/spa/src/pages/app/settings.tsx +0 -91
- package/templates/spa/src/pages/auth/_layout.tsx +0 -38
- package/templates/spa/src/pages/auth/_routes.tsx +0 -6
- package/templates/spa/src/pages/auth/login.tsx +0 -63
- package/templates/spa/src/pages/not-found.tsx +0 -22
- package/templates/spa/src/pages/public/_layout.tsx +0 -47
- package/templates/spa/src/pages/public/_routes.tsx +0 -6
- package/templates/spa/src/pages/public/home.tsx +0 -143
- package/templates/spa/src/shared/format.ts +0 -12
- package/templates/spa/src/shared/navigation.ts +0 -15
- package/templates/spa/src/styles/components.css +0 -49
- package/templates/spa/src/styles/layout.css +0 -192
- package/templates/spa/src/styles/reset.css +0 -44
- package/templates/spa/src/styles/theme.css +0 -57
- package/templates/spa/src/styles/tokens.css +0 -20
- package/templates/spa/src/styles.css +0 -7
- package/templates/spa/src/styles.d.ts +0 -1
- package/templates/spa/tests/app.test.tsx +0 -37
- package/templates/spa/tests/components/shared.test.tsx +0 -12
- package/templates/spa/tests/resources.test.ts +0 -22
- package/templates/spa/tsconfig.json +0 -20
- package/templates/spa/tsconfig.node.json +0 -10
- package/templates/spa/vite.config.ts +0 -24
- package/templates/spa/vitest.config.ts +0 -17
- package/templates/ssg/AGENTS.md +0 -55
- package/templates/ssg/README.md +0 -39
- package/templates/ssg/gitignore.template +0 -17
- package/templates/ssg/index.html +0 -13
- package/templates/ssg/package.json +0 -35
- package/templates/ssg/src/app.tsx +0 -14
- package/templates/ssg/src/components/badge.tsx +0 -3
- package/templates/ssg/src/components/counter.tsx +0 -30
- package/templates/ssg/src/components/site-shell.tsx +0 -111
- package/templates/ssg/src/jsx.d.ts +0 -23
- package/templates/ssg/src/main.tsx +0 -8
- package/templates/ssg/src/pages/about.tsx +0 -82
- package/templates/ssg/src/pages/content.tsx +0 -63
- package/templates/ssg/src/pages/example.tsx +0 -97
- package/templates/ssg/src/pages/home.tsx +0 -51
- package/templates/ssg/src/resources/user.ts +0 -15
- package/templates/ssg/src/routes.tsx +0 -15
- package/templates/ssg/src/styles.css +0 -89
- package/templates/ssg/src/vite-env.d.ts +0 -5
- package/templates/ssg/ssg.config.ts +0 -34
- package/templates/ssg/tests/app.test.tsx +0 -19
- package/templates/ssg/tests/components/counter.test.tsx +0 -18
- package/templates/ssg/tests/resources.test.ts +0 -11
- package/templates/ssg/tests/ssg-config.test.ts +0 -11
- package/templates/ssg/tsconfig.json +0 -20
- package/templates/ssg/tsconfig.node.json +0 -10
- package/templates/ssg/vite.config.ts +0 -24
- package/templates/ssg/vitest.config.ts +0 -17
- package/templates/ssr/AGENTS.md +0 -28
- package/templates/ssr/README.md +0 -38
- package/templates/ssr/gitignore.template +0 -16
- package/templates/ssr/index.html +0 -13
- package/templates/ssr/package.json +0 -34
- package/templates/ssr/server.ts +0 -16
- package/templates/ssr/src/app.tsx +0 -21
- package/templates/ssr/src/components/counter.tsx +0 -26
- package/templates/ssr/src/entry-server.tsx +0 -10
- package/templates/ssr/src/main.tsx +0 -11
- package/templates/ssr/src/pages/about.tsx +0 -75
- package/templates/ssr/src/pages/example.tsx +0 -113
- package/templates/ssr/src/pages/home.tsx +0 -56
- package/templates/ssr/src/resources/user.ts +0 -15
- package/templates/ssr/src/routes.tsx +0 -13
- package/templates/ssr/src/styles.css +0 -148
- package/templates/ssr/src/vite-server.d.ts +0 -7
- package/templates/ssr/tests/app.test.tsx +0 -24
- package/templates/ssr/tests/components/counter.test.tsx +0 -13
- package/templates/ssr/tests/resources.test.ts +0 -11
- package/templates/ssr/tsconfig.json +0 -22
- package/templates/ssr/tsconfig.node.json +0 -10
- package/templates/ssr/tsconfig.server.json +0 -15
- package/templates/ssr/vite.config.ts +0 -25
- package/templates/ssr/vitest.config.ts +0 -13
- package/templates/startkit/AGENTS.md +0 -77
- package/templates/startkit/README.md +0 -192
- package/templates/startkit/gitignore.template +0 -16
- package/templates/startkit/index.html +0 -13
- package/templates/startkit/package.json +0 -35
- package/templates/startkit/src/components/app-header.tsx +0 -93
- package/templates/startkit/src/components/app-sidebar.tsx +0 -95
- package/templates/startkit/src/components/data-table.tsx +0 -80
- package/templates/startkit/src/components/empty-state.tsx +0 -13
- package/templates/startkit/src/components/page-header.tsx +0 -23
- package/templates/startkit/src/components/stat-card.tsx +0 -24
- package/templates/startkit/src/features/accounts/account-filters.tsx +0 -61
- package/templates/startkit/src/features/accounts/account-table.tsx +0 -79
- package/templates/startkit/src/lib/format.ts +0 -31
- package/templates/startkit/src/lib/mock-data.ts +0 -441
- package/templates/startkit/src/lib/routes.ts +0 -116
- package/templates/startkit/src/main.tsx +0 -12
- package/templates/startkit/src/pages/_layout.tsx +0 -56
- package/templates/startkit/src/pages/auth/_layout.tsx +0 -7
- package/templates/startkit/src/pages/auth/login.tsx +0 -129
- package/templates/startkit/src/pages/home.tsx +0 -83
- package/templates/startkit/src/pages/not-found.tsx +0 -24
- package/templates/startkit/src/pages/workspace/_layout.tsx +0 -17
- package/templates/startkit/src/pages/workspace/accounts/index.tsx +0 -217
- package/templates/startkit/src/pages/workspace/dashboard.tsx +0 -118
- package/templates/startkit/src/pages/workspace/settings.tsx +0 -218
- package/templates/startkit/src/router.tsx +0 -7
- package/templates/startkit/src/routes/auth-config.ts +0 -38
- package/templates/startkit/src/routes/auth.ts +0 -8
- package/templates/startkit/src/routes/index.ts +0 -28
- package/templates/startkit/src/routes/public.ts +0 -8
- package/templates/startkit/src/routes/workspace/accounts.ts +0 -8
- package/templates/startkit/src/routes/workspace/index.ts +0 -14
- package/templates/startkit/src/styles/components.css +0 -396
- package/templates/startkit/src/styles/layout.css +0 -192
- package/templates/startkit/src/styles/reset.css +0 -38
- package/templates/startkit/src/styles/theme.css +0 -76
- package/templates/startkit/src/styles/tokens.css +0 -67
- package/templates/startkit/src/styles.css +0 -7
- package/templates/startkit/src/toast.ts +0 -55
- package/templates/startkit/src/utils/join-classes.ts +0 -4
- package/templates/startkit/src/vite-env.d.ts +0 -5
- package/templates/startkit/tests/app.test.tsx +0 -32
- package/templates/startkit/tests/preferences.test.ts +0 -43
- package/templates/startkit/tests/resources.test.ts +0 -82
- package/templates/startkit/tsconfig.json +0 -20
- package/templates/startkit/tsconfig.node.json +0 -10
- package/templates/startkit/vite.config.ts +0 -32
- package/templates/startkit/vitest.config.ts +0 -13
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# {{appName}}
|
|
2
|
-
|
|
3
|
-
This project is the progressive full-stack stage of an Askr application.
|
|
4
|
-
|
|
5
|
-
- Prefer functions, closures, and structural interfaces over classes.
|
|
6
|
-
- Keep action descriptors and schemas browser-safe under `src/actions` and `src/schemas.ts`.
|
|
7
|
-
- Keep handlers, repositories, secrets, and composition dependencies under `src/server`.
|
|
8
|
-
- Authorize every page action through the matched `route(..., { actions })` declaration.
|
|
9
|
-
- Keep `index.html` as the only document source. Preserve exactly one `<!--askr-head-->` and one `<!--askr-app-->` marker.
|
|
10
|
-
- Use native forms first; enhanced submission must preserve the same validation and authorization behavior.
|
|
11
|
-
- Never log cookies, authorization values, tokens, form fields, request bodies, or personal data.
|
|
12
|
-
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# {{appName}}
|
|
2
|
-
|
|
3
|
-
An Askr full-stack application with shared routes, executable schemas, declared actions, API routes, SSR hydration, i18n, telemetry seams, and Node/Vite production entrypoints.
|
|
4
|
-
|
|
5
|
-
## Commands
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install
|
|
9
|
-
npm run dev
|
|
10
|
-
npm run check
|
|
11
|
-
npm run build
|
|
12
|
-
npm run preview
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Use `askr add action <name> --route <path>` to generate a browser-safe descriptor, server handler, composition-root registration, route authorization, and focused test.
|
|
16
|
-
|
|
17
|
-
`index.html` owns the static document. `@askrjs/vite/server` injects only Askr-owned head nodes and composes the streamed app response at `<!--askr-app-->`.
|
|
18
|
-
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en" dir="ltr">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<meta name="theme-color" content="#111827" />
|
|
7
|
-
<!--askr-head-->
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="app"><!--askr-app--></div>
|
|
11
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
|
14
|
-
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{appName}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vp dev",
|
|
8
|
-
"build": "vp build && vp build --ssr server.ts --outDir dist/server",
|
|
9
|
-
"preview": "node dist/server/server.js",
|
|
10
|
-
"test": "vp test run",
|
|
11
|
-
"typecheck": "tsc --noEmit",
|
|
12
|
-
"lint": "vp lint src server.ts tests vite.config.ts",
|
|
13
|
-
"fmt": "vp fmt .",
|
|
14
|
-
"check": "npm run lint && npm run typecheck && npm test && npm run build"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@askrjs/auth": ">=0.0.1 <0.1.0",
|
|
18
|
-
"@askrjs/askr": ">=0.0.53 <0.1.0",
|
|
19
|
-
"@askrjs/i18n": ">=0.0.1 <0.1.0",
|
|
20
|
-
"@askrjs/node": ">=0.0.1 <0.1.0",
|
|
21
|
-
"@askrjs/otel": ">=0.0.1 <0.1.0",
|
|
22
|
-
"@askrjs/schema": ">=0.0.1 <0.1.0",
|
|
23
|
-
"@askrjs/server": ">=0.0.1 <0.1.0",
|
|
24
|
-
"@askrjs/themes": ">=0.0.12 <0.1.0",
|
|
25
|
-
"@askrjs/ui": ">=0.0.13 <0.1.0",
|
|
26
|
-
"@opentelemetry/api": "^1.9.0"
|
|
27
|
-
},
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"@askrjs/vite": ">=0.0.6 <0.1.0",
|
|
30
|
-
"@types/node": "^26.0.0",
|
|
31
|
-
"jsdom": "^29.1.1",
|
|
32
|
-
"typescript": "^6.0.3",
|
|
33
|
-
"vite-plus": "^0.2.4",
|
|
34
|
-
"vitest": "^4.1.10"
|
|
35
|
-
},
|
|
36
|
-
"engines": { "node": "^20.19.0 || >=22.12.0" }
|
|
37
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { listen } from '@askrjs/node';
|
|
2
|
-
import app from 'virtual:askr-server';
|
|
3
|
-
|
|
4
|
-
const port = Number(process.env.PORT ?? 3000);
|
|
5
|
-
const server = await listen(app, {
|
|
6
|
-
host: process.env.HOST ?? '127.0.0.1',
|
|
7
|
-
port,
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
const shutdown = () => server.close();
|
|
11
|
-
process.once('SIGINT', shutdown);
|
|
12
|
-
process.once('SIGTERM', shutdown);
|
|
13
|
-
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ActionDescriptor } from '@askrjs/askr/actions';
|
|
2
|
-
import { createMessageAction } from './actions/create-message';
|
|
3
|
-
|
|
4
|
-
const actionsByRoute: Readonly<Record<string, readonly ActionDescriptor[]>> = Object.freeze({
|
|
5
|
-
"/": [createMessageAction],
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export function actionsFor(path: string): readonly ActionDescriptor[] {
|
|
9
|
-
return actionsByRoute[path] ?? [];
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { defineAction } from '@askrjs/askr/actions';
|
|
2
|
-
import { MessageInput } from '../schemas';
|
|
3
|
-
|
|
4
|
-
export const createMessageActionRoute = '/';
|
|
5
|
-
|
|
6
|
-
export const createMessageAction = defineAction({
|
|
7
|
-
id: 'create-message',
|
|
8
|
-
input: MessageInput,
|
|
9
|
-
invalidates: ['messages'],
|
|
10
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { createSPA, hydrateSPA } from '@askrjs/askr/boot';
|
|
2
|
-
import { pageRegistry } from './routes';
|
|
3
|
-
|
|
4
|
-
const root = document.getElementById('app');
|
|
5
|
-
if (!root) throw new Error('Missing #app root element.');
|
|
6
|
-
|
|
7
|
-
if (root.hasChildNodes()) {
|
|
8
|
-
await hydrateSPA({ root, registry: pageRegistry });
|
|
9
|
-
} else {
|
|
10
|
-
await createSPA({ root, registry: pageRegistry });
|
|
11
|
-
}
|
|
12
|
-
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ActionForm } from '@askrjs/askr/actions';
|
|
2
|
-
import { createMessageAction } from '../actions/create-message';
|
|
3
|
-
import { messages } from '../i18n';
|
|
4
|
-
|
|
5
|
-
export function HomePage() {
|
|
6
|
-
return (
|
|
7
|
-
<main>
|
|
8
|
-
<h1>{messages.text('title')}</h1>
|
|
9
|
-
<p>{messages.text('intro')}</p>
|
|
10
|
-
<form method="post" action="/api/session">
|
|
11
|
-
<button type="submit">Start demo session</button>
|
|
12
|
-
</form>
|
|
13
|
-
{ActionForm({
|
|
14
|
-
action: createMessageAction,
|
|
15
|
-
children: (
|
|
16
|
-
<>
|
|
17
|
-
<label htmlFor="message">Message</label>
|
|
18
|
-
<input id="message" name="value" required />
|
|
19
|
-
<button type="submit">Create message</button>
|
|
20
|
-
</>
|
|
21
|
-
),
|
|
22
|
-
})}
|
|
23
|
-
</main>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Link } from '@askrjs/askr/router';
|
|
2
|
-
import { Block, Container, Header, Navbar, NavBrand, NavGroup, NavLink } from '@askrjs/themes/components';
|
|
3
|
-
import { ThemeScope, ThemeToggle } from '@askrjs/themes/theme';
|
|
4
|
-
import { messages } from '../i18n';
|
|
5
|
-
|
|
6
|
-
function ScopedLayout({ children }: { children?: unknown }) {
|
|
7
|
-
return (
|
|
8
|
-
<ThemeScope defaultTheme="light" storageKey="{{appName}}-theme">
|
|
9
|
-
<Block minHeight="screen">
|
|
10
|
-
<Header sticky>
|
|
11
|
-
<Container paddingY="sm">
|
|
12
|
-
<Navbar aria-label="Primary navigation" width="full">
|
|
13
|
-
<NavBrand><Link href="/">{messages.text('title')}</Link></NavBrand>
|
|
14
|
-
<NavGroup align="end">
|
|
15
|
-
<NavLink href="/">Home</NavLink>
|
|
16
|
-
<ThemeToggle aria-label="Toggle theme" />
|
|
17
|
-
</NavGroup>
|
|
18
|
-
</Navbar>
|
|
19
|
-
</Container>
|
|
20
|
-
</Header>
|
|
21
|
-
<Container paddingY="2xl">{children}</Container>
|
|
22
|
-
</Block>
|
|
23
|
-
</ThemeScope>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function AppLayout({ children }: { children?: unknown }) {
|
|
28
|
-
return (
|
|
29
|
-
<messages.Scope locale="en" dir="ltr">
|
|
30
|
-
<ScopedLayout>{children}</ScopedLayout>
|
|
31
|
-
</messages.Scope>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { createRouteRegistry, group, route } from '@askrjs/askr/router';
|
|
2
|
-
import { actionsFor } from './action-authorizations';
|
|
3
|
-
import { AppLayout } from './pages/layout';
|
|
4
|
-
import { HomePage } from './pages/home';
|
|
5
|
-
import { NotFoundPage } from './pages/not-found';
|
|
6
|
-
|
|
7
|
-
export const pageRegistry = createRouteRegistry(() => {
|
|
8
|
-
group({ layout: AppLayout }, () => {
|
|
9
|
-
route('/', HomePage, {
|
|
10
|
-
actions: actionsFor('/'),
|
|
11
|
-
meta: {
|
|
12
|
-
title: '{{appName}}',
|
|
13
|
-
description: 'A progressive full-stack Askr application.',
|
|
14
|
-
html: { lang: 'en', dir: 'ltr' },
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
route('/*', NotFoundPage);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { createActionRegistry } from '@askrjs/server/askr';
|
|
2
|
-
import { createMessageAction } from '../actions/create-message';
|
|
3
|
-
import { createMessage } from './actions/create-message';
|
|
4
|
-
import type { AppDependencies } from './dependencies';
|
|
5
|
-
|
|
6
|
-
export function createActions(deps: AppDependencies) {
|
|
7
|
-
const registry = createActionRegistry(deps, {
|
|
8
|
-
csrf: { secret: process.env.CSRF_SECRET ?? 'development-only-secret' },
|
|
9
|
-
});
|
|
10
|
-
registry.register(createMessageAction, createMessage);
|
|
11
|
-
return registry;
|
|
12
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { InferSchema } from '@askrjs/schema';
|
|
2
|
-
import { createMessageAction } from '../../actions/create-message';
|
|
3
|
-
import type { AppDependencies } from '../dependencies';
|
|
4
|
-
|
|
5
|
-
type Input = InferSchema<typeof createMessageAction.input>;
|
|
6
|
-
|
|
7
|
-
export async function createMessage(
|
|
8
|
-
_context: unknown,
|
|
9
|
-
input: Input,
|
|
10
|
-
deps: Pick<AppDependencies, 'actions'>,
|
|
11
|
-
) {
|
|
12
|
-
await deps.actions.record({ action: createMessageAction.id, value: input.value });
|
|
13
|
-
return { result: { accepted: true } };
|
|
14
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { createAuth } from '@askrjs/auth';
|
|
2
|
-
import { createServerApp } from '@askrjs/server';
|
|
3
|
-
import { createAskrPageHandler } from '@askrjs/server/askr';
|
|
4
|
-
import { csrf, rateLimit, requestId, securityHeaders } from '@askrjs/server/middleware';
|
|
5
|
-
import { createApi } from '@askrjs/server/openapi';
|
|
6
|
-
import { pageRegistry } from '../routes';
|
|
7
|
-
import { Message, MessageInput } from '../schemas';
|
|
8
|
-
import { telemetry } from '../telemetry';
|
|
9
|
-
import { createActions } from './action-registry';
|
|
10
|
-
import type { AppDependencies } from './dependencies';
|
|
11
|
-
|
|
12
|
-
export function createApp(deps: AppDependencies) {
|
|
13
|
-
const api = createApi<AppDependencies>({
|
|
14
|
-
info: { title: '{{appName}} API', version: '1.0.0' },
|
|
15
|
-
});
|
|
16
|
-
api.post('/api/messages', {
|
|
17
|
-
input: {
|
|
18
|
-
body: {
|
|
19
|
-
schema: MessageInput,
|
|
20
|
-
mediaTypes: ['application/json'],
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
documentation: { body: { required: true } },
|
|
24
|
-
async handler(ctx, input, dependencies) {
|
|
25
|
-
const message = { id: crypto.randomUUID(), value: input.body.value };
|
|
26
|
-
await dependencies.actions.record({ action: 'api.create-message', value: message.value });
|
|
27
|
-
return ctx.created(message);
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
.operationId('createMessage')
|
|
31
|
-
.summary('Create a message')
|
|
32
|
-
.tags('Messages')
|
|
33
|
-
.use(
|
|
34
|
-
csrf({ secret: process.env.CSRF_SECRET ?? 'development-only-secret' }),
|
|
35
|
-
rateLimit({ store: deps.rateLimits, limit: 30, windowMs: 60_000 }),
|
|
36
|
-
)
|
|
37
|
-
.created(Message)
|
|
38
|
-
.badRequest()
|
|
39
|
-
.unprocessableEntity()
|
|
40
|
-
.tooManyRequests();
|
|
41
|
-
|
|
42
|
-
const router = api.createRouter(deps);
|
|
43
|
-
router.post('/api/session', (ctx) => {
|
|
44
|
-
const response = ctx.redirect('/', 303);
|
|
45
|
-
return ctx.setCookie(response, 'askr-session', 'demo-session', {
|
|
46
|
-
httpOnly: true,
|
|
47
|
-
sameSite: 'lax',
|
|
48
|
-
secure: ctx.url.protocol === 'https:',
|
|
49
|
-
path: '/',
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
return createServerApp({
|
|
53
|
-
router,
|
|
54
|
-
auth: createAuth({
|
|
55
|
-
sessions: deps.sessions,
|
|
56
|
-
principals: deps.principals,
|
|
57
|
-
sessionCookie: 'askr-session',
|
|
58
|
-
}),
|
|
59
|
-
middleware: [requestId(), securityHeaders()],
|
|
60
|
-
telemetry,
|
|
61
|
-
fallback: createAskrPageHandler({
|
|
62
|
-
registry: pageRegistry,
|
|
63
|
-
actions: createActions(deps),
|
|
64
|
-
}),
|
|
65
|
-
});
|
|
66
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
export interface AppDependencies {
|
|
2
|
-
readonly sessions: {
|
|
3
|
-
get(id: string): Promise<{ id: string; subject: string } | null>;
|
|
4
|
-
};
|
|
5
|
-
readonly principals: {
|
|
6
|
-
get(subject: string): Promise<{
|
|
7
|
-
id: string;
|
|
8
|
-
subject: string;
|
|
9
|
-
permissions: readonly string[];
|
|
10
|
-
} | null>;
|
|
11
|
-
};
|
|
12
|
-
readonly actions: {
|
|
13
|
-
record(entry: { action: string; value: string }): Promise<void>;
|
|
14
|
-
};
|
|
15
|
-
readonly rateLimits: {
|
|
16
|
-
consume(key: string, limit: number, windowMs: number): Promise<{
|
|
17
|
-
allowed: boolean;
|
|
18
|
-
remaining: number;
|
|
19
|
-
reset: number;
|
|
20
|
-
}>;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function createDependencies(): AppDependencies {
|
|
25
|
-
const counters = new Map<string, { count: number; reset: number }>();
|
|
26
|
-
return {
|
|
27
|
-
sessions: {
|
|
28
|
-
get: async (id) => id === 'demo-session'
|
|
29
|
-
? { id, subject: 'demo-user' }
|
|
30
|
-
: null,
|
|
31
|
-
},
|
|
32
|
-
principals: {
|
|
33
|
-
get: async (subject) => subject === 'demo-user'
|
|
34
|
-
? { id: subject, subject, permissions: ['messages:create'] }
|
|
35
|
-
: null,
|
|
36
|
-
},
|
|
37
|
-
actions: { record: async () => undefined },
|
|
38
|
-
rateLimits: {
|
|
39
|
-
async consume(key, limit, windowMs) {
|
|
40
|
-
const now = Date.now();
|
|
41
|
-
const current = counters.get(key);
|
|
42
|
-
const window = !current || current.reset <= now
|
|
43
|
-
? { count: 0, reset: now + windowMs }
|
|
44
|
-
: current;
|
|
45
|
-
window.count += 1;
|
|
46
|
-
counters.set(key, window);
|
|
47
|
-
return {
|
|
48
|
-
allowed: window.count <= limit,
|
|
49
|
-
remaining: Math.max(0, limit - window.count),
|
|
50
|
-
reset: window.reset,
|
|
51
|
-
};
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createMessageAction } from '../../src/actions/create-message';
|
|
3
|
-
|
|
4
|
-
describe('create message action', () => {
|
|
5
|
-
it('should reject an empty message given declared input validation', () => {
|
|
6
|
-
expect(createMessageAction.input.safeParse({ value: '' }).success).toBe(false);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('should accept a message given valid input', () => {
|
|
10
|
-
expect(createMessageAction.input.safeParse({ value: 'hello' }).success).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"strict": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"noEmit": true,
|
|
10
|
-
"jsx": "preserve",
|
|
11
|
-
"jsxImportSource": "@askrjs/askr",
|
|
12
|
-
"types": ["vite/client", "node"]
|
|
13
|
-
},
|
|
14
|
-
"include": ["src", "tests", "server.ts", "vite.config.ts"]
|
|
15
|
-
}
|
|
16
|
-
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite-plus';
|
|
2
|
-
import { askr } from '@askrjs/vite';
|
|
3
|
-
import { askrServer } from '@askrjs/vite/server';
|
|
4
|
-
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
plugins: [askr(), askrServer({ entry: './src/server/entry-server.ts' })],
|
|
7
|
-
lint: { ignorePatterns: ['dist/**', 'node_modules/**', 'coverage/**'] },
|
|
8
|
-
server: { port: 5173, open: true },
|
|
9
|
-
build: { manifest: true, sourcemap: true },
|
|
10
|
-
});
|
|
11
|
-
|
package/templates/spa/AGENTS.md
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# {{appName}}
|
|
2
|
-
|
|
3
|
-
Client-side SPA built with Askr, askr-ui, askr-themes, and askr-charts.
|
|
4
|
-
|
|
5
|
-
## Commands
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm run dev # Vite dev server with HMR (port 5173)
|
|
9
|
-
npm run build # Production build to dist/
|
|
10
|
-
npm run preview # Serve production build locally
|
|
11
|
-
npm test # Vitest (jsdom)
|
|
12
|
-
npm run typecheck # tsc --noEmit
|
|
13
|
-
npm run lint # ESLint
|
|
14
|
-
npm run fmt # Prettier
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Architecture
|
|
18
|
-
|
|
19
|
-
- **Routing:** `src/main.tsx` imports `src/pages/_routes.tsx`, then boots `createSPA()` with the route manifest. Route branches live under `src/pages/public`, `src/pages/auth`, and `src/pages/app`.
|
|
20
|
-
- **Layouts:** `_layout.tsx` files own shells. The root layout owns `ThemeScope`; public layouts own landing chrome, auth layouts own sign-in chrome, and app layouts own authenticated sidebar chrome.
|
|
21
|
-
- **UI:** Prefer the `@askrjs/themes/components` catalog before writing local components. Use app-local components only for product concepts such as `MetricCard` and `StatusBadge`; keep charts in `@askrjs/charts`.
|
|
22
|
-
- **State:** `const [value, setValue] = state(initial)`. Read with `value()`, update with `setValue(...)`. Use `derive()` for computed values and `resource()` for async data.
|
|
23
|
-
- **Data:** Route/container components own resources; `src/features` owns product workflows; `src/adapters` owns API clients, transports, abort handling, and generated clients.
|
|
24
|
-
- **Consistency:** Event-sourced screens should expose pending writes, projection lag, stale data, retries, and manual refresh instead of hiding everything behind one loading state.
|
|
25
|
-
- **Styling:** Keep `src/styles.css` thin. Customize theme values in `src/styles/tokens.css`, global defaults in `src/styles/theme.css`, shell structure in `src/styles/layout.css`, and component classes in `src/styles/components.css`.
|
|
26
|
-
- **Charts:** Import chart components from `@askrjs/charts/components`; chart CSS is loaded from `@askrjs/charts/default`.
|
|
27
|
-
- **Vite plugin:** `askr()` from `@askrjs/vite` handles JSX transform. Do not add manual esbuild JSX config.
|
|
28
|
-
|
|
29
|
-
## File Structure
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
src/
|
|
33
|
-
main.tsx
|
|
34
|
-
pages/
|
|
35
|
-
_routes.tsx
|
|
36
|
-
_layout.tsx
|
|
37
|
-
public/
|
|
38
|
-
auth/
|
|
39
|
-
app/
|
|
40
|
-
components/shared/
|
|
41
|
-
features/
|
|
42
|
-
adapters/
|
|
43
|
-
shared/
|
|
44
|
-
styles/
|
|
45
|
-
reset.css
|
|
46
|
-
tokens.css
|
|
47
|
-
theme.css
|
|
48
|
-
layout.css
|
|
49
|
-
components.css
|
|
50
|
-
tests/
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Conventions
|
|
54
|
-
|
|
55
|
-
- Keep routes thin and route-first.
|
|
56
|
-
- Keep shell chrome in layouts, not leaf pages.
|
|
57
|
-
- Keep business logic out of `src/pages`.
|
|
58
|
-
- Use `Link` and `navigate` from `@askrjs/askr/router`.
|
|
59
|
-
- Use headless `@askrjs/ui/*` for behavior primitives and `@askrjs/themes/*` for composed visual surfaces.
|
|
60
|
-
- Avoid hardcoded color systems, custom component catalogs, and React habits like effect-driven data loading.
|
package/templates/spa/README.md
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
# {{appName}} - SPA Template
|
|
2
|
-
|
|
3
|
-
A route-first Askr SPA built with `@askrjs/askr`, `@askrjs/ui`, `@askrjs/themes`, and `@askrjs/charts`.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install
|
|
9
|
-
npm run dev # Start dev server at http://localhost:5173
|
|
10
|
-
npm run build # Build for production
|
|
11
|
-
npm run preview # Preview production build
|
|
12
|
-
npm test # Run tests with Vitest
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Project Structure
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
src/
|
|
19
|
-
|-- main.tsx # SPA boot and route manifest
|
|
20
|
-
|-- pages/
|
|
21
|
-
| |-- _routes.tsx # Top-level route branches
|
|
22
|
-
| |-- _layout.tsx # Theme provider and app root
|
|
23
|
-
| |-- public/ # Guest-facing landing pages and shell
|
|
24
|
-
| |-- auth/ # Sign-in routes and shell
|
|
25
|
-
| `-- app/ # Authenticated branch routes and shell
|
|
26
|
-
|-- components/shared/ # App-local wrappers around theme primitives
|
|
27
|
-
|-- features/operations/ # Product workflow queries and mutations
|
|
28
|
-
|-- adapters/ # API clients and transport adapters
|
|
29
|
-
|-- shared/ # Cross-cutting helpers and navigation data
|
|
30
|
-
`-- styles/ # reset.css, tokens.css, theme.css, layout.css, components.css
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Core Patterns
|
|
34
|
-
|
|
35
|
-
Routes live in `src/pages`, shells live in `_layout.tsx`, reusable UI lives in
|
|
36
|
-
`src/components/shared`, business workflows live in `src/features`, and adapter
|
|
37
|
-
code lives in `src/adapters`.
|
|
38
|
-
|
|
39
|
-
```tsx
|
|
40
|
-
// src/pages/_routes.tsx
|
|
41
|
-
import { fallback, group, registerRoutes } from '@askrjs/askr/router';
|
|
42
|
-
import RootLayout from './_layout';
|
|
43
|
-
import NotFoundPage from './not-found';
|
|
44
|
-
import AuthLayout from './auth/_layout';
|
|
45
|
-
import { registerAuthRoutes } from './auth/_routes';
|
|
46
|
-
import AppLayout from './app/_layout';
|
|
47
|
-
import { registerAppRoutes } from './app/_routes';
|
|
48
|
-
import PublicLayout from './public/_layout';
|
|
49
|
-
import { registerPublicRoutes } from './public/_routes';
|
|
50
|
-
|
|
51
|
-
registerRoutes(() => {
|
|
52
|
-
group({ layout: RootLayout }, () => {
|
|
53
|
-
group({ layout: PublicLayout }, () => {
|
|
54
|
-
registerPublicRoutes();
|
|
55
|
-
});
|
|
56
|
-
group({ layout: AuthLayout }, () => {
|
|
57
|
-
registerAuthRoutes();
|
|
58
|
-
});
|
|
59
|
-
group({ layout: AppLayout }, () => {
|
|
60
|
-
registerAppRoutes();
|
|
61
|
-
});
|
|
62
|
-
fallback(NotFoundPage);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The template uses the `@askrjs/themes/components` catalog for styled UI
|
|
68
|
-
composition and keeps charts in `@askrjs/charts`. Add app-local components only
|
|
69
|
-
when they compose those primitives into a product concept. Start with
|
|
70
|
-
`src/styles/tokens.css` for theme knobs, then tune `theme.css`, `layout.css`,
|
|
71
|
-
and `components.css`.
|
|
72
|
-
|
|
73
|
-
Async reads are owned by route or container components with `resource()` and
|
|
74
|
-
delegated through feature/adapters:
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
const operations = resource(({ signal }) => loadOperations({ signal }), []);
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Cancellation belongs in adapters, loading/error/value states stay visible in the
|
|
81
|
-
route, and event-sourced consistency states should be modeled explicitly.
|
|
82
|
-
|
|
83
|
-
## What This Template Includes
|
|
84
|
-
|
|
85
|
-
- Public, auth, and authenticated app branches with separate layout shells
|
|
86
|
-
- Theme provider, header/nav/sidebar, cards, badges, buttons, empty states, and layout primitives
|
|
87
|
-
- A dashboard with async loading, charts, projection lag, and explicit refresh
|
|
88
|
-
- Feature and adapter boundaries for data ownership
|
|
89
|
-
- Tests that protect the route-first structure and cancellation behavior
|
|
90
|
-
|
|
91
|
-
## Next Steps
|
|
92
|
-
|
|
93
|
-
1. Replace the mock operations adapter with your generated API client.
|
|
94
|
-
2. Tune `src/styles/tokens.css` and `src/styles/theme.css` to match your brand.
|
|
95
|
-
3. Add route metadata for real auth and loader policies.
|
|
96
|
-
4. Keep new screens route-first and compose solved UI through `@askrjs/themes`.
|
package/templates/spa/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>{{appName}}</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="app"></div>
|
|
11
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|