@stacksjs/defaults 0.70.207 → 0.70.208
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/TODO.md +21 -0
- package/ai/AGENTS.md +79 -0
- package/ai/README.md +45 -0
- package/ai/claude/launch.json +12 -0
- package/ai/skills/stacks-actions/SKILL.md +128 -0
- package/ai/skills/stacks-ai/SKILL.md +276 -0
- package/ai/skills/stacks-alias/SKILL.md +94 -0
- package/ai/skills/stacks-analytics/SKILL.md +91 -0
- package/ai/skills/stacks-api/SKILL.md +738 -0
- package/ai/skills/stacks-arrays/SKILL.md +277 -0
- package/ai/skills/stacks-auth/SKILL.md +422 -0
- package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
- package/ai/skills/stacks-brandkit/SKILL.md +811 -0
- package/ai/skills/stacks-browse/SKILL.md +127 -0
- package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
- package/ai/skills/stacks-browser/SKILL.md +145 -0
- package/ai/skills/stacks-buddy/SKILL.md +676 -0
- package/ai/skills/stacks-build/SKILL.md +163 -0
- package/ai/skills/stacks-cache/SKILL.md +125 -0
- package/ai/skills/stacks-calendar/SKILL.md +113 -0
- package/ai/skills/stacks-chat/SKILL.md +371 -0
- package/ai/skills/stacks-cli/SKILL.md +112 -0
- package/ai/skills/stacks-cloud/SKILL.md +192 -0
- package/ai/skills/stacks-cms/SKILL.md +217 -0
- package/ai/skills/stacks-collections/SKILL.md +82 -0
- package/ai/skills/stacks-commerce/SKILL.md +114 -0
- package/ai/skills/stacks-composables/SKILL.md +161 -0
- package/ai/skills/stacks-config/SKILL.md +144 -0
- package/ai/skills/stacks-configuration/SKILL.md +154 -0
- package/ai/skills/stacks-cron/SKILL.md +123 -0
- package/ai/skills/stacks-crosswind/SKILL.md +190 -0
- package/ai/skills/stacks-dashboard/SKILL.md +173 -0
- package/ai/skills/stacks-database/SKILL.md +286 -0
- package/ai/skills/stacks-datetime/SKILL.md +269 -0
- package/ai/skills/stacks-dependencies/SKILL.md +111 -0
- package/ai/skills/stacks-deploy/SKILL.md +102 -0
- package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
- package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
- package/ai/skills/stacks-design-output/SKILL.md +55 -0
- package/ai/skills/stacks-design-soft/SKILL.md +156 -0
- package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
- package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
- package/ai/skills/stacks-desktop/SKILL.md +78 -0
- package/ai/skills/stacks-development/SKILL.md +298 -0
- package/ai/skills/stacks-dns/SKILL.md +149 -0
- package/ai/skills/stacks-docs/SKILL.md +109 -0
- package/ai/skills/stacks-email/SKILL.md +236 -0
- package/ai/skills/stacks-enums/SKILL.md +100 -0
- package/ai/skills/stacks-env/SKILL.md +181 -0
- package/ai/skills/stacks-error-handling/SKILL.md +137 -0
- package/ai/skills/stacks-events/SKILL.md +228 -0
- package/ai/skills/stacks-faker/SKILL.md +158 -0
- package/ai/skills/stacks-git/SKILL.md +158 -0
- package/ai/skills/stacks-guard/SKILL.md +98 -0
- package/ai/skills/stacks-health/SKILL.md +75 -0
- package/ai/skills/stacks-http/SKILL.md +316 -0
- package/ai/skills/stacks-i18n/SKILL.md +198 -0
- package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
- package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
- package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
- package/ai/skills/stacks-investigate/SKILL.md +119 -0
- package/ai/skills/stacks-jobs/SKILL.md +136 -0
- package/ai/skills/stacks-lint/SKILL.md +184 -0
- package/ai/skills/stacks-listeners/SKILL.md +99 -0
- package/ai/skills/stacks-logging/SKILL.md +78 -0
- package/ai/skills/stacks-mail/SKILL.md +120 -0
- package/ai/skills/stacks-middleware/SKILL.md +249 -0
- package/ai/skills/stacks-migrations/SKILL.md +105 -0
- package/ai/skills/stacks-models/SKILL.md +267 -0
- package/ai/skills/stacks-new-feature/SKILL.md +206 -0
- package/ai/skills/stacks-notifications/SKILL.md +210 -0
- package/ai/skills/stacks-objects/SKILL.md +205 -0
- package/ai/skills/stacks-office-hours/SKILL.md +134 -0
- package/ai/skills/stacks-orm/SKILL.md +342 -0
- package/ai/skills/stacks-path/SKILL.md +118 -0
- package/ai/skills/stacks-payments/SKILL.md +373 -0
- package/ai/skills/stacks-plan-review/SKILL.md +152 -0
- package/ai/skills/stacks-plugins/SKILL.md +149 -0
- package/ai/skills/stacks-push/SKILL.md +376 -0
- package/ai/skills/stacks-query-builder/SKILL.md +128 -0
- package/ai/skills/stacks-queue/SKILL.md +559 -0
- package/ai/skills/stacks-realtime/SKILL.md +442 -0
- package/ai/skills/stacks-redesign/SKILL.md +182 -0
- package/ai/skills/stacks-registry/SKILL.md +58 -0
- package/ai/skills/stacks-repl/SKILL.md +80 -0
- package/ai/skills/stacks-retro/SKILL.md +122 -0
- package/ai/skills/stacks-review/SKILL.md +135 -0
- package/ai/skills/stacks-router/SKILL.md +231 -0
- package/ai/skills/stacks-routes/SKILL.md +105 -0
- package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
- package/ai/skills/stacks-scheduler/SKILL.md +363 -0
- package/ai/skills/stacks-search-engine/SKILL.md +112 -0
- package/ai/skills/stacks-security/SKILL.md +129 -0
- package/ai/skills/stacks-security-audit/SKILL.md +128 -0
- package/ai/skills/stacks-server/SKILL.md +458 -0
- package/ai/skills/stacks-shell/SKILL.md +62 -0
- package/ai/skills/stacks-slug/SKILL.md +87 -0
- package/ai/skills/stacks-sms/SKILL.md +385 -0
- package/ai/skills/stacks-socials/SKILL.md +303 -0
- package/ai/skills/stacks-storage/SKILL.md +576 -0
- package/ai/skills/stacks-strings/SKILL.md +416 -0
- package/ai/skills/stacks-stx/SKILL.md +176 -0
- package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
- package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
- package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
- package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
- package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
- package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
- package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
- package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
- package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
- package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
- package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
- package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
- package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
- package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
- package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
- package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
- package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
- package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
- package/ai/skills/stacks-testing/SKILL.md +129 -0
- package/ai/skills/stacks-tunnel/SKILL.md +125 -0
- package/ai/skills/stacks-types/SKILL.md +178 -0
- package/ai/skills/stacks-ui/SKILL.md +246 -0
- package/ai/skills/stacks-utils/SKILL.md +444 -0
- package/ai/skills/stacks-validation/SKILL.md +371 -0
- package/ai/skills/stacks-whois/SKILL.md +315 -0
- package/assets/world-map-data.ts +121 -0
- package/bootstrap.ts +114 -0
- package/docs/api-examples.md +46 -0
- package/docs/index.md +24 -0
- package/docs/markdown-examples.md +87 -0
- package/functions/analytics.ts +97 -0
- package/functions/api.ts +143 -0
- package/functions/auth.ts +208 -0
- package/functions/billing/payments.ts +149 -0
- package/functions/blog.ts +120 -0
- package/functions/cms/categorizables.ts +107 -0
- package/functions/cms/pages.ts +108 -0
- package/functions/cms/posts.ts +100 -0
- package/functions/cms/taggables.ts +102 -0
- package/functions/commerce/coupons.ts +128 -0
- package/functions/commerce/customers.ts +128 -0
- package/functions/commerce/gift-cards.ts +119 -0
- package/functions/commerce/orders.ts +112 -0
- package/functions/commerce/payments.ts +119 -0
- package/functions/commerce/products/categories.ts +123 -0
- package/functions/commerce/products/manufacturers.ts +123 -0
- package/functions/commerce/products/products.ts +123 -0
- package/functions/commerce/products/reviews.ts +123 -0
- package/functions/commerce/products/units.ts +123 -0
- package/functions/commerce/products/variants.ts +123 -0
- package/functions/commerce/shippings/delivery-routes.ts +123 -0
- package/functions/commerce/shippings/digital-deliveries.ts +119 -0
- package/functions/commerce/shippings/drivers.ts +123 -0
- package/functions/commerce/shippings/license-keys.ts +123 -0
- package/functions/commerce/shippings/shipping-methods.ts +123 -0
- package/functions/commerce/shippings/shipping-rates.ts +119 -0
- package/functions/commerce/shippings/shipping-zones.ts +119 -0
- package/functions/commerce/taxes.ts +119 -0
- package/functions/commerce/waitlist/products.ts +123 -0
- package/functions/commerce/waitlist/restaurants.ts +123 -0
- package/functions/craft.ts +107 -0
- package/functions/dashboard.ts +153 -0
- package/functions/inbox.ts +95 -0
- package/functions/jobs.ts +86 -0
- package/functions/monitoring/errors.ts +295 -0
- package/functions/queue.ts +111 -0
- package/functions/realtime/websockets.ts +96 -0
- package/functions/realtime.ts +52 -0
- package/functions/releases.ts +49 -0
- package/functions/search.ts +126 -0
- package/functions/toasts.ts +95 -0
- package/functions/uploads.ts +216 -0
- package/ide/.gitignore +4 -0
- package/ide/cursor/rules/auto-imports.mdc +574 -0
- package/ide/cursor/rules/code-style.mdc +12 -0
- package/ide/cursor/rules/documentation.mdc +9 -0
- package/ide/cursor/rules/error-handling-validation.mdc +11 -0
- package/ide/cursor/rules/key-conventions.mdc +11 -0
- package/ide/cursor/rules/project-structure.mdc +169 -0
- package/ide/cursor/rules/readme.mdc +318 -0
- package/ide/cursor/rules/syntax-formatting.mdc +9 -0
- package/ide/cursor/rules/testing.mdc +10 -0
- package/ide/cursor/rules/typescript.mdc +9 -0
- package/ide/cursor/rules/ui-styling.mdc +0 -0
- package/ide/dictionary.txt +397 -0
- package/ide/jetbrains/.fleet/settings.json +4 -0
- package/ide/jetbrains/.idea/.gitignore +5 -0
- package/ide/jetbrains/.idea/.name +1 -0
- package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
- package/ide/jetbrains/.idea/modules.xml +8 -0
- package/ide/jetbrains/.idea/stacks.iml +22 -0
- package/ide/jetbrains/.idea/vcs.xml +6 -0
- package/ide/jetbrains/.idea/webResources.xml +14 -0
- package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
- package/ide/jetbrains/installed.txt +4 -0
- package/ide/jetbrains/options/github-copilot.xml +5 -0
- package/ide/jetbrains/options/nodejs.xml +7 -0
- package/ide/jetbrains/options/templates.xml +124 -0
- package/ide/jetbrains/templates/JavaScript.xml +6 -0
- package/ide/jetbrains/templates/Stacks.xml +8 -0
- package/ide/vscode/.vscode/README.md +23 -0
- package/ide/vscode/.vscode/extensions.json +5 -0
- package/ide/vscode/.vscode/settings.json +176 -0
- package/ide/vscode/.vscode/stacks.code-snippets +181 -0
- package/ide/vscode/LICENSE.md +21 -0
- package/ide/vscode/README.md +36 -0
- package/ide/vscode/logo.png +0 -0
- package/ide/vscode/package.json +79 -0
- package/ide/zed/.zed/settings.json +139 -0
- package/lang/README.md +28 -0
- package/lang/de.yml +14 -0
- package/lang/en.yml +15 -0
- package/package.json +19 -3
- package/project/bootstrap +161 -0
- package/project/buddy +175 -0
- package/project/storage/framework/tsconfig.app.json +50 -0
- package/project/storage/framework/tsconfig.base.json +58 -0
- package/routes/core.ts +78 -0
- package/routes/dashboard-api.ts +233 -0
- package/routes/dashboard.ts +739 -0
- package/routes/email.ts +129 -0
- package/stores/[name].stx +16 -0
- package/stores/git.ts +71 -0
- package/stores/payment.ts +474 -0
- package/stores/queue.ts +43 -0
- package/stores/user.ts +29 -0
- package/stx-components-plugin.ts +25 -0
- package/styles/design-tokens.css +597 -0
- package/types/dashboard.ts +70 -0
- package/types/defaults.ts +438 -0
- package/vcs/github/CODE_OF_CONDUCT.md +83 -0
- package/vcs/github/CONTRIBUTING.md +187 -0
- package/vcs/github/EXPLANATIONS.md +41 -0
- package/vcs/github/FUNDING.yml +1 -0
- package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
- package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
- package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
- package/vcs/github/SECURITY.md +70 -0
- package/vcs/github/renovate.json +5 -0
- package/vcs/github/stale.yml +14 -0
- package/vcs/github/workflows/README.md +16 -0
- package/vcs/github/workflows/ci.yml +114 -0
- package/vcs/github/workflows/export-size.yml +25 -0
- package/vcs/github/workflows/labeler.yml +13 -0
- package/vcs/github/workflows/release.yml +219 -0
- package/views/[...all].stx +8 -0
- package/views/about.md +3 -0
- package/views/auth/login.stx +1 -0
- package/views/auth/signup.stx +85 -0
- package/views/dashboard/.discovered-models.json +542 -0
- package/views/dashboard/AUDIT.md +904 -0
- package/views/dashboard/[...all].stx +89 -0
- package/views/dashboard/access-tokens/index.stx +177 -0
- package/views/dashboard/actions/index.stx +129 -0
- package/views/dashboard/analytics/blog/index.stx +356 -0
- package/views/dashboard/analytics/browsers/index.stx +263 -0
- package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
- package/views/dashboard/analytics/commerce/web/index.stx +379 -0
- package/views/dashboard/analytics/countries/index.stx +289 -0
- package/views/dashboard/analytics/devices/index.stx +305 -0
- package/views/dashboard/analytics/events/index.stx +536 -0
- package/views/dashboard/analytics/index.stx +88 -0
- package/views/dashboard/analytics/marketing/index.stx +315 -0
- package/views/dashboard/analytics/pages/index.stx +271 -0
- package/views/dashboard/analytics/referrers/index.stx +319 -0
- package/views/dashboard/analytics/web/index.stx +356 -0
- package/views/dashboard/buddy/index.stx +198 -0
- package/views/dashboard/ci/index.stx +462 -0
- package/views/dashboard/cloud/index.stx +452 -0
- package/views/dashboard/commands/index.stx +295 -0
- package/views/dashboard/commerce/categories/index.stx +406 -0
- package/views/dashboard/commerce/coupons/index.stx +485 -0
- package/views/dashboard/commerce/customers/index.stx +410 -0
- package/views/dashboard/commerce/dashboard/index.stx +287 -0
- package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
- package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
- package/views/dashboard/commerce/delivery/drivers.stx +397 -0
- package/views/dashboard/commerce/delivery/index.stx +18 -0
- package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
- package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
- package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
- package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
- package/views/dashboard/commerce/delivery/tabs.ts +25 -0
- package/views/dashboard/commerce/gift-cards/index.stx +561 -0
- package/views/dashboard/commerce/manufacturers/index.stx +308 -0
- package/views/dashboard/commerce/orders/index.stx +247 -0
- package/views/dashboard/commerce/payments/index.stx +535 -0
- package/views/dashboard/commerce/pos/index.stx +653 -0
- package/views/dashboard/commerce/printers/devices/index.stx +273 -0
- package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
- package/views/dashboard/commerce/products/detail.stx +563 -0
- package/views/dashboard/commerce/products/index.stx +370 -0
- package/views/dashboard/commerce/reviews/index.stx +656 -0
- package/views/dashboard/commerce/reviews/index.ts +0 -0
- package/views/dashboard/commerce/taxes/index.stx +377 -0
- package/views/dashboard/commerce/units/index.stx +317 -0
- package/views/dashboard/commerce/variants/index.stx +319 -0
- package/views/dashboard/commerce/waitlist/products.stx +942 -0
- package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
- package/views/dashboard/components/index.stx +330 -0
- package/views/dashboard/composables/index.ts +19 -0
- package/views/dashboard/composables/useChart.ts +77 -0
- package/views/dashboard/composables/useNavigation.ts +99 -0
- package/views/dashboard/composables/useRole.ts +58 -0
- package/views/dashboard/composables/useTheme.ts +74 -0
- package/views/dashboard/content/authors/index.stx +171 -0
- package/views/dashboard/content/blog/index.stx +493 -0
- package/views/dashboard/content/categories/index.stx +169 -0
- package/views/dashboard/content/comments/index.stx +189 -0
- package/views/dashboard/content/dashboard.stx +383 -0
- package/views/dashboard/content/files/index.stx +706 -0
- package/views/dashboard/content/pages/index.stx +174 -0
- package/views/dashboard/content/posts/index.stx +269 -0
- package/views/dashboard/content/seo/index.stx +240 -0
- package/views/dashboard/content/tags/index.stx +167 -0
- package/views/dashboard/data/activity/index.stx +261 -0
- package/views/dashboard/data/dashboard/index.stx +116 -0
- package/views/dashboard/data/subscribers.stx +270 -0
- package/views/dashboard/data/teams.stx +275 -0
- package/views/dashboard/data/users.stx +279 -0
- package/views/dashboard/dependencies/index.stx +178 -0
- package/views/dashboard/deployments/[id].stx +234 -0
- package/views/dashboard/deployments/index.stx +9 -0
- package/views/dashboard/dns/index.stx +163 -0
- package/views/dashboard/environment/index.stx +283 -0
- package/views/dashboard/errors/index.stx +274 -0
- package/views/dashboard/forgot-password.stx +57 -0
- package/views/dashboard/functions/index.stx +159 -0
- package/views/dashboard/health/index.stx +248 -0
- package/views/dashboard/inbox/activity.stx +350 -0
- package/views/dashboard/inbox/index.stx +452 -0
- package/views/dashboard/inbox/settings.stx +290 -0
- package/views/dashboard/index.stx +252 -0
- package/views/dashboard/insights/index.stx +529 -0
- package/views/dashboard/jobs/[id].stx +430 -0
- package/views/dashboard/jobs/history.stx +222 -0
- package/views/dashboard/jobs/index.stx +214 -0
- package/views/dashboard/kanban/[id].stx +899 -0
- package/views/dashboard/kanban/index.stx +260 -0
- package/views/dashboard/layouts/default.stx +565 -0
- package/views/dashboard/layouts/guest.stx +10 -0
- package/views/dashboard/login.stx +48 -0
- package/views/dashboard/logs/index.stx +540 -0
- package/views/dashboard/mailboxes/index.stx +149 -0
- package/views/dashboard/management/permissions/index.stx +490 -0
- package/views/dashboard/marketing/campaigns/index.stx +604 -0
- package/views/dashboard/marketing/lists/index.stx +514 -0
- package/views/dashboard/marketing/reviews/index.stx +475 -0
- package/views/dashboard/marketing/social-posts/index.stx +560 -0
- package/views/dashboard/models/[model].stx +195 -0
- package/views/dashboard/models/index.stx +159 -0
- package/views/dashboard/monitoring/errors/index.stx +415 -0
- package/views/dashboard/notifications/dashboard.stx +383 -0
- package/views/dashboard/notifications/email.stx +336 -0
- package/views/dashboard/notifications/history.stx +373 -0
- package/views/dashboard/notifications/sms.stx +332 -0
- package/views/dashboard/packages/index.stx +195 -0
- package/views/dashboard/queries/[id].stx +381 -0
- package/views/dashboard/queries/history.stx +227 -0
- package/views/dashboard/queries/index.stx +335 -0
- package/views/dashboard/queries/slow.stx +318 -0
- package/views/dashboard/queue/index.stx +427 -0
- package/views/dashboard/realtime/index.stx +429 -0
- package/views/dashboard/register.stx +46 -0
- package/views/dashboard/releases/index.stx +389 -0
- package/views/dashboard/requests/index.stx +462 -0
- package/views/dashboard/serverless/index.stx +475 -0
- package/views/dashboard/servers/[id].stx +299 -0
- package/views/dashboard/servers/index.stx +483 -0
- package/views/dashboard/settings/billing.stx +50 -0
- package/views/dashboard/settings/index.stx +256 -0
- package/views/dashboard/settings/mail.stx +252 -0
- package/views/dashboard/stores/auth.ts +123 -0
- package/views/dashboard/stores/ci.ts +342 -0
- package/views/dashboard/stores/index.ts +129 -0
- package/views/dashboard/stores/kanban.ts +723 -0
- package/views/dashboard/stores/rbac.ts +350 -0
- package/views/dashboard/stx.config.ts +34 -0
- package/views/dashboard/teams/[id].stx +170 -0
- package/views/forum/category.stx +92 -0
- package/views/forum/index.stx +209 -0
- package/views/forum/new-topic.stx +113 -0
- package/views/forum/profile.stx +1 -0
- package/views/forum/topic.stx +143 -0
- package/views/hello-world/[name].stx +47 -0
- package/views/index.stx +21 -0
- package/views/system-tray/index.stx +147 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-models
|
|
3
|
+
description: Use when working with data models in Stacks — the defineModel() API, model attributes with validation and factories, relationships (hasOne/hasMany/belongsTo/belongsToMany), traits (useAuth, useUuid, useTimestamps, useSearch, useApi, billable, taggable, categorizable, commentable, likeable, observe), computed properties (get/set), model generation, and the 50+ built-in framework models. Covers model definitions and storage/framework/defaults/app/Models/.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript, SQLite >= 3.47.2
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Models
|
|
10
|
+
|
|
11
|
+
## Key Paths
|
|
12
|
+
- Your models: `app/Models/` (create it; it does not exist in a fresh project)
|
|
13
|
+
- Built-in models: `storage/framework/defaults/app/Models/` (62 files, grouped
|
|
14
|
+
into `commerce/`, `Content/`, `realtime/` and a flat top level)
|
|
15
|
+
- `ModelOptions` / `Attribute` types: `storage/framework/core/types/src/model.ts`
|
|
16
|
+
- Attribute presets: `storage/framework/types/attributes.ts`
|
|
17
|
+
|
|
18
|
+
To customize a built-in model, create the same filename under `app/Models/` -
|
|
19
|
+
`app/Models/User.ts` wins over the default. `buddy publish:model User` copies the
|
|
20
|
+
default across as a starting point.
|
|
21
|
+
|
|
22
|
+
## Writing a model
|
|
23
|
+
|
|
24
|
+
Everything - schema, validation, factory, relationships, behavior - is declared
|
|
25
|
+
in one `defineModel()` call. Migrations are derived from this; you do not write
|
|
26
|
+
the SQL.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// app/Models/Product.ts
|
|
30
|
+
import { defineModel } from '@stacksjs/orm'
|
|
31
|
+
import { schema } from '@stacksjs/validation'
|
|
32
|
+
|
|
33
|
+
export default defineModel({
|
|
34
|
+
name: 'Product', // defaults to the file name
|
|
35
|
+
table: 'products', // defaults to lowercase plural of `name`
|
|
36
|
+
primaryKey: 'id', // default
|
|
37
|
+
autoIncrement: true, // default
|
|
38
|
+
|
|
39
|
+
traits: {
|
|
40
|
+
useUuid: true,
|
|
41
|
+
useTimestamps: true,
|
|
42
|
+
useApi: { uri: 'products', routes: ['index', 'store', 'show', 'update', 'destroy'] },
|
|
43
|
+
useSearch: { searchable: ['name'], filterable: ['status'] },
|
|
44
|
+
observe: true,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
belongsTo: ['Category'],
|
|
48
|
+
hasMany: ['Review'],
|
|
49
|
+
|
|
50
|
+
attributes: {
|
|
51
|
+
name: {
|
|
52
|
+
required: true,
|
|
53
|
+
fillable: true,
|
|
54
|
+
order: 1,
|
|
55
|
+
validation: {
|
|
56
|
+
rule: schema.string().min(3).max(100),
|
|
57
|
+
message: { max: 'Name must have a maximum of 100 characters' },
|
|
58
|
+
},
|
|
59
|
+
factory: faker => faker.commerce.productName(),
|
|
60
|
+
},
|
|
61
|
+
status: {
|
|
62
|
+
required: true,
|
|
63
|
+
fillable: true,
|
|
64
|
+
default: 'draft',
|
|
65
|
+
validation: { rule: schema.enum(['draft', 'published', 'archived']) },
|
|
66
|
+
factory: faker => faker.helpers.arrayElement(['draft', 'published', 'archived']),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
} as const)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`as const` is what the built-in models use - it narrows literal types so the
|
|
73
|
+
generated model types stay precise.
|
|
74
|
+
|
|
75
|
+
### Attribute fields
|
|
76
|
+
|
|
77
|
+
`validation.rule` is the only required key on an attribute.
|
|
78
|
+
|
|
79
|
+
| Field | Effect |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `required` | Value required; emits a `NOT NULL` column |
|
|
82
|
+
| `nullable` | Explicit nullability override |
|
|
83
|
+
| `default` | Column default (`string \| number \| boolean \| Date`) |
|
|
84
|
+
| `unique` | Unique constraint |
|
|
85
|
+
| `type` | Force the column type instead of inferring from the rule |
|
|
86
|
+
| `order` | Column order in the table and in dashboard forms |
|
|
87
|
+
| `fillable` | Allow mass assignment |
|
|
88
|
+
| `guarded` | Block mass assignment |
|
|
89
|
+
| `hidden` | Exclude from JSON serialization (passwords, tokens) |
|
|
90
|
+
| `foreignKey` | Disable, infer, or configure the FK constraint |
|
|
91
|
+
| `factory` | `(faker) => value`, used by seeders and tests |
|
|
92
|
+
| `validation` | `{ rule, message? }` - `rule` from `schema`, `message` keyed by rule name |
|
|
93
|
+
|
|
94
|
+
### Traits
|
|
95
|
+
|
|
96
|
+
| Trait | What it adds |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `useUuid` | UUID column alongside the primary key |
|
|
99
|
+
| `useTimestamps` (alias `timestampable`) | `created_at` / `updated_at`. On by default |
|
|
100
|
+
| `useSoftDeletes` (alias `softDeletable`) | `deleted_at` plus soft-delete query scopes |
|
|
101
|
+
| `useAuth` (alias `authenticatable`) | Auth columns; `{ usePasskey: true }` adds passkeys |
|
|
102
|
+
| `useApi` | Generates REST actions and routes: `{ uri, routes }` |
|
|
103
|
+
| `useSearch` (alias `searchable`) | Search-engine indexing: `{ displayable, searchable, sortable, filterable }` |
|
|
104
|
+
| `useSocials` | OAuth identities, e.g. `['github']` |
|
|
105
|
+
| `useActivityLog` | Writes an `Activity` row per change |
|
|
106
|
+
| `observe` | Emits `{model}:created` / `:updated` / `:deleted` events |
|
|
107
|
+
| `billable` | Stripe methods (`checkout()`, `activeSubscription()`, ...) |
|
|
108
|
+
| `taggable` / `categorizable` / `commentable` / `likeable` | Pivot tables and their relation methods |
|
|
109
|
+
|
|
110
|
+
Also at the top level: `indexes: [{ name, columns, unique?, where? }]` for
|
|
111
|
+
composite and partial-unique indexes, and `dashboard: { highlight: true }` to
|
|
112
|
+
feature the model in the admin UI.
|
|
113
|
+
|
|
114
|
+
### Relationships
|
|
115
|
+
|
|
116
|
+
`hasOne`, `hasMany`, `belongsTo`, `belongsToMany`, `hasOneThrough`,
|
|
117
|
+
`hasManyThrough`, `morphOne`, `morphMany`, `morphTo`, `morphToMany`,
|
|
118
|
+
`morphedByMany`. Each takes an array of model names, or an object form when you
|
|
119
|
+
need to name the foreign key.
|
|
120
|
+
|
|
121
|
+
### Computed properties and scopes
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
get: {
|
|
125
|
+
fullName: (model) => `${model.firstName} ${model.lastName}`,
|
|
126
|
+
},
|
|
127
|
+
set: {
|
|
128
|
+
password: (value) => makeHash(value),
|
|
129
|
+
},
|
|
130
|
+
scopes: {
|
|
131
|
+
published: (query) => query.where('status', 'published'),
|
|
132
|
+
},
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Workflow
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
buddy make:model Product # scaffold app/Models/Product.ts
|
|
139
|
+
buddy generate:migrations # diff models against the schema, emit SQL
|
|
140
|
+
# review the generated file in database/migrations/
|
|
141
|
+
buddy migrate # apply it
|
|
142
|
+
buddy migrate:fresh --seed # dev only: drop, re-migrate, seed
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Models resolve at runtime through `createModel()` from `bun-query-builder` -
|
|
146
|
+
there is no build step between editing a model and querying it. Only migrations
|
|
147
|
+
need generating.
|
|
148
|
+
|
|
149
|
+
## Seeding
|
|
150
|
+
|
|
151
|
+
Seed data is declared on the model, through the `useSeeder` trait plus the
|
|
152
|
+
per-attribute `factory` functions:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
traits: {
|
|
156
|
+
useSeeder: {
|
|
157
|
+
count: 20,
|
|
158
|
+
// Optional: pin specific rows over the generated ones. Keys use the
|
|
159
|
+
// model's camelCase attribute names.
|
|
160
|
+
fixtures: [
|
|
161
|
+
{ name: 'Flagship Widget', status: 'published' },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`buddy seed` walks every model carrying the trait and fills its table from the
|
|
168
|
+
attribute factories. Nothing else is needed - no seeder files, no registration.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
buddy seed # every model with a useSeeder trait
|
|
172
|
+
buddy seed --fresh # truncate each table first
|
|
173
|
+
buddy seed --only Product,Review # just these models
|
|
174
|
+
buddy seed --except User # everything but these
|
|
175
|
+
buddy seed --include-defaults # framework built-ins too
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
A model with no `useSeeder` trait is never seeded. Auth and OAuth models are
|
|
179
|
+
skipped on a non-fresh database so re-seeding cannot invalidate live sessions -
|
|
180
|
+
pass `--allow-protected` to override.
|
|
181
|
+
|
|
182
|
+
## All 62 built-in models by category
|
|
183
|
+
|
|
184
|
+
### Users & Auth
|
|
185
|
+
- **User** — name, email, password | traits: useAuth(passkey), useUuid, useTimestamps, useSocials(github) | hasOne: Subscriber, Driver, Author | hasMany: PersonalAccessToken, Customer
|
|
186
|
+
- **Author** — name, email | belongsTo: User | hasMany: Post
|
|
187
|
+
- **Customer** — name, email, phone, totalSpent, lastOrder, status, avatar | belongsTo: User | hasMany: Order, GiftCard, Review, Payment
|
|
188
|
+
- **Driver** — name, phone, vehicleNumber, license, status | belongsTo: User | hasMany: DeliveryRoute
|
|
189
|
+
- **Subscriber** — email, status, source | belongsTo: User | hasMany: SubscriberEmail
|
|
190
|
+
|
|
191
|
+
### Content
|
|
192
|
+
- **Post** — title, content, poster, excerpt, views, publishedAt, status, isFeatured | belongsTo: Author | traits: categorizable, taggable, commentable | seeder: 20
|
|
193
|
+
- **Page** — similar to Post with taggable, categorizable
|
|
194
|
+
- **Comment** — author info, approval, content fields
|
|
195
|
+
- **Tag** — name(unique), slug(unique), description, postCount, color | seeder: 15
|
|
196
|
+
- **Category** — name, description, slug, imageUrl, isActive, parentCategoryId, displayOrder | hasMany: Product | seeder: 10
|
|
197
|
+
|
|
198
|
+
### Commerce (20+ models)
|
|
199
|
+
- **Product** — name(max100), description, price(min1), imageUrl, isAvailable, inventoryCount, preparationTime, allergens(JSON), nutritionalInfo(JSON) | belongsTo: Category, Manufacturer | hasMany: Review, ProductUnit, ProductVariant, LicenseKey, WaitlistProduct, Coupon | seeder: 10, dashboard: highlighted
|
|
200
|
+
- **ProductVariant** — SKU, options, pricing
|
|
201
|
+
- **ProductUnit** — unit-specific pricing
|
|
202
|
+
- **Cart** — status(active|abandoned|converted|expired), totalItems, subtotal, taxAmount, discountAmount, total, expiresAt, currency(USD), notes | hasMany: CartItem | belongsTo: Customer, Coupon
|
|
203
|
+
- **CartItem** — quantity(min1), unitPrice, totalPrice, taxRate, taxAmount, discountPercentage, productName, productSku | belongsTo: Cart
|
|
204
|
+
- **Order** — status, totalAmount, taxAmount, discountAmount, deliveryFee, tipAmount, orderType(DINE_IN|TAKEOUT|DELIVERY), deliveryAddress, specialInstructions | hasMany: OrderItem, Payment | belongsTo: Customer, Coupon | observe: true | seeder: 20
|
|
205
|
+
- **OrderItem** — quantity(min1), price(min0), specialInstructions | belongsTo: Order, Product
|
|
206
|
+
- **Coupon** — code(unique), discountType(fixed_amount|percentage), discountValue, minOrderAmount, usageLimit, usageCount, startDate, endDate | seeder: 15
|
|
207
|
+
- **GiftCard** — code(unique), initialBalance, currentBalance, currency, status, recipientEmail, isDigital, isReloadable, expiryDate | seeder: 20
|
|
208
|
+
- **Manufacturer** — manufacturer info
|
|
209
|
+
- **Review** — rating(1-5), title, content(max2000), isVerifiedPurchase, isApproved, isFeatured, helpfulVotes, unhelpfulVotes | belongsTo: Product, Customer | seeder: 50
|
|
210
|
+
|
|
211
|
+
### Shipping & Delivery
|
|
212
|
+
- **ShippingMethod**, **ShippingRate** (weightFrom, weightTo, rate), **ShippingZone**
|
|
213
|
+
- **DeliveryRoute** — driver, vehicle, stops, totalDistance | belongsTo: Driver
|
|
214
|
+
- **DigitalDelivery** — name, downloadLimit, expiryDays, automaticDelivery
|
|
215
|
+
- **LicenseKey** — key(XXXX-XXXX-XXXX-XXXX-XXXX), template, expiryDate, status
|
|
216
|
+
|
|
217
|
+
### Payments & Financial
|
|
218
|
+
- **Payment** — amount, method(creditCard|debitCard|paypal|...), status(pending|completed|failed|refunded), currency, transactionId(unique) | belongsTo: Order, Customer | seeder: 50
|
|
219
|
+
- **PaymentMethod**, **PaymentProduct**, **PaymentTransaction**
|
|
220
|
+
- **Subscription** — type, providerId, providerStatus, unitPrice
|
|
221
|
+
- **Transaction** — standard transaction tracking
|
|
222
|
+
- **TaxRate** — name, rate(0-100), type(VAT|GST|Sales Tax|Customs Duty), country, region, isDefault
|
|
223
|
+
|
|
224
|
+
### Engagement & Marketing
|
|
225
|
+
- **Notification** — type, channel, recipient, subject, body, status(pending|sent|delivered|failed|read) | belongsTo: User | seeder: 30
|
|
226
|
+
- **Campaign** — name, type(email|sms|push|social|multi-channel), status, audienceSize, openRate, clickRate, budget | seeder: 10
|
|
227
|
+
- **Activity** — type, description, subjectType, subjectId, causer, properties(JSON), ipAddress | belongsTo: User | seeder: 50
|
|
228
|
+
- **EmailList**, **SocialPost**, **LoyaltyPoint** (walletId, points, source, expiryDate), **LoyaltyReward**
|
|
229
|
+
|
|
230
|
+
### System
|
|
231
|
+
- **Job** — queue, payload, attempts, available_at, reserved_at | seeder: 15
|
|
232
|
+
- **FailedJob** — failed background jobs
|
|
233
|
+
- **Error** — type, message, stack, status, additionalInfo | seeder: 10
|
|
234
|
+
- **Log** — application logs
|
|
235
|
+
- **Request** — method, path, statusCode, durationMs, ipAddress, memoryUsage, userAgent, errorMessage | seeder: 50
|
|
236
|
+
- **Websocket** — connection tracking
|
|
237
|
+
- **PrintDevice** — name, location, terminal, lastPing, printCount, isActive
|
|
238
|
+
- **WaitlistProduct**, **WaitlistRestaurant** — waitlist tracking
|
|
239
|
+
- **Receipt** — receipt records
|
|
240
|
+
|
|
241
|
+
## CLI Commands
|
|
242
|
+
- `buddy make:model [name]` — scaffold a model in `app/Models/`
|
|
243
|
+
- `buddy publish:model [name]` — copy a built-in model into `app/Models/` to override it
|
|
244
|
+
- `buddy generate:migrations` — diff models against the schema and emit SQL
|
|
245
|
+
- `buddy migrate` / `buddy migrate:fresh --seed` — apply migrations
|
|
246
|
+
- `buddy make:migration [name]` — hand-write a migration instead
|
|
247
|
+
- `buddy make:factory [name]` — standalone factory
|
|
248
|
+
- `buddy seed` — seed every model carrying a `useSeeder` trait
|
|
249
|
+
|
|
250
|
+
## Gotchas
|
|
251
|
+
- **No code generation step for models.** `defineModel()` calls `createModel()`
|
|
252
|
+
from bun-query-builder at runtime, so a model is queryable the moment you save
|
|
253
|
+
it. Only migrations are generated.
|
|
254
|
+
- **Migrations come from models.** Change the model, run `buddy generate:migrations`,
|
|
255
|
+
review the SQL, then `buddy migrate`. Editing a generated migration by hand
|
|
256
|
+
will be overwritten by the next diff.
|
|
257
|
+
- **`commentable`, not `commentables`.** `define-model` only checks the singular
|
|
258
|
+
key. The plural spelling used to type check while leaving the trait inert.
|
|
259
|
+
- **Seeding is model-declared.** `useSeeder` sets the count and fixtures; the
|
|
260
|
+
per-attribute `factory` functions produce the values. There are no seeder
|
|
261
|
+
files to write or register.
|
|
262
|
+
- **`hidden` is serialization, `guarded` is mass assignment.** They are different
|
|
263
|
+
protections; a password wants both `hidden` and no `fillable`.
|
|
264
|
+
- **`validation.rule` is mandatory** on every attribute - it drives both request
|
|
265
|
+
validation and the inferred column type.
|
|
266
|
+
- Dashboard-highlighted models (`dashboard: { highlight: true }`) appear
|
|
267
|
+
prominently in the admin UI.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-new-feature
|
|
3
|
+
description: Use when adding a new feature end-to-end in a Stacks application — the complete workflow from model definition through migration, action, route, test, and deployment. Covers the recommended order of operations for building features.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Adding a New Feature to Stacks
|
|
10
|
+
|
|
11
|
+
Step-by-step guide for building features end-to-end.
|
|
12
|
+
|
|
13
|
+
## Workflow Overview
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. Model → 2. Migration → 3. Action → 4. Route → 5. Test → 6. Lint → 7. Deploy
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Step 1: Define the Model
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Create or edit in storage/framework/defaults/app/Models/
|
|
23
|
+
import { defineModel } from '@stacksjs/orm'
|
|
24
|
+
import { schema } from '@stacksjs/validation'
|
|
25
|
+
|
|
26
|
+
export default defineModel({
|
|
27
|
+
name: 'Article',
|
|
28
|
+
table: 'articles',
|
|
29
|
+
primaryKey: 'id',
|
|
30
|
+
autoIncrement: true,
|
|
31
|
+
|
|
32
|
+
traits: {
|
|
33
|
+
useTimestamps: true,
|
|
34
|
+
useSearch: {
|
|
35
|
+
searchable: ['title', 'content'],
|
|
36
|
+
sortable: ['title', 'created_at'],
|
|
37
|
+
filterable: ['status']
|
|
38
|
+
},
|
|
39
|
+
useApi: {
|
|
40
|
+
uri: 'articles',
|
|
41
|
+
routes: ['index', 'store', 'show', 'update', 'destroy']
|
|
42
|
+
},
|
|
43
|
+
useSeeder: { count: 10 },
|
|
44
|
+
observe: true
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
belongsTo: ['User'],
|
|
48
|
+
|
|
49
|
+
attributes: {
|
|
50
|
+
title: {
|
|
51
|
+
fillable: true,
|
|
52
|
+
required: true,
|
|
53
|
+
validation: { rule: schema.string().maxLength(200) },
|
|
54
|
+
factory: (faker) => faker.lorem.sentence()
|
|
55
|
+
},
|
|
56
|
+
content: {
|
|
57
|
+
fillable: true,
|
|
58
|
+
required: true,
|
|
59
|
+
validation: { rule: schema.string() },
|
|
60
|
+
factory: (faker) => faker.lorem.paragraphs(3)
|
|
61
|
+
},
|
|
62
|
+
status: {
|
|
63
|
+
fillable: true,
|
|
64
|
+
default: 'draft',
|
|
65
|
+
validation: { rule: schema.enum(['draft', 'published', 'archived']) },
|
|
66
|
+
factory: () => 'draft'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
get: {
|
|
71
|
+
excerpt: (attrs) => attrs.content?.substring(0, 150) + '...'
|
|
72
|
+
}
|
|
73
|
+
} as const)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Step 2: Generate & Run Migration
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
buddy generate:migrations # generate migration SQL from model diffs
|
|
80
|
+
buddy migrate # apply migration
|
|
81
|
+
buddy seed # seed with factory data
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Step 3: Create Actions
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// app/Actions/CreateArticle.ts
|
|
88
|
+
export default {
|
|
89
|
+
name: 'CreateArticle',
|
|
90
|
+
description: 'Create a new article',
|
|
91
|
+
|
|
92
|
+
async handle(request: any) {
|
|
93
|
+
const title = request.get('title')
|
|
94
|
+
const content = request.get('content')
|
|
95
|
+
const user = await request.user()
|
|
96
|
+
|
|
97
|
+
const article = await Article.create({
|
|
98
|
+
title,
|
|
99
|
+
content,
|
|
100
|
+
user_id: user.id,
|
|
101
|
+
status: 'draft'
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return { success: true, data: article }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Step 4: Define Routes
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// routes/api.ts (add to existing)
|
|
113
|
+
route.group({ prefix: '/articles', middleware: ['auth'] }, () => {
|
|
114
|
+
route.get('/', 'Actions/ListArticles')
|
|
115
|
+
route.post('/', 'Actions/CreateArticle')
|
|
116
|
+
route.get('/{id}', 'Actions/ShowArticle')
|
|
117
|
+
route.put('/{id}', 'Actions/UpdateArticle')
|
|
118
|
+
route.delete('/{id}', 'Actions/DeleteArticle')
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Or rely on auto-generated routes from `useApi` trait — they're created automatically.
|
|
123
|
+
|
|
124
|
+
## Step 5: Add Event Listeners (Optional)
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// app/Events.ts — add to existing
|
|
128
|
+
{
|
|
129
|
+
'article:created': ['NotifySubscribers'],
|
|
130
|
+
'article:published': ['SendNewsletter', 'IndexInSearchEngine']
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Step 6: Write Tests
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// tests/feature/articles.test.ts
|
|
138
|
+
import { describe, test, expect, beforeAll } from 'bun:test'
|
|
139
|
+
import { setupDatabase, refreshDatabase } from '@stacksjs/testing'
|
|
140
|
+
|
|
141
|
+
describe('Articles', () => {
|
|
142
|
+
beforeAll(async () => {
|
|
143
|
+
await setupDatabase()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('can create an article', async () => {
|
|
147
|
+
const article = await Article.create({
|
|
148
|
+
title: 'Test Article',
|
|
149
|
+
content: 'Test content',
|
|
150
|
+
status: 'draft'
|
|
151
|
+
})
|
|
152
|
+
expect(article.title).toBe('Test Article')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('can list articles', async () => {
|
|
156
|
+
const articles = await Article.all()
|
|
157
|
+
expect(articles.length).toBeGreaterThan(0)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Step 7: Lint & Deploy
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
bunx --bun pickier . --fix # lint and auto-fix
|
|
166
|
+
buddy test # run tests
|
|
167
|
+
buddy deploy # deploy to cloud
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Common Patterns
|
|
171
|
+
|
|
172
|
+
### Adding a Dashboard View
|
|
173
|
+
Models with `useApi` + `dashboard: { highlight: true }` auto-appear in the admin dashboard.
|
|
174
|
+
|
|
175
|
+
### Adding Email Notifications
|
|
176
|
+
```typescript
|
|
177
|
+
// app/Mail/ArticlePublished.ts
|
|
178
|
+
export async function sendArticlePublished({ to, article }) {
|
|
179
|
+
const { html, text } = await template('article-published', {
|
|
180
|
+
variables: { title: article.title, url: `${config.app.url}/articles/${article.id}` }
|
|
181
|
+
})
|
|
182
|
+
await mail.send({ to, subject: `New: ${article.title}`, html, text })
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Adding Background Processing
|
|
187
|
+
```typescript
|
|
188
|
+
// app/Jobs/IndexArticle.ts
|
|
189
|
+
export default new Job({
|
|
190
|
+
name: 'IndexArticle',
|
|
191
|
+
queue: 'search',
|
|
192
|
+
tries: 3,
|
|
193
|
+
async handle(payload: { articleId: number }) {
|
|
194
|
+
const article = await Article.find(payload.articleId)
|
|
195
|
+
await search.addDocuments('articles', [article])
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Gotchas
|
|
201
|
+
- Models work directly via the dynamic ORM — no generation step needed before migrations
|
|
202
|
+
- The `useApi` trait auto-generates both routes AND dashboard views
|
|
203
|
+
- Model events (observe: true) emit `article:created`, `article:updated`, `article:deleted`
|
|
204
|
+
- Factories in model attributes are used by `buddy seed`
|
|
205
|
+
- Always lint after code generation: `bunx --bun pickier . --fix`
|
|
206
|
+
- Use conventional commits: `feat: add article management`
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-notifications
|
|
3
|
+
description: Use when implementing notifications in Stacks — multi-channel notifications (email, SMS, push, chat, database), the database notification driver with read/unread tracking, notification factories (useEmail, useSMS, useChat, useDatabase), or notification configuration. Covers @stacksjs/notifications and config/notification.ts.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Notifications
|
|
10
|
+
|
|
11
|
+
Multi-channel notification system with 5 channel types: email, SMS, chat, push, and database.
|
|
12
|
+
|
|
13
|
+
## Key Paths
|
|
14
|
+
- Core package: `storage/framework/core/notifications/src/`
|
|
15
|
+
- Main entry: `storage/framework/core/notifications/src/index.ts`
|
|
16
|
+
- Drivers: `storage/framework/core/notifications/src/drivers/`
|
|
17
|
+
- Database driver: `storage/framework/core/notifications/src/drivers/database.ts`
|
|
18
|
+
- Configuration: `config/notification.ts`
|
|
19
|
+
- Notification model: `storage/framework/defaults/app/Models/Notification.ts`
|
|
20
|
+
|
|
21
|
+
## Package Exports
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import {
|
|
25
|
+
useChat,
|
|
26
|
+
useEmail,
|
|
27
|
+
useSMS,
|
|
28
|
+
useDatabase,
|
|
29
|
+
useNotification,
|
|
30
|
+
notification,
|
|
31
|
+
DatabaseNotificationDriver,
|
|
32
|
+
} from '@stacksjs/notifications'
|
|
33
|
+
|
|
34
|
+
// Types
|
|
35
|
+
import type { CreateNotificationOptions, DatabaseNotification } from '@stacksjs/notifications'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Channel Factories
|
|
39
|
+
|
|
40
|
+
Each factory returns the underlying driver module for that channel.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Email -- defaults to 'mailtrap', returns @stacksjs/email driver
|
|
44
|
+
const emailDriver = useEmail('ses')
|
|
45
|
+
const emailDriver = useEmail('sendgrid')
|
|
46
|
+
const emailDriver = useEmail('mailgun')
|
|
47
|
+
const emailDriver = useEmail('mailtrap')
|
|
48
|
+
const emailDriver = useEmail('smtp')
|
|
49
|
+
|
|
50
|
+
// SMS -- defaults to 'twilio', returns @stacksjs/sms driver
|
|
51
|
+
const smsDriver = useSMS('twilio')
|
|
52
|
+
const smsDriver = useSMS('vonage')
|
|
53
|
+
|
|
54
|
+
// Chat -- defaults to 'slack', returns @stacksjs/chat driver
|
|
55
|
+
const chatDriver = useChat('slack')
|
|
56
|
+
const chatDriver = useChat('discord')
|
|
57
|
+
const chatDriver = useChat('teams')
|
|
58
|
+
|
|
59
|
+
// Database -- returns DatabaseNotificationDriver object
|
|
60
|
+
const dbDriver = useDatabase()
|
|
61
|
+
|
|
62
|
+
// Auto-detect by type -- dispatches to the correct factory
|
|
63
|
+
const driver = useNotification('email', 'ses')
|
|
64
|
+
const driver = useNotification('sms', 'twilio')
|
|
65
|
+
const driver = useNotification('chat', 'slack')
|
|
66
|
+
const driver = useNotification('database')
|
|
67
|
+
|
|
68
|
+
// Default (uses config.default, falls back to 'email' + 'mailtrap')
|
|
69
|
+
const driver = notification()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`useNotification()` reads `config/notification.ts` for the default type. If no default is set, it throws `'No default notification type set in config/notification.ts'`.
|
|
73
|
+
|
|
74
|
+
The channel drivers are re-exports from their respective packages:
|
|
75
|
+
- `email` driver: `@stacksjs/email`
|
|
76
|
+
- `sms` driver: `@stacksjs/sms`
|
|
77
|
+
- `chat` driver: `@stacksjs/chat`
|
|
78
|
+
- `push` driver: `@stacksjs/push`
|
|
79
|
+
|
|
80
|
+
## Database Notification Driver
|
|
81
|
+
|
|
82
|
+
The `DatabaseNotificationDriver` provides CRUD operations for notifications stored in the `notifications` database table using Kysely query builder.
|
|
83
|
+
|
|
84
|
+
### Send a Notification
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const db = useDatabase()
|
|
88
|
+
|
|
89
|
+
const notification = await db.send({
|
|
90
|
+
userId: 1,
|
|
91
|
+
type: 'order.shipped',
|
|
92
|
+
data: { orderId: 42, trackingNumber: 'ABC123' },
|
|
93
|
+
})
|
|
94
|
+
// Returns the created DatabaseNotification with auto-generated id, timestamps
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`send()` inserts a row into the `notifications` table with:
|
|
98
|
+
- `user_id` from `options.userId`
|
|
99
|
+
- `type` from `options.type`
|
|
100
|
+
- `data` -- JSON.stringify'd from `options.data`
|
|
101
|
+
- `read_at` set to `null`
|
|
102
|
+
- `created_at` and `updated_at` set to current ISO timestamp
|
|
103
|
+
|
|
104
|
+
### Query Notifications
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// All notifications for a user, ordered by created_at desc
|
|
108
|
+
const all = await db.getUserNotifications(userId)
|
|
109
|
+
|
|
110
|
+
// Only unread (where read_at is null), ordered by created_at desc
|
|
111
|
+
const unread = await db.getUnreadNotifications(userId)
|
|
112
|
+
|
|
113
|
+
// Count of unread notifications
|
|
114
|
+
const count = await db.unreadCount(userId)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Mark as Read
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Mark a single notification as read (sets read_at to current timestamp)
|
|
121
|
+
await db.markAsRead(notificationId)
|
|
122
|
+
|
|
123
|
+
// Mark all unread notifications for a user as read
|
|
124
|
+
await db.markAllAsRead(userId)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`markAllAsRead()` only updates rows where `read_at` is null.
|
|
128
|
+
|
|
129
|
+
### Delete Notifications
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Delete a single notification by ID
|
|
133
|
+
await db.deleteNotification(notificationId)
|
|
134
|
+
|
|
135
|
+
// Delete all notifications for a user
|
|
136
|
+
await db.deleteAllNotifications(userId)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### DatabaseNotification Interface
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
interface DatabaseNotification {
|
|
143
|
+
id: number
|
|
144
|
+
user_id: number
|
|
145
|
+
type: string // e.g., 'order.shipped', 'payment.received'
|
|
146
|
+
data: string // JSON stringified -- parse with JSON.parse() when reading
|
|
147
|
+
read_at: string | null // ISO timestamp or null if unread
|
|
148
|
+
created_at: string // ISO timestamp
|
|
149
|
+
updated_at: string | null // ISO timestamp
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### CreateNotificationOptions Interface
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
interface CreateNotificationOptions {
|
|
157
|
+
userId: number
|
|
158
|
+
type: string
|
|
159
|
+
data: Record<string, any>
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Notification Model Fields
|
|
164
|
+
|
|
165
|
+
The Notification model (at `storage/framework/defaults/app/Models/Notification.ts`) defines:
|
|
166
|
+
- `type` -- notification type: email, sms, push, slack, webhook
|
|
167
|
+
- `channel` -- delivery channel
|
|
168
|
+
- `recipient` -- target email, phone, or user
|
|
169
|
+
- `subject` -- notification subject line
|
|
170
|
+
- `body` -- notification body content
|
|
171
|
+
- `status` -- pending, sent, delivered, failed, read
|
|
172
|
+
- `readAt`, `sentAt`, `metadata`
|
|
173
|
+
|
|
174
|
+
Note: The Notification model uses 30 seeded records by default.
|
|
175
|
+
|
|
176
|
+
## CLI Commands
|
|
177
|
+
- `buddy make:notification [name]` -- scaffold a new notification
|
|
178
|
+
|
|
179
|
+
## config/notification.ts
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import type { NotificationConfig } from '@stacksjs/types'
|
|
183
|
+
|
|
184
|
+
export default {
|
|
185
|
+
default: 'email',
|
|
186
|
+
} satisfies NotificationConfig
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The `default` field controls which channel type `useNotification()` and `notification()` use when no type is specified. Valid values: `'email'`, `'sms'`, `'chat'`, `'database'`.
|
|
190
|
+
|
|
191
|
+
## Architecture
|
|
192
|
+
|
|
193
|
+
The notifications package is a thin aggregation layer. Each channel delegates to its own dedicated package:
|
|
194
|
+
|
|
195
|
+
- **Email channel** (`useEmail`) -- re-exports `@stacksjs/email` (configured via `config/email.ts`)
|
|
196
|
+
- **SMS channel** (`useSMS`) -- re-exports `@stacksjs/sms` (configured via `config/sms.ts`)
|
|
197
|
+
- **Chat channel** (`useChat`) -- re-exports `@stacksjs/chat` (configured via `config/services.ts`)
|
|
198
|
+
- **Push channel** -- re-exports `@stacksjs/push`
|
|
199
|
+
- **Database channel** (`useDatabase`) -- built-in driver using `@stacksjs/database`
|
|
200
|
+
|
|
201
|
+
The driver modules (`drivers/email.ts`, `drivers/sms.ts`, etc.) are single-line re-exports: `export * as email from '@stacksjs/email'`, `export * as sms from '@stacksjs/sms'`, etc.
|
|
202
|
+
|
|
203
|
+
## Gotchas
|
|
204
|
+
- The `data` field in database notifications is stored as a JSON string -- always `JSON.parse()` when reading
|
|
205
|
+
- `read_at` is `null` for unread notifications -- use this to filter unread
|
|
206
|
+
- The database driver uses Kysely's query builder with `as any` type casts on table/column names since the notifications table is dynamically referenced
|
|
207
|
+
- Channel-specific configuration (SMTP credentials, Twilio keys, Slack tokens) lives in each channel's own config file, not in `config/notification.ts`
|
|
208
|
+
- `useNotification()` throws if `config.default` is not set in `config/notification.ts`
|
|
209
|
+
- The `notification()` function (without arguments) is a shorthand for `useNotification()` with defaults
|
|
210
|
+
- The `nexmo` driver is a legacy alias for `vonage` in the SMS drivers index
|