@stacksjs/defaults 0.75.467 → 0.75.473

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.
Files changed (33) hide show
  1. package/ai/skills/stacks-actions/SKILL.md +1 -1
  2. package/ai/skills/stacks-mobile/SKILL.md +15 -2
  3. package/ai/skills/stacks-native/CATALOG.md +15 -15
  4. package/ai/skills/stacks-storage/UPLOADS-AND-DISKS.md +19 -0
  5. package/app/Actions/Dashboard/Content/FileDestroyAction.ts +3 -4
  6. package/app/Actions/Dashboard/Content/FileDirectoryStoreAction.ts +3 -4
  7. package/app/Actions/Dashboard/Content/FileDuplicateAction.ts +3 -4
  8. package/app/Actions/Dashboard/Content/FileFavoriteAction.ts +3 -4
  9. package/app/Actions/Dashboard/Content/FileIndexAction.ts +3 -5
  10. package/app/Actions/Dashboard/Content/FileRenameAction.ts +3 -4
  11. package/app/Actions/Dashboard/Content/FileReprocessAction.ts +3 -4
  12. package/app/Actions/Dashboard/Content/FileTagsAction.ts +3 -4
  13. package/app/Actions/Dashboard/Content/FileUploadAction.ts +3 -4
  14. package/app/Actions/Dashboard/Content/FileVisibilityAction.ts +3 -4
  15. package/app/Actions/Dashboard/Content/file-response.test.ts +61 -0
  16. package/app/Actions/Dashboard/Content/file-response.ts +18 -0
  17. package/app/Actions/Dashboard/dashboard-response.ts +20 -0
  18. package/functions/dashboard-badge.test.ts +42 -0
  19. package/functions/dashboard-badge.ts +104 -0
  20. package/functions/dashboard-button.test.ts +33 -0
  21. package/functions/dashboard-button.ts +44 -0
  22. package/ide/vscode/package.json +1 -1
  23. package/package.json +4 -4
  24. package/resources/components/Dashboard/Analytics/AnalyticsHub.stx +10 -1
  25. package/resources/components/Dashboard/Data/DataRecords.stx +1 -1
  26. package/resources/components/Dashboard/Email/InboxMessageList.stx +1 -1
  27. package/resources/components/Dashboard/MobileSidebar.stx +4 -1
  28. package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
  29. package/resources/components/Dashboard/UI/Badge.stx +21 -44
  30. package/resources/components/Dashboard/UI/Button.stx +13 -24
  31. package/resources/components/Dashboard/UI/Card.stx +8 -0
  32. package/resources/functions/dashboard/sidebar.ts +12 -3
  33. package/views/dashboard/layouts/default.stx +43 -18
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: stacks-actions
3
- description: Use when working with Stacks server actions - creating actions in app/Actions/, auto-generated API actions from the useApi model trait, the 675 default framework actions (auth, dashboard, commerce, content, deployment, jobs), action request/response handling, or action registration. Covers @stacksjs/actions and storage/framework/defaults/app/Actions/.
3
+ description: Use when working with Stacks server actions - creating actions in app/Actions/, auto-generated API actions from the useApi model trait, the 676 default framework actions (auth, dashboard, commerce, content, deployment, jobs), action request/response handling, or action registration. Covers @stacksjs/actions and storage/framework/defaults/app/Actions/.
4
4
  license: MIT
5
5
  compatibility: Bun >= 1.3.0, TypeScript
6
6
  allowed-tools: Read Edit Write Bash Grep Glob
@@ -177,8 +177,10 @@ browser with an explicit return path.
177
177
 
178
178
  A named route parameter may need to exclude website paths. Configure
179
179
  `nativeScreenConstraints` on both platforms using the exact route key, for
180
- example `{ '/:username': { username: { pattern: '[a-z]+', exclude: ['terms'], ignoreCase: true } } }`.
181
- Patterns match the complete decoded parameter; use syntax shared by JavaScript,
180
+ example `{ '/:username': { username: { pattern: '[a-z]+', exclude: ['terms'], normalize: 'lowercase' } } }`.
181
+ Normalization lowercases decoded parameters and excluded values independently
182
+ of device locale; the match returns the normalized parameter. Patterns then
183
+ match the complete value case-sensitively; use syntax shared by JavaScript,
182
184
  Foundation and Kotlin. Constraints apply before query items and preserve static
183
185
  route priority. The build requires Craft's `nativeRouteConstraints` capability
184
186
  and stops if an installed host would ignore the configuration. For application
@@ -292,6 +294,17 @@ appears; `buddy release:notes <tag> --testflight` does it later, and
292
294
  `buddy release:notes <tag> --play` prints the 500-character Google Play text.
293
295
  The code is `@stacksjs/actions/release-notes`.
294
296
 
297
+ Set `ios.testFlightGroups` to the names of existing internal groups (for example,
298
+ `['Internal testers']`) to distribute processed builds to those groups.
299
+ `release:ios --wait` confirms `IN_BETA_TESTING` and the configured memberships
300
+ before retiring older builds; upload or `VALID` processing alone is insufficient.
301
+ Without a wait, older builds stay available. Resume a tagged release with
302
+ `buddy release:testflight <tag> --wait [minutes]`, preserving its existing
303
+ localized notes; `--dry-run` validates the
304
+ existing internal groups without assigning or expiring builds. This creates no
305
+ testers, public links or external beta review submissions. `--keep-builds` retains
306
+ that many older processed builds in addition to the newly available release.
307
+
295
308
  ## Device search index
296
309
 
297
310
  `spotlight` says what of the application's own content a device may index, so
@@ -11,7 +11,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
11
11
  | Package | Task guidance | Source entry | Retained tests |
12
12
  |---|---|---|---|
13
13
  | `@stacksjs/action-runner` | [`stacks-actions`](../stacks-actions/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/action-runner/src/index.ts) | 1 |
14
- | `@stacksjs/actions` | [`stacks-actions`](../stacks-actions/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/index.ts) | 101 |
14
+ | `@stacksjs/actions` | [`stacks-actions`](../stacks-actions/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/index.ts) | 102 |
15
15
  | `@stacksjs/ai` | [`stacks-ai`](../stacks-ai/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ai/src/index.ts) | 21 |
