@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,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-plugins
|
|
3
|
+
description: Use when working with the Stacks preload chain and Bun plugins — the env plugin, the framework preloader, how auto-imports get injected into globalThis, why a command does or does not see framework globals, the bun-plugin-stx static-serve plugin, or writing a Bun plugin. Covers bunfig.toml preload and storage/framework/defaults/resources/plugins/preloader.ts.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Plugins and the Preload Chain
|
|
10
|
+
|
|
11
|
+
Stacks has **no `@stacksjs/plugins` package**. "Plugins" here means Bun's native
|
|
12
|
+
plugin API plus the two modules `bunfig.toml` preloads before any Stacks code
|
|
13
|
+
runs. If you are chasing "why is `User` undefined in this command" or "why did
|
|
14
|
+
`.env` not decrypt", this is the skill.
|
|
15
|
+
|
|
16
|
+
## Key Paths
|
|
17
|
+
|
|
18
|
+
- Preload declaration: `bunfig.toml` (`preload`, at the root and under `[test]`)
|
|
19
|
+
- Env plugin: `storage/framework/core/env/plugin.ts`
|
|
20
|
+
- Framework preloader: `storage/framework/defaults/resources/plugins/preloader.ts`
|
|
21
|
+
- Static-serve plugin: `bunfig.toml`, `[serve.static] plugins = ["bun-plugin-stx"]`
|
|
22
|
+
|
|
23
|
+
## The preload chain
|
|
24
|
+
|
|
25
|
+
`bunfig.toml` preloads two modules, **in this order**:
|
|
26
|
+
|
|
27
|
+
```toml
|
|
28
|
+
preload = [
|
|
29
|
+
"./storage/framework/core/env/plugin.ts",
|
|
30
|
+
"./storage/framework/defaults/resources/plugins/preloader.ts",
|
|
31
|
+
]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
1. **Env plugin** loads `.env` and decrypts dotenvx-style encrypted values, so
|
|
35
|
+
everything downstream reads plaintext from `process.env`.
|
|
36
|
+
2. **Preloader** loads env files for the resolved environment, then injects the
|
|
37
|
+
framework's server auto-imports into `globalThis`.
|
|
38
|
+
|
|
39
|
+
Order is load-bearing: the preloader reads decrypted secrets, so it has to run
|
|
40
|
+
second.
|
|
41
|
+
|
|
42
|
+
Tests preload a different pair - the env plugin and `tests/setup.ts` - so the
|
|
43
|
+
suite gets production env semantics without the auto-import graph.
|
|
44
|
+
|
|
45
|
+
## What the preloader does
|
|
46
|
+
|
|
47
|
+
### 1. Resolves the environment
|
|
48
|
+
|
|
49
|
+
Before loading any `.env` file it works out `APP_ENV`, because that decides
|
|
50
|
+
*which* file gets decrypted:
|
|
51
|
+
|
|
52
|
+
- `deploy staging` and `deploy --staging` both resolve to `staging`. Both forms
|
|
53
|
+
are handled; CI uses the flag form.
|
|
54
|
+
- `cloud:remove`, `cloud:destroy`, `cloud:cleanup`, `undeploy` force `production`.
|
|
55
|
+
- Bare `deploy` defaults to `production`.
|
|
56
|
+
|
|
57
|
+
### 2. Loads and decrypts env files
|
|
58
|
+
|
|
59
|
+
Gated on **`isRepl` / `isPostinstall` only** - never on the fast-command list
|
|
60
|
+
below. Fast commands (`migrate`, `build`, `seed`, ...) genuinely need decrypted
|
|
61
|
+
config; gating them here caused stacksjs/stacks#2048, where an encrypted
|
|
62
|
+
`.env.<env>` silently never decrypted even with the key present.
|
|
63
|
+
|
|
64
|
+
Postinstall skips because `@stacksjs/env` may not be linked yet mid-install.
|
|
65
|
+
|
|
66
|
+
### 3. Injects auto-imports into `globalThis`
|
|
67
|
+
|
|
68
|
+
Loads ~24 framework packages plus everything in `app/Jobs/` and
|
|
69
|
+
`resources/functions/`, assigning every named export onto `globalThis`. That is
|
|
70
|
+
why `await User.find(1)` and `response.json(...)` work in an action with no
|
|
71
|
+
import statement. See `stacks-auto-imports` for the full manifest.
|
|
72
|
+
|
|
73
|
+
A `protectedGlobals` set guards the runtime: `process`, `fetch`, `Promise`,
|
|
74
|
+
`Bun`, `console` and friends are never overwritten, whatever a package exports.
|
|
75
|
+
|
|
76
|
+
## The fast-command skip (the usual gotcha)
|
|
77
|
+
|
|
78
|
+
Auto-import injection is **skipped** when the first CLI argument matches:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
dev build test lint migrate fresh seed generate make
|
|
82
|
+
key:generate scaffold:crud version help --version -v --help -h
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Prefix matches count, so `generate:migrations` skips along with `generate`.
|
|
86
|
+
|
|
87
|
+
Two reasons: cold-start speed, and correctness. Codegen commands must not pull
|
|
88
|
+
the router and ORM graph in before `bun-query-builder` can diff schemas - a
|
|
89
|
+
broken `@stacksjs/bun-router` install used to make `generate:migrations` exit 1
|
|
90
|
+
with no output at all, because the preloader died loading `@stacksjs/router`.
|
|
91
|
+
|
|
92
|
+
**Consequence:** inside those commands, framework globals do not exist. Import
|
|
93
|
+
what you need explicitly.
|
|
94
|
+
|
|
95
|
+
The REPL skips too (`!process.argv[1]`), as does anything running under
|
|
96
|
+
`npm_lifecycle_event === 'postinstall'`.
|
|
97
|
+
|
|
98
|
+
### Opting back in
|
|
99
|
+
|
|
100
|
+
The preloader exports `loadAutoImports()` so a server entrypoint can pull the
|
|
101
|
+
graph in deliberately:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const { loadAutoImports } = await import('../../../defaults/resources/plugins/preloader.ts')
|
|
105
|
+
await loadAutoImports()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`dev/api.ts` does exactly this. Bun consumes `--watch`, so a directly-run server
|
|
109
|
+
script sees an empty `argv.slice(2)` and would otherwise be indistinguishable
|
|
110
|
+
from a bare invocation.
|
|
111
|
+
|
|
112
|
+
## Writing a Bun plugin
|
|
113
|
+
|
|
114
|
+
Use Bun's native API directly - there is no framework wrapper to import:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { plugin } from 'bun'
|
|
118
|
+
|
|
119
|
+
plugin({
|
|
120
|
+
name: 'my-loader',
|
|
121
|
+
setup(build) {
|
|
122
|
+
build.onLoad({ filter: /\.custom$/ }, async (args) => {
|
|
123
|
+
const contents = await Bun.file(args.path).text()
|
|
124
|
+
return { contents: transform(contents), loader: 'ts' }
|
|
125
|
+
})
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Register it by adding the module to `preload` in `bunfig.toml`. A plugin only
|
|
131
|
+
affects the process that preloaded it.
|
|
132
|
+
|
|
133
|
+
## Gotchas
|
|
134
|
+
|
|
135
|
+
- **No `@stacksjs/plugins` package.** Import `plugin` from `bun`.
|
|
136
|
+
- **Preload order matters.** Env decryption has to precede the preloader.
|
|
137
|
+
- **Fast commands have no globals.** If a `make:*` or `generate:*` command needs
|
|
138
|
+
a framework symbol, import it - do not rely on auto-imports.
|
|
139
|
+
- **`bun-plugin-stx` is a static-serve plugin**, wired through
|
|
140
|
+
`[serve.static] plugins` in `bunfig.toml`. The preloader's own
|
|
141
|
+
`import 'bun-plugin-stx'` line is commented out; stx compilation in the dev
|
|
142
|
+
server runs through the stx package, not that import.
|
|
143
|
+
- **Preloader failures are silent per package.** Each import sits in its own
|
|
144
|
+
`try {} catch {}`, so a package that fails to build leaves its globals
|
|
145
|
+
undefined instead of crashing the process. A mysteriously undefined global is
|
|
146
|
+
usually an unbuilt package - check `storage/framework/core/<pkg>/dist`.
|
|
147
|
+
- **Adding a global?** Export it from a package already in the preloader's list,
|
|
148
|
+
or from `resources/functions/`, then run `buddy generate --types` so the
|
|
149
|
+
declarations match the runtime.
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-push
|
|
3
|
+
description: Use when implementing push notifications in Stacks — sending via Expo Push Service or Firebase Cloud Messaging (FCM legacy and v1 APIs), configuring push drivers, batch sending, multicast, topic subscriptions, push notification payloads, token validation, or receipt checking. Covers @stacksjs/push.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Push Notifications
|
|
10
|
+
|
|
11
|
+
Push notification support with Expo Push Service and FCM (Firebase Cloud Messaging) drivers. FCM supports both the legacy server key API and the v1 service account API.
|
|
12
|
+
|
|
13
|
+
## Key Paths
|
|
14
|
+
- Core package: `storage/framework/core/push/src/`
|
|
15
|
+
- Types: `storage/framework/core/types/src/push.ts`
|
|
16
|
+
|
|
17
|
+
## Source Files
|
|
18
|
+
```
|
|
19
|
+
push/src/
|
|
20
|
+
├── index.ts # send(), configureFCM(), PushNotification type
|
|
21
|
+
└── drivers/
|
|
22
|
+
├── index.ts # re-exports expo and fcm
|
|
23
|
+
├── expo.ts # Expo Push Service driver
|
|
24
|
+
└── fcm.ts # FCM legacy + v1 API driver
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Main send() Function
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
async function send(
|
|
31
|
+
to: string | string[],
|
|
32
|
+
notification: PushNotification,
|
|
33
|
+
options?: SendOptions,
|
|
34
|
+
): Promise<PushResult>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Routes to the appropriate driver based on `options.driver` (default: `'expo'`).
|
|
38
|
+
|
|
39
|
+
- For **Expo**: Passes the notification directly to `expo.send()` with all fields mapped.
|
|
40
|
+
- For **FCM** with single token: Calls `fcm.send()` with notification mapped to `{ to, notification: { title, body }, data, priority }`.
|
|
41
|
+
- For **FCM** with multiple tokens: Calls `fcm.sendMulticast()` and returns an aggregated result with success count.
|
|
42
|
+
- FCM priority mapping: `'default'` is converted to `'normal'`; `'high'` and `'normal'` pass through.
|
|
43
|
+
|
|
44
|
+
## PushNotification Interface (index.ts)
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
interface PushNotification {
|
|
48
|
+
title?: string
|
|
49
|
+
body: string // required
|
|
50
|
+
data?: Record<string, any>
|
|
51
|
+
badge?: number
|
|
52
|
+
sound?: 'default' | null
|
|
53
|
+
priority?: 'high' | 'normal' | 'default'
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## PushResult Interface (from @stacksjs/types)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface PushResult {
|
|
61
|
+
success: boolean
|
|
62
|
+
provider: string // 'expo' | 'fcm'
|
|
63
|
+
message: string
|
|
64
|
+
messageId?: string
|
|
65
|
+
data?: Record<string, any>
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Types
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
type PushDriver = 'expo' | 'fcm'
|
|
73
|
+
|
|
74
|
+
interface SendOptions {
|
|
75
|
+
driver?: PushDriver // default: 'expo'
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Expo Push Driver (expo.ts)
|
|
80
|
+
|
|
81
|
+
### ExpoPushMessage
|
|
82
|
+
```typescript
|
|
83
|
+
interface ExpoPushMessage {
|
|
84
|
+
to: string | string[]
|
|
85
|
+
title?: string
|
|
86
|
+
body: string
|
|
87
|
+
data?: Record<string, any>
|
|
88
|
+
sound?: 'default' | null
|
|
89
|
+
badge?: number
|
|
90
|
+
channelId?: string // Android notification channel
|
|
91
|
+
priority?: 'default' | 'normal' | 'high'
|
|
92
|
+
ttl?: number // time-to-live in seconds
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### ExpoPushTicket
|
|
97
|
+
```typescript
|
|
98
|
+
interface ExpoPushTicket {
|
|
99
|
+
id?: string
|
|
100
|
+
status: 'ok' | 'error'
|
|
101
|
+
message?: string
|
|
102
|
+
details?: {
|
|
103
|
+
error?: 'DeviceNotRegistered' | 'MessageTooBig' | 'MessageRateExceeded' | 'InvalidCredentials'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### ExpoPushReceipt
|
|
109
|
+
```typescript
|
|
110
|
+
interface ExpoPushReceipt {
|
|
111
|
+
status: 'ok' | 'error'
|
|
112
|
+
message?: string
|
|
113
|
+
details?: { error?: string }
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Functions
|
|
118
|
+
|
|
119
|
+
#### send()
|
|
120
|
+
```typescript
|
|
121
|
+
async function send(message: ExpoPushMessage): Promise<PushResult>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- Endpoint: `https://exp.host/--/api/v2/push/send`
|
|
125
|
+
- Validates tokens with `isExpoPushToken()` before sending
|
|
126
|
+
- Filters out invalid tokens and warns via `log.warn()`
|
|
127
|
+
- Returns early with failure if no valid tokens remain
|
|
128
|
+
- Creates one payload entry per token (fans out `to` array into individual messages)
|
|
129
|
+
- Default `sound: 'default'`, default `priority: 'high'`
|
|
130
|
+
- Returns `PushResult` with `data: { tickets }` containing the full ticket array
|
|
131
|
+
- `success` is `true` only if zero tickets have `status: 'error'`
|
|
132
|
+
- `messageId` is the first ticket's `id` (if any)
|
|
133
|
+
|
|
134
|
+
#### isExpoPushToken()
|
|
135
|
+
```typescript
|
|
136
|
+
function isExpoPushToken(token: string): boolean
|
|
137
|
+
```
|
|
138
|
+
Validates token format: matches `Expo(nent)?PushToken[...]` pattern OR alphanumeric with hyphens/underscores.
|
|
139
|
+
|
|
140
|
+
#### getReceipts()
|
|
141
|
+
```typescript
|
|
142
|
+
async function getReceipts(ticketIds: string[]): Promise<Record<string, ExpoPushReceipt>>
|
|
143
|
+
```
|
|
144
|
+
Endpoint: `https://exp.host/--/api/v2/push/getReceipts`. Checks delivery status for previously sent notifications.
|
|
145
|
+
|
|
146
|
+
#### sendBatch()
|
|
147
|
+
```typescript
|
|
148
|
+
async function sendBatch(messages: ExpoPushMessage[], chunkSize?: number): Promise<PushResult[]>
|
|
149
|
+
```
|
|
150
|
+
Sends multiple messages in chunks (default chunk size: 100, per Expo's recommendation). Processes chunks sequentially, messages within each chunk in parallel via `Promise.all`.
|
|
151
|
+
|
|
152
|
+
### Exports
|
|
153
|
+
- `send`, `sendBatch`, `getReceipts`, `isExpoPushToken` functions
|
|
154
|
+
- `Send` alias for `send`
|
|
155
|
+
|
|
156
|
+
## FCM Driver (fcm.ts)
|
|
157
|
+
|
|
158
|
+
Supports two APIs:
|
|
159
|
+
1. **Legacy API** (`https://fcm.googleapis.com/fcm/send`) -- uses server key
|
|
160
|
+
2. **v1 API** (`https://fcm.googleapis.com/v1/projects/{projectId}/messages:send`) -- uses service account with OAuth2 JWT
|
|
161
|
+
|
|
162
|
+
### FCMConfig
|
|
163
|
+
```typescript
|
|
164
|
+
interface FCMConfig {
|
|
165
|
+
serverKey?: string // for legacy API
|
|
166
|
+
projectId?: string // for v1 API
|
|
167
|
+
serviceAccount?: {
|
|
168
|
+
clientEmail: string
|
|
169
|
+
privateKey: string // PEM format
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### FCMMessage
|
|
175
|
+
```typescript
|
|
176
|
+
interface FCMMessage {
|
|
177
|
+
to?: string // single device token
|
|
178
|
+
registrationIds?: string[] // multiple device tokens (legacy only)
|
|
179
|
+
topic?: string // topic name (auto-prefixed with /topics/)
|
|
180
|
+
condition?: string // topic condition expression
|
|
181
|
+
notification?: {
|
|
182
|
+
title: string
|
|
183
|
+
body: string
|
|
184
|
+
icon?: string
|
|
185
|
+
image?: string
|
|
186
|
+
sound?: string
|
|
187
|
+
badge?: string
|
|
188
|
+
clickAction?: string
|
|
189
|
+
tag?: string
|
|
190
|
+
}
|
|
191
|
+
data?: Record<string, string> // NOTE: values must be strings for FCM
|
|
192
|
+
priority?: 'high' | 'normal'
|
|
193
|
+
ttl?: number
|
|
194
|
+
collapseKey?: string
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### FCMResponse (legacy API)
|
|
199
|
+
```typescript
|
|
200
|
+
interface FCMResponse {
|
|
201
|
+
multicastId?: number
|
|
202
|
+
success: number
|
|
203
|
+
failure: number
|
|
204
|
+
results?: Array<{
|
|
205
|
+
messageId?: string
|
|
206
|
+
error?: string
|
|
207
|
+
}>
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Functions
|
|
212
|
+
|
|
213
|
+
#### configure()
|
|
214
|
+
```typescript
|
|
215
|
+
function configure(options: FCMConfig): void
|
|
216
|
+
```
|
|
217
|
+
Sets module-level config. Called by `configureFCM()` from the main index.
|
|
218
|
+
|
|
219
|
+
#### send()
|
|
220
|
+
```typescript
|
|
221
|
+
async function send(message: FCMMessage): Promise<PushResult>
|
|
222
|
+
```
|
|
223
|
+
If `serviceAccount` and `projectId` are configured, uses the v1 API. Otherwise falls back to `sendLegacy()`.
|
|
224
|
+
|
|
225
|
+
**v1 API flow:**
|
|
226
|
+
1. Generates JWT with RS256 signing using the service account private key
|
|
227
|
+
2. Exchanges JWT for OAuth2 access token at `https://oauth2.googleapis.com/token`
|
|
228
|
+
3. Sends to `https://fcm.googleapis.com/v1/projects/{projectId}/messages:send`
|
|
229
|
+
4. Includes Android priority settings and APNS headers (`apns-priority: 10` for high, `5` for normal)
|
|
230
|
+
5. Supports `to` (token), `topic`, or `condition` targeting
|
|
231
|
+
|
|
232
|
+
**JWT generation details:**
|
|
233
|
+
- Scope: `https://www.googleapis.com/auth/firebase.messaging`
|
|
234
|
+
- Algorithm: RS256 (RSASSA-PKCS1-v1_5 with SHA-256)
|
|
235
|
+
- Expiry: 1 hour
|
|
236
|
+
- Key imported via `crypto.subtle.importKey('pkcs8', ...)`
|
|
237
|
+
|
|
238
|
+
#### sendLegacy()
|
|
239
|
+
```typescript
|
|
240
|
+
async function sendLegacy(message: FCMMessage): Promise<PushResult>
|
|
241
|
+
```
|
|
242
|
+
Uses the legacy API with `Authorization: key=<serverKey>`. Supports:
|
|
243
|
+
- Single device via `to`
|
|
244
|
+
- Multiple devices via `registration_ids`
|
|
245
|
+
- Topics via `/topics/<topic>`
|
|
246
|
+
- Conditions via `condition`
|
|
247
|
+
|
|
248
|
+
#### sendMulticast()
|
|
249
|
+
```typescript
|
|
250
|
+
async function sendMulticast(
|
|
251
|
+
tokens: string[],
|
|
252
|
+
message: Omit<FCMMessage, 'to' | 'registrationIds'>,
|
|
253
|
+
): Promise<PushResult[]>
|
|
254
|
+
```
|
|
255
|
+
- v1 API: sends individually to each token via `Promise.all` (v1 doesn't support multicast natively)
|
|
256
|
+
- Legacy API: uses `registrationIds` field for native multicast in a single request
|
|
257
|
+
|
|
258
|
+
#### sendToTopic()
|
|
259
|
+
```typescript
|
|
260
|
+
async function sendToTopic(
|
|
261
|
+
topic: string,
|
|
262
|
+
message: Omit<FCMMessage, 'to' | 'registrationIds' | 'topic'>,
|
|
263
|
+
): Promise<PushResult>
|
|
264
|
+
```
|
|
265
|
+
Convenience wrapper that sets the `topic` field and calls `send()`.
|
|
266
|
+
|
|
267
|
+
#### subscribeToTopic()
|
|
268
|
+
```typescript
|
|
269
|
+
async function subscribeToTopic(tokens: string[], topic: string): Promise<boolean>
|
|
270
|
+
```
|
|
271
|
+
Uses `https://iid.googleapis.com/iid/v1:batchAdd` with legacy server key auth. Subscribes device tokens to a topic.
|
|
272
|
+
|
|
273
|
+
#### unsubscribeFromTopic()
|
|
274
|
+
```typescript
|
|
275
|
+
async function unsubscribeFromTopic(tokens: string[], topic: string): Promise<boolean>
|
|
276
|
+
```
|
|
277
|
+
Uses `https://iid.googleapis.com/iid/v1:batchRemove` with legacy server key auth.
|
|
278
|
+
|
|
279
|
+
### Exports
|
|
280
|
+
- `send`, `sendLegacy`, `sendMulticast`, `sendToTopic`, `subscribeToTopic`, `unsubscribeFromTopic`, `configure` functions
|
|
281
|
+
- `Send` alias for `send`
|
|
282
|
+
|
|
283
|
+
## Configuration
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { configureFCM } from '@stacksjs/push'
|
|
287
|
+
|
|
288
|
+
// FCM v1 API (recommended)
|
|
289
|
+
configureFCM({
|
|
290
|
+
projectId: 'my-firebase-project',
|
|
291
|
+
serviceAccount: {
|
|
292
|
+
clientEmail: 'firebase@project.iam.gserviceaccount.com',
|
|
293
|
+
privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----'
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
// FCM Legacy API (simpler setup)
|
|
298
|
+
configureFCM({
|
|
299
|
+
serverKey: 'your-fcm-server-key'
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
// Expo requires no configuration
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Usage Examples
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import { send } from '@stacksjs/push'
|
|
309
|
+
|
|
310
|
+
// Expo (default driver)
|
|
311
|
+
await send('ExponentPushToken[xxxxxx]', {
|
|
312
|
+
title: 'New Order',
|
|
313
|
+
body: 'You have a new order #1234',
|
|
314
|
+
data: { orderId: 1234 },
|
|
315
|
+
badge: 1,
|
|
316
|
+
sound: 'default',
|
|
317
|
+
priority: 'high',
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
// Multiple Expo tokens
|
|
321
|
+
await send(
|
|
322
|
+
['ExponentPushToken[aaa]', 'ExponentPushToken[bbb]'],
|
|
323
|
+
{ title: 'Announcement', body: 'Maintenance at 3pm' },
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
// FCM single device
|
|
327
|
+
await send('fcm-device-token', notification, { driver: 'fcm' })
|
|
328
|
+
|
|
329
|
+
// FCM multiple devices
|
|
330
|
+
await send(
|
|
331
|
+
['token1', 'token2', 'token3'],
|
|
332
|
+
{ title: 'Alert', body: 'New message' },
|
|
333
|
+
{ driver: 'fcm' },
|
|
334
|
+
)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Direct driver access
|
|
338
|
+
```typescript
|
|
339
|
+
import { expo, fcm } from '@stacksjs/push'
|
|
340
|
+
|
|
341
|
+
// Expo batch
|
|
342
|
+
await expo.sendBatch(messages, 100)
|
|
343
|
+
|
|
344
|
+
// Expo receipt check
|
|
345
|
+
const receipts = await expo.getReceipts(['ticket-id-1', 'ticket-id-2'])
|
|
346
|
+
|
|
347
|
+
// FCM topic
|
|
348
|
+
await fcm.sendToTopic('news', { notification: { title: 'Breaking', body: '...' } })
|
|
349
|
+
|
|
350
|
+
// FCM topic subscription
|
|
351
|
+
await fcm.subscribeToTopic(['token1', 'token2'], 'news')
|
|
352
|
+
await fcm.unsubscribeFromTopic(['token1'], 'news')
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Dependencies
|
|
356
|
+
- `@stacksjs/types` -- `PushResult`, `PushMessage`, `PushTicket`
|
|
357
|
+
- `@stacksjs/cli` -- `log` for info/warn/error logging
|
|
358
|
+
- Web Crypto API -- used by FCM for JWT signing (`crypto.subtle`)
|
|
359
|
+
|
|
360
|
+
## Gotchas
|
|
361
|
+
- Default driver is `'expo'`, not FCM
|
|
362
|
+
- Expo token validation accepts both `ExponentPushToken[...]` format and plain alphanumeric strings
|
|
363
|
+
- Expo `send()` fans out array tokens into individual payload entries (one per token)
|
|
364
|
+
- Expo default `sound` is `'default'` and default `priority` is `'high'` (set in the driver, not the main send)
|
|
365
|
+
- FCM `data` values must be `Record<string, string>` (strings only, not arbitrary JSON)
|
|
366
|
+
- FCM `send()` auto-falls back to legacy API if no `serviceAccount`/`projectId` configured
|
|
367
|
+
- FCM v1 API does not support native multicast -- `sendMulticast()` sends individually via `Promise.all`
|
|
368
|
+
- FCM legacy API supports multicast via `registrationIds` in a single request
|
|
369
|
+
- FCM topic names are auto-prefixed with `/topics/` in the legacy API
|
|
370
|
+
- `subscribeToTopic()` and `unsubscribeFromTopic()` require the legacy `serverKey` (not service account)
|
|
371
|
+
- The main `send()` converts `priority: 'default'` to `'normal'` for FCM
|
|
372
|
+
- Expo `sendBatch()` processes chunks sequentially but messages within each chunk in parallel
|
|
373
|
+
- FCM v1 JWT tokens expire after 1 hour -- a new token is generated for each `send()` call (no caching)
|
|
374
|
+
- FCM v1 includes both Android and APNS-specific priority headers automatically
|
|
375
|
+
- `badge` only works on iOS -- Android uses notification channels (set via `channelId` on Expo)
|
|
376
|
+
- `sound: null` sends a silent notification
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-query-builder
|
|
3
|
+
description: Use when building database queries in a Stacks application — constructing SQL queries, using the fluent query API, or configuring the query builder. Covers @stacksjs/query-builder which wraps bun-query-builder, and config/query-builder.ts.
|
|
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 Query Builder
|
|
10
|
+
|
|
11
|
+
## Key Paths
|
|
12
|
+
- Core package: `storage/framework/core/query-builder/src/`
|
|
13
|
+
- Configuration: `config/query-builder.ts`
|
|
14
|
+
- QB state: `.qb/`
|
|
15
|
+
- External library: `bun-query-builder`
|
|
16
|
+
- Package: `@stacksjs/query-builder`
|
|
17
|
+
|
|
18
|
+
## API
|
|
19
|
+
|
|
20
|
+
The package re-exports everything from `bun-query-builder` plus a compatibility alias:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
export * from 'bun-query-builder'
|
|
24
|
+
export { createQueryBuilder as QueryBuilder } from 'bun-query-builder'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { db } from '@stacksjs/database'
|
|
31
|
+
|
|
32
|
+
// Select
|
|
33
|
+
const users = await db.selectFrom('users')
|
|
34
|
+
.where('active', '=', true)
|
|
35
|
+
.orderBy('name', 'asc')
|
|
36
|
+
.limit(10)
|
|
37
|
+
.get()
|
|
38
|
+
|
|
39
|
+
// Insert
|
|
40
|
+
await db.insertInto('users')
|
|
41
|
+
.values({ name: 'Alice', email: 'alice@example.com' })
|
|
42
|
+
.execute()
|
|
43
|
+
|
|
44
|
+
// Update
|
|
45
|
+
await db.update('users')
|
|
46
|
+
.set({ active: false })
|
|
47
|
+
.where('id', '=', 1)
|
|
48
|
+
.execute()
|
|
49
|
+
|
|
50
|
+
// Delete
|
|
51
|
+
await db.deleteFrom('users')
|
|
52
|
+
.where('id', '=', 1)
|
|
53
|
+
.execute()
|
|
54
|
+
|
|
55
|
+
// Joins
|
|
56
|
+
const posts = await db.selectFrom('posts')
|
|
57
|
+
.join('users', 'posts.user_id', '=', 'users.id')
|
|
58
|
+
.select(['posts.*', 'users.name as author_name'])
|
|
59
|
+
.get()
|
|
60
|
+
|
|
61
|
+
// Aggregates
|
|
62
|
+
const count = await db.selectFrom('users').count()
|
|
63
|
+
const total = await db.selectFrom('orders').sum('amount')
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Configuration (config/query-builder.ts)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
{
|
|
70
|
+
verbose: true,
|
|
71
|
+
dialect: env.DB_CONNECTION || 'sqlite',
|
|
72
|
+
database: { database, username?, password?, host?, port? },
|
|
73
|
+
|
|
74
|
+
timestamps: {
|
|
75
|
+
createdAt: 'created_at',
|
|
76
|
+
updatedAt: 'updated_at',
|
|
77
|
+
defaultOrderColumn: 'created_at',
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
pagination: {
|
|
81
|
+
defaultPerPage: 25,
|
|
82
|
+
cursorColumn: 'id',
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
aliasing: {
|
|
86
|
+
relationColumnAliasFormat: 'table_column',
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
relations: {
|
|
90
|
+
foreignKeyFormat: 'singularParent_id',
|
|
91
|
+
maxDepth: 10,
|
|
92
|
+
maxEagerLoad: 50,
|
|
93
|
+
detectCycles: true,
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
transactionDefaults: {
|
|
97
|
+
retries: 2,
|
|
98
|
+
isolation: 'read committed',
|
|
99
|
+
sqlStates: ['40001', '40P01'],
|
|
100
|
+
backoff: { baseMs: 50, factor: 2, maxMs: 2000, jitter: true },
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
sql: {
|
|
104
|
+
randomFunction: 'RANDOM()',
|
|
105
|
+
sharedLockSyntax: 'FOR SHARE',
|
|
106
|
+
jsonContainsMode: 'operator',
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
features: { distinctOn: true },
|
|
110
|
+
debug: { captureText: true },
|
|
111
|
+
|
|
112
|
+
softDeletes: {
|
|
113
|
+
enabled: false,
|
|
114
|
+
column: 'deleted_at',
|
|
115
|
+
defaultFilter: true,
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Gotchas
|
|
121
|
+
- **Thin wrapper** — re-exports `bun-query-builder` with no additions
|
|
122
|
+
- **One config file** — `config/query-builder.ts`. Its `dialect` and connection details are derived from `DB_CONNECTION` and the other `DB_*` env vars, so switching databases is an env change, not a config edit
|
|
123
|
+
- **`.qb/` directory** — stores query builder state for migration diffing
|
|
124
|
+
- **Prefer ORM models** — query builder is the low-level interface; use models for most operations
|
|
125
|
+
- **`db` proxy** — the `db` export from `@stacksjs/database` is a lazy proxy that auto-initializes the query builder on first access
|
|
126
|
+
- **Soft deletes disabled by default** — must be explicitly enabled in config
|
|
127
|
+
- **Transaction retries** — defaults to 2 retries with exponential backoff + jitter
|
|
128
|
+
- **Dialect detection** — reads `DB_CONNECTION` env var, falls back to `sqlite`
|