@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,576 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-storage
|
|
3
|
+
description: Use when working with file storage in Stacks — the Storage facade (put/get/delete/copy/move/list), StorageAdapter interface, local and S3 disk configurations, file uploads (UploadedFile class), file operations (read/write/copy/move/delete/hash/glob/zip), visibility management, checksums, MIME types, temporary URLs, or filesystem configuration. Covers @stacksjs/storage and config/filesystems.ts.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stacks Storage
|
|
10
|
+
|
|
11
|
+
File system abstraction with a Laravel-style Storage facade, local/S3 adapters, file upload handling, and low-level file utilities.
|
|
12
|
+
|
|
13
|
+
## Key Paths
|
|
14
|
+
- Core package: `storage/framework/core/storage/src/`
|
|
15
|
+
- Storage facade: `storage/framework/core/storage/src/facade.ts`
|
|
16
|
+
- Uploaded file: `storage/framework/core/storage/src/uploaded-file.ts`
|
|
17
|
+
- Types: `storage/framework/core/storage/src/types.ts`
|
|
18
|
+
- Filesystem config types: `storage/framework/core/storage/src/types/filesystem.ts`
|
|
19
|
+
- Local adapter: `storage/framework/core/storage/src/adapters/local.ts`
|
|
20
|
+
- S3 adapter: `storage/framework/core/storage/src/adapters/s3.ts`
|
|
21
|
+
- Memory adapter: `storage/framework/core/storage/src/adapters/memory.ts`
|
|
22
|
+
- Bun adapter: `storage/framework/core/storage/src/adapters/bun.ts`
|
|
23
|
+
- File utilities: `storage/framework/core/storage/src/files.ts`
|
|
24
|
+
- Folder utilities: `storage/framework/core/storage/src/folders.ts`
|
|
25
|
+
- Copy: `storage/framework/core/storage/src/copy.ts`
|
|
26
|
+
- Move: `storage/framework/core/storage/src/move.ts`
|
|
27
|
+
- Delete: `storage/framework/core/storage/src/delete.ts`
|
|
28
|
+
- Hash: `storage/framework/core/storage/src/hash.ts`
|
|
29
|
+
- Glob: `storage/framework/core/storage/src/glob.ts`
|
|
30
|
+
- Zip: `storage/framework/core/storage/src/zip.ts`
|
|
31
|
+
- Helpers: `storage/framework/core/storage/src/helpers.ts`
|
|
32
|
+
- Configuration: `config/filesystems.ts`
|
|
33
|
+
|
|
34
|
+
## Package Exports
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Storage facade (Laravel-style)
|
|
38
|
+
import { Storage, StorageManager } from '@stacksjs/storage'
|
|
39
|
+
|
|
40
|
+
// File uploads
|
|
41
|
+
import { UploadedFile, uploadedFile, uploadedFiles } from '@stacksjs/storage'
|
|
42
|
+
|
|
43
|
+
// Adapters
|
|
44
|
+
import { createLocalStorage, LocalStorageAdapter } from '@stacksjs/storage'
|
|
45
|
+
import { createS3Storage, S3StorageAdapter } from '@stacksjs/storage'
|
|
46
|
+
|
|
47
|
+
// Config helpers
|
|
48
|
+
import { localDisk, s3Disk, configFromEnv } from '@stacksjs/storage'
|
|
49
|
+
|
|
50
|
+
// Types
|
|
51
|
+
import type { StorageAdapter, FileContents, StatEntry, DirectoryEntry, DirectoryListing } from '@stacksjs/storage'
|
|
52
|
+
import type { ListOptions, PublicUrlOptions, TemporaryUrlOptions, ChecksumOptions, MimeTypeOptions } from '@stacksjs/storage'
|
|
53
|
+
import type { DiskConfig, FilesystemConfig, LocalDiskConfig, S3DiskConfig } from '@stacksjs/storage'
|
|
54
|
+
import { Visibility, createDirectoryListing, normalizeExpiryToMilliseconds, normalizeExpiryToDate, isFile, isDirectory } from '@stacksjs/storage'
|
|
55
|
+
|
|
56
|
+
// Low-level file operations
|
|
57
|
+
import { copy, copyFile, copyFolder } from '@stacksjs/storage'
|
|
58
|
+
import { move, rename } from '@stacksjs/storage'
|
|
59
|
+
import { del, deleteFile, deleteFolder, deleteEmptyFolder, deleteEmptyFolders, deleteGlob, isDirectoryEmpty } from '@stacksjs/storage'
|
|
60
|
+
import { readJsonFile, readPackageJson, readTextFile, writeFile, writeJsonFile, writeTextFile, put, get, getFiles, deleteFiles, hasFiles } from '@stacksjs/storage'
|
|
61
|
+
import { isFolder, isDir, doesFolderExist, createFolder, getFolders } from '@stacksjs/storage'
|
|
62
|
+
import { glob, globSync } from '@stacksjs/storage'
|
|
63
|
+
import { hashDirectory, hashPath, hashPaths } from '@stacksjs/storage'
|
|
64
|
+
import { zip, unzip, archive, unarchive, compress, decompress, gzipSync, gunzipSync, deflateSync, inflateSync } from '@stacksjs/storage'
|
|
65
|
+
import * as storage from '@stacksjs/storage'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Storage Facade (StorageManager)
|
|
69
|
+
|
|
70
|
+
The `Storage` singleton is a pre-instantiated `StorageManager`. It lazily builds its config from `@stacksjs/config` (which reads `config/filesystems.ts` and env vars).
|
|
71
|
+
|
|
72
|
+
### Basic Operations
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { Storage } from '@stacksjs/storage'
|
|
76
|
+
|
|
77
|
+
// Write a file (to default disk)
|
|
78
|
+
await Storage.put('file.txt', 'Hello World')
|
|
79
|
+
await Storage.put('data.bin', new Uint8Array([1, 2, 3]))
|
|
80
|
+
|
|
81
|
+
// Read a file as string
|
|
82
|
+
const content = await Storage.get('file.txt')
|
|
83
|
+
|
|
84
|
+
// Check existence
|
|
85
|
+
const exists = await Storage.exists('file.txt')
|
|
86
|
+
const missing = await Storage.missing('file.txt')
|
|
87
|
+
|
|
88
|
+
// Delete
|
|
89
|
+
await Storage.delete('file.txt')
|
|
90
|
+
|
|
91
|
+
// Copy and move
|
|
92
|
+
await Storage.copy('source.txt', 'dest.txt')
|
|
93
|
+
await Storage.move('old.txt', 'new.txt')
|
|
94
|
+
|
|
95
|
+
// File info
|
|
96
|
+
const size = await Storage.size('file.txt') // bytes
|
|
97
|
+
const modified = await Storage.lastModified('file.txt') // ms timestamp
|
|
98
|
+
const mime = await Storage.mimeType('file.txt')
|
|
99
|
+
const hash = await Storage.checksum('file.txt', 'sha256') // 'md5' | 'sha1' | 'sha256'
|
|
100
|
+
|
|
101
|
+
// Public URL
|
|
102
|
+
const url = await Storage.url('file.txt')
|
|
103
|
+
|
|
104
|
+
// Directories
|
|
105
|
+
await Storage.makeDirectory('uploads/images')
|
|
106
|
+
await Storage.deleteDirectory('uploads/old')
|
|
107
|
+
|
|
108
|
+
// List files (returns AsyncIterable<{ path, type }>)
|
|
109
|
+
for await (const entry of Storage.files('uploads/')) {
|
|
110
|
+
console.log(entry.path, entry.type) // 'file' or 'directory'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Recursive listing
|
|
114
|
+
for await (const entry of Storage.allFiles('uploads/')) {
|
|
115
|
+
console.log(entry.path)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Using Named Disks
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Use a specific disk
|
|
123
|
+
await Storage.disk('s3').write('uploads/file.txt', contents)
|
|
124
|
+
await Storage.disk('public').write('images/logo.png', imageData)
|
|
125
|
+
await Storage.disk('local').readToString('config.json')
|
|
126
|
+
|
|
127
|
+
// Disk instances are cached -- subsequent calls return the same adapter
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Configuring Disks
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Initialize with custom config (overrides env-based config)
|
|
134
|
+
Storage.init({
|
|
135
|
+
default: 's3',
|
|
136
|
+
disks: {
|
|
137
|
+
custom: { driver: 'local', root: '/custom/path' },
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Add/update a disk at runtime
|
|
142
|
+
Storage.configure('backups', { driver: 's3', bucket: 'my-backups', region: 'eu-west-1' })
|
|
143
|
+
|
|
144
|
+
// Change default disk
|
|
145
|
+
Storage.setDefaultDisk('s3')
|
|
146
|
+
|
|
147
|
+
// Query configuration
|
|
148
|
+
Storage.getDefaultDisk() // 'local'
|
|
149
|
+
Storage.getConfiguredDisks() // ['local', 'public', 's3']
|
|
150
|
+
Storage.getDiskConfig('s3') // S3DiskConfig object
|
|
151
|
+
|
|
152
|
+
// Reset all caches (useful for testing)
|
|
153
|
+
Storage.reset()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Built-in Disk Configurations
|
|
157
|
+
|
|
158
|
+
The facade auto-configures these disks from `config/filesystems.ts`:
|
|
159
|
+
|
|
160
|
+
- **`local`** -- `driver: 'local'`, root: `<project>/storage/app`, visibility: from config (default `'private'`)
|
|
161
|
+
- **`public`** -- `driver: 'local'`, root: `<project>/public`, url: `<appUrl>/storage`, visibility: `'public'`
|
|
162
|
+
- **`s3`** -- only added if `s3.bucket` is configured in filesystems config
|
|
163
|
+
|
|
164
|
+
## StorageAdapter Interface
|
|
165
|
+
|
|
166
|
+
All adapters implement this interface:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
interface StorageAdapter {
|
|
170
|
+
write(path: string, contents: FileContents): Promise<void>
|
|
171
|
+
read(path: string): Promise<FileContents>
|
|
172
|
+
readToString(path: string): Promise<string>
|
|
173
|
+
readToBuffer(path: string): Promise<Buffer>
|
|
174
|
+
readToUint8Array(path: string): Promise<Uint8Array>
|
|
175
|
+
deleteFile(path: string): Promise<void>
|
|
176
|
+
deleteDirectory(path: string): Promise<void>
|
|
177
|
+
createDirectory(path: string): Promise<void>
|
|
178
|
+
moveFile(from: string, to: string): Promise<void>
|
|
179
|
+
copyFile(from: string, to: string): Promise<void>
|
|
180
|
+
stat(path: string): Promise<StatEntry>
|
|
181
|
+
list(path: string, options?: ListOptions): DirectoryListing // AsyncIterable
|
|
182
|
+
changeVisibility(path: string, visibility: Visibility): Promise<void>
|
|
183
|
+
visibility(path: string): Promise<Visibility>
|
|
184
|
+
fileExists(path: string): Promise<boolean>
|
|
185
|
+
directoryExists(path: string): Promise<boolean>
|
|
186
|
+
publicUrl(path: string, options?: PublicUrlOptions): Promise<string>
|
|
187
|
+
temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>
|
|
188
|
+
checksum(path: string, options?: ChecksumOptions): Promise<string>
|
|
189
|
+
mimeType(path: string, options?: MimeTypeOptions): Promise<string>
|
|
190
|
+
lastModified(path: string): Promise<number>
|
|
191
|
+
fileSize(path: string): Promise<number>
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Local Adapter (`LocalStorageAdapter`)
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { createLocalStorage } from '@stacksjs/storage'
|
|
199
|
+
|
|
200
|
+
const local = createLocalStorage({ root: './storage' })
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- Uses Node.js `fs/promises` for file operations
|
|
204
|
+
- Path traversal protection: throws if resolved path escapes the root directory
|
|
205
|
+
- `write()` auto-creates parent directories with `mkdir({ recursive: true })`
|
|
206
|
+
- `moveFile()` uses `fs.rename()`; `copyFile()` uses `fs.copyFile()`
|
|
207
|
+
- `checksum()` uses `Bun.CryptoHasher` (default algorithm: `sha256`)
|
|
208
|
+
- `temporaryUrl()` generates HMAC-signed URLs using `APP_KEY` env var
|
|
209
|
+
- `changeVisibility()` is a no-op (Node.js doesn't map to public/private simply)
|
|
210
|
+
- `visibility()` always returns `'private'`
|
|
211
|
+
- MIME type detection is extension-based (supports txt, html, css, js, json, xml, pdf, zip, jpg, jpeg, png, gif, svg, mp4, mp3, wav)
|
|
212
|
+
- `list()` returns an async iterable; supports `deep: true` for recursive listing
|
|
213
|
+
|
|
214
|
+
### S3 Adapter (`S3StorageAdapter`)
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { createS3Storage, S3StorageAdapter } from '@stacksjs/storage'
|
|
218
|
+
import { S3Client } from '@stacksjs/ts-cloud'
|
|
219
|
+
|
|
220
|
+
const client = new S3Client('us-east-1')
|
|
221
|
+
const s3 = new S3StorageAdapter(client, { bucket: 'my-bucket', region: 'us-east-1', prefix: 'uploads/' })
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- Uses `@stacksjs/ts-cloud` S3Client for AWS operations
|
|
225
|
+
- Supports key prefix: all paths are prefixed with `config.prefix`
|
|
226
|
+
- `write()` auto-detects MIME type from extension and sets `contentType`
|
|
227
|
+
- `createDirectory()` is a no-op (S3 directories are implicit)
|
|
228
|
+
- `moveFile()` = `copyFile()` + `deleteFile()`
|
|
229
|
+
- `deleteDirectory()` lists all objects with prefix and deletes them in bulk
|
|
230
|
+
- `temporaryUrl()` uses `client.getSignedUrl()` for pre-signed URLs
|
|
231
|
+
- `checksum()` downloads the file content and hashes with `Bun.CryptoHasher`
|
|
232
|
+
- `publicUrl()` defaults to `https://<bucket>.s3.<region>.amazonaws.com/<key>`
|
|
233
|
+
- `list()` supports pagination via continuation tokens; `deep: true` uses `listAllObjects()`
|
|
234
|
+
- `fileExists()` uses `headObject()` and catches 404/NoSuchKey/NotFound errors
|
|
235
|
+
|
|
236
|
+
## File Uploads (UploadedFile)
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { UploadedFile, uploadedFile, uploadedFiles } from '@stacksjs/storage'
|
|
240
|
+
|
|
241
|
+
// Create from native File object
|
|
242
|
+
const file = uploadedFile(nativeFile)
|
|
243
|
+
const files = uploadedFiles([file1, file2])
|
|
244
|
+
|
|
245
|
+
// Properties
|
|
246
|
+
file.name // original filename
|
|
247
|
+
file.extension // lowercase extension without dot
|
|
248
|
+
file.mimeType // client MIME type
|
|
249
|
+
file.size // size in bytes
|
|
250
|
+
file.file // underlying File object
|
|
251
|
+
|
|
252
|
+
// Content access
|
|
253
|
+
const buffer = await file.arrayBuffer()
|
|
254
|
+
const bytes = await file.bytes() // Uint8Array
|
|
255
|
+
const text = await file.text()
|
|
256
|
+
|
|
257
|
+
// Validation
|
|
258
|
+
file.isValid() // size > 0
|
|
259
|
+
file.isImage() // MIME starts with 'image/'
|
|
260
|
+
file.isVideo() // MIME starts with 'video/'
|
|
261
|
+
file.isAudio() // MIME starts with 'audio/'
|
|
262
|
+
file.isPdf() // MIME is 'application/pdf'
|
|
263
|
+
file.isOneOf(['image/*', 'application/pdf']) // wildcard match
|
|
264
|
+
|
|
265
|
+
// Name helpers
|
|
266
|
+
file.hashName() // UUID-based hash filename: 'a1b2c3d4...f5.jpg'
|
|
267
|
+
file.hashName('png') // override extension: 'a1b2c3d4...f5.png'
|
|
268
|
+
file.getClientOriginalName() // filename without extension
|
|
269
|
+
file.getClientOriginalExtension() // same as .extension
|
|
270
|
+
file.getClientMimeType() // same as .mimeType
|
|
271
|
+
file.getSize() // human-readable: '1.50 MB'
|
|
272
|
+
|
|
273
|
+
// Store to disk
|
|
274
|
+
const path = await file.store('uploads/avatars') // default disk: 'local', hash name
|
|
275
|
+
const path = await file.store('uploads/', 's3') // specify disk
|
|
276
|
+
const path = await file.storeAs('uploads/', 'avatar.jpg') // specific name, 'local' disk
|
|
277
|
+
const path = await file.storeAs('uploads/', 'pic.jpg', 's3') // specific name, specific disk
|
|
278
|
+
const path = await file.storePublicly('images/') // stores to 'public' disk
|
|
279
|
+
const path = await file.storePubliclyAs('images/', 'logo.png')
|
|
280
|
+
|
|
281
|
+
// Move (write to new location)
|
|
282
|
+
const path = await file.move('new/path/file.jpg', 'local')
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
`store()` generates a hash-based filename using `crypto.randomUUID()`. `storeAs()` validates the filename: rejects path traversal (`..`), forward slashes, and backslashes.
|
|
286
|
+
|
|
287
|
+
## Low-Level File Operations
|
|
288
|
+
|
|
289
|
+
### Read/Write Files
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { readJsonFile, readTextFile, readPackageJson, writeFile, writeJsonFile, writeTextFile, put, get } from '@stacksjs/storage'
|
|
293
|
+
|
|
294
|
+
// JSON files (auto-detects indent and newline style)
|
|
295
|
+
const jsonFile = await readJsonFile('package.json')
|
|
296
|
+
// Returns { path, data (parsed), indent, newline }
|
|
297
|
+
await writeJsonFile(jsonFile) // preserves formatting
|
|
298
|
+
|
|
299
|
+
// Package.json helper
|
|
300
|
+
const pkg = await readPackageJson('package.json')
|
|
301
|
+
|
|
302
|
+
// Text files
|
|
303
|
+
const textFile = await readTextFile('config.txt')
|
|
304
|
+
// Returns { path, data (string) }
|
|
305
|
+
await writeTextFile({ path: 'out.txt', data: 'content' })
|
|
306
|
+
|
|
307
|
+
// Bun.write shorthand
|
|
308
|
+
const bytes = await writeFile('output.txt', 'data') // returns bytes written
|
|
309
|
+
|
|
310
|
+
// Simple put/get
|
|
311
|
+
put('file.txt', 'contents') // sync write, auto-creates dirs
|
|
312
|
+
const content = await get('file.txt') // async read via Bun.file().text()
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Copy
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { copy, copyFile, copyFolder } from '@stacksjs/storage'
|
|
319
|
+
|
|
320
|
+
copy('src.txt', 'dest.txt') // single file
|
|
321
|
+
copy(['a.txt', 'b.txt'], 'dest/') // multiple files
|
|
322
|
+
copy('srcDir/', 'destDir/') // directory (recursive)
|
|
323
|
+
copy('srcDir/', 'destDir/', ['node_modules']) // with exclusions
|
|
324
|
+
copyFile('src.txt', 'dest.txt') // single file only
|
|
325
|
+
copyFolder('srcDir/', 'destDir/', ['dist']) // directory with exclusions
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Move/Rename
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { move, rename } from '@stacksjs/storage'
|
|
332
|
+
|
|
333
|
+
const result = await move('old.txt', 'new.txt') // single file
|
|
334
|
+
const result = await move(['a.txt', 'b.txt'], 'dest/') // multiple files to directory
|
|
335
|
+
const result = await move('old.txt', 'new.txt', { overwrite: true })
|
|
336
|
+
|
|
337
|
+
const result = await rename('old.txt', 'new.txt')
|
|
338
|
+
// Returns Result<{ message: string }, Error>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
`move()` creates destination directories if needed. Without `overwrite: true`, throws if destination exists.
|
|
342
|
+
|
|
343
|
+
### Delete
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { del, deleteFile, deleteFolder, deleteEmptyFolder, deleteEmptyFolders, deleteGlob, isDirectoryEmpty } from '@stacksjs/storage'
|
|
347
|
+
|
|
348
|
+
await del('path') // auto-detects: file, folder, or glob
|
|
349
|
+
await deleteFile('file.txt') // file only
|
|
350
|
+
await deleteFolder('dir/') // recursive delete
|
|
351
|
+
await deleteEmptyFolder('dir/') // only if empty
|
|
352
|
+
await deleteEmptyFolders('parent/') // recursively delete all empty subdirs
|
|
353
|
+
await deleteGlob('dist/*') // delete matching directories
|
|
354
|
+
|
|
355
|
+
const result = await isDirectoryEmpty('dir/') // Result<boolean, Error>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
All delete functions return `Result<string, Error>` using `@stacksjs/error-handling`.
|
|
359
|
+
|
|
360
|
+
### Glob
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { glob, globSync } from '@stacksjs/storage'
|
|
364
|
+
|
|
365
|
+
const files = await glob('**/*.ts')
|
|
366
|
+
const files = await glob(['src/**/*.ts', 'tests/**/*.ts'])
|
|
367
|
+
const files = await glob('**/*.ts', {
|
|
368
|
+
cwd: '/project',
|
|
369
|
+
absolute: true,
|
|
370
|
+
dot: true, // include dotfiles
|
|
371
|
+
onlyFiles: true,
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
// Sync version
|
|
375
|
+
const files = globSync('**/*.ts', { cwd: '/project' })
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Uses `Bun.Glob` internally.
|
|
379
|
+
|
|
380
|
+
### Hash
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { hashDirectory, hashPath, hashPaths } from '@stacksjs/storage'
|
|
384
|
+
|
|
385
|
+
const hash = hashDirectory('src/') // SHA-256 of all files
|
|
386
|
+
const hash = hashPath('src/index.ts') // SHA-256 of single file or directory
|
|
387
|
+
const hash = hashPaths(['src/', 'tests/']) // combined SHA-256
|
|
388
|
+
const hash = hashPaths('src/index.ts') // accepts single string too
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
All use `createHash('sha256')` from Node.js crypto.
|
|
392
|
+
|
|
393
|
+
### Zip/Compression
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import { zip, unzip, archive, unarchive, compress, decompress } from '@stacksjs/storage'
|
|
397
|
+
import { gzipSync, gunzipSync, deflateSync, inflateSync } from '@stacksjs/storage'
|
|
398
|
+
|
|
399
|
+
// Shell-based zip (via `zip -r` command)
|
|
400
|
+
await zip('src/', 'archive.zip')
|
|
401
|
+
await zip(['file1.txt', 'file2.txt'], 'archive.zip')
|
|
402
|
+
await zip('src/', 'archive.zip', { cwd: '/project' })
|
|
403
|
+
await unzip('archive.zip')
|
|
404
|
+
await unzip(['a.zip', 'b.zip'])
|
|
405
|
+
|
|
406
|
+
// Aliases
|
|
407
|
+
archive('src/') // = zip
|
|
408
|
+
unarchive('a.zip') // = unzip
|
|
409
|
+
compress(['a', 'b']) // = zip
|
|
410
|
+
decompress('a.zip') // = unzip
|
|
411
|
+
|
|
412
|
+
// Bun-native compression (synchronous)
|
|
413
|
+
const compressed = gzipSync(data) // Bun.gzipSync
|
|
414
|
+
const decompressed = gunzipSync(compressed) // Bun.gunzipSync
|
|
415
|
+
const deflated = deflateSync(data) // Bun.deflateSync
|
|
416
|
+
const inflated = inflateSync(deflated) // Bun.inflateSync
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Folder Utilities
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import { isFolder, isDir, doesFolderExist, createFolder, getFolders } from '@stacksjs/storage'
|
|
423
|
+
|
|
424
|
+
isFolder('/path') // boolean, checks fs.statSync().isDirectory()
|
|
425
|
+
isDir('/path') // alias for isFolder
|
|
426
|
+
doesFolderExist('/path') // boolean, checks fs.existsSync()
|
|
427
|
+
await createFolder('dir/') // mkdirSync with recursive: true
|
|
428
|
+
const dirs = getFolders('parent/') // returns subdirectory names
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### File Query Utilities
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { doesExist, doesNotExist, hasFiles, getFiles, deleteFiles } from '@stacksjs/storage'
|
|
435
|
+
|
|
436
|
+
doesExist('/path') // file or folder exists
|
|
437
|
+
doesNotExist('/path') // neither file nor folder
|
|
438
|
+
hasFiles('dir/') // directory has any entries
|
|
439
|
+
const files = getFiles('dir/') // all files recursively
|
|
440
|
+
const files = getFiles('dir/', ['node_modules']) // with exclusions
|
|
441
|
+
deleteFiles('dir/', ['keep.txt']) // delete all except exclusions
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Config File Helper
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { updateConfigFile } from '@stacksjs/storage'
|
|
448
|
+
|
|
449
|
+
await updateConfigFile('config.json', { key: 'newValue' })
|
|
450
|
+
// Reads, merges, writes JSON config file
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Types
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
type FileContents = string | Buffer | Uint8Array | ReadableStream
|
|
457
|
+
|
|
458
|
+
enum Visibility {
|
|
459
|
+
PUBLIC = 'public',
|
|
460
|
+
PRIVATE = 'private',
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
interface StatEntry {
|
|
464
|
+
path: string
|
|
465
|
+
type: 'file' | 'directory'
|
|
466
|
+
visibility: Visibility
|
|
467
|
+
size: number
|
|
468
|
+
lastModified: number // milliseconds
|
|
469
|
+
mimeType?: string
|
|
470
|
+
metadata?: Record<string, any>
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
interface DirectoryEntry {
|
|
474
|
+
path: string
|
|
475
|
+
type: 'file' | 'directory'
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
interface DirectoryListing extends AsyncIterable<DirectoryEntry> {}
|
|
479
|
+
|
|
480
|
+
interface ListOptions {
|
|
481
|
+
deep?: boolean
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
interface TemporaryUrlOptions {
|
|
485
|
+
expiresIn: number | Date // seconds or Date object
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
interface ChecksumOptions {
|
|
489
|
+
algorithm?: 'md5' | 'sha1' | 'sha256'
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Filesystem config types
|
|
493
|
+
type FilesystemDriver = 'local' | 's3'
|
|
494
|
+
type DiskConfig = LocalDiskConfig | S3DiskConfig
|
|
495
|
+
|
|
496
|
+
interface LocalDiskConfig {
|
|
497
|
+
driver: 'local'
|
|
498
|
+
root: string
|
|
499
|
+
url?: string
|
|
500
|
+
visibility?: 'public' | 'private'
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
interface S3DiskConfig {
|
|
504
|
+
driver: 's3'
|
|
505
|
+
bucket: string
|
|
506
|
+
region?: string
|
|
507
|
+
prefix?: string
|
|
508
|
+
endpoint?: string
|
|
509
|
+
usePathStyleEndpoint?: boolean
|
|
510
|
+
url?: string
|
|
511
|
+
credentials?: { key: string; secret: string }
|
|
512
|
+
visibility?: 'public' | 'private'
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
interface FilesystemConfig {
|
|
516
|
+
default: string
|
|
517
|
+
disks: Record<string, DiskConfig>
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Config Helpers
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
import { localDisk, s3Disk, configFromEnv } from '@stacksjs/storage'
|
|
525
|
+
|
|
526
|
+
// Create a local disk config
|
|
527
|
+
const disk = localDisk('/storage/app', { visibility: 'public', url: '/storage' })
|
|
528
|
+
|
|
529
|
+
// Create an S3 disk config (region defaults to AWS_DEFAULT_REGION env or 'us-east-1')
|
|
530
|
+
const disk = s3Disk('my-bucket', { region: 'eu-west-1', prefix: 'uploads/' })
|
|
531
|
+
|
|
532
|
+
// Build config from environment variables
|
|
533
|
+
// Reads: FILESYSTEM_DISK, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, AWS_BUCKET, AWS_ENDPOINT, AWS_URL, AWS_USE_PATH_STYLE_ENDPOINT
|
|
534
|
+
const config = configFromEnv({ default: 'local' })
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## config/filesystems.ts
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
{
|
|
541
|
+
driver: (env.STORAGE_DRIVER || 'bun') as any, // 'local' | 'bun' | 's3' | 'memory'
|
|
542
|
+
root: env.STORAGE_ROOT || process.cwd(),
|
|
543
|
+
s3: {
|
|
544
|
+
bucket: env.AWS_S3_BUCKET || '',
|
|
545
|
+
region: env.AWS_REGION || 'us-east-1',
|
|
546
|
+
prefix: env.AWS_S3_PREFIX || '',
|
|
547
|
+
credentials: env.AWS_ACCESS_KEY_ID && env.AWS_SECRET_ACCESS_KEY
|
|
548
|
+
? { accessKeyId: env.AWS_ACCESS_KEY_ID, secretAccessKey: env.AWS_SECRET_ACCESS_KEY }
|
|
549
|
+
: undefined,
|
|
550
|
+
},
|
|
551
|
+
publicUrl: {
|
|
552
|
+
domain: env.STORAGE_PUBLIC_URL || env.APP_URL || 'http://localhost',
|
|
553
|
+
},
|
|
554
|
+
defaultVisibility: 'private',
|
|
555
|
+
} satisfies FilesystemsConfig
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## Gotchas
|
|
559
|
+
- Default driver is `'bun'` (not `'local'`), but the Storage facade only supports `'local'` and `'s3'` drivers -- the `buildConfig()` in the facade maps the filesystems config to `'local'` as the default disk driver
|
|
560
|
+
- The `local` disk root is `<project>/storage/app`, not the project root
|
|
561
|
+
- The `public` disk root is `<project>/public` with URL prefix `<appUrl>/storage`
|
|
562
|
+
- S3 disk is only added if `s3.bucket` is configured
|
|
563
|
+
- `list()` returns an `AsyncIterable` -- use `for await` to iterate
|
|
564
|
+
- `checksum()` defaults to `sha256` in both adapters (not `md5`)
|
|
565
|
+
- `temporaryUrl()` on local adapter generates HMAC-signed URLs using `APP_KEY` env var
|
|
566
|
+
- `temporaryUrl()` on S3 adapter uses pre-signed URLs via `getSignedUrl()`
|
|
567
|
+
- `LocalStorageAdapter` prevents path traversal -- throws if resolved path escapes the root
|
|
568
|
+
- `S3StorageAdapter` requires a bucket name -- throws on construction if missing
|
|
569
|
+
- `changeVisibility()` is a no-op on both local and S3 adapters
|
|
570
|
+
- `visibility()` always returns `'private'` on both adapters
|
|
571
|
+
- The `Storage` singleton is pre-instantiated -- use `Storage.reset()` to clear caches for testing
|
|
572
|
+
- `UploadedFile.storeAs()` rejects filenames containing `..`, `/`, or `\` to prevent path traversal
|
|
573
|
+
- `UploadedFile.hashName()` is cached -- calling it multiple times returns the same hash
|
|
574
|
+
- Low-level operations (`copy`, `move`, `delete*`, etc.) use synchronous `fs` methods, while the Storage facade uses async operations
|
|
575
|
+
- `zip()` and `unzip()` shell out to the `zip`/`unzip` commands -- they require these tools to be installed on the system
|
|
576
|
+
- `STORAGE_DRIVER` env var controls the driver selection in `config/filesystems.ts`
|