16
16
  | `@stacksjs/alias` | [`stacks-alias`](../stacks-alias/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/alias/src/index.ts) | 3 |
17
17
  | `@stacksjs/analytics` | [`stacks-analytics`](../stacks-analytics/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/analytics/src/index.ts) | 7 |
@@ -31,11 +31,11 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
31
31
  | `@stacksjs/cloud` | [`stacks-cloud`](../stacks-cloud/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cloud/src/index.ts) | 10 |
32
32
  | `@stacksjs/cms` | [`stacks-cms`](../stacks-cms/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cms/src/index.ts) | 5 |
33
33
  | `@stacksjs/collections` | [`stacks-collections`](../stacks-collections/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/collections/src/index.ts) | 2 |
34
- | `@stacksjs/commerce` | [`stacks-commerce`](../stacks-commerce/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/index.ts) | 14 |
34
+ | `@stacksjs/commerce` | [`stacks-commerce`](../stacks-commerce/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/index.ts) | 15 |
35
35
  | `@stacksjs/composables` | [`stacks-composables`](../stacks-composables/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/composables/src/index.ts) | 18 |
36
36
  | `@stacksjs/config` | [`stacks-config`](../stacks-config/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/config/src/index.ts) | 16 |
37
37
  | `@stacksjs/cron` | [`stacks-cron`](../stacks-cron/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cron/src/index.ts) | 6 |
38
- | `@stacksjs/database` | [`stacks-database`](../stacks-database/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index.ts) | 139 |
38
+ | `@stacksjs/database` | [`stacks-database`](../stacks-database/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index.ts) | 140 |
39
39
  | `@stacksjs/datetime` | [`stacks-datetime`](../stacks-datetime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts) | 22 |
40
40
  | `@stacksjs/defaults` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/defaults/package.json) | 2 |
41
41
  | `@stacksjs/deploy` | [`stacks-deploy`](../stacks-deploy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/deploy/package.json) | 1 |
@@ -43,7 +43,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
43
43
  | `@stacksjs/development` | [`stacks-development`](../stacks-development/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/development/package.json) | 1 |
44
44
  | `@stacksjs/dns` | [`stacks-dns`](../stacks-dns/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/dns/src/index.ts) | 4 |
45
45
  | `@stacksjs/docs` | [`stacks-docs`](../stacks-docs/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/docs/src/index.ts) | 2 |
46
- | `@stacksjs/email` | [`stacks-email`](../stacks-email/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/index.ts) | 23 |
46
+ | `@stacksjs/email` | [`stacks-email`](../stacks-email/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/index.ts) | 24 |
47
47
  | `@stacksjs/enums` | [`stacks-enums`](../stacks-enums/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/enums/src/index.ts) | 3 |
48
48
  | `@stacksjs/env` | [`stacks-env`](../stacks-env/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/index.ts) | 31 |
49
49
  | `@stacksjs/error-handling` | [`stacks-error-handling`](../stacks-error-handling/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/error-handling/src/index.ts) | 10 |
@@ -86,7 +86,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
86
86
  | `@stacksjs/slug` | [`stacks-slug`](../stacks-slug/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/slug/src/index.ts) | 5 |
87
87
  | `@stacksjs/sms` | [`stacks-sms`](../stacks-sms/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/index.ts) | 6 |
88
88
  | `@stacksjs/socials` | [`stacks-socials`](../stacks-socials/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/socials/src/index.ts) | 18 |
89
- | `@stacksjs/storage` | [`stacks-storage`](../stacks-storage/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/index.ts) | 49 |
89
+ | `@stacksjs/storage` | [`stacks-storage`](../stacks-storage/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/index.ts) | 50 |
90
90
  | `@stacksjs/strings` | [`stacks-strings`](../stacks-strings/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/strings/src/index.ts) | 11 |
91
91
  | `@stacksjs/testing` | [`stacks-testing`](../stacks-testing/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/index.ts) | 24 |
92
92
  | `@stacksjs/tinker` | [`stacks-repl`](../stacks-repl/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tinker/src/index.ts) | 1 |
