@askrjs/cli 0.0.2
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/LICENSE +201 -0
- package/README.md +140 -0
- package/dist/add-DqhqRzBQ.js +555 -0
- package/dist/add-DqhqRzBQ.js.map +1 -0
- package/dist/add.js +3 -0
- package/dist/cli.js +91 -0
- package/dist/cli.js.map +1 -0
- package/dist/create.js +923 -0
- package/dist/create.js.map +1 -0
- package/dist/is-direct-execution-Cdlr-ZUl.js +20 -0
- package/dist/is-direct-execution-Cdlr-ZUl.js.map +1 -0
- package/dist/openapi.js +127 -0
- package/dist/openapi.js.map +1 -0
- package/dist/skills/askr-accessibility/SKILL.md +68 -0
- package/dist/skills/askr-accessibility/agents/openai.yaml +4 -0
- package/dist/skills/askr-agent-execution/SKILL.md +85 -0
- package/dist/skills/askr-agent-execution/agents/openai.yaml +4 -0
- package/dist/skills/askr-agent-workflows/SKILL.md +57 -0
- package/dist/skills/askr-agent-workflows/agents/openai.yaml +4 -0
- package/dist/skills/askr-api-integration/SKILL.md +79 -0
- package/dist/skills/askr-api-integration/agents/openai.yaml +4 -0
- package/dist/skills/askr-app-builder/SKILL.md +99 -0
- package/dist/skills/askr-app-builder/agents/openai.yaml +4 -0
- package/dist/skills/askr-auth-access/SKILL.md +74 -0
- package/dist/skills/askr-auth-access/agents/openai.yaml +4 -0
- package/dist/skills/askr-cli-vite/SKILL.md +76 -0
- package/dist/skills/askr-cli-vite/agents/openai.yaml +4 -0
- package/dist/skills/askr-dashboard-charts/SKILL.md +82 -0
- package/dist/skills/askr-dashboard-charts/agents/openai.yaml +4 -0
- package/dist/skills/askr-design-system/SKILL.md +86 -0
- package/dist/skills/askr-design-system/agents/openai.yaml +4 -0
- package/dist/skills/askr-env-config/SKILL.md +74 -0
- package/dist/skills/askr-env-config/agents/openai.yaml +4 -0
- package/dist/skills/askr-error-loading-empty/SKILL.md +89 -0
- package/dist/skills/askr-error-loading-empty/agents/openai.yaml +4 -0
- package/dist/skills/askr-file-upload-artifacts/SKILL.md +77 -0
- package/dist/skills/askr-file-upload-artifacts/agents/openai.yaml +4 -0
- package/dist/skills/askr-forms-tables-crud/SKILL.md +98 -0
- package/dist/skills/askr-forms-tables-crud/agents/openai.yaml +4 -0
- package/dist/skills/askr-mental-model/SKILL.md +109 -0
- package/dist/skills/askr-mental-model/agents/openai.yaml +4 -0
- package/dist/skills/askr-migration-react/SKILL.md +81 -0
- package/dist/skills/askr-migration-react/agents/openai.yaml +4 -0
- package/dist/skills/askr-observability-debugging/SKILL.md +83 -0
- package/dist/skills/askr-observability-debugging/agents/openai.yaml +4 -0
- package/dist/skills/askr-project-structure/SKILL.md +79 -0
- package/dist/skills/askr-project-structure/agents/openai.yaml +4 -0
- package/dist/skills/askr-query-mutation/SKILL.md +115 -0
- package/dist/skills/askr-query-mutation/agents/openai.yaml +4 -0
- package/dist/skills/askr-realtime-streaming/SKILL.md +76 -0
- package/dist/skills/askr-realtime-streaming/agents/openai.yaml +4 -0
- package/dist/skills/askr-resources-data/SKILL.md +100 -0
- package/dist/skills/askr-resources-data/agents/openai.yaml +4 -0
- package/dist/skills/askr-routing-layouts/SKILL.md +107 -0
- package/dist/skills/askr-routing-layouts/agents/openai.yaml +4 -0
- package/dist/skills/askr-runtime-reactivity/SKILL.md +92 -0
- package/dist/skills/askr-runtime-reactivity/agents/openai.yaml +4 -0
- package/dist/skills/askr-ssr-ssg/SKILL.md +76 -0
- package/dist/skills/askr-ssr-ssg/agents/openai.yaml +4 -0
- package/dist/skills/askr-testing-determinism/SKILL.md +75 -0
- package/dist/skills/askr-testing-determinism/agents/openai.yaml +4 -0
- package/dist/skills/askr-theming/SKILL.md +86 -0
- package/dist/skills/askr-theming/agents/openai.yaml +4 -0
- package/dist/skills/askr-ui-composition/SKILL.md +92 -0
- package/dist/skills/askr-ui-composition/agents/openai.yaml +4 -0
- package/dist/skills-Bh0mEhIx.js +682 -0
- package/dist/skills-Bh0mEhIx.js.map +1 -0
- package/dist/skills.js +3 -0
- package/dist/ssg.js +188 -0
- package/dist/ssg.js.map +1 -0
- package/dist/templates/full-stack/AGENTS.md +12 -0
- package/dist/templates/full-stack/README.md +18 -0
- package/dist/templates/full-stack/gitignore.template +4 -0
- package/dist/templates/full-stack/index.html +14 -0
- package/dist/templates/full-stack/package.json +37 -0
- package/dist/templates/full-stack/server.ts +13 -0
- package/dist/templates/full-stack/src/action-authorizations.ts +10 -0
- package/dist/templates/full-stack/src/actions/create-message.ts +10 -0
- package/dist/templates/full-stack/src/i18n.ts +8 -0
- package/dist/templates/full-stack/src/main.tsx +12 -0
- package/dist/templates/full-stack/src/pages/home.tsx +25 -0
- package/dist/templates/full-stack/src/pages/layout.tsx +33 -0
- package/dist/templates/full-stack/src/pages/not-found.tsx +4 -0
- package/dist/templates/full-stack/src/routes.tsx +20 -0
- package/dist/templates/full-stack/src/schemas.ts +11 -0
- package/dist/templates/full-stack/src/server/action-registry.ts +12 -0
- package/dist/templates/full-stack/src/server/actions/create-message.ts +14 -0
- package/dist/templates/full-stack/src/server/app.ts +66 -0
- package/dist/templates/full-stack/src/server/dependencies.ts +55 -0
- package/dist/templates/full-stack/src/server/entry-server.ts +8 -0
- package/dist/templates/full-stack/src/telemetry.ts +4 -0
- package/dist/templates/full-stack/src/vite-server.d.ts +6 -0
- package/dist/templates/full-stack/tests/actions/create-message.test.ts +13 -0
- package/dist/templates/full-stack/tsconfig.json +16 -0
- package/dist/templates/full-stack/vite.config.ts +11 -0
- package/dist/templates/spa/AGENTS.md +60 -0
- package/dist/templates/spa/README.md +96 -0
- package/dist/templates/spa/gitignore.template +16 -0
- package/dist/templates/spa/index.html +13 -0
- package/dist/templates/spa/package.json +35 -0
- package/dist/templates/spa/src/adapters/operations-client.ts +90 -0
- package/dist/templates/spa/src/components/shared/metric-card.tsx +27 -0
- package/dist/templates/spa/src/components/shared/status-badge.tsx +24 -0
- package/dist/templates/spa/src/features/operations/operations.query.ts +9 -0
- package/dist/templates/spa/src/main.tsx +10 -0
- package/dist/templates/spa/src/pages/_layout.tsx +9 -0
- package/dist/templates/spa/src/pages/_routes.tsx +27 -0
- package/dist/templates/spa/src/pages/app/_layout.tsx +94 -0
- package/dist/templates/spa/src/pages/app/_routes.tsx +10 -0
- package/dist/templates/spa/src/pages/app/admin-home.tsx +152 -0
- package/dist/templates/spa/src/pages/app/agent-runs.tsx +93 -0
- package/dist/templates/spa/src/pages/app/settings.tsx +91 -0
- package/dist/templates/spa/src/pages/auth/_layout.tsx +38 -0
- package/dist/templates/spa/src/pages/auth/_routes.tsx +6 -0
- package/dist/templates/spa/src/pages/auth/login.tsx +63 -0
- package/dist/templates/spa/src/pages/not-found.tsx +22 -0
- package/dist/templates/spa/src/pages/public/_layout.tsx +47 -0
- package/dist/templates/spa/src/pages/public/_routes.tsx +6 -0
- package/dist/templates/spa/src/pages/public/home.tsx +143 -0
- package/dist/templates/spa/src/shared/format.ts +12 -0
- package/dist/templates/spa/src/shared/navigation.ts +15 -0
- package/dist/templates/spa/src/styles/components.css +49 -0
- package/dist/templates/spa/src/styles/layout.css +192 -0
- package/dist/templates/spa/src/styles/reset.css +44 -0
- package/dist/templates/spa/src/styles/theme.css +57 -0
- package/dist/templates/spa/src/styles/tokens.css +20 -0
- package/dist/templates/spa/src/styles.css +7 -0
- package/dist/templates/spa/src/styles.d.ts +1 -0
- package/dist/templates/spa/tests/app.test.tsx +37 -0
- package/dist/templates/spa/tests/components/shared.test.tsx +12 -0
- package/dist/templates/spa/tests/resources.test.ts +22 -0
- package/dist/templates/spa/tsconfig.json +20 -0
- package/dist/templates/spa/tsconfig.node.json +10 -0
- package/dist/templates/spa/vite.config.ts +24 -0
- package/dist/templates/spa/vitest.config.ts +17 -0
- package/dist/templates/ssg/AGENTS.md +55 -0
- package/dist/templates/ssg/README.md +39 -0
- package/dist/templates/ssg/gitignore.template +17 -0
- package/dist/templates/ssg/index.html +13 -0
- package/dist/templates/ssg/package.json +35 -0
- package/dist/templates/ssg/src/app.tsx +14 -0
- package/dist/templates/ssg/src/components/badge.tsx +3 -0
- package/dist/templates/ssg/src/components/counter.tsx +30 -0
- package/dist/templates/ssg/src/components/site-shell.tsx +111 -0
- package/dist/templates/ssg/src/jsx.d.ts +23 -0
- package/dist/templates/ssg/src/main.tsx +8 -0
- package/dist/templates/ssg/src/pages/about.tsx +82 -0
- package/dist/templates/ssg/src/pages/content.tsx +63 -0
- package/dist/templates/ssg/src/pages/example.tsx +97 -0
- package/dist/templates/ssg/src/pages/home.tsx +51 -0
- package/dist/templates/ssg/src/resources/user.ts +15 -0
- package/dist/templates/ssg/src/routes.tsx +15 -0
- package/dist/templates/ssg/src/styles.css +89 -0
- package/dist/templates/ssg/src/vite-env.d.ts +5 -0
- package/dist/templates/ssg/ssg.config.ts +34 -0
- package/dist/templates/ssg/tests/app.test.tsx +19 -0
- package/dist/templates/ssg/tests/components/counter.test.tsx +18 -0
- package/dist/templates/ssg/tests/resources.test.ts +11 -0
- package/dist/templates/ssg/tests/ssg-config.test.ts +11 -0
- package/dist/templates/ssg/tsconfig.json +20 -0
- package/dist/templates/ssg/tsconfig.node.json +10 -0
- package/dist/templates/ssg/vite.config.ts +24 -0
- package/dist/templates/ssg/vitest.config.ts +17 -0
- package/dist/templates/ssr/AGENTS.md +28 -0
- package/dist/templates/ssr/README.md +38 -0
- package/dist/templates/ssr/gitignore.template +16 -0
- package/dist/templates/ssr/index.html +13 -0
- package/dist/templates/ssr/package.json +34 -0
- package/dist/templates/ssr/server.ts +16 -0
- package/dist/templates/ssr/src/app.tsx +21 -0
- package/dist/templates/ssr/src/components/counter.tsx +26 -0
- package/dist/templates/ssr/src/entry-server.tsx +10 -0
- package/dist/templates/ssr/src/main.tsx +11 -0
- package/dist/templates/ssr/src/pages/about.tsx +75 -0
- package/dist/templates/ssr/src/pages/example.tsx +113 -0
- package/dist/templates/ssr/src/pages/home.tsx +56 -0
- package/dist/templates/ssr/src/resources/user.ts +15 -0
- package/dist/templates/ssr/src/routes.tsx +13 -0
- package/dist/templates/ssr/src/styles.css +148 -0
- package/dist/templates/ssr/src/vite-server.d.ts +7 -0
- package/dist/templates/ssr/tests/app.test.tsx +24 -0
- package/dist/templates/ssr/tests/components/counter.test.tsx +13 -0
- package/dist/templates/ssr/tests/resources.test.ts +11 -0
- package/dist/templates/ssr/tsconfig.json +22 -0
- package/dist/templates/ssr/tsconfig.node.json +10 -0
- package/dist/templates/ssr/tsconfig.server.json +15 -0
- package/dist/templates/ssr/vite.config.ts +25 -0
- package/dist/templates/ssr/vitest.config.ts +13 -0
- package/dist/templates/startkit/AGENTS.md +77 -0
- package/dist/templates/startkit/README.md +192 -0
- package/dist/templates/startkit/gitignore.template +16 -0
- package/dist/templates/startkit/index.html +13 -0
- package/dist/templates/startkit/package.json +35 -0
- package/dist/templates/startkit/src/components/app-header.tsx +93 -0
- package/dist/templates/startkit/src/components/app-sidebar.tsx +95 -0
- package/dist/templates/startkit/src/components/data-table.tsx +80 -0
- package/dist/templates/startkit/src/components/empty-state.tsx +13 -0
- package/dist/templates/startkit/src/components/page-header.tsx +23 -0
- package/dist/templates/startkit/src/components/stat-card.tsx +24 -0
- package/dist/templates/startkit/src/features/accounts/account-filters.tsx +61 -0
- package/dist/templates/startkit/src/features/accounts/account-table.tsx +79 -0
- package/dist/templates/startkit/src/lib/format.ts +31 -0
- package/dist/templates/startkit/src/lib/mock-data.ts +441 -0
- package/dist/templates/startkit/src/lib/routes.ts +116 -0
- package/dist/templates/startkit/src/main.tsx +12 -0
- package/dist/templates/startkit/src/pages/_layout.tsx +56 -0
- package/dist/templates/startkit/src/pages/auth/_layout.tsx +7 -0
- package/dist/templates/startkit/src/pages/auth/login.tsx +129 -0
- package/dist/templates/startkit/src/pages/home.tsx +83 -0
- package/dist/templates/startkit/src/pages/not-found.tsx +24 -0
- package/dist/templates/startkit/src/pages/workspace/_layout.tsx +17 -0
- package/dist/templates/startkit/src/pages/workspace/accounts/index.tsx +217 -0
- package/dist/templates/startkit/src/pages/workspace/dashboard.tsx +118 -0
- package/dist/templates/startkit/src/pages/workspace/settings.tsx +218 -0
- package/dist/templates/startkit/src/router.tsx +7 -0
- package/dist/templates/startkit/src/routes/auth-config.ts +38 -0
- package/dist/templates/startkit/src/routes/auth.ts +8 -0
- package/dist/templates/startkit/src/routes/index.ts +28 -0
- package/dist/templates/startkit/src/routes/public.ts +8 -0
- package/dist/templates/startkit/src/routes/workspace/accounts.ts +8 -0
- package/dist/templates/startkit/src/routes/workspace/index.ts +14 -0
- package/dist/templates/startkit/src/styles/components.css +396 -0
- package/dist/templates/startkit/src/styles/layout.css +192 -0
- package/dist/templates/startkit/src/styles/reset.css +38 -0
- package/dist/templates/startkit/src/styles/theme.css +76 -0
- package/dist/templates/startkit/src/styles/tokens.css +67 -0
- package/dist/templates/startkit/src/styles.css +7 -0
- package/dist/templates/startkit/src/toast.ts +55 -0
- package/dist/templates/startkit/src/utils/join-classes.ts +4 -0
- package/dist/templates/startkit/src/vite-env.d.ts +5 -0
- package/dist/templates/startkit/tests/app.test.tsx +32 -0
- package/dist/templates/startkit/tests/preferences.test.ts +43 -0
- package/dist/templates/startkit/tests/resources.test.ts +82 -0
- package/dist/templates/startkit/tsconfig.json +20 -0
- package/dist/templates/startkit/tsconfig.node.json +10 -0
- package/dist/templates/startkit/vite.config.ts +32 -0
- package/dist/templates/startkit/vitest.config.ts +13 -0
- package/dist/update-DyW1na5V.js +1140 -0
- package/dist/update-DyW1na5V.js.map +1 -0
- package/dist/update.js +3 -0
- package/package.json +84 -0
- package/skills/askr-accessibility/SKILL.md +68 -0
- package/skills/askr-accessibility/agents/openai.yaml +4 -0
- package/skills/askr-agent-execution/SKILL.md +85 -0
- package/skills/askr-agent-execution/agents/openai.yaml +4 -0
- package/skills/askr-agent-workflows/SKILL.md +57 -0
- package/skills/askr-agent-workflows/agents/openai.yaml +4 -0
- package/skills/askr-api-integration/SKILL.md +79 -0
- package/skills/askr-api-integration/agents/openai.yaml +4 -0
- package/skills/askr-app-builder/SKILL.md +99 -0
- package/skills/askr-app-builder/agents/openai.yaml +4 -0
- package/skills/askr-auth-access/SKILL.md +74 -0
- package/skills/askr-auth-access/agents/openai.yaml +4 -0
- package/skills/askr-cli-vite/SKILL.md +76 -0
- package/skills/askr-cli-vite/agents/openai.yaml +4 -0
- package/skills/askr-dashboard-charts/SKILL.md +82 -0
- package/skills/askr-dashboard-charts/agents/openai.yaml +4 -0
- package/skills/askr-design-system/SKILL.md +86 -0
- package/skills/askr-design-system/agents/openai.yaml +4 -0
- package/skills/askr-env-config/SKILL.md +74 -0
- package/skills/askr-env-config/agents/openai.yaml +4 -0
- package/skills/askr-error-loading-empty/SKILL.md +89 -0
- package/skills/askr-error-loading-empty/agents/openai.yaml +4 -0
- package/skills/askr-file-upload-artifacts/SKILL.md +77 -0
- package/skills/askr-file-upload-artifacts/agents/openai.yaml +4 -0
- package/skills/askr-forms-tables-crud/SKILL.md +98 -0
- package/skills/askr-forms-tables-crud/agents/openai.yaml +4 -0
- package/skills/askr-mental-model/SKILL.md +109 -0
- package/skills/askr-mental-model/agents/openai.yaml +4 -0
- package/skills/askr-migration-react/SKILL.md +81 -0
- package/skills/askr-migration-react/agents/openai.yaml +4 -0
- package/skills/askr-observability-debugging/SKILL.md +83 -0
- package/skills/askr-observability-debugging/agents/openai.yaml +4 -0
- package/skills/askr-project-structure/SKILL.md +79 -0
- package/skills/askr-project-structure/agents/openai.yaml +4 -0
- package/skills/askr-query-mutation/SKILL.md +115 -0
- package/skills/askr-query-mutation/agents/openai.yaml +4 -0
- package/skills/askr-realtime-streaming/SKILL.md +76 -0
- package/skills/askr-realtime-streaming/agents/openai.yaml +4 -0
- package/skills/askr-resources-data/SKILL.md +100 -0
- package/skills/askr-resources-data/agents/openai.yaml +4 -0
- package/skills/askr-routing-layouts/SKILL.md +107 -0
- package/skills/askr-routing-layouts/agents/openai.yaml +4 -0
- package/skills/askr-runtime-reactivity/SKILL.md +92 -0
- package/skills/askr-runtime-reactivity/agents/openai.yaml +4 -0
- package/skills/askr-ssr-ssg/SKILL.md +76 -0
- package/skills/askr-ssr-ssg/agents/openai.yaml +4 -0
- package/skills/askr-testing-determinism/SKILL.md +75 -0
- package/skills/askr-testing-determinism/agents/openai.yaml +4 -0
- package/skills/askr-theming/SKILL.md +86 -0
- package/skills/askr-theming/agents/openai.yaml +4 -0
- package/skills/askr-ui-composition/SKILL.md +92 -0
- package/skills/askr-ui-composition/agents/openai.yaml +4 -0
- package/templates/full-stack/AGENTS.md +12 -0
- package/templates/full-stack/README.md +18 -0
- package/templates/full-stack/gitignore.template +4 -0
- package/templates/full-stack/index.html +14 -0
- package/templates/full-stack/package.json +37 -0
- package/templates/full-stack/server.ts +13 -0
- package/templates/full-stack/src/action-authorizations.ts +10 -0
- package/templates/full-stack/src/actions/create-message.ts +10 -0
- package/templates/full-stack/src/i18n.ts +8 -0
- package/templates/full-stack/src/main.tsx +12 -0
- package/templates/full-stack/src/pages/home.tsx +25 -0
- package/templates/full-stack/src/pages/layout.tsx +33 -0
- package/templates/full-stack/src/pages/not-found.tsx +4 -0
- package/templates/full-stack/src/routes.tsx +20 -0
- package/templates/full-stack/src/schemas.ts +11 -0
- package/templates/full-stack/src/server/action-registry.ts +12 -0
- package/templates/full-stack/src/server/actions/create-message.ts +14 -0
- package/templates/full-stack/src/server/app.ts +66 -0
- package/templates/full-stack/src/server/dependencies.ts +55 -0
- package/templates/full-stack/src/server/entry-server.ts +8 -0
- package/templates/full-stack/src/telemetry.ts +4 -0
- package/templates/full-stack/src/vite-server.d.ts +6 -0
- package/templates/full-stack/tests/actions/create-message.test.ts +13 -0
- package/templates/full-stack/tsconfig.json +16 -0
- package/templates/full-stack/vite.config.ts +11 -0
- package/templates/spa/AGENTS.md +60 -0
- package/templates/spa/README.md +96 -0
- package/templates/spa/gitignore.template +16 -0
- package/templates/spa/index.html +13 -0
- package/templates/spa/package.json +35 -0
- package/templates/spa/src/adapters/operations-client.ts +90 -0
- package/templates/spa/src/components/shared/metric-card.tsx +27 -0
- package/templates/spa/src/components/shared/status-badge.tsx +24 -0
- package/templates/spa/src/features/operations/operations.query.ts +9 -0
- package/templates/spa/src/main.tsx +10 -0
- package/templates/spa/src/pages/_layout.tsx +9 -0
- package/templates/spa/src/pages/_routes.tsx +27 -0
- package/templates/spa/src/pages/app/_layout.tsx +94 -0
- package/templates/spa/src/pages/app/_routes.tsx +10 -0
- package/templates/spa/src/pages/app/admin-home.tsx +152 -0
- package/templates/spa/src/pages/app/agent-runs.tsx +93 -0
- package/templates/spa/src/pages/app/settings.tsx +91 -0
- package/templates/spa/src/pages/auth/_layout.tsx +38 -0
- package/templates/spa/src/pages/auth/_routes.tsx +6 -0
- package/templates/spa/src/pages/auth/login.tsx +63 -0
- package/templates/spa/src/pages/not-found.tsx +22 -0
- package/templates/spa/src/pages/public/_layout.tsx +47 -0
- package/templates/spa/src/pages/public/_routes.tsx +6 -0
- package/templates/spa/src/pages/public/home.tsx +143 -0
- package/templates/spa/src/shared/format.ts +12 -0
- package/templates/spa/src/shared/navigation.ts +15 -0
- package/templates/spa/src/styles/components.css +49 -0
- package/templates/spa/src/styles/layout.css +192 -0
- package/templates/spa/src/styles/reset.css +44 -0
- package/templates/spa/src/styles/theme.css +57 -0
- package/templates/spa/src/styles/tokens.css +20 -0
- package/templates/spa/src/styles.css +7 -0
- package/templates/spa/src/styles.d.ts +1 -0
- package/templates/spa/tests/app.test.tsx +37 -0
- package/templates/spa/tests/components/shared.test.tsx +12 -0
- package/templates/spa/tests/resources.test.ts +22 -0
- package/templates/spa/tsconfig.json +20 -0
- package/templates/spa/tsconfig.node.json +10 -0
- package/templates/spa/vite.config.ts +24 -0
- package/templates/spa/vitest.config.ts +17 -0
- package/templates/ssg/AGENTS.md +55 -0
- package/templates/ssg/README.md +39 -0
- package/templates/ssg/gitignore.template +17 -0
- package/templates/ssg/index.html +13 -0
- package/templates/ssg/package.json +35 -0
- package/templates/ssg/src/app.tsx +14 -0
- package/templates/ssg/src/components/badge.tsx +3 -0
- package/templates/ssg/src/components/counter.tsx +30 -0
- package/templates/ssg/src/components/site-shell.tsx +111 -0
- package/templates/ssg/src/jsx.d.ts +23 -0
- package/templates/ssg/src/main.tsx +8 -0
- package/templates/ssg/src/pages/about.tsx +82 -0
- package/templates/ssg/src/pages/content.tsx +63 -0
- package/templates/ssg/src/pages/example.tsx +97 -0
- package/templates/ssg/src/pages/home.tsx +51 -0
- package/templates/ssg/src/resources/user.ts +15 -0
- package/templates/ssg/src/routes.tsx +15 -0
- package/templates/ssg/src/styles.css +89 -0
- package/templates/ssg/src/vite-env.d.ts +5 -0
- package/templates/ssg/ssg.config.ts +34 -0
- package/templates/ssg/tests/app.test.tsx +19 -0
- package/templates/ssg/tests/components/counter.test.tsx +18 -0
- package/templates/ssg/tests/resources.test.ts +11 -0
- package/templates/ssg/tests/ssg-config.test.ts +11 -0
- package/templates/ssg/tsconfig.json +20 -0
- package/templates/ssg/tsconfig.node.json +10 -0
- package/templates/ssg/vite.config.ts +24 -0
- package/templates/ssg/vitest.config.ts +17 -0
- package/templates/ssr/AGENTS.md +28 -0
- package/templates/ssr/README.md +38 -0
- package/templates/ssr/gitignore.template +16 -0
- package/templates/ssr/index.html +13 -0
- package/templates/ssr/package.json +34 -0
- package/templates/ssr/server.ts +16 -0
- package/templates/ssr/src/app.tsx +21 -0
- package/templates/ssr/src/components/counter.tsx +26 -0
- package/templates/ssr/src/entry-server.tsx +10 -0
- package/templates/ssr/src/main.tsx +11 -0
- package/templates/ssr/src/pages/about.tsx +75 -0
- package/templates/ssr/src/pages/example.tsx +113 -0
- package/templates/ssr/src/pages/home.tsx +56 -0
- package/templates/ssr/src/resources/user.ts +15 -0
- package/templates/ssr/src/routes.tsx +13 -0
- package/templates/ssr/src/styles.css +148 -0
- package/templates/ssr/src/vite-server.d.ts +7 -0
- package/templates/ssr/tests/app.test.tsx +24 -0
- package/templates/ssr/tests/components/counter.test.tsx +13 -0
- package/templates/ssr/tests/resources.test.ts +11 -0
- package/templates/ssr/tsconfig.json +22 -0
- package/templates/ssr/tsconfig.node.json +10 -0
- package/templates/ssr/tsconfig.server.json +15 -0
- package/templates/ssr/vite.config.ts +25 -0
- package/templates/ssr/vitest.config.ts +13 -0
- package/templates/startkit/AGENTS.md +77 -0
- package/templates/startkit/README.md +192 -0
- package/templates/startkit/gitignore.template +16 -0
- package/templates/startkit/index.html +13 -0
- package/templates/startkit/package.json +35 -0
- package/templates/startkit/src/components/app-header.tsx +93 -0
- package/templates/startkit/src/components/app-sidebar.tsx +95 -0
- package/templates/startkit/src/components/data-table.tsx +80 -0
- package/templates/startkit/src/components/empty-state.tsx +13 -0
- package/templates/startkit/src/components/page-header.tsx +23 -0
- package/templates/startkit/src/components/stat-card.tsx +24 -0
- package/templates/startkit/src/features/accounts/account-filters.tsx +61 -0
- package/templates/startkit/src/features/accounts/account-table.tsx +79 -0
- package/templates/startkit/src/lib/format.ts +31 -0
- package/templates/startkit/src/lib/mock-data.ts +441 -0
- package/templates/startkit/src/lib/routes.ts +116 -0
- package/templates/startkit/src/main.tsx +12 -0
- package/templates/startkit/src/pages/_layout.tsx +56 -0
- package/templates/startkit/src/pages/auth/_layout.tsx +7 -0
- package/templates/startkit/src/pages/auth/login.tsx +129 -0
- package/templates/startkit/src/pages/home.tsx +83 -0
- package/templates/startkit/src/pages/not-found.tsx +24 -0
- package/templates/startkit/src/pages/workspace/_layout.tsx +17 -0
- package/templates/startkit/src/pages/workspace/accounts/index.tsx +217 -0
- package/templates/startkit/src/pages/workspace/dashboard.tsx +118 -0
- package/templates/startkit/src/pages/workspace/settings.tsx +218 -0
- package/templates/startkit/src/router.tsx +7 -0
- package/templates/startkit/src/routes/auth-config.ts +38 -0
- package/templates/startkit/src/routes/auth.ts +8 -0
- package/templates/startkit/src/routes/index.ts +28 -0
- package/templates/startkit/src/routes/public.ts +8 -0
- package/templates/startkit/src/routes/workspace/accounts.ts +8 -0
- package/templates/startkit/src/routes/workspace/index.ts +14 -0
- package/templates/startkit/src/styles/components.css +396 -0
- package/templates/startkit/src/styles/layout.css +192 -0
- package/templates/startkit/src/styles/reset.css +38 -0
- package/templates/startkit/src/styles/theme.css +76 -0
- package/templates/startkit/src/styles/tokens.css +67 -0
- package/templates/startkit/src/styles.css +7 -0
- package/templates/startkit/src/toast.ts +55 -0
- package/templates/startkit/src/utils/join-classes.ts +4 -0
- package/templates/startkit/src/vite-env.d.ts +5 -0
- package/templates/startkit/tests/app.test.tsx +32 -0
- package/templates/startkit/tests/preferences.test.ts +43 -0
- package/templates/startkit/tests/resources.test.ts +82 -0
- package/templates/startkit/tsconfig.json +20 -0
- package/templates/startkit/tsconfig.node.json +10 -0
- package/templates/startkit/vite.config.ts +32 -0
- package/templates/startkit/vitest.config.ts +13 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: askr-ui-composition
|
|
3
|
+
description: Use when composing Askr UI with @askrjs/ui headless primitives, accessibility behavior, asChild polymorphism, root-owned state, private component context, slots, keyboard behavior, overlays, and app-local components.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Askr UI Composition
|
|
7
|
+
|
|
8
|
+
Use this when the hard part is behavior and composition rather than theming. The goal is to reuse `@askrjs/ui` for focus, keyboard, dismissal, and ARIA behavior instead of rebuilding it locally.
|
|
9
|
+
|
|
10
|
+
## Inspect First
|
|
11
|
+
|
|
12
|
+
- Existing component imports and slot naming
|
|
13
|
+
- The nearest shared component with similar behavior
|
|
14
|
+
- Existing theme usage to decide whether headless or themed primitives fit better
|
|
15
|
+
- Existing tests for keyboard or focus behavior
|
|
16
|
+
|
|
17
|
+
## Use This When
|
|
18
|
+
|
|
19
|
+
- You need dialog, popover, menu, select, toggle, checkbox, or other interactive behavior.
|
|
20
|
+
- The app needs headless primitives or already owns its own CSS.
|
|
21
|
+
- You are tempted to rebuild keyboard, focus, or dismissal logic locally.
|
|
22
|
+
- Themed primitives are not enough because behavior composition is the real problem.
|
|
23
|
+
|
|
24
|
+
## Choose UI Or Theme
|
|
25
|
+
|
|
26
|
+
- Use `@askrjs/ui` directly when the app has its own CSS or needs a headless behavior primitive.
|
|
27
|
+
- Use `@askrjs/themes` when the existing visual system should also style the primitive.
|
|
28
|
+
- Do not create app-local wrappers for solved theme surfaces unless the wrapper adds product semantics.
|
|
29
|
+
|
|
30
|
+
## Do This In Order
|
|
31
|
+
|
|
32
|
+
1. Pick the primitive family that already owns the behavior.
|
|
33
|
+
2. Keep coordination state in the root component that owns the interaction.
|
|
34
|
+
3. Use `asChild` only when caller markup must be preserved.
|
|
35
|
+
4. Use `data-slot` on app structure that needs stable styling hooks.
|
|
36
|
+
5. Validate keyboard, pointer, and ARIA behavior.
|
|
37
|
+
|
|
38
|
+
## Copy This Shape
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import {
|
|
42
|
+
AlertDialog,
|
|
43
|
+
AlertDialogAction,
|
|
44
|
+
AlertDialogCancel,
|
|
45
|
+
AlertDialogContent,
|
|
46
|
+
AlertDialogOverlay,
|
|
47
|
+
AlertDialogPortal,
|
|
48
|
+
AlertDialogTitle,
|
|
49
|
+
AlertDialogTrigger,
|
|
50
|
+
} from "@askrjs/ui/alert-dialog";
|
|
51
|
+
|
|
52
|
+
<AlertDialog>
|
|
53
|
+
<AlertDialogTrigger asChild>
|
|
54
|
+
<button>Archive</button>
|
|
55
|
+
</AlertDialogTrigger>
|
|
56
|
+
<AlertDialogPortal>
|
|
57
|
+
<AlertDialogOverlay />
|
|
58
|
+
<AlertDialogContent>
|
|
59
|
+
<AlertDialogTitle>Archive account?</AlertDialogTitle>
|
|
60
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
61
|
+
<AlertDialogAction>Archive</AlertDialogAction>
|
|
62
|
+
</AlertDialogContent>
|
|
63
|
+
</AlertDialogPortal>
|
|
64
|
+
</AlertDialog>;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Never Do These
|
|
68
|
+
|
|
69
|
+
- Reimplementing keyboard, focus, or dismissal behavior already owned by a primitive.
|
|
70
|
+
- Styling or business logic inside behavior primitives.
|
|
71
|
+
- Leaf components that fetch data or own routing.
|
|
72
|
+
- Silent invalid composition when a part requires a root scope.
|
|
73
|
+
- Prop bloat where composition would be clearer.
|
|
74
|
+
|
|
75
|
+
## Validate
|
|
76
|
+
|
|
77
|
+
- Keyboard and pointer behavior match the primitive contract.
|
|
78
|
+
- ARIA labels, names, and roles are present where required.
|
|
79
|
+
- `asChild` preserves expected markup and events.
|
|
80
|
+
- User-facing interaction has jsdom or browser coverage.
|
|
81
|
+
|
|
82
|
+
## Done When
|
|
83
|
+
|
|
84
|
+
- The app reused the primitive that already owns the behavior.
|
|
85
|
+
- Product composition stayed in the app, while behavior stayed in the primitive.
|
|
86
|
+
- No local behavior clone was introduced.
|
|
87
|
+
|
|
88
|
+
## Handoff
|
|
89
|
+
|
|
90
|
+
- Use `askr-theming` when the next step is visual styling and shell coherence.
|
|
91
|
+
- Use `askr-accessibility` when the next step is announcement, focus, or semantic review.
|
|
92
|
+
- Use `askr-testing-determinism` before closing user-facing interaction changes.
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
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.
|