@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,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-arrays
|
|
3
|
+
description: Use when working with array utilities in Stacks — statistical operations (average, median, mode, standard deviation, z-score, percentile, covariance), array manipulation (unique, flatten, partition, shuffle, sample, move), containment checks, or the Arr facade. Covers @stacksjs/arrays.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Array Utilities
|
|
10
|
+
|
|
11
|
+
## Key Paths
|
|
12
|
+
- Core package: `storage/framework/core/arrays/src/`
|
|
13
|
+
- Package: `@stacksjs/arrays`
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
The `index.ts` exports:
|
|
18
|
+
```typescript
|
|
19
|
+
export * from './arr'
|
|
20
|
+
export * as arr from './arr'
|
|
21
|
+
export * from './macro'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The `arr.ts` re-exports from three submodules:
|
|
25
|
+
- `contains.ts` — containment check functions
|
|
26
|
+
- `helpers.ts` — array manipulation (toArray, flatten, partition, unique, shuffle, etc.)
|
|
27
|
+
- `math.ts` — statistical functions (average, median, mode, variance, etc.)
|
|
28
|
+
|
|
29
|
+
The `macro.ts` provides the `Arr` and `arr` facade objects.
|
|
30
|
+
|
|
31
|
+
## Array Manipulation (`helpers.ts`)
|
|
32
|
+
|
|
33
|
+
### Converting to Arrays
|
|
34
|
+
```typescript
|
|
35
|
+
import type { Arrayable, Nullable } from '@stacksjs/types'
|
|
36
|
+
|
|
37
|
+
toArray('foo') // ['foo']
|
|
38
|
+
toArray(['foo']) // ['foo']
|
|
39
|
+
toArray(null) // []
|
|
40
|
+
toArray(undefined) // []
|
|
41
|
+
toArray(1) // [1]
|
|
42
|
+
toArray({ a: 1 }) // [{ a: 1 }]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Flattening
|
|
46
|
+
```typescript
|
|
47
|
+
flatten([1, [2, [3, [4, [5]]]]]) // [1, 2, 3, 4, 5] -- fully recursive
|
|
48
|
+
flatten(null) // []
|
|
49
|
+
flatten('hello') // ['hello']
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Merging
|
|
53
|
+
```typescript
|
|
54
|
+
mergeArrayable([1, 2], [3, 4], [5, 6]) // [1, 2, 3, 4, 5, 6]
|
|
55
|
+
mergeArrayable(1, [2, 3], null) // [1, 2, 3] -- handles non-arrays and nulls
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Unique Values
|
|
59
|
+
```typescript
|
|
60
|
+
uniq([1, 1, 2, 3]) // [1, 2, 3] -- uses Set
|
|
61
|
+
unique([1, 1, 2, 3]) // [1, 2, 3] -- alias for uniq
|
|
62
|
+
|
|
63
|
+
uniqueBy(
|
|
64
|
+
[{ id: 1 }, { id: 1 }, { id: 2 }],
|
|
65
|
+
(a, b) => a.id === b.id
|
|
66
|
+
) // [{ id: 1 }, { id: 2 }]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Accessing Elements
|
|
70
|
+
```typescript
|
|
71
|
+
last([1, 2, 3]) // 3
|
|
72
|
+
last([]) // undefined
|
|
73
|
+
|
|
74
|
+
at([1, 2, 3], 0) // 1
|
|
75
|
+
at([1, 2, 3], -1) // 3 -- negative indexing
|
|
76
|
+
at([1, 2, 3], 3) // undefined -- out of bounds
|
|
77
|
+
at([], 0) // undefined
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Mutation
|
|
81
|
+
```typescript
|
|
82
|
+
const arr = [1, 2, 3]
|
|
83
|
+
remove(arr, 2) // true -- arr is now [1, 3]
|
|
84
|
+
remove(arr, 4) // false -- value not found, arr unchanged
|
|
85
|
+
// WARNING: remove() MUTATES the original array
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Reordering
|
|
89
|
+
```typescript
|
|
90
|
+
move([1, 2, 3, 4], 0, 2) // [2, 3, 1, 4]
|
|
91
|
+
move([1, 2, 3, 4], 0, -1) // [2, 3, 4, 1] -- negative indexing
|
|
92
|
+
move([1, 2, 3, 4], -1, 0) // [4, 1, 2, 3]
|
|
93
|
+
// WARNING: move() MUTATES the original array (splices in place)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Random Selection
|
|
97
|
+
```typescript
|
|
98
|
+
sample([1, 2, 3, 4, 5], 3) // e.g. [3, 1, 5] -- 3 random items
|
|
99
|
+
// Items can repeat -- uses Math.random() * arr.length per pick
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Shuffling
|
|
103
|
+
```typescript
|
|
104
|
+
shuffle([1, 2, 3, 4]) // e.g. [3, 1, 4, 2]
|
|
105
|
+
// WARNING: shuffle() MUTATES the original array (Fisher-Yates in-place)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Clamping
|
|
109
|
+
```typescript
|
|
110
|
+
clampArrayRange([1, 2, 3], 0) // 0
|
|
111
|
+
clampArrayRange([1, 2, 3], 5) // 2 -- clamped to last valid index
|
|
112
|
+
clampArrayRange([1, 2, 3], -1) // 0 -- clamped to 0
|
|
113
|
+
// Uses clamp() from @stacksjs/browser
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Partitioning
|
|
117
|
+
```typescript
|
|
118
|
+
// With 1 filter: returns [matches, rest]
|
|
119
|
+
const [odds, evens] = partition([1, 2, 3, 4], i => i % 2 !== 0)
|
|
120
|
+
// odds = [1, 3], evens = [2, 4]
|
|
121
|
+
|
|
122
|
+
// With 2 filters: returns [f1 matches, f2 matches, rest]
|
|
123
|
+
const [small, medium, large] = partition(
|
|
124
|
+
[1, 5, 10, 50, 100],
|
|
125
|
+
n => n < 10,
|
|
126
|
+
n => n < 50
|
|
127
|
+
)
|
|
128
|
+
// small = [1, 5], medium = [10], large = [50, 100]
|
|
129
|
+
|
|
130
|
+
// Supports up to 6 filter functions, always returns one extra array for unmatched items
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Partition filter signature: `(item: T, index: number, array: readonly T[]) => any`
|
|
134
|
+
|
|
135
|
+
## Containment Checks (`contains.ts`)
|
|
136
|
+
|
|
137
|
+
All containment functions work with `string[]`. The `contains` function checks if the needle includes any element from the haystack (substring matching via `needle.includes(hay)`).
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
contains('foobar', ['foo', 'baz']) // true -- 'foobar'.includes('foo')
|
|
141
|
+
contains('hello', ['foo', 'bar']) // false
|
|
142
|
+
|
|
143
|
+
containsAll(['foo', 'bar'], ['foobar', 'barx']) // true -- all needles found
|
|
144
|
+
containsAll(['foo', 'qux'], ['foobar']) // false -- 'qux' not found
|
|
145
|
+
|
|
146
|
+
containsAny(['foo', 'qux'], ['foobar']) // true -- at least one found
|
|
147
|
+
containsAny(['baz', 'qux'], ['foobar']) // false
|
|
148
|
+
|
|
149
|
+
containsNone(['baz', 'qux'], ['foobar']) // true -- none found
|
|
150
|
+
containsNone(['foo', 'qux'], ['foobar']) // false
|
|
151
|
+
|
|
152
|
+
containsOnly(['foo', 'bar'], ['foobar', 'barx']) // checks if haystack items only contain needles
|
|
153
|
+
// Implemented as: containsAll(haystack, needles) -- reversed args
|
|
154
|
+
|
|
155
|
+
doesNotContain('qux', ['foo', 'bar']) // true
|
|
156
|
+
doesNotContain('foo', ['foobar']) // false
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Important**: `contains` uses `needle.includes(hay)`, NOT `haystack.includes(needle)`. It checks if the needle string contains any of the haystack substrings. This is substring matching, not array membership.
|
|
160
|
+
|
|
161
|
+
## Statistical Functions (`math.ts`)
|
|
162
|
+
|
|
163
|
+
All statistical functions expect `number[]` and throw `Error` on empty arrays (except `sum` and `product` which return identity values).
|
|
164
|
+
|
|
165
|
+
### Basic Aggregates
|
|
166
|
+
```typescript
|
|
167
|
+
sum([1, 2, 3, 4]) // 10 -- reduce with +
|
|
168
|
+
product([2, 3, 4]) // 24 -- reduce with *
|
|
169
|
+
min([5, 1, 3]) // 1 -- Math.min(), throws on empty
|
|
170
|
+
max([5, 1, 3]) // 5 -- Math.max(), throws on empty
|
|
171
|
+
range([1, 5, 3]) // 4 -- max - min
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Central Tendency
|
|
175
|
+
```typescript
|
|
176
|
+
average([1, 2, 3, 4, 5]) // 3 -- sum / length
|
|
177
|
+
avg([1, 2, 3]) // 2 -- alias for average
|
|
178
|
+
|
|
179
|
+
median([1, 2, 3, 4, 5]) // 3 -- middle value (sorted)
|
|
180
|
+
median([1, 2, 3, 4]) // 2.5 -- average of two middle values
|
|
181
|
+
|
|
182
|
+
mode([1, 2, 2, 3, 3, 4, 4, 4]) // 4 -- most frequent value
|
|
183
|
+
mode([1, 2, 3, 4]) // 1 -- first value when all equal frequency
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Dispersion
|
|
187
|
+
```typescript
|
|
188
|
+
variance([1, 2, 3, 4]) // 1.25 -- population variance (not sample)
|
|
189
|
+
standardDeviation([1, 2, 3, 4]) // 1.118... -- sqrt(variance), population std dev
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Advanced Statistics
|
|
193
|
+
```typescript
|
|
194
|
+
zScore([1, 2, 3, 4], 2) // -0.447... -- (value - mean) / stddev
|
|
195
|
+
zScore([1, 2, 3, 4], 3) // 0.447...
|
|
196
|
+
|
|
197
|
+
percentile([1, 2, 3, 4, 5], 50) // 3 -- interpolated percentile
|
|
198
|
+
percentile([1, 2, 3, 4], 25) // 1.75 -- linear interpolation
|
|
199
|
+
// Note: takes percentile value (0-100), uses linear interpolation
|
|
200
|
+
|
|
201
|
+
interquartileRange([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
202
|
+
// Q3 median - Q1 median
|
|
203
|
+
// Splits array at midpoint, computes median of each half
|
|
204
|
+
|
|
205
|
+
covariance([1, 2, 3, 4], [1, 2, 3, 4]) // 1.25 -- positive correlation
|
|
206
|
+
covariance([1, 2, 3, 4], [4, 3, 2, 1]) // -1.25 -- negative correlation
|
|
207
|
+
// Throws if arrays have different lengths
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Important**: `variance` and `standardDeviation` compute POPULATION metrics (divides by N), not sample metrics (which would divide by N-1).
|
|
211
|
+
|
|
212
|
+
## Arr Facade (`macro.ts`)
|
|
213
|
+
|
|
214
|
+
Both `Arr` and `arr` are exported as equivalent facade objects.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { Arr, arr } from '@stacksjs/arrays'
|
|
218
|
+
|
|
219
|
+
// Array manipulation
|
|
220
|
+
Arr.toArray(value) // convert to array
|
|
221
|
+
Arr.flatten(array) // recursive flatten
|
|
222
|
+
Arr.mergeArrayable(...arrays) // merge arrays
|
|
223
|
+
Arr.unique(arr) // deduplicate (Set-based)
|
|
224
|
+
Arr.uniqueBy(arr, equalFn) // deduplicate with custom equality
|
|
225
|
+
Arr.last(arr) // last element
|
|
226
|
+
Arr.at(arr, index) // get by index (negative supported)
|
|
227
|
+
Arr.remove(arr, value) // remove first occurrence (mutates)
|
|
228
|
+
Arr.move(arr, from, to) // move element (mutates)
|
|
229
|
+
Arr.clampArrayRange(arr, n) // clamp index to bounds
|
|
230
|
+
Arr.shuffle(arr) // shuffle in place
|
|
231
|
+
Arr.sample(arr, count) // random items
|
|
232
|
+
Arr.random(arr, count) // alias for sample, filters nulls
|
|
233
|
+
Arr.partition(arr, filter) // partition by filter
|
|
234
|
+
|
|
235
|
+
// Containment
|
|
236
|
+
Arr.contains(needle, haystack)
|
|
237
|
+
Arr.containsAll(needles, haystack)
|
|
238
|
+
Arr.containsAny(needles, haystack)
|
|
239
|
+
Arr.containsNone(needles, haystack)
|
|
240
|
+
Arr.containsOnly(needles, haystack)
|
|
241
|
+
Arr.doesNotContain(needle, haystack)
|
|
242
|
+
|
|
243
|
+
// Statistics
|
|
244
|
+
Arr.sum(arr) // sum
|
|
245
|
+
Arr.average(arr) // mean
|
|
246
|
+
Arr.avg(arr) // alias for average
|
|
247
|
+
Arr.median(arr) // median
|
|
248
|
+
Arr.mode(arr) // mode
|
|
249
|
+
Arr.range(arr) // max - min
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Note: The `Arr` facade does NOT expose `variance`, `standardDeviation`, `zScore`, `percentile`, `interquartileRange`, `covariance`, `product`, `min`, or `max` -- use the standalone functions for those.
|
|
253
|
+
|
|
254
|
+
The facade adds one extra method not in the standalone functions:
|
|
255
|
+
```typescript
|
|
256
|
+
Arr.random(arr, count) // like sample but filters null/undefined from results
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Exported Types
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
type PartitionFilter<T> = (item: T, index: number, array: readonly T[]) => any
|
|
263
|
+
// Also uses Arrayable<T> and Nullable<T> from @stacksjs/types
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Gotchas
|
|
267
|
+
- `shuffle()` and `move()` MUTATE the original array -- they do NOT return copies
|
|
268
|
+
- `remove()` MUTATES the original array and returns a boolean (not the removed element)
|
|
269
|
+
- `sample()` can return duplicate items since each pick is independent
|
|
270
|
+
- `contains()` uses SUBSTRING matching (`needle.includes(hay)`), not array membership -- `contains('foobar', ['foo'])` is true
|
|
271
|
+
- Statistical functions throw on empty arrays (except `sum` which returns 0 and `product` which returns 1)
|
|
272
|
+
- `variance` and `standardDeviation` use POPULATION formulas (divide by N), not sample formulas
|
|
273
|
+
- `percentile` takes a value 0-100 and uses linear interpolation between sorted array elements
|
|
274
|
+
- `interquartileRange` splits the sorted array at the midpoint and takes median of each half
|
|
275
|
+
- `mode` returns the first most-frequent value when there's a tie
|
|
276
|
+
- For complex collection operations with chaining, consider `@stacksjs/collections`
|
|
277
|
+
- The `Arr` facade wraps most but not all standalone functions -- advanced stats need direct imports
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-auth
|
|
3
|
+
description: Use when implementing authentication, authorization, passkeys, TOTP/2FA, RBAC, gates, policies, session auth, token management, email verification, password resets, or rate limiting in a Stacks application. Covers the @stacksjs/auth package, config/auth.ts, app/Gates.ts, and app/Middleware/.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Authentication & Authorization
|
|
10
|
+
|
|
11
|
+
The `@stacksjs/auth` package provides comprehensive authentication and authorization for Stacks applications, built on `@stacksjs/ts-auth`.
|
|
12
|
+
|
|
13
|
+
## Key Paths
|
|
14
|
+
|
|
15
|
+
- Core package source: `storage/framework/core/auth/src/`
|
|
16
|
+
- Configuration: `config/auth.ts`
|
|
17
|
+
- Security config: `config/security.ts`
|
|
18
|
+
- Hashing config: `config/hashing.ts`
|
|
19
|
+
- Application gates: `app/Gates.ts`
|
|
20
|
+
- Application middleware: `app/Middleware/`
|
|
21
|
+
- Middleware aliases: `app/Middleware.ts`
|
|
22
|
+
- Auth types: `storage/framework/core/types/src/auth.ts`
|
|
23
|
+
|
|
24
|
+
## Source Files
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
auth/src/
|
|
28
|
+
├── index.ts # All re-exports
|
|
29
|
+
├── authentication.ts # Auth class - core auth logic
|
|
30
|
+
├── authenticator.ts # 2FA and personal access client
|
|
31
|
+
├── client.ts # Client re-exports
|
|
32
|
+
├── middleware.ts # Auth middleware handler
|
|
33
|
+
├── rate-limiter.ts # RateLimiter class (5 attempts, 15min lockout)
|
|
34
|
+
├── passkey.ts # WebAuthn/Passkey support
|
|
35
|
+
├── password/reset.ts # Password reset flow
|
|
36
|
+
├── register.ts # User registration
|
|
37
|
+
├── user.ts # Auth user helpers
|
|
38
|
+
├── tokens.ts # Token CRUD, scopes, refresh tokens, OAuth clients
|
|
39
|
+
├── gate.ts # Authorization gates & policies
|
|
40
|
+
├── policy.ts # BasePolicy class + discovery
|
|
41
|
+
├── authorizable.ts # User authorization mixin
|
|
42
|
+
├── rbac.ts # Full RBAC system
|
|
43
|
+
├── email-verification.ts # Email verification flow
|
|
44
|
+
└── session-auth.ts # Session-based SPA auth
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Auth Class (authentication.ts) — Static Methods
|
|
48
|
+
|
|
49
|
+
### Login & Authentication
|
|
50
|
+
- `Auth.attempt(credentials: AuthCredentials): Promise<boolean>` — validate credentials without creating token
|
|
51
|
+
- `Auth.validate(credentials: AuthCredentials): Promise<boolean>` — alias for attempt
|
|
52
|
+
- `Auth.login(credentials: AuthCredentials, options?: TokenCreateOptions): Promise<{ user, token } | null>` — login and create token
|
|
53
|
+
- `Auth.loginUsingId(userId: number, options?: TokenCreateOptions): Promise<{ user, token } | null>` — login by user ID
|
|
54
|
+
- `Auth.logout(): Promise<void>` — revoke current token
|
|
55
|
+
- `Auth.once(credentials: AuthCredentials): Promise<boolean>` — one-time auth without token
|
|
56
|
+
- `Auth.requestToken(credentials, clientId, clientSecret): Promise<{ token } | null>` — OAuth token request
|
|
57
|
+
|
|
58
|
+
### User State
|
|
59
|
+
- `Auth.user(): Promise<UserModel | undefined>` — get authenticated user from bearer token
|
|
60
|
+
- `Auth.check(): Promise<boolean>` — is user authenticated?
|
|
61
|
+
- `Auth.guest(): Promise<boolean>` — is user a guest?
|
|
62
|
+
- `Auth.id(): Promise<number | undefined>` — get authenticated user ID
|
|
63
|
+
- `Auth.setUser(user: UserModel): void` — manually set user
|
|
64
|
+
|
|
65
|
+
### Token Creation
|
|
66
|
+
- `Auth.createTokenForUser(user, options?: TokenCreateOptions): Promise<NewAccessToken>`
|
|
67
|
+
- `Auth.createToken(user, name?, abilities?): Promise<AuthToken>`
|
|
68
|
+
|
|
69
|
+
### Token Validation
|
|
70
|
+
- `Auth.validateToken(token: string): Promise<boolean>` — validate bearer token
|
|
71
|
+
- `Auth.getUserFromToken(token: string): Promise<UserModel | undefined>`
|
|
72
|
+
- `Auth.currentAccessToken(): Promise<PersonalAccessToken | undefined>`
|
|
73
|
+
|
|
74
|
+
### Token Abilities (Scopes)
|
|
75
|
+
- `Auth.tokenCan(ability: string): Promise<boolean>`
|
|
76
|
+
- `Auth.tokenCant(ability: string): Promise<boolean>`
|
|
77
|
+
- `Auth.tokenAbilities(): Promise<string[]>`
|
|
78
|
+
- `Auth.tokenCanAll(abilities: string[]): Promise<boolean>`
|
|
79
|
+
- `Auth.tokenCanAny(abilities: string[]): Promise<boolean>`
|
|
80
|
+
|
|
81
|
+
### Token Management
|
|
82
|
+
- `Auth.tokens(userId?: number): Promise<PersonalAccessToken[]>`
|
|
83
|
+
- `Auth.revokeToken(token: string): Promise<void>`
|
|
84
|
+
- `Auth.revokeTokenById(tokenId: number): Promise<void>`
|
|
85
|
+
- `Auth.revokeAllTokens(userId?: number): Promise<void>`
|
|
86
|
+
- `Auth.revokeOtherTokens(userId?: number): Promise<void>`
|
|
87
|
+
- `Auth.pruneExpiredTokens(): Promise<number>`
|
|
88
|
+
- `Auth.pruneRevokedTokens(): Promise<number>`
|
|
89
|
+
- `Auth.rotateToken(oldToken: string): Promise<AuthToken | null>`
|
|
90
|
+
- `Auth.findToken(tokenId: number): Promise<PersonalAccessToken | null>`
|
|
91
|
+
|
|
92
|
+
### Utility
|
|
93
|
+
- `Auth.guard(name?: string): typeof Auth` — select guard (returns self)
|
|
94
|
+
- `Auth.viaRemember(): boolean` — always false currently
|
|
95
|
+
- `Auth.clearState(): void` — clear cached user/token
|
|
96
|
+
|
|
97
|
+
## Token System (tokens.ts)
|
|
98
|
+
|
|
99
|
+
### Access Tokens
|
|
100
|
+
- `tokens(userId: number): Promise<AccessToken[]>`
|
|
101
|
+
- `findToken(plainTextToken: string): Promise<AccessToken | null>`
|
|
102
|
+
- `currentAccessToken(): Promise<AccessToken | null>`
|
|
103
|
+
- `createToken(userId, name?, scopes?, options?): Promise<PersonalAccessTokenResult>`
|
|
104
|
+
- Options: `{ expiresInMinutes?, withRefreshToken?, refreshExpiresInDays? }`
|
|
105
|
+
|
|
106
|
+
### Refresh Tokens
|
|
107
|
+
- `refreshToken(refreshTokenPlain, options?): Promise<RefreshTokenResult>`
|
|
108
|
+
- `validateRefreshToken(refreshTokenPlain): Promise<boolean>`
|
|
109
|
+
- `revokeRefreshToken(refreshTokenPlain): Promise<void>`
|
|
110
|
+
- `revokeAllRefreshTokens(userId): Promise<void>`
|
|
111
|
+
- `deleteExpiredRefreshTokens(): Promise<number>`
|
|
112
|
+
- `deleteRevokedRefreshTokens(daysOld?): Promise<number>`
|
|
113
|
+
|
|
114
|
+
### Token Revocation
|
|
115
|
+
- `revokeToken(plainTextToken): Promise<void>`
|
|
116
|
+
- `revokeTokenById(tokenId): Promise<void>`
|
|
117
|
+
- `revokeAllTokens(userId): Promise<void>`
|
|
118
|
+
- `revokeOtherTokens(userId): Promise<void>`
|
|
119
|
+
- `deleteExpiredTokens(): Promise<number>`
|
|
120
|
+
- `deleteRevokedTokens(daysOld?): Promise<number>`
|
|
121
|
+
|
|
122
|
+
### Token Scopes
|
|
123
|
+
- `tokenCan(scope): Promise<boolean>`
|
|
124
|
+
- `tokenCant(scope): Promise<boolean>`
|
|
125
|
+
- `tokenCanAll(scopes): Promise<boolean>`
|
|
126
|
+
- `tokenCanAny(scopes): Promise<boolean>`
|
|
127
|
+
- `tokenAbilities(): Promise<string[]>`
|
|
128
|
+
- `parseScopes(scopes: string | string[] | null | undefined): TokenScopes`
|
|
129
|
+
|
|
130
|
+
### OAuth Clients
|
|
131
|
+
- `clients(userId): Promise<OAuthClient[]>`
|
|
132
|
+
- `findClient(clientId): Promise<OAuthClient | null>`
|
|
133
|
+
- `createClient(options: CreateClientOptions): Promise<CreateClientResult>`
|
|
134
|
+
- `revokeClient(clientId): Promise<void>`
|
|
135
|
+
|
|
136
|
+
## Two-Factor Authentication (authenticator.ts)
|
|
137
|
+
|
|
138
|
+
- `generateTwoFactorSecret(): string`
|
|
139
|
+
- `generateTwoFactorToken(secret: Secret): Promise<Token>`
|
|
140
|
+
- `verifyTwoFactorCode(token: Token, secret: Secret): Promise<boolean>`
|
|
141
|
+
- `generateTwoFactorUri(user?, service?, secret?): string`
|
|
142
|
+
- `createPersonalAccessClient(): Promise<Result<string, never>>`
|
|
143
|
+
|
|
144
|
+
### Re-exported from @stacksjs/ts-auth
|
|
145
|
+
- `generateTOTP`, `verifyTOTP`, `generateTOTPSecret`, `totpKeyUri`
|
|
146
|
+
|
|
147
|
+
## Authorization Gates (gate.ts)
|
|
148
|
+
|
|
149
|
+
### Gate Functions
|
|
150
|
+
- `define<T>(ability: string, callback: GateCallback<T>): void`
|
|
151
|
+
- `policy(model: string | { name }, policyClass: new () => Policy): void`
|
|
152
|
+
- `before(callback): void` — run before any gate check
|
|
153
|
+
- `after(callback): void` — run after any gate check
|
|
154
|
+
- `allows(ability, user, ...args): Promise<boolean>`
|
|
155
|
+
- `denies(ability, user, ...args): Promise<boolean>`
|
|
156
|
+
- `can(ability, user, ...args): Promise<boolean>`
|
|
157
|
+
- `cannot(ability, user, ...args): Promise<boolean>`
|
|
158
|
+
- `any(abilities[], user, ...args): Promise<boolean>`
|
|
159
|
+
- `all(abilities[], user, ...args): Promise<boolean>`
|
|
160
|
+
- `none(abilities[], user, ...args): Promise<boolean>`
|
|
161
|
+
- `authorize(ability, user, ...args): Promise<AuthorizationResponse>` — throws on deny
|
|
162
|
+
- `inspect(ability, user, ...args): Promise<AuthorizationResponse>` — never throws
|
|
163
|
+
- `has(ability): boolean`
|
|
164
|
+
- `hasPolicy(model): boolean`
|
|
165
|
+
- `abilities(): string[]`
|
|
166
|
+
- `getPolicyFor<T>(model: T): Policy<T> | null`
|
|
167
|
+
- `flush(): void` — clear all gates
|
|
168
|
+
|
|
169
|
+
### Gate Facade — `Gate.define()`, `Gate.can()`, etc
|
|
170
|
+
|
|
171
|
+
### AuthorizationResponse Class
|
|
172
|
+
- `static allow(message?): AuthorizationResponse`
|
|
173
|
+
- `static deny(message?, code?): AuthorizationResponse`
|
|
174
|
+
- `allowed(): boolean`, `denied(): boolean`
|
|
175
|
+
- `authorize(): void` — throws AuthorizationException if denied
|
|
176
|
+
|
|
177
|
+
### Policy Interface
|
|
178
|
+
Methods: `before?`, `viewAny?`, `view?`, `create?`, `update?`, `delete?`, `restore?`, `forceDelete?`
|
|
179
|
+
|
|
180
|
+
### BasePolicy Abstract Class
|
|
181
|
+
Protected helpers: `allow(message?)`, `deny(message?, code?)`, `denyIf(condition)`, `denyUnless(condition)`, `allowIf(condition)`
|
|
182
|
+
|
|
183
|
+
## RBAC System (rbac.ts)
|
|
184
|
+
|
|
185
|
+
### Role Management
|
|
186
|
+
- `Rbac.createRole(name, guardName?, description?): Promise<RoleRecord>`
|
|
187
|
+
- `Rbac.findRole(name, guardName?): Promise<RoleRecord | null>`
|
|
188
|
+
- `Rbac.deleteRole(name, guardName?): Promise<void>`
|
|
189
|
+
- `Rbac.getAllRoles(guardName?): Promise<RoleRecord[]>`
|
|
190
|
+
|
|
191
|
+
### Permission Management
|
|
192
|
+
- `Rbac.createPermission(name, guardName?, description?): Promise<PermissionRecord>`
|
|
193
|
+
- `Rbac.findPermission(name, guardName?): Promise<PermissionRecord | null>`
|
|
194
|
+
- `Rbac.deletePermission(name, guardName?): Promise<void>`
|
|
195
|
+
- `Rbac.getAllPermissions(guardName?): Promise<PermissionRecord[]>`
|
|
196
|
+
|
|
197
|
+
### User-Role Operations
|
|
198
|
+
- `Rbac.getUserRoles(user): Promise<RoleRecord[]>`
|
|
199
|
+
- `Rbac.assignRole(user, roleName, guardName?): Promise<void>`
|
|
200
|
+
- `Rbac.removeRole(user, roleName, guardName?): Promise<void>`
|
|
201
|
+
- `Rbac.removeAllRoles(user): Promise<void>`
|
|
202
|
+
- `Rbac.syncRoles(user, roleNames[], guardName?): Promise<void>`
|
|
203
|
+
- `Rbac.hasRole(user, roleName, guardName?): Promise<boolean>`
|
|
204
|
+
- `Rbac.hasAnyRole(user, roleNames[], guardName?): Promise<boolean>`
|
|
205
|
+
- `Rbac.hasAllRoles(user, roleNames[], guardName?): Promise<boolean>`
|
|
206
|
+
|
|
207
|
+
### User-Permission Operations
|
|
208
|
+
- `Rbac.getUserPermissions(user): Promise<PermissionRecord[]>`
|
|
209
|
+
- `Rbac.givePermission(user, permissionName, guardName?): Promise<void>`
|
|
210
|
+
- `Rbac.revokePermission(user, permissionName, guardName?): Promise<void>`
|
|
211
|
+
- `Rbac.revokeAllPermissions(user): Promise<void>`
|
|
212
|
+
- `Rbac.syncPermissions(user, permissionNames[], guardName?): Promise<void>`
|
|
213
|
+
- `Rbac.hasPermission(user, permissionName, guardName?): Promise<boolean>`
|
|
214
|
+
- `Rbac.hasAnyPermission(user, permissionNames[], guardName?): Promise<boolean>`
|
|
215
|
+
- `Rbac.hasAllPermissions(user, permissionNames[], guardName?): Promise<boolean>`
|
|
216
|
+
|
|
217
|
+
### Role-Permission Operations
|
|
218
|
+
- `Rbac.getRolePermissions(roleId): Promise<PermissionRecord[]>`
|
|
219
|
+
- `Rbac.givePermissionToRole(roleName, permissionName, guardName?): Promise<void>`
|
|
220
|
+
- `Rbac.revokePermissionFromRole(roleName, permissionName, guardName?): Promise<void>`
|
|
221
|
+
- `Rbac.syncRolePermissions(roleName, permissionNames[], guardName?): Promise<void>`
|
|
222
|
+
|
|
223
|
+
### withRbac Mixin
|
|
224
|
+
`withRbac(user)` — adds `hasRole()`, `hasPermission()`, `assignRole()`, `givePermission()`, etc. to any user object
|
|
225
|
+
|
|
226
|
+
### RBAC Types
|
|
227
|
+
```typescript
|
|
228
|
+
interface RoleRecord { id, name, guard_name, description?, created_at?, updated_at? }
|
|
229
|
+
interface PermissionRecord { id, name, guard_name, description?, created_at?, updated_at? }
|
|
230
|
+
interface RbacStore { findRoleByName, createRole, deleteRole, getAllRoles, findPermissionByName, createPermission, ... }
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Session Auth (session-auth.ts)
|
|
234
|
+
|
|
235
|
+
- `SessionAuth.login(email, password): Promise<{ user, sessionId }>`
|
|
236
|
+
- `SessionAuth.logout(sessionId): void`
|
|
237
|
+
- `SessionAuth.user(sessionId): Promise<UserModel | undefined>`
|
|
238
|
+
- `SessionAuth.check(sessionId): boolean`
|
|
239
|
+
- `SessionAuth.refresh(sessionId, ttlMs?): boolean`
|
|
240
|
+
|
|
241
|
+
Internal: in-memory Map with 10k session limit, 5-minute eviction interval, timing-safe password comparison with dummy bcrypt hash.
|
|
242
|
+
|
|
243
|
+
## Email Verification (email-verification.ts)
|
|
244
|
+
|
|
245
|
+
- `EmailVerification.isVerified(user): boolean`
|
|
246
|
+
- `EmailVerification.send(user): Promise<void>`
|
|
247
|
+
- `EmailVerification.verify(userId, token): Promise<EmailVerificationResult>`
|
|
248
|
+
- `EmailVerification.resend(user): Promise<EmailVerificationResult>`
|
|
249
|
+
|
|
250
|
+
## Password Reset (password/reset.ts)
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const actions = passwordResets(email)
|
|
254
|
+
await actions.sendEmail()
|
|
255
|
+
const valid = await actions.verifyToken(token)
|
|
256
|
+
const result = await actions.resetPassword(token, newPassword)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Registration (register.ts)
|
|
260
|
+
|
|
261
|
+
- `register(credentials: NewUser): Promise<{ token: AuthToken }>`
|
|
262
|
+
|
|
263
|
+
## User Helpers (user.ts)
|
|
264
|
+
|
|
265
|
+
- `authUser(): Promise<UserModel | undefined>`
|
|
266
|
+
- `check(): Promise<boolean>`
|
|
267
|
+
- `id(): Promise<number | undefined>`
|
|
268
|
+
- `email(): Promise<string | undefined>`
|
|
269
|
+
- `name(): Promise<string | undefined>`
|
|
270
|
+
- `isAuthenticated(): Promise<boolean>`
|
|
271
|
+
- `logout(): Promise<void>`
|
|
272
|
+
- `refresh(): Promise<void>`
|
|
273
|
+
|
|
274
|
+
## Passkey/WebAuthn (passkey.ts)
|
|
275
|
+
|
|
276
|
+
- `getUserPasskeys(userId): Promise<PasskeyAttribute[]>`
|
|
277
|
+
- `getUserPasskey(userId, passkeyId): Promise<PasskeyAttribute | undefined>`
|
|
278
|
+
- `setCurrentRegistrationOptions(user, verified): Promise<void>`
|
|
279
|
+
|
|
280
|
+
### Re-exported from @stacksjs/ts-auth
|
|
281
|
+
- `generateRegistrationOptions`, `generateAuthenticationOptions`
|
|
282
|
+
- `verifyRegistrationResponse`, `verifyAuthenticationResponse`
|
|
283
|
+
- `startRegistration`, `startAuthentication` (browser)
|
|
284
|
+
- `browserSupportsWebAuthn`, `browserSupportsWebAuthnAutofill`
|
|
285
|
+
- `platformAuthenticatorIsAvailable`
|
|
286
|
+
|
|
287
|
+
## Auth Middleware (middleware.ts)
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
export const authMiddlewareHandler = {
|
|
291
|
+
name: 'auth',
|
|
292
|
+
handle: authMiddleware, // validates bearer token, throws 401
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Rate Limiter (rate-limiter.ts)
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
class RateLimiter {
|
|
300
|
+
static MAX_ATTEMPTS = 5
|
|
301
|
+
static LOCKOUT_DURATION = 15 * 60 * 1000 // 15 minutes
|
|
302
|
+
static MAX_STORE_SIZE = 10_000
|
|
303
|
+
static EVICTION_INTERVAL = 5 * 60 * 1000 // 5 minutes
|
|
304
|
+
|
|
305
|
+
static isRateLimited(email): boolean
|
|
306
|
+
static recordFailedAttempt(email): void
|
|
307
|
+
static resetAttempts(email): void
|
|
308
|
+
static validateAttempt(email): void // throws HttpError 429
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Authorizable Mixin (authorizable.ts)
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const authUser = withAuthorization(user)
|
|
316
|
+
await authUser.can('edit-post', post)
|
|
317
|
+
await authUser.cannot('delete-post', post)
|
|
318
|
+
await authUser.canAny(['edit', 'delete'], post)
|
|
319
|
+
await authUser.canAll(['edit', 'publish'], post)
|
|
320
|
+
await authUser.authorize('edit-post', post) // throws if denied
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Configuration
|
|
324
|
+
|
|
325
|
+
### config/auth.ts
|
|
326
|
+
```typescript
|
|
327
|
+
{
|
|
328
|
+
default: 'api',
|
|
329
|
+
guards: { api: { driver: 'token', provider: 'users' } },
|
|
330
|
+
providers: { users: { driver: 'database', table: 'users' } },
|
|
331
|
+
username: 'email', // AUTH_USERNAME_FIELD env
|
|
332
|
+
password: 'password', // AUTH_PASSWORD_FIELD env
|
|
333
|
+
tokenExpiry: 30, // days, AUTH_TOKEN_EXPIRY env
|
|
334
|
+
tokenRotation: 7, // days, AUTH_TOKEN_ROTATION env
|
|
335
|
+
defaultAbilities: ['*'],
|
|
336
|
+
defaultTokenName: 'auth_token',
|
|
337
|
+
passwordReset: { expire: 60, throttle: 60 }
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### config/hashing.ts
|
|
342
|
+
```typescript
|
|
343
|
+
{
|
|
344
|
+
driver: 'bcrypt', // 'bcrypt' | 'argon2'
|
|
345
|
+
bcrypt: { rounds: 12 },
|
|
346
|
+
argon2: { memory: 65536, time: 3 }
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### config/security.ts
|
|
351
|
+
```typescript
|
|
352
|
+
{
|
|
353
|
+
firewall: {
|
|
354
|
+
enabled: true,
|
|
355
|
+
countryCodes: [],
|
|
356
|
+
ipAddresses: { allowlist: [], blocklist: [] },
|
|
357
|
+
rateLimitPerMinute: 500,
|
|
358
|
+
useIpReputationLists: true,
|
|
359
|
+
useKnownBadInputsRuleSet: true
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Middleware Aliases (app/Middleware.ts)
|
|
365
|
+
|
|
366
|
+
Available middleware names: `maintenance`, `auth`, `guest`, `api`, `team`, `logger`, `abilities`, `can`, `throttle`, `local`, `development`, `staging`, `production`, `env.local`, `env.development`, `env.staging`, `env.production`, `role`, `permission`, `verified` (EnsureEmailIsVerified)
|
|
367
|
+
|
|
368
|
+
## Application Gates Example (app/Gates.ts)
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
Gate.define('access-admin', (user) => user?.email?.endsWith('@stacksjs.org') ?? false)
|
|
372
|
+
Gate.define('edit-settings', (user) => !!user)
|
|
373
|
+
Gate.define('view-dashboard', (user) => !!user)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Default API Routes
|
|
377
|
+
|
|
378
|
+
- `POST /login` → LoginAction (validates email + password)
|
|
379
|
+
- `POST /register` → RegisterAction
|
|
380
|
+
- `POST /auth/refresh` → RefreshTokenAction
|
|
381
|
+
- `POST /auth/token` → CreateTokenAction
|
|
382
|
+
- `GET /auth/tokens` → ListTokensAction (auth middleware)
|
|
383
|
+
- `DELETE /auth/tokens/{id}` → RevokeTokenAction (auth middleware)
|
|
384
|
+
- `GET /me` → GetMeAction (auth middleware)
|
|
385
|
+
- `POST /logout` → LogoutAction (auth middleware)
|
|
386
|
+
|
|
387
|
+
## User Model Traits
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// User model uses:
|
|
391
|
+
traits: {
|
|
392
|
+
useAuth: { usePasskey: true },
|
|
393
|
+
useUuid: true,
|
|
394
|
+
useTimestamps: true,
|
|
395
|
+
useSocials: ['github'],
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Gotchas
|
|
400
|
+
|
|
401
|
+
- Auth depends on `@stacksjs/ts-auth` for TOTP and passkey functions
|
|
402
|
+
- Password hashing defaults to bcrypt with 12 rounds (config/hashing.ts)
|
|
403
|
+
- Rate limiter uses in-memory Map, resets on server restart — not shared across workers
|
|
404
|
+
- Session auth also uses in-memory Map with 10k limit — for SPA cookie auth
|
|
405
|
+
- Token format is `tokenId|plainText` — the `|` separates the encrypted ID from the plain token
|
|
406
|
+
- The `parseToken()` helper splits on `|` to extract both parts
|
|
407
|
+
- Bearer tokens come from the `Authorization: Bearer <token>` header
|
|
408
|
+
- `Auth.user()` internally calls `getBearerToken()` → `parseToken()` → `getTokenFromId()` → validates hash
|
|
409
|
+
- RBAC has an internal cache (`userRoles`, `userPermissions`, `rolePermissions`) — call `Rbac.flushCache()` after direct DB changes
|
|
410
|
+
- Gate `before` callbacks can short-circuit — return `true` to allow, `null` to continue checking
|
|
411
|
+
- `withRbac()` and `withAuthorization()` return new objects with methods mixed in
|
|
412
|
+
- The `RbacStore` interface must be implemented and set via `Rbac.setStore()` for RBAC to work
|
|
413
|
+
- Password reset tokens expire after 60 minutes by default
|
|
414
|
+
- Default token abilities are `['*']` — wildcard access
|
|
415
|
+
- Token expiry defaults to 30 days
|
|
416
|
+
- Session auth uses timing-safe bcrypt comparison even for failed lookups (dummy hash prevents timing attacks)
|
|
417
|
+
|
|
418
|
+
## Build
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
cd storage/framework/core/auth && bun build.ts
|
|
422
|
+
```
|