@@ -115,11 +115,11 @@ Evidence directory: [1 retained tests](https://github.com/stacksjs/stacks/blob/m
115
115
 
116
116
  Guidance: [`stacks-actions`](../stacks-actions/SKILL.md). Source: [storage/framework/core/actions/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/index.ts).
117
117
 
118
- Declared runtime exports: `BlogAdminError`, `DEFAULT_IOS_DEPENDENCIES`, `DEFAULT_STX_LINT_CONFIG`, `IosToolchainError`, `PUBLICATION_CHECK_SCRIPT`, `XCODE_NOTE`, `assertPublicationAllowed`, `blogSlugify`, `craftSupportsWebkit`, `createComponent`, `createDatabase`, `createFactory`, `createFunction`, `createLanguage`, `createMail`, `createMiddleware`, `createMigration`, `createModel`, `createNotification`, `createPage`, `deleteBlogPost`, `describeDanglingRouteActions`, `describeGitHubRelease`, `discoverPackages`, `ensureDiscoveredPackages`, `findDanglingRouteActions`, `formatProject`, `getBlogPost`, `installStack`, `isDryRunActive`, `lintFix`, `lintProject`, `listBlogPosts`, `listStacks`, `loadIosDependencies`, `loadStxLintConfig`, `macosAgentActionFiles`, `macosAgentDevFiles`, `macosAgentDevManifest`, `macosAgentEntry`, `macosAgentRouteFiles`, `makeAction`, `makeCertificate`, `makeCommand`, `makeComponent`, `makeDatabase`, `makeFunction`, `makeJob`, `makeLanguage`, `makeMail`, `makeNotification`, `makePage`, `makePolicy`, `makeQueueTable`, `makeResource`, `makeStack`, `measureDefaultsDrift`, `pantryExecutable`, `pantryInstallArgs`, `parseFields`, `parseGitHubRemote`, `prepareMacosAgent`, `prepareMacosNative`, `publicationChannels`, `publishGitHubRelease`, `resolveStxSource`, `resolveXcodegen`, `runAdd`, `runCommit`, `runMake`, `runStxLint`, `saveBlogPost`, `scaffoldCrud`, `setDryRun`, `summarizeStructureChanges`, `uninstallStack`, `validateReleaseEvidencePlan`, `verifyReleaseEvidence`, `xcodeDeveloperDir`.
118
+ Declared runtime exports: `BlogAdminError`, `DEFAULT_IOS_DEPENDENCIES`, `DEFAULT_STX_LINT_CONFIG`, `IosToolchainError`, `PUBLICATION_CHECK_SCRIPT`, `XCODE_NOTE`, `assertPublicationAllowed`, `blogSlugify`, `craftSupportsWebkit`, `createComponent`, `createDatabase`, `createFactory`, `createFunction`, `createLanguage`, `createMail`, `createMiddleware`, `createMigration`, `createModel`, `createNotification`, `createPage`, `deleteBlogPost`, `describeDanglingRouteActions`, `describeGitHubRelease`, `discoverPackages`, `ensureDiscoveredPackages`, `evaluateReviewOsEvidence`, `findDanglingRouteActions`, `formatProject`, `getBlogPost`, `inspectReviewOsEvidence`, `installStack`, `isDryRunActive`, `lintFix`, `lintProject`, `listBlogPosts`, `listStacks`, `loadIosDependencies`, `loadStxLintConfig`, `macosAgentActionFiles`, `macosAgentDevFiles`, `macosAgentDevManifest`, `macosAgentEntry`, `macosAgentRouteFiles`, `makeAction`, `makeCertificate`, `makeCommand`, `makeComponent`, `makeDatabase`, `makeFunction`, `makeJob`, `makeLanguage`, `makeMail`, `makeNotification`, `makePage`, `makePolicy`, `makeQueueTable`, `makeResource`, `makeStack`, `measureDefaultsDrift`, `pantryExecutable`, `pantryInstallArgs`, `parseFields`, `parseGitHubRemote`, `prepareMacosAgent`, `prepareMacosNative`, `publicationChannels`, `publishGitHubRelease`, `resolveStxSource`, `resolveXcodegen`, `runAdd`, `runCommit`, `runMake`, `runStxLint`, `saveBlogPost`, `scaffoldCrud`, `setDryRun`, `summarizeStructureChanges`, `uninstallStack`, `validateReleaseEvidencePlan`, `verifyReleaseEvidence`, `xcodeDeveloperDir`.
119
119
 
120
- Local source modules: [src/action.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/action.ts), [src/add.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/add.ts), [src/blog-admin.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/blog-admin.ts), [src/build/ios-toolchain.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/build/ios-toolchain.ts), [src/build/macos-native.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/build/macos-native.ts), [src/commit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/commit.ts), [src/dev/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/dev/index.ts), [src/discover-packages.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/discover-packages.ts), [src/generate/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/generate/index.ts), [src/github-release.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/github-release.ts), [src/helpers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/helpers/index.ts), [src/library/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/library/index.ts), [src/lint/lint.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/lint/lint.ts), [src/lint/stx-gate.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/lint/stx-gate.ts), [src/make-command.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-command.ts), [src/make-job.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-job.ts), [src/make-policy.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-policy.ts), [src/make-resource.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-resource.ts), [src/make.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make.ts), [src/publication-preflight.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/publication-preflight.ts), [src/release-evidence.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/release-evidence.ts), [src/scaffold-crud.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/scaffold-crud.ts), [src/setup/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/setup/index.ts), [src/stacks.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/stacks.ts), [src/stx-source.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/stx-source.ts), [src/upgrade/package-project.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/upgrade/package-project.ts), [src/upgrade/route-actions.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/upgrade/route-actions.ts).
120
+ Local source modules: [src/action.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/action.ts), [src/add.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/add.ts), [src/blog-admin.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/blog-admin.ts), [src/build/ios-toolchain.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/build/ios-toolchain.ts), [src/build/macos-native.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/build/macos-native.ts), [src/commit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/commit.ts), [src/dev/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/dev/index.ts), [src/discover-packages.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/discover-packages.ts), [src/generate/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/generate/index.ts), [src/github-release.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/github-release.ts), [src/helpers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/helpers/index.ts), [src/library/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/library/index.ts), [src/lint/lint.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/lint/lint.ts), [src/lint/stx-gate.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/lint/stx-gate.ts), [src/make-command.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-command.ts), [src/make-job.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-job.ts), [src/make-policy.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-policy.ts), [src/make-resource.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make-resource.ts), [src/make.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/make.ts), [src/publication-preflight.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/publication-preflight.ts), [src/release-evidence.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/release-evidence.ts), [src/reviewos-evidence.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/reviewos-evidence.ts), [src/scaffold-crud.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/scaffold-crud.ts), [src/setup/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/setup/index.ts), [src/stacks.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/stacks.ts), [src/stx-source.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/stx-source.ts), [src/upgrade/package-project.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/upgrade/package-project.ts), [src/upgrade/route-actions.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/src/upgrade/route-actions.ts).
121
121
 
122
- Evidence directory: [101 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
122
+ Evidence directory: [102 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/actions/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
123
123
 
124
124
  ### @stacksjs/ai
125
125
 
@@ -317,7 +317,7 @@ Declared runtime exports: `PriceFormatError`, `auctions`, `breakdownFor`, `carts
317
317
 
318
318
  Local source modules: [src/auctions/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/auctions/index.ts), [src/carts/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/carts/index.ts), [src/catalog.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/catalog.ts), [src/coupons/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/coupons/index.ts), [src/customers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/customers/index.ts), [src/devices/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/devices/index.ts), [src/errors/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/errors/index.ts), [src/gift-cards/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/gift-cards/index.ts), [src/imports/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/imports/index.ts), [src/inventory.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/inventory.ts), [src/money.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/money.ts), [src/orders/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/orders/index.ts), [src/payments/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/payments/index.ts), [src/products/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/products/index.ts), [src/receipt-text.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/receipt-text.ts), [src/receipts/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/receipts/index.ts), [src/register.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/register.ts), [src/releases.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/releases.ts), [src/sales-tax.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/sales-tax.ts), [src/shippings/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/shippings/index.ts), [src/tax-input.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/tax-input.ts), [src/tax/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/tax/index.ts), [src/waitlists/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/waitlists/index.ts), [src/waitlists/restaurant/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/waitlists/restaurant/index.ts).
319
319
 
320
- Evidence directory: [14 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
320
+ Evidence directory: [15 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
321
321
 
322
322
  ### @stacksjs/composables
323
323
 
@@ -357,7 +357,7 @@ Declared runtime exports: `Database`, `EntityQueryBuilder`, `__flushAfterCommitN
357
357
 
358
358
  Local source modules: [src/affected-rows.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/affected-rows.ts), [src/auth-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/auth-tables.ts), [src/custom/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/custom/index.ts), [src/database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/database.ts), [src/datetime-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/datetime-columns.ts), [src/ddl-constraints.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/ddl-constraints.ts), [src/defaults.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/defaults.ts), [src/dialect.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/dialect.ts), [src/driver-config.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/driver-config.ts), [src/drivers/dynamodb.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/drivers/dynamodb.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/drivers/index.ts), [src/ensure-database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/ensure-database.ts), [src/erd.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/erd.ts), [src/fk-audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/fk-audit.ts), [src/framework-renames.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/framework-renames.ts), [src/framework-route-admission.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/framework-route-admission.ts), [src/hand-written-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/hand-written-migrations.ts), [src/ignored-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/ignored-migrations.ts), [src/index-shape.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index-shape.ts), [src/managed-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/managed-columns.ts), [src/migration-dialect.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-dialect.ts), [src/migration-ledger.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-ledger.ts), [src/migration-names.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-names.ts), [src/migration-path.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-path.ts), [src/migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migrations.ts), [src/model-sources.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/model-sources.ts), [src/notification-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/notification-tables.ts), [src/package-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/package-migrations.ts), [src/package-models.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/package-models.ts), [src/query-logger.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/query-logger.ts), [src/rbac-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/rbac-tables.ts), [src/relation-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/relation-columns.ts), [src/replicas.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/replicas.ts), [src/row-lease.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/row-lease.ts), [src/row-lock.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/row-lock.ts), [src/safe-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/safe-migrations.ts), [src/schema-align.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/schema-align.ts), [src/schema-drift.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/schema-drift.ts), [src/seeder.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/seeder.ts), [src/shadowed-models.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/shadowed-models.ts), [src/sql-helpers.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/sql-helpers.ts), [src/trait-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/trait-tables.ts), [src/transaction-context.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/transaction-context.ts), [src/transaction-options.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/transaction-options.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/types.ts), [src/unique-audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/unique-audit.ts), [src/utc-defaults.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/utc-defaults.ts), [src/utils.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/utils.ts), [src/uuid-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/uuid-columns.ts), [src/vschema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/vschema.ts).
359
359
 
360
- Evidence directory: [139 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
360
+ Evidence directory: [140 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
361
361
 
362
362
  ### @stacksjs/datetime
363
363
 
@@ -431,7 +431,7 @@ Declared runtime exports: `discoverMailables`, `inlineCss`, `loadSampleProps`, `
431
431
 
432
432
  Local source modules: [src/css-inliner.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/css-inliner.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/drivers/index.ts), [src/email.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/email.ts), [src/idempotency.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/idempotency.ts), [src/inbound-parser.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/inbound-parser.ts), [src/inbox-mailbox.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/inbox-mailbox.ts), [src/mailable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/mailable.ts), [src/mime-preview.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/mime-preview.ts), [src/preview-ui.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/preview-ui.ts), [src/preview.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/preview.ts), [src/sdk/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/sdk/index.ts), [src/suppression.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/suppression.ts), [src/template.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/template.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/types.ts), [src/unsubscribe.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/unsubscribe.ts), [src/webhook-dedup.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/webhook-dedup.ts), [src/webhook-events.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/webhook-events.ts), [src/webhook-handlers.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/webhook-handlers.ts), [src/webhook-signatures.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/webhook-signatures.ts).
433
433
 
434
- Evidence directory: [23 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
434
+ Evidence directory: [24 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
435
435
 
436
436
  ### @stacksjs/enums
437
437
 
@@ -637,7 +637,7 @@ Evidence directory: [10 retained tests](https://github.com/stacksjs/stacks/blob/
637
637
 
638
638
  Guidance: [`stacks-orm`](../stacks-orm/SKILL.md). Source: [storage/framework/core/orm/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts).
639
639
 
640
- Declared runtime exports: `Activity`, `AnalyticsEvent`, `Auction`, `AuctionItem`, `Author`, `Automation`, `AutomationRun`, `BILLABLE_INSTANCE_METHODS`, `Bid`, `Board`, `BoardColumn`, `Campaign`, `CampaignSend`, `CampaignVariant`, `Card`, `CardAttachment`, `CardComment`, `Cart`, `CartItem`, `Categorizable`, `Category`, `Comment`, `CommunicationSuppression`, `ConsentEvent`, `Coupon`, `Courier`, `CourierPing`, `Customer`, `DeliveryRoute`, `DeliveryStop`, `Deployment`, `DigitalDelivery`, `EmailIdempotency`, `EmailList`, `EmailListSubscriber`, `EmailSuppression`, `EmailWebhookEvent`, `ErrorModel`, `FailedJob`, `FeedbackToken`, `Form`, `FormField`, `FormSubmission`, `GdprRequest`, `GiftCard`, `Job`, `Label`, `LicenseKey`, `Log`, `LoyaltyPoint`, `LoyaltyReward`, `MagicLinkToken`, `MailPreference`, `Manufacturer`, `Menu`, `MenuItem`, `Notification`, `NotificationDelivery`, `Order`, `OrderIdempotency`, `OrderItem`, `Page`, `PageRevision`, `Payment`, `PaymentMethod`, `PaymentProduct`, `PaymentTransaction`, `PaymentWebhookEvent`, `PersonalAccessToken`, `Pledge`, `Post`, `PrintDevice`, `Product`, `ProductUnit`, `ProductVariant`, `QueryLog`, `Receipt`, `Redirect`, `Referral`, `ReferralCode`, `Release`, `Request`, `Review`, `SenderDomain`, `ShippingMethod`, `ShippingRate`, `ShippingZone`, `Site`, `SiteDomain`, `SmsOptOut`, `SocialAccount`, `SocialPost`, `StorageItem`, `StorageItemTask`, `Subscriber`, `SubscriberEmail`, `Subscription`, `SubscriptionNotOwnedError`, `Tag`, `TaxRate`, `Team`, `TeamInvitation`, `TeamMember`, `Transaction`, `UsageEvent`, `User`, `WaitlistProduct`, `WaitlistRestaurant`, `Websocket`, `buildDatabaseSchema`, `collectEncryptedAttributes`, `createAuditMethods`, `createCategorizableMethods`, `createCommentableMethods`, `createLikeableMethods`, `createTaggableMethods`, `customerOwnership`, `decryptValue`, `encryptValue`, `enrichPaginatorUrls`, `filterFillable`, `frameworkModelFeature`, `getWritableFields`, `isBillable`, `isCursorPaginator`, `isEncrypted`, `isPaginator`, `isSimplePaginator`, `isUniqueViolation`, `mapWriteError`, `modelGlobalNames`, `ormReady`, `parentOwnership`, `parseCursor`, `renderDatabaseTypeFile`, `resolveCursorArgs`, `resolvePageArgs`, `selfOwnership`, `setAuditUser`, `siteOwnership`, `teamMembershipOwnership`, `toCursorPaginator`, `toPaginator`, `toSimplePaginator`, `toSnakeCaseKeys`.
640
+ Declared runtime exports: `Activity`, `AnalyticsEvent`, `Auction`, `AuctionItem`, `Author`, `Automation`, `AutomationRun`, `BILLABLE_INSTANCE_METHODS`, `Bid`, `Board`, `BoardColumn`, `Campaign`, `CampaignSend`, `CampaignVariant`, `Card`, `CardAttachment`, `CardComment`, `Cart`, `CartItem`, `Categorizable`, `Category`, `Comment`, `CommunicationSuppression`, `ConsentEvent`, `Coupon`, `Courier`, `CourierPing`, `Customer`, `DeliveryRoute`, `DeliveryStop`, `Deployment`, `DigitalDelivery`, `EmailIdempotency`, `EmailList`, `EmailListSubscriber`, `EmailSuppression`, `EmailWebhookEvent`, `ErrorModel`, `FailedJob`, `FeedbackToken`, `Form`, `FormField`, `FormSubmission`, `GdprRequest`, `GiftCard`, `Job`, `Label`, `LicenseKey`, `Log`, `LoyaltyPoint`, `LoyaltyReward`, `MagicLinkToken`, `MailPreference`, `Manufacturer`, `Menu`, `MenuItem`, `ModelUnavailableError`, `Notification`, `NotificationDelivery`, `Order`, `OrderIdempotency`, `OrderItem`, `Page`, `PageRevision`, `Payment`, `PaymentMethod`, `PaymentProduct`, `PaymentTransaction`, `PaymentWebhookEvent`, `PersonalAccessToken`, `Pledge`, `Post`, `PrintDevice`, `Product`, `ProductUnit`, `ProductVariant`, `QueryLog`, `Receipt`, `Redirect`, `Referral`, `ReferralCode`, `Release`, `Request`, `Review`, `SenderDomain`, `ShippingMethod`, `ShippingRate`, `ShippingZone`, `Site`, `SiteDomain`, `SmsOptOut`, `SocialAccount`, `SocialPost`, `StorageItem`, `StorageItemTask`, `Subscriber`, `SubscriberEmail`, `Subscription`, `SubscriptionNotOwnedError`, `Tag`, `TaxRate`, `Team`, `TeamInvitation`, `TeamMember`, `Transaction`, `UsageEvent`, `User`, `WaitlistProduct`, `WaitlistRestaurant`, `Websocket`, `buildDatabaseSchema`, `collectEncryptedAttributes`, `createAuditMethods`, `createCategorizableMethods`, `createCommentableMethods`, `createLikeableMethods`, `createTaggableMethods`, `customerOwnership`, `decryptValue`, `encryptValue`, `enrichPaginatorUrls`, `filterFillable`, `frameworkModelFeature`, `getWritableFields`, `isBillable`, `isCursorPaginator`, `isEncrypted`, `isPaginator`, `isSimplePaginator`, `isUniqueViolation`, `mapWriteError`, `modelGlobalNames`, `ormReady`, `parentOwnership`, `parseCursor`, `renderDatabaseTypeFile`, `resolveCursorArgs`, `resolvePageArgs`, `selfOwnership`, `setAuditUser`, `siteOwnership`, `teamMembershipOwnership`, `toCursorPaginator`, `toPaginator`, `toSimplePaginator`, `toSnakeCaseKeys`.
641
641
 
642
642
  Local source modules: [src/auto-crud.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/auto-crud.ts), [src/batch-loader.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/batch-loader.ts), [src/db.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/db.ts), [src/define-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/define-model.ts), [src/extend-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/extend-model.ts), [src/gdpr/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/gdpr/index.ts), [src/generate-database-schema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/generate-database-schema.ts), [src/locked-row.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/locked-row.ts), [src/messaging.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/messaging.ts), [src/model-types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/model-types.ts), [src/ownership.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/ownership.ts), [src/paginator-request.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/paginator-request.ts), [src/row-token.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/row-token.ts), [src/subquery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/subquery.ts), [src/traits/audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/audit.ts), [src/traits/billable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/billable.ts), [src/traits/categorizable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/categorizable.ts), [src/traits/commentable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/commentable.ts), [src/traits/likeable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/likeable.ts), [src/traits/taggable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/taggable.ts), [src/transaction.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/transaction.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/types.ts), [src/utils/encrypted.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/encrypted.ts), [src/utils/prunable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/prunable.ts).
643
643
 
@@ -839,11 +839,11 @@ Evidence directory: [18 retained tests](https://github.com/stacksjs/stacks/blob/
839
839
 
840
840
  Guidance: [`stacks-storage`](../stacks-storage/SKILL.md). Source: [storage/framework/core/storage/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/index.ts).
841
841
 
842
- Declared runtime exports: `PathSanitizeError`, `Storage`, `StorageManager`, `UploadedFile`, `azureDisk`, `backblazeDisk`, `clearRevokedSignedStorageTokens`, `configFromEnv`, `createSignedStorageToken`, `detectMimeFromMagicBytes`, `ensureBucket`, `ensureConfiguredBuckets`, `fileInfo`, `fileInfoFromBytes`, `fileKind`, `filebaseDisk`, `gcsDisk`, `hetznerDisk`, `isPreviewable`, `isSignedStorageTokenRevoked`, `localDisk`, `parseDiskPath`, `r2Disk`, `resolveMime`, `revokeSignedStorageToken`, `s3Disk`, `sanitizePresignedDir`, `sanitizePresignedFilename`, `serveFile`, `signS3PresignedPost`, `storage`, `uploadedFile`, `uploadedFiles`, `verifySignedStorageToken`, `verifyUploadedMime`.
842
+ Declared runtime exports: `DirectoryListingChangedError`, `PathSanitizeError`, `Storage`, `StorageManager`, `UploadedFile`, `azureDisk`, `backblazeDisk`, `clearRevokedSignedStorageTokens`, `configFromEnv`, `createSignedStorageToken`, `detectMimeFromMagicBytes`, `ensureBucket`, `ensureConfiguredBuckets`, `fileInfo`, `fileInfoFromBytes`, `fileKind`, `filebaseDisk`, `gcsDisk`, `hetznerDisk`, `isPreviewable`, `isSignedStorageTokenRevoked`, `localDisk`, `parseDiskPath`, `r2Disk`, `resolveMime`, `revokeSignedStorageToken`, `s3Disk`, `sanitizePresignedDir`, `sanitizePresignedFilename`, `serveFile`, `signS3PresignedPost`, `storage`, `uploadedFile`, `uploadedFiles`, `verifySignedStorageToken`, `verifyUploadedMime`.
843
843
 
844
- Local source modules: [src/adapters/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/adapters/index.ts), [src/bounded-text-file.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/bounded-text-file.ts), [src/completed-artifact-directory.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/completed-artifact-directory.ts), [src/copy.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/copy.ts), [src/delete.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/delete.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/drivers/index.ts), [src/facade.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/facade.ts), [src/file-info.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/file-info.ts), [src/files.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/files.ts), [src/folders.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/folders.ts), [src/fs.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/fs.ts), [src/glob.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/glob.ts), [src/hash.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/hash.ts), [src/helpers.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/helpers.ts), [src/mime-verify.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/mime-verify.ts), [src/path-sanitize.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/path-sanitize.ts), [src/provision.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/provision.ts), [src/s3-presigned-post.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/s3-presigned-post.ts), [src/signed-url.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/signed-url.ts), [src/static-serve.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/static-serve.ts), [src/storage.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/storage.ts), [src/temporary-workspaces.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/temporary-workspaces.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/types.ts), [src/types/filesystem.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/types/filesystem.ts), [src/uploaded-file.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/uploaded-file.ts), [src/zip.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/zip.ts).
844
+ Local source modules: [src/adapters/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/adapters/index.ts), [src/bounded-text-file.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/bounded-text-file.ts), [src/completed-artifact-directory.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/completed-artifact-directory.ts), [src/copy.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/copy.ts), [src/delete.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/delete.ts), [src/directory-page.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/directory-page.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/drivers/index.ts), [src/facade.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/facade.ts), [src/file-info.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/file-info.ts), [src/files.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/files.ts), [src/folders.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/folders.ts), [src/fs.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/fs.ts), [src/glob.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/glob.ts), [src/hash.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/hash.ts), [src/helpers.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/helpers.ts), [src/mime-verify.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/mime-verify.ts), [src/path-sanitize.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/path-sanitize.ts), [src/provision.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/provision.ts), [src/s3-presigned-post.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/s3-presigned-post.ts), [src/signed-url.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/signed-url.ts), [src/static-serve.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/static-serve.ts), [src/storage.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/storage.ts), [src/temporary-workspaces.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/temporary-workspaces.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/types.ts), [src/types/filesystem.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/types/filesystem.ts), [src/uploaded-file.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/uploaded-file.ts), [src/zip.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/zip.ts).
845
845
 
846
- Evidence directory: [49 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
846
+ Evidence directory: [50 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
847
847
 
848
848
  ### @stacksjs/strings
849
849
 
@@ -131,3 +131,22 @@ tenant. Underlying optional methods still need driver support.
131
131
 
132
132
  Source: `signed-url.ts`, `static-serve.ts`, `adapters/scoped.ts`; evidence:
133
133
  `tests/scoped.test.ts`, `static-serve.test.ts`, `path-sanitize.test.ts`.
134
+
135
+ ## Resumable inventories
136
+
137
+ Use a disk's optional `listPage(path, { limit, cursor })` for a durable shallow
138
+ inventory. Each page contains at most 1,000 entries (100 by default) and an
139
+ opaque `nextCursor`. Persist the cursor only in the transaction that enrolls the
140
+ whole page; retrying a page must be idempotent. A null cursor completes the pass.
141
+ Do not emulate this API by materializing `list()` when an adapter lacks it.
142
+
143
+ S3 forwards the provider's continuation token and MaxKeys. Local/Bun disks on
144
+ Darwin or glibc Linux use a bounded 32 KiB native directory block plus its record
145
+ position, preserving that cursor across process restarts without rescanning the
146
+ preceding directory. Local cursors bind to directory identity and modification
147
+ times. `DirectoryListingChangedError` means restart that directory from null;
148
+ previously enrolled records remain valid. Concurrent additions are therefore
149
+ visited during the restarted pass. Symlinks and special files are excluded.
150
+ Permission/provider failures throw and must leave the durable cursor unchanged.
151
+ Windows and unsupported libc hosts fail explicitly rather than claiming a
152
+ bounded inventory through an unbounded fallback.
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, deleteDashboardFile } from './file-manager'
4
+ import { deleteDashboardFile } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileDestroyAction',
@@ -16,9 +17,7 @@ export default new Action({
16
17
  return response.json(deleted)
17
18
  }
18
19
  catch (error) {
19
- if (error instanceof DashboardFileError)
20
- return response.json({ message: error.message, fields: error.fields }, error.status)
21
- throw error
20
+ return dashboardFileFailure(error, 'The file could not be deleted.', 'FileDestroyAction')
22
21
  }
23
22
  },
24
23
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { createDashboardDirectory, DashboardFileError } from './file-manager'
4
+ import { createDashboardDirectory } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileDirectoryStoreAction',
@@ -17,9 +18,7 @@ export default new Action({
17
18
  return response.json(directory, 201)
18
19
  }
19
20
  catch (error) {
20
- if (error instanceof DashboardFileError)
21
- return response.json({ message: error.message, fields: error.fields }, error.status)
22
- throw error
21
+ return dashboardFileFailure(error, 'The folder could not be created.', 'FileDirectoryStoreAction')
23
22
  }
24
23
  },
25
24
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, duplicateDashboardFile } from './file-manager'
4
+ import { duplicateDashboardFile } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileDuplicateAction',
@@ -17,9 +18,7 @@ export default new Action({
17
18
  return response.json(duplicated)
18
19
  }
19
20
  catch (error) {
20
- if (error instanceof DashboardFileError)
21
- return response.json({ message: error.message, fields: error.fields }, error.status)
22
- throw error
21
+ return dashboardFileFailure(error, 'The file could not be duplicated.', 'FileDuplicateAction')
23
22
  }
24
23
  },
25
24
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, setDashboardFileFavorite } from './file-manager'
4
+ import { setDashboardFileFavorite } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileFavoriteAction',
@@ -17,9 +18,7 @@ export default new Action({
17
18
  return response.json(result)
18
19
  }
19
20
  catch (error) {
20
- if (error instanceof DashboardFileError)
21
- return response.json({ message: error.message, fields: error.fields }, error.status)
22
- throw error
21
+ return dashboardFileFailure(error, 'The favorite could not be saved.', 'FileFavoriteAction')
23
22
  }
24
23
  },
25
24
  })
@@ -1,7 +1,7 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
- import { response } from '@stacksjs/router'
4
- import { DashboardFileError, getDashboardFileSnapshot, normalizeDashboardFileLimit } from './file-manager'
3
+ import { getDashboardFileSnapshot, normalizeDashboardFileLimit } from './file-manager'
4
+ import { dashboardFileFailure } from './file-response'
5
5
 
6
6
  export default new Action({
7
7
  name: 'FileIndexAction',
@@ -16,9 +16,7 @@ export default new Action({
16
16
  })
17
17
  }
18
18
  catch (error) {
19
- if (error instanceof DashboardFileError)
20
- return response.json({ message: error.message, fields: error.fields }, error.status)
21
- throw error
19
+ return dashboardFileFailure(error, 'Files could not be loaded.', 'FileIndexAction')
22
20
  }
23
21
  },
24
22
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, renameDashboardFile } from './file-manager'
4
+ import { renameDashboardFile } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileRenameAction',
@@ -17,9 +18,7 @@ export default new Action({
17
18
  return response.json(renamed)
18
19
  }
19
20
  catch (error) {
20
- if (error instanceof DashboardFileError)
21
- return response.json({ message: error.message, fields: error.fields }, error.status)
22
- throw error
21
+ return dashboardFileFailure(error, 'The file could not be renamed.', 'FileRenameAction')
23
22
  }
24
23
  },
25
24
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, reprocessDashboardFile } from './file-manager'
4
+ import { reprocessDashboardFile } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileReprocessAction',
@@ -23,9 +24,7 @@ export default new Action({
23
24
  return response.json(result)
24
25
  }
25
26
  catch (error) {
26
- if (error instanceof DashboardFileError)
27
- return response.json({ message: error.message, fields: error.fields }, error.status)
28
- throw error
27
+ return dashboardFileFailure(error, 'The file could not be reprocessed.', 'FileReprocessAction')
29
28
  }
30
29
  },
31
30
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, setDashboardFileTags } from './file-manager'
4
+ import { setDashboardFileTags } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileTagsAction',
@@ -19,9 +20,7 @@ export default new Action({
19
20
  return response.json(result)
20
21
  }
21
22
  catch (error) {
22
- if (error instanceof DashboardFileError)
23
- return response.json({ message: error.message, fields: error.fields }, error.status)
24
- throw error
23
+ return dashboardFileFailure(error, 'The tags could not be saved.', 'FileTagsAction')
25
24
  }
26
25
  },
27
26
  })
@@ -2,7 +2,8 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import type { UploadedFileLike } from '@stacksjs/storage'
3
3
  import { Action } from '@stacksjs/actions/runtime'
4
4
  import { response } from '@stacksjs/router'
5
- import { DashboardFileError, uploadDashboardFiles } from './file-manager'
5
+ import { uploadDashboardFiles } from './file-manager'
6
+ import { dashboardFileFailure } from './file-response'
6
7
 
7
8
  const MAX_FILES = 20
8
9
  const MAX_FILE_BYTES = 10 * 1024 * 1024
@@ -34,9 +35,7 @@ export default new Action({
34
35
  return response.json({ uploaded }, 201)
35
36
  }
36
37
  catch (error) {
37
- if (error instanceof DashboardFileError)
38
- return response.json({ message: error.message, fields: error.fields }, error.status)
39
- throw error
38
+ return dashboardFileFailure(error, 'The upload could not be completed.', 'FileUploadAction')
40
39
  }
41
40
  },
42
41
  })
@@ -1,7 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions/runtime'
3
3
  import { response } from '@stacksjs/router'
4
- import { DashboardFileError, setDashboardFileVisibility } from './file-manager'
4
+ import { setDashboardFileVisibility } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'FileVisibilityAction',
@@ -17,9 +18,7 @@ export default new Action({
17
18
  return response.json(updated)
18
19
  }
19
20
  catch (error) {
20
- if (error instanceof DashboardFileError)
21
- return response.json({ message: error.message, fields: error.fields }, error.status)
22
- throw error
21
+ return dashboardFileFailure(error, 'The visibility could not be changed.', 'FileVisibilityAction')
23
22
  }
24
23
  },
25
24
  })
@@ -0,0 +1,61 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
2
+ import { afterEach, describe, expect, spyOn, test } from 'bun:test'
3
+ import FileIndexAction from './FileIndexAction'
4
+ import { DashboardFileError } from './file-manager'
5
+ import { dashboardFileFailure } from './file-response'
6
+
7
+ /**
8
+ * File actions answer a failure they did not write with a safe 503
9
+ * (stacksjs/stacks#3174). They rethrew it, and the router put the driver's own
10
+ * message - `relation "storage_items" does not exist` - in the response body
11
+ * in every environment, which the file manager then showed in its banner.
12
+ */
13
+ const driverMessage = 'relation "storage_items" does not exist'
14
+
15
+ let quiet: ReturnType<typeof spyOn> | null = null
16
+ afterEach(() => {
17
+ quiet?.mockRestore()
18
+ quiet = null
19
+ })
20
+
21
+ describe('dashboardFileFailure', () => {
22
+ test('keeps the status, message and fields of an error written to be shown', async () => {
23
+ const reply = dashboardFileFailure(
24
+ new DashboardFileError('Choose a limit between 1 and 5000.', 422, { limit: 'Too high.' }),
25
+ 'Files could not be loaded.',
26
+ 'FileIndexAction',
27
+ )
28
+
29
+ expect(reply.status).toBe(422)
30
+ expect(await reply.json()).toEqual({ message: 'Choose a limit between 1 and 5000.', fields: { limit: 'Too high.' } })
31
+ })
32
+
33
+ test('answers any other failure 503 with the safe message and logs the real one', async () => {
34
+ quiet = spyOn(console, 'error').mockImplementation(() => {})
35
+ const failure = new Error(driverMessage)
36
+
37
+ const reply = dashboardFileFailure(failure, 'Files could not be loaded.', 'FileIndexAction')
38
+
39
+ expect(reply.status).toBe(503)
40
+ expect(await reply.json()).toEqual({ message: 'Files could not be loaded.' })
41
+ expect(quiet).toHaveBeenCalledWith('[dashboard/api] FileIndexAction failed:', failure)
42
+ })
43
+ })
44
+
45
+ describe('FileIndexAction', () => {
46
+ test('does not send a driver error to the browser', async () => {
47
+ quiet = spyOn(console, 'error').mockImplementation(() => {})
48
+ const request = {
49
+ get: () => {
50
+ throw new Error(driverMessage)
51
+ },
52
+ } as unknown as RequestInstance
53
+
54
+ const reply = await FileIndexAction.handle(request) as Response
55
+
56
+ expect(reply.status).toBe(503)
57
+ const body = await reply.text()
58
+ expect(body).not.toContain('storage_items')
59
+ expect(JSON.parse(body)).toEqual({ message: 'Files could not be loaded.' })
60
+ })
61
+ })
@@ -0,0 +1,18 @@
1
+ import { response } from '@stacksjs/router'
2
+ import { dashboardOperationalError } from '../dashboard-response'
3
+ import { DashboardFileError } from './file-manager'
4
+
5
+ /**
6
+ * The response for a file action that threw.
7
+ *
8
+ * A `DashboardFileError` is written to be shown, so it keeps its status,
9
+ * message and fields. Anything else - the database behind `storage_items`
10
+ * being down or not migrated, a driver failing - is logged and answered 503
11
+ * with `message`, because rethrowing it let the router send the raw driver
12
+ * message to the browser in every environment (stacksjs/stacks#3174).
13
+ */
14
+ export function dashboardFileFailure(error: unknown, message: string, action: string): Response {
15
+ if (error instanceof DashboardFileError)
16
+ return response.json({ message: error.message, fields: error.fields }, error.status)
17
+ return dashboardOperationalError(error, message, action)
18
+ }
@@ -1,12 +1,32 @@
1
1
  import type { ResponseStatus } from '@stacksjs/bun-router'
2
2
  import { response } from '@stacksjs/router'
3
3
 
4
+ /**
5
+ * The feature whose models this error says were never loaded, if it is the
6
+ * ORM's `ModelUnavailableError` for a feature that is off.
7
+ */
8
+ function disabledFeature(error: unknown): string | undefined {
9
+ if (!(error instanceof Error) || error.name !== 'ModelUnavailableError')
10
+ return undefined
11
+ const feature = (error as Error & { feature?: unknown }).feature
12
+ return typeof feature === 'string' && feature ? feature : undefined
13
+ }
14
+
4
15
  export function dashboardOperationalError(
5
16
  error: unknown,
6
17
  message: string,
7
18
  action: string,
8
19
  status: ResponseStatus = 503,
9
20
  ): Response {
21
+ // A page whose feature is off is not an outage, and saying "could not be
22
+ // read" sent people looking at their database (stacksjs/stacks#3184).
23
+ const feature = disabledFeature(error)
24
+ if (feature) {
25
+ console.warn(`[dashboard/api] ${action}: the '${feature}' feature is off`)
26
+ return response.json({
27
+ message: `${feature.charAt(0).toUpperCase()}${feature.slice(1)} is turned off for this app, so this page has no data. Set \`enabled: true\` in config/${feature}.ts to use it.`,
28
+ }, 404)
29
+ }
10
30
  console.error(`[dashboard/api] ${action} failed:`, error)
11
31
  return response.json({ message }, status)
12
32
  }