@stacksjs/defaults 0.75.484 → 0.75.487
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/ai/skills/stacks-actions/SKILL.md +1 -1
- package/ai/skills/stacks-auto-imports/SKILL.md +1 -1
- package/ai/skills/stacks-dashboard/SKILL.md +33 -2
- package/ai/skills/stacks-database/SKILL.md +1 -1
- package/ai/skills/stacks-error-handling/SKILL.md +8 -0
- package/ai/skills/stacks-migrations/SKILL.md +2 -2
- package/ai/skills/stacks-models/SKILL.md +2 -2
- package/ai/skills/stacks-native/CATALOG.md +3 -3
- package/ai/skills/stacks-orm/SKILL.md +2 -2
- package/app/Actions/Dashboard/Content/CategoryDestroyAction.ts +2 -2
- package/app/Actions/Dashboard/Content/CategoryIndexAction.ts +2 -13
- package/app/Actions/Dashboard/Content/CategoryStoreAction.ts +5 -11
- package/app/Actions/Dashboard/Content/ContentDashboardAction.ts +1 -1
- package/app/Actions/Dashboard/Content/PostIndexAction.ts +1 -1
- package/app/Actions/Dashboard/Content/post-input.ts +1 -1
- package/app/Actions/Dashboard/Jobs/JobDefinitionsAction.ts +26 -0
- package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +2 -2
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +1 -11
- package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +9 -41
- package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +5 -7
- package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +6 -6
- package/app/Actions/Dashboard/Queue/queue-stats.ts +5 -1
- package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +47 -89
- package/app/Actions/Dashboard/Search/SearchEngineIndexAction.ts +59 -0
- package/app/Models/QueueWorker.ts +17 -0
- package/functions/jobs.ts +14 -0
- package/functions/search-engine.ts +13 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/resources/components/Dashboard/Jobs/JobDashboard.stx +24 -5
- package/resources/components/Dashboard/Queue/QueueDashboard.stx +2 -3
- package/resources/components/Dashboard/Search/SearchEngineDashboard.stx +58 -0
- package/resources/functions/dashboard/model-columns.ts +12 -0
- package/resources/functions/dashboard/sidebar.ts +4 -2
- package/routes/dashboard-api.ts +178 -166
- package/views/dashboard/search-engine/index.stx +8 -0
|
@@ -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
|
|
3
|
+
description: Use when working with Stacks server actions - creating actions in app/Actions/, auto-generated API actions from the useApi model trait, the 685 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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-auto-imports
|
|
3
|
-
description: Use before adding imports in Stacks app code, checking available runtime functions, browser/server delivery, model globals, or registry generation. Covers Stacks auto-imports, the generated runtime inventory, stx client delivery and server boot (
|
|
3
|
+
description: Use before adding imports in Stacks app code, checking available runtime functions, browser/server delivery, model globals, or registry generation. Covers Stacks auto-imports, the generated runtime inventory, stx client delivery and server boot (108 models).
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-dashboard
|
|
3
|
-
description: Use when building or customizing the Stacks admin dashboard, including dashboard pages, model management views, analytics widgets, commerce dashboards, content management, settings panels, deployment monitoring, job/queue management, or the
|
|
3
|
+
description: Use when building or customizing the Stacks admin dashboard, including dashboard pages, model management views, analytics widgets, commerce dashboards, content management, settings panels, deployment monitoring, job/queue management, or the 415 built-in dashboard components. Covers the dashboard system at storage/framework/defaults/.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -8,7 +8,7 @@ allowed-tools: Read Edit Write Bash Grep Glob
|
|
|
8
8
|
|
|
9
9
|
# Stacks Dashboard
|
|
10
10
|
|
|
11
|
-
The Stacks admin dashboard provides a full-featured admin panel with 100+ route views,
|
|
11
|
+
The Stacks admin dashboard provides a full-featured admin panel with 100+ route views, 415 components, and a multi-section layout.
|
|
12
12
|
|
|
13
13
|
## Key Paths
|
|
14
14
|
- Dashboard components: `storage/framework/defaults/resources/components/Dashboard/`
|
|
@@ -684,3 +684,34 @@ or separate routes generated by `useApi`. Configure `security.api.models` or
|
|
|
684
684
|
`STACKS_MODEL_APIS`, middleware, ownership and policies for those APIs. The
|
|
685
685
|
new boundary does not provide row ownership or permission checks for models
|
|
686
686
|
that remain exposed.
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
## Application observability
|
|
690
|
+
|
|
691
|
+
The sidebar, model catalog and global search use the database's native model
|
|
692
|
+
scope: application overrides and discovered package models, with framework
|
|
693
|
+
models as a fallback. Global search uses the configured ORM connection on
|
|
694
|
+
PostgreSQL, SQLite and MySQL. Only stored, visible fields from `useSearch`
|
|
695
|
+
are queried; projected search-document fields are not assumed to be SQL columns.
|
|
696
|
+
|
|
697
|
+
`/search-engine` inspects the configured driver, provider indexes and model
|
|
698
|
+
`useSearch` settings, including custom index names. Provider failure leaves the
|
|
699
|
+
model inventory visible with an unavailable state. It never reindexes on a read.
|
|
700
|
+
`/jobs` discovers application definitions even before they have queued rows.
|
|
701
|
+
Definition import failures are operational failures, not a successful empty list.
|
|
702
|
+
|
|
703
|
+
Queue workers publish snapshots to the model-backed `queue_workers` table every
|
|
704
|
+
10 seconds and on graceful exit. Publish `QueueWorker` into an application's
|
|
705
|
+
model scope and generate/migrate its schema before using worker metrics. The
|
|
706
|
+
dashboard reads snapshots across processes. A heartbeat older than 60 seconds
|
|
707
|
+
marks the worker stopped and clears live throughput; sessions are retained for
|
|
708
|
+
seven days. Completion/failure totals describe those retained worker sessions,
|
|
709
|
+
not a durable per-job completion history. Queues containing only failed jobs
|
|
710
|
+
stay visible. Database queue rows are authoritative for pending/reserved work.
|
|
711
|
+
|
|
712
|
+
The local error page reads `errors`. An Error model does not capture exceptions
|
|
713
|
+
by itself: configure `createDatabaseErrorReporter()` from
|
|
714
|
+
`@stacksjs/database/error-reporter` in `monitoring.reporters`, publish
|
|
715
|
+
`Error`, and migrate it. Reporting is bounded, buffered, identity-deduplicated
|
|
716
|
+
by `captureError`, and flushed by `flushErrorReporters`/the logger. The database
|
|
717
|
+
reporter stores allowlisted operational context and excludes bodies and headers.
|
|
@@ -12,7 +12,7 @@ allowed-tools: Read Edit Write Bash Grep Glob
|
|
|
12
12
|
- Database package: `storage/framework/core/database/src/`
|
|
13
13
|
- Configuration: `config/database.ts`
|
|
14
14
|
- QB config: `config/query-builder.ts`
|
|
15
|
-
- Migrations: `database/migrations/` (
|
|
15
|
+
- Migrations: `database/migrations/` (248 migration files, `.sql` format)
|
|
16
16
|
- QB state: `.qb/`
|
|
17
17
|
- ORM: `storage/framework/orm/`
|
|
18
18
|
|
|
@@ -82,3 +82,11 @@ http.ts, error-page.ts, reporters.ts, bughq.ts and unique-violation.ts.
|
|
|
82
82
|
Tests: result-type.test.ts, http-errors.test.ts, error-page-render.test.ts,
|
|
83
83
|
custom-error-pages.test.ts and reporters.test.ts under core/error-handling/tests.
|
|
84
84
|
Keep error payloads and credentials out of public responses and diagnostics.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
For the local dashboard error store, add `createDatabaseErrorReporter()` from
|
|
88
|
+
`@stacksjs/database/error-reporter` to `monitoring.reporters`. Publish
|
|
89
|
+
and migrate the native Error model in an application with its own schema.
|
|
90
|
+
The bounded reporter buffers at most 100 writes, isolates storage failures,
|
|
91
|
+
stores only allowlisted operational context, and drains through
|
|
92
|
+
`flushErrorReporters()`. Remote reporters may coexist with the local store.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-migrations
|
|
3
|
-
description: Use when working with database migrations in a Stacks application - creating migration files, running migrations, fresh migration (drop + recreate), seeding after migration, migration file naming conventions, or the
|
|
3
|
+
description: Use when working with database migrations in a Stacks application - creating migration files, running migrations, fresh migration (drop + recreate), seeding after migration, migration file naming conventions, or the 248 built-in migration files. For the database API itself (queries, connections, SQL helpers), see stacks-database.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript, SQLite >= 3.47.2
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -236,7 +236,7 @@ lossless widenings; reports everything destructive), then
|
|
|
236
236
|
- Do not commit a second snapshot under `.qb/`; that indicates a missing `snapshotDir` configuration
|
|
237
237
|
- If a generated SQLite migration rebuilds tables, test it against a copy of the current database and run `PRAGMA integrity_check` plus `PRAGMA foreign_key_check`
|
|
238
238
|
- `--seed` flag after `migrate:fresh` seeds the database with factory data
|
|
239
|
-
-
|
|
239
|
+
- 248 migration files exist by default for all framework models
|
|
240
240
|
- SQLite >= 3.47.2 is required (system requirement)
|
|
241
241
|
- For the database API (queries, connections), see the `stacks-database` skill
|
|
242
242
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-models
|
|
3
|
-
description: Use when defining or extending Stacks models, deriving migrations and CRUD, configuring attributes/relationships/traits/ownership, or preserving model inference. Covers defineModel, extendModel, model types, native data workflows, and the
|
|
3
|
+
description: Use when defining or extending Stacks models, deriving migrations and CRUD, configuring attributes/relationships/traits/ownership, or preserving model inference. Covers defineModel, extendModel, model types, native data workflows, and the 108 built-in framework models.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript, SQLite >= 3.47.2
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -420,7 +420,7 @@ honoured: fewer rows are seeded rather than a duplicate combination.
|
|
|
420
420
|
## Built-in models by category
|
|
421
421
|
|
|
422
422
|
The ones below are worth knowing by name. They are a selection, not the set:
|
|
423
|
-
`storage/framework/defaults/app/Models/` holds
|
|
423
|
+
`storage/framework/defaults/app/Models/` holds 108, and that directory is the
|
|
424
424
|
authority. This section said "All 62 built-in models by category" while
|
|
425
425
|
listing fewer than that against 102 on disk, so an agent reading to the end
|
|
426
426
|
had no way to tell it was short.
|
|
@@ -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`, `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`.
|
|
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`, `QueueWorker`, `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
|
|
|
@@ -695,9 +695,9 @@ Evidence directory: [7 retained tests](https://github.com/stacksjs/stacks/blob/m
|
|
|
695
695
|
|
|
696
696
|
Guidance: [`stacks-queue`](../stacks-queue/SKILL.md). Source: [storage/framework/core/queue/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/index.ts).
|
|
697
697
|
|
|
698
|
-
Declared runtime exports: `Batch`, `DispatchedBatch`, `FailedJobNotifier`, `JOB_ENVELOPE_VERSION`, `Job`, `Jobs`, `OnQueueEvent`, `PendingBatch`, `QueueEvents`, `QueueMetrics`, `QueueTester`, `assertEnvelopeSerializable`, `cancelJob`, `checkQueueHealth`, `claimDispatchKey`, `clearEnvelopeWarnings`, `clearJobState`, `configureFailedJobNotifications`, `createEnvelope`, `createHealthCheckHandler`, `createQueueTester`, `discoverJobs`, `emitQueueEvent`, `executeFailedJobs`, `executeJob`, `expectJobToFail`, `fake`, `getActionRunner`, `getActiveJobCount`, `getAllJobs`, `getBatchCallbacks`, `getFailedJobNotifier`, `getFakeQueue`, `getGlobalMetrics`, `getJob`, `getJobProgress`, `getQueueEvents`, `getRedisQueue`, `getRegisteredJobs`, `getScheduledJobs`, `getSchedulerStatus`, `getWorkerTracker`, `hasDispatchedKey`, `hashPayload`, `isBatchCancelled`, `isCircuitOpen`, `isFaked`, `isJobCancelled`, `isQuarantined`, `isQueueHealthy`, `isSchedulerRunning`, `isWorkerRunning`, `job`, `jobBatch`, `jobRegistry`, `listCircuitState`, `listDeadLetterJobs`, `listQuarantined`, `moveToDeadLetter`, `notifyJobFailed`, `onQueueEvent`, `parseEnvelope`, `pauseQueue`, `purgeDeadLetterJobs`, `quarantineJob`, `queuedJobState`, `recordBatchJobCompletion`, `recordBatchJobFailure`, `recordCircuitFailure`, `recordCircuitSuccess`, `recordDispatchedKey`, `recordFailureForPoison`, `releaseDispatchKey`, `resolveQueueDriver`, `restore`, `resumeQueue`, `retryDeadLetterJob`, `retryFailedJob`, `runJob`, `runTestJob`, `serializeEnvelope`, `setActionRunner`, `setJobProgress`, `startProcessor`, `startScheduler`, `stopProcessor`, `stopScheduler`, `toJobOptions`, `triggerJob`, `unquarantineJob`, `withEvents`.
|
|
698
|
+
Declared runtime exports: `Batch`, `DispatchedBatch`, `FailedJobNotifier`, `JOB_ENVELOPE_VERSION`, `Job`, `Jobs`, `OnQueueEvent`, `PendingBatch`, `QueueEvents`, `QueueMetrics`, `QueueTester`, `aggregateWorkerMetrics`, `assertEnvelopeSerializable`, `cancelJob`, `checkQueueHealth`, `claimDispatchKey`, `clearEnvelopeWarnings`, `clearJobState`, `configureFailedJobNotifications`, `createEnvelope`, `createHealthCheckHandler`, `createQueueTester`, `discoverJobs`, `emitQueueEvent`, `executeFailedJobs`, `executeJob`, `expectJobToFail`, `fake`, `getActionRunner`, `getActiveJobCount`, `getAllJobs`, `getBatchCallbacks`, `getFailedJobNotifier`, `getFakeQueue`, `getGlobalMetrics`, `getJob`, `getJobProgress`, `getQueueEvents`, `getRedisQueue`, `getRegisteredJobs`, `getScheduledJobs`, `getSchedulerStatus`, `getWorkerTracker`, `hasDispatchedKey`, `hashPayload`, `isBatchCancelled`, `isCircuitOpen`, `isFaked`, `isJobCancelled`, `isQuarantined`, `isQueueHealthy`, `isSchedulerRunning`, `isWorkerRunning`, `job`, `jobBatch`, `jobRegistry`, `listCircuitState`, `listDeadLetterJobs`, `listQuarantined`, `moveToDeadLetter`, `notifyJobFailed`, `onQueueEvent`, `parseEnvelope`, `pauseQueue`, `purgeDeadLetterJobs`, `quarantineJob`, `queuedJobState`, `readWorkerSnapshots`, `recordBatchJobCompletion`, `recordBatchJobFailure`, `recordCircuitFailure`, `recordCircuitSuccess`, `recordDispatchedKey`, `recordFailureForPoison`, `releaseDispatchKey`, `resolveQueueDriver`, `restore`, `resumeQueue`, `retryDeadLetterJob`, `retryFailedJob`, `runJob`, `runTestJob`, `serializeEnvelope`, `setActionRunner`, `setJobProgress`, `startProcessor`, `startScheduler`, `stopProcessor`, `stopScheduler`, `toJobOptions`, `triggerJob`, `unquarantineJob`, `withEvents`.
|
|
699
699
|
|
|
700
|
-
Local source modules: [src/action-runner.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/action-runner.ts), [src/action.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/action.ts), [src/batch.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/batch.ts), [src/bun-queue.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/bun-queue.ts), [src/circuit-breaker.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/circuit-breaker.ts), [src/dead-letter.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/dead-letter.ts), [src/discovery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/discovery.ts), [src/driver.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/driver.ts), [src/drivers/redis.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/drivers/redis.ts), [src/envelope.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/envelope.ts), [src/events.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/events.ts), [src/health.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/health.ts), [src/idempotency.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/idempotency.ts), [src/job-progress.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/job-progress.ts), [src/job.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/job.ts), [src/notifications.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/notifications.ts), [src/poison.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/poison.ts), [src/scheduler.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/scheduler.ts), [src/testing.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/testing.ts), [src/worker.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/worker.ts).
|
|
700
|
+
Local source modules: [src/action-runner.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/action-runner.ts), [src/action.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/action.ts), [src/batch.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/batch.ts), [src/bun-queue.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/bun-queue.ts), [src/circuit-breaker.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/circuit-breaker.ts), [src/dead-letter.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/dead-letter.ts), [src/discovery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/discovery.ts), [src/driver.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/driver.ts), [src/drivers/redis.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/drivers/redis.ts), [src/envelope.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/envelope.ts), [src/events.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/events.ts), [src/health.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/health.ts), [src/idempotency.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/idempotency.ts), [src/job-progress.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/job-progress.ts), [src/job.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/job.ts), [src/notifications.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/notifications.ts), [src/poison.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/poison.ts), [src/scheduler.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/scheduler.ts), [src/testing.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/testing.ts), [src/worker-monitor.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/worker-monitor.ts), [src/worker.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/worker.ts).
|
|
701
701
|
|
|
702
702
|
Evidence directory: [56 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
703
703
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-orm
|
|
3
|
-
description: Use when querying or writing Stacks models, using transactions and relationships, preserving inferred model types, or enabling native model traits. Covers @stacksjs/orm, the model runtime, and
|
|
3
|
+
description: Use when querying or writing Stacks models, using transactions and relationships, preserving inferred model types, or enabling native model traits. Covers @stacksjs/orm, the model runtime, and 108 built-in models.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -10,7 +10,7 @@ allowed-tools: Read Edit Write Bash Grep Glob
|
|
|
10
10
|
|
|
11
11
|
`defineModel` produces an immediately usable query surface over
|
|
12
12
|
`bun-query-builder`. Model-driven migrations change the schema; there are no
|
|
13
|
-
per-model ORM classes to generate. The framework declares
|
|
13
|
+
per-model ORM classes to generate. The framework declares 108 models on disk,
|
|
14
14
|
but optional feature gates determine which built-ins load in a running app.
|
|
15
15
|
|
|
16
16
|
## Definitions and typed rows
|
|
@@ -25,7 +25,7 @@ export default new Action({
|
|
|
25
25
|
try {
|
|
26
26
|
const deleted = await transaction(async (rawTrx) => {
|
|
27
27
|
const trx = rawTrx as unknown as typeof db
|
|
28
|
-
const category = await trx.selectFrom('
|
|
28
|
+
const category = await trx.selectFrom('categorizables').where('categorizable_type', '=', 'posts').select(['id']).where('id', '=', id).executeTakeFirst()
|
|
29
29
|
if (!category)
|
|
30
30
|
return false
|
|
31
31
|
|
|
@@ -34,7 +34,7 @@ export default new Action({
|
|
|
34
34
|
.where('category_id', '=', id)
|
|
35
35
|
.where('categorizable_type', '=', 'posts')
|
|
36
36
|
.execute()
|
|
37
|
-
await trx.deleteFrom('
|
|
37
|
+
await trx.deleteFrom('categorizables').where('categorizable_type', '=', 'posts').where('id', '=', id).execute()
|
|
38
38
|
return true
|
|
39
39
|
})
|
|
40
40
|
|
|
@@ -12,18 +12,7 @@ interface CategoryRow {
|
|
|
12
12
|
updated_at: string | null
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
* `GET /api/dashboard/categories` — backs `views/dashboard/content/categories/index.stx`.
|
|
17
|
-
*
|
|
18
|
-
* Reads the `categories` table via `db`. The previous `Category.all()` call
|
|
19
|
-
* threw on every request (the ORM model exposes no query methods) and the catch
|
|
20
|
-
* turned that into an empty list, so a broken read was indistinguishable from a
|
|
21
|
-
* CMS with no categories. It also mapped `parent_id` and `post_count`, neither
|
|
22
|
-
* of which this table has.
|
|
23
|
-
*
|
|
24
|
-
* Note this is the same `categories` table the commerce `Category` model owns —
|
|
25
|
-
* CMS and commerce categories are not separated in the schema today.
|
|
26
|
-
*/
|
|
15
|
+
/** The CMS catalog owns categorizables; commerce's categories have a separate id space. */
|
|
27
16
|
export default new Action({
|
|
28
17
|
name: 'CategoryIndexAction',
|
|
29
18
|
description: 'Returns CMS categories for the dashboard.',
|
|
@@ -32,7 +21,7 @@ export default new Action({
|
|
|
32
21
|
async handle() {
|
|
33
22
|
try {
|
|
34
23
|
const rows = await db
|
|
35
|
-
.selectFrom('
|
|
24
|
+
.selectFrom('categorizables').where('categorizable_type', '=', 'posts')
|
|
36
25
|
.selectAll()
|
|
37
26
|
.orderBy('created_at', 'desc')
|
|
38
27
|
.execute() as unknown as CategoryRow[]
|
|
@@ -3,7 +3,6 @@ import { Action } from '@stacksjs/actions/runtime'
|
|
|
3
3
|
import { db } from '@stacksjs/database/runtime'
|
|
4
4
|
import { transaction } from '@stacksjs/orm'
|
|
5
5
|
import { response } from '@stacksjs/router'
|
|
6
|
-
import { randomUUIDv7 } from 'bun'
|
|
7
6
|
import { dashboardOperationalError } from '../dashboard-response'
|
|
8
7
|
import { findRow, insertedId, slugify, str, timestamp } from './content-input'
|
|
9
8
|
|
|
@@ -32,7 +31,7 @@ export default new Action({
|
|
|
32
31
|
const category = await transaction(async (rawTrx) => {
|
|
33
32
|
const trx = rawTrx as unknown as typeof db
|
|
34
33
|
const duplicate = await trx
|
|
35
|
-
.selectFrom('
|
|
34
|
+
.selectFrom('categorizables').where('categorizable_type', '=', 'posts')
|
|
36
35
|
.select(['id'])
|
|
37
36
|
.where('slug', '=', slug)
|
|
38
37
|
.executeTakeFirst()
|
|
@@ -41,21 +40,16 @@ export default new Action({
|
|
|
41
40
|
return null
|
|
42
41
|
|
|
43
42
|
const now = timestamp()
|
|
44
|
-
const displayOrder = await trx
|
|
45
|
-
.selectFrom('categories')
|
|
46
|
-
.select(db.fn.max('display_order').as('max_display_order'))
|
|
47
|
-
.executeTakeFirst()
|
|
48
|
-
|
|
49
43
|
const result = await trx
|
|
50
|
-
.insertInto('
|
|
44
|
+
.insertInto('categorizables')
|
|
51
45
|
.values({
|
|
52
|
-
uuid: randomUUIDv7(),
|
|
53
46
|
name,
|
|
54
47
|
slug,
|
|
55
48
|
description,
|
|
56
49
|
// A real boolean: on PostgreSQL the column is boolean and refuses 1.
|
|
57
50
|
is_active: true,
|
|
58
|
-
|
|
51
|
+
categorizable_type: 'posts',
|
|
52
|
+
categorizable_id: 0,
|
|
59
53
|
created_at: now,
|
|
60
54
|
updated_at: now,
|
|
61
55
|
} as any)
|
|
@@ -68,7 +62,7 @@ export default new Action({
|
|
|
68
62
|
if (!id)
|
|
69
63
|
throw new Error('Category insert did not return an id.')
|
|
70
64
|
|
|
71
|
-
const created = await findRow('
|
|
65
|
+
const created = await findRow('categorizables', id, trx)
|
|
72
66
|
if (!created)
|
|
73
67
|
throw new Error('Created category could not be loaded.')
|
|
74
68
|
return created
|
|
@@ -230,7 +230,7 @@ export default new Action({
|
|
|
230
230
|
(since ? postQuery.where('created_at', '>=', since) : postQuery).execute() as unknown as Promise<ContentOverviewPostRow[]>,
|
|
231
231
|
(since ? pageQuery.where('created_at', '>=', since) : pageQuery).execute() as unknown as Promise<ContentOverviewPageRow[]>,
|
|
232
232
|
(since ? commentQuery.where('created_at', '>=', since) : commentQuery).execute() as unknown as Promise<ContentOverviewCommentRow[]>,
|
|
233
|
-
db.selectFrom('
|
|
233
|
+
db.selectFrom('categorizables').where('categorizable_type', '=', 'posts').select(['id', 'name']).execute() as unknown as Promise<NamedRow[]>,
|
|
234
234
|
db.selectFrom('authors').select(['id', 'name']).execute() as unknown as Promise<NamedRow[]>,
|
|
235
235
|
])
|
|
236
236
|
|
|
@@ -76,7 +76,7 @@ export default new Action({
|
|
|
76
76
|
|
|
77
77
|
const postIds = posts.map(post => post.id)
|
|
78
78
|
const [categories, tags, authors, categoryRelations, tagRelations] = await Promise.all([
|
|
79
|
-
db.selectFrom('
|
|
79
|
+
db.selectFrom('categorizables').where('categorizable_type', '=', 'posts').select(['id', 'name', 'slug']).orderBy('name').execute(),
|
|
80
80
|
db.selectFrom('tags').select(['id', 'name', 'slug']).orderBy('name').execute(),
|
|
81
81
|
db.selectFrom('authors').select(['id', 'name', 'email']).orderBy('name').execute(),
|
|
82
82
|
postIds.length
|
|
@@ -89,7 +89,7 @@ export async function invalidPostReference(payload: PostPayload, database: typeo
|
|
|
89
89
|
? database.selectFrom('authors').select(['id']).where('id', '=', payload.authorId).executeTakeFirst()
|
|
90
90
|
: Promise.resolve(undefined),
|
|
91
91
|
payload.categoryIds.length
|
|
92
|
-
? database.selectFrom('
|
|
92
|
+
? database.selectFrom('categorizables').where('categorizable_type', '=', 'posts').whereIn('id', payload.categoryIds).select(['id']).execute()
|
|
93
93
|
: Promise.resolve([]),
|
|
94
94
|
payload.tagIds.length
|
|
95
95
|
? database.selectFrom('tags').whereIn('id', payload.tagIds).select(['id']).execute()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Action } from '@stacksjs/actions/runtime'
|
|
2
|
+
import { discoverJobs } from '@stacksjs/queue'
|
|
3
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
4
|
+
|
|
5
|
+
export default new Action({
|
|
6
|
+
name: 'Job Definitions',
|
|
7
|
+
description: 'Discover application jobs and their queue, schedule and retry conventions without dispatching them.',
|
|
8
|
+
method: 'GET',
|
|
9
|
+
async handle() {
|
|
10
|
+
try {
|
|
11
|
+
const jobs = await discoverJobs(undefined, { strict: true })
|
|
12
|
+
return { data: jobs.map(job => ({
|
|
13
|
+
name: job.name,
|
|
14
|
+
description: job.config.description ?? '',
|
|
15
|
+
queue: job.config.queue ?? 'default',
|
|
16
|
+
tries: job.config.tries ?? 3,
|
|
17
|
+
timeout: job.config.timeout ?? null,
|
|
18
|
+
schedule: job.config.rate ?? job.config.schedule ?? '',
|
|
19
|
+
enabled: job.config.enabled !== false,
|
|
20
|
+
})).sort((a, b) => a.name.localeCompare(b.name)) }
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return dashboardOperationalError(error, 'Job definitions could not be loaded.', 'JobDefinitionsAction')
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions/runtime'
|
|
2
2
|
import { FailedJob, Job } from '@stacksjs/orm'
|
|
3
|
-
import {
|
|
3
|
+
import { aggregateWorkerMetrics, readWorkerSnapshots } from '@stacksjs/queue'
|
|
4
4
|
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
5
|
|
|
6
6
|
export default new Action({
|
|
@@ -14,7 +14,7 @@ export default new Action({
|
|
|
14
14
|
FailedJob.count(),
|
|
15
15
|
])
|
|
16
16
|
|
|
17
|
-
const metrics =
|
|
17
|
+
const metrics = aggregateWorkerMetrics(await readWorkerSnapshots())
|
|
18
18
|
const completedSeen = metrics.counts.completed
|
|
19
19
|
const failedSeen = metrics.counts.failed
|
|
20
20
|
const totalSeen = completedSeen + failedSeen
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hiddenModelColumns } from '../../../../resources/functions/dashboard/model-columns'
|
|
1
2
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
3
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
4
|
import { response as routerResponse } from '@stacksjs/router'
|
|
@@ -83,17 +84,6 @@ function humanize(column: string): string {
|
|
|
83
84
|
// dropped from the response body AND from the sort/filter allowlist: a
|
|
84
85
|
// column you cannot see is still an enumeration oracle if you can sort or
|
|
85
86
|
// filter by it.
|
|
86
|
-
const HIDDEN_COLUMNS = new Set(['password', 'remember_token', 'api_token', 'access_token', 'refresh_token', 'secret', 'two_factor_secret'])
|
|
87
|
-
|
|
88
|
-
function hiddenModelColumns(Model: any): Set<string> {
|
|
89
|
-
const hidden = new Set(HIDDEN_COLUMNS)
|
|
90
|
-
for (const [name, definition] of Object.entries(Model?.attributes ?? {})) {
|
|
91
|
-
if ((definition as { hidden?: boolean })?.hidden)
|
|
92
|
-
hidden.add(pascalToSnake(name))
|
|
93
|
-
}
|
|
94
|
-
return hidden
|
|
95
|
-
}
|
|
96
|
-
|
|
97
87
|
/**
|
|
98
88
|
* Column identifiers reach the query builder as raw, unquoted SQL, so only
|
|
99
89
|
* valid identifiers from the table's own column list may ever be used.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions/runtime'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import process from 'node:process'
|
|
5
|
-
import { frameworkDefaultsRoot } from '@stacksjs/path'
|
|
2
|
+
import { overridesReady } from '@stacksjs/config'
|
|
3
|
+
import { resolveModelSources } from '@stacksjs/database/model-sources'
|
|
6
4
|
import { countRows } from '../../../../resources/functions/dashboard/data'
|
|
7
5
|
import { dashboardOperationalError, dashboardOperationalIssue } from '../dashboard-response'
|
|
8
6
|
import { resolveDashboardModel } from '../../../../resources/functions/dashboard/model-access'
|
|
@@ -64,25 +62,6 @@ function pluralize(word: string): string {
|
|
|
64
62
|
return `${word}s`
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
function walkModels(dir: string, recursive: boolean): Array<{ name: string, file: string }> {
|
|
68
|
-
const out: Array<{ name: string, file: string }> = []
|
|
69
|
-
if (!existsSync(dir))
|
|
70
|
-
return out
|
|
71
|
-
|
|
72
|
-
const entries = readdirSync(dir, { withFileTypes: true })
|
|
73
|
-
for (const ent of entries) {
|
|
74
|
-
const full = join(dir, ent.name)
|
|
75
|
-
if (ent.isDirectory()) {
|
|
76
|
-
if (recursive) out.push(...walkModels(full, true))
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
if (!ent.name.endsWith('.ts')) continue
|
|
80
|
-
if (ent.name.startsWith('_') || ent.name === 'index.ts' || ent.name === 'README.ts') continue
|
|
81
|
-
out.push({ name: ent.name.replace('.ts', ''), file: full })
|
|
82
|
-
}
|
|
83
|
-
return out
|
|
84
|
-
}
|
|
85
|
-
|
|
86
65
|
function categorize(model: { source: 'userland' | 'framework', file: string }): string {
|
|
87
66
|
if (model.source === 'userland') return 'userland'
|
|
88
67
|
const segs = model.file.split('/Models/')[1]?.split('/') ?? []
|
|
@@ -122,30 +101,19 @@ export default new Action({
|
|
|
122
101
|
method: 'GET',
|
|
123
102
|
apiResponse: true,
|
|
124
103
|
async handle() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const defaultsDir = join(frameworkDefaultsRoot(), 'app/Models')
|
|
128
|
-
|
|
129
|
-
let userland: Array<Omit<DiscoveredModel, 'source'>>
|
|
130
|
-
let defaults: Array<Omit<DiscoveredModel, 'source'>>
|
|
104
|
+
await overridesReady
|
|
105
|
+
let merged: DiscoveredModel[]
|
|
131
106
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
107
|
+
merged = (resolveModelSources()?.models ?? []).map(model => ({
|
|
108
|
+
name: model.name,
|
|
109
|
+
file: model.file,
|
|
110
|
+
source: model.origin === 'framework' ? 'framework' : 'userland',
|
|
111
|
+
}))
|
|
134
112
|
}
|
|
135
113
|
catch (error) {
|
|
136
114
|
return dashboardOperationalError(error, 'Model catalog could not be loaded.', 'ModelsIndexAction.catalog')
|
|
137
115
|
}
|
|
138
116
|
|
|
139
|
-
const seen = new Set<string>()
|
|
140
|
-
const merged: DiscoveredModel[] = []
|
|
141
|
-
for (const list of [userland, defaults]) {
|
|
142
|
-
for (const m of list) {
|
|
143
|
-
if (seen.has(m.name)) continue
|
|
144
|
-
seen.add(m.name)
|
|
145
|
-
merged.push({ ...m, source: list === userland ? 'userland' : 'framework' })
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
117
|
const candidates = await Promise.all(merged.map(async (m): Promise<ModelRow | null> => {
|
|
150
118
|
let Model: any
|
|
151
119
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions/runtime'
|
|
2
2
|
import { FailedJob, Job } from '@stacksjs/orm'
|
|
3
|
-
import {
|
|
3
|
+
import { aggregateWorkerMetrics, readWorkerSnapshots } from '@stacksjs/queue'
|
|
4
4
|
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
5
|
import { readQueueCounts } from './queue-stats'
|
|
6
6
|
|
|
@@ -32,18 +32,16 @@ export default new Action({
|
|
|
32
32
|
total: queue.total,
|
|
33
33
|
}))
|
|
34
34
|
const active = counts.reduce((sum, queue) => sum + queue.active, 0)
|
|
35
|
-
const completed = 0
|
|
36
35
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
const metrics = getGlobalMetrics().getMetrics()
|
|
36
|
+
// Workers publish bounded snapshots through their native QueueWorker model.
|
|
37
|
+
// The dashboard reads those across processes rather than its own empty tracker.
|
|
38
|
+
const metrics = aggregateWorkerMetrics(await readWorkerSnapshots())
|
|
41
39
|
|
|
42
40
|
const stats = {
|
|
43
41
|
totalQueues: queues.length,
|
|
44
42
|
totalJobs,
|
|
45
43
|
activeJobs: active,
|
|
46
|
-
completedJobs: completed,
|
|
44
|
+
completedJobs: metrics.counts.completed,
|
|
47
45
|
failedJobs: failedJobCount,
|
|
48
46
|
throughputPerMinute: metrics.throughputPerMinute,
|
|
49
47
|
averageProcessingTimeMs: Math.round(metrics.averageDuration),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions/runtime'
|
|
2
|
-
import {
|
|
2
|
+
import { readWorkerSnapshots } from '@stacksjs/queue'
|
|
3
3
|
import { dashboardOperationalError } from '../dashboard-response'
|
|
4
4
|
|
|
5
5
|
export default new Action({
|
|
@@ -8,8 +8,8 @@ export default new Action({
|
|
|
8
8
|
method: 'GET',
|
|
9
9
|
async handle() {
|
|
10
10
|
try {
|
|
11
|
-
const
|
|
12
|
-
const tracked =
|
|
11
|
+
const snapshots = await readWorkerSnapshots()
|
|
12
|
+
const tracked = snapshots.map(snapshot => snapshot.worker)
|
|
13
13
|
|
|
14
14
|
// Translate the tracker's internal status into the dashboard's vocabulary
|
|
15
15
|
// ("running" | "paused" | "stopped") so the existing UI badges keep working.
|
|
@@ -29,15 +29,15 @@ export default new Action({
|
|
|
29
29
|
jobs_processed: w.processedCount,
|
|
30
30
|
failed_jobs: w.failedCount,
|
|
31
31
|
uptime,
|
|
32
|
-
last_heartbeat: w.
|
|
32
|
+
last_heartbeat: new Date(snapshots.find(snapshot => snapshot.worker.id === w.id)!.heartbeatAt).toISOString(),
|
|
33
33
|
memory: '-',
|
|
34
34
|
}
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
return {
|
|
38
38
|
data,
|
|
39
|
-
worker_running:
|
|
40
|
-
active_jobs:
|
|
39
|
+
worker_running: tracked.some(worker => worker.status !== 'stopped'),
|
|
40
|
+
active_jobs: snapshots.reduce((sum, snapshot) => sum + snapshot.activeJobs, 0),
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
@@ -22,7 +22,7 @@ const queueName = (value: unknown): string => String(value || 'default')
|
|
|
22
22
|
* worker has reserved it, which is `queuedJobState`'s rule in
|
|
23
23
|
* `@stacksjs/queue`: a `reserved_at` that is set and not 0, so the filter
|
|
24
24
|
* below is `!= 0`, which NULL never matches. Queues are listed in the order of
|
|
25
|
-
* their first job,
|
|
25
|
+
* their first job, including queues whose only remaining records are failures.
|
|
26
26
|
*/
|
|
27
27
|
export async function readQueueCounts(database: typeof db = db): Promise<QueueCounts[]> {
|
|
28
28
|
const [jobRows, failedRows] = await Promise.all([
|
|
@@ -57,6 +57,10 @@ export async function readQueueCounts(database: typeof db = db): Promise<QueueCo
|
|
|
57
57
|
})
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
for (const name of failed.keys()) {
|
|
61
|
+
if (!queues.has(name)) queues.set(name, { total: 0, active: 0, firstId: Number.POSITIVE_INFINITY })
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
return [...queues.entries()]
|
|
61
65
|
.sort(([, a], [, b]) => a.firstId - b.firstId)
|
|
62
66
|
.map(([name, counts]) => ({
|