velocious 1.0.644 → 1.0.646
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/README.md +11 -9
- package/build/authorization/ability.js +36 -13
- package/build/background-jobs/adapter.js +9 -0
- package/build/background-jobs/main.js +35 -10
- package/build/background-jobs/store.js +217 -38
- package/build/background-jobs/types.js +15 -0
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +45 -9
- package/build/database/drivers/base-column.js +8 -0
- package/build/database/drivers/mssql/sql/update.js +1 -3
- package/build/database/drivers/mysql/sql/update.js +1 -3
- package/build/database/drivers/pgsql/column.js +22 -0
- package/build/database/drivers/pgsql/sql/update.js +1 -3
- package/build/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/build/database/drivers/sqlite/sql/update.js +1 -3
- package/build/database/query/index.js +2 -2
- package/build/database/query/model-class-query.js +26 -10
- package/build/database/query/preloader/has-many.js +4 -1
- package/build/database/query/query-data.js +2 -1
- package/build/database/query/update-base.js +14 -0
- package/build/database/query/with-count.js +3 -1
- package/build/database/record/acts-as-list.js +37 -13
- package/build/database/record/attachments/store.js +336 -26
- package/build/database/record/auditing.js +6 -3
- package/build/database/record/index.js +197 -42
- package/build/database/record/instance-relationships/belongs-to.js +2 -1
- package/build/database/record/validators/uniqueness.js +8 -5
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/build/frontend-model-controller.js +136 -53
- package/build/frontend-model-resource/base-resource.js +87 -37
- package/build/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/build/frontend-models/base.js +307 -59
- package/build/frontend-models/preloader.js +6 -3
- package/build/frontend-models/query.js +15 -6
- package/build/frontend-models/resource-definition.js +21 -2
- package/build/frontend-models/use-destroyed-event.js +1 -1
- package/build/frontend-models/use-model-class-event.js +2 -2
- package/build/frontend-models/use-updated-event.js +1 -1
- package/build/frontend-models/websocket-channel.js +256 -93
- package/build/frontend-models/websocket-publishers.js +218 -18
- package/build/http-server/client/websocket-session.js +13 -1
- package/build/http-server/websocket-channel.js +15 -2
- package/build/src/authorization/ability.d.ts +9 -3
- package/build/src/authorization/ability.d.ts.map +1 -1
- package/build/src/authorization/ability.js +33 -14
- package/build/src/background-jobs/adapter.d.ts +6 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +9 -1
- package/build/src/background-jobs/main.d.ts +6 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +34 -11
- package/build/src/background-jobs/store.d.ts +66 -12
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +191 -35
- package/build/src/background-jobs/types.d.ts +55 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +16 -1
- package/build/src/configuration-types.d.ts +3 -3
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +18 -8
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +38 -7
- package/build/src/database/drivers/base-column.d.ts +5 -0
- package/build/src/database/drivers/base-column.d.ts.map +1 -1
- package/build/src/database/drivers/base-column.js +8 -1
- package/build/src/database/drivers/mssql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/sql/update.js +2 -4
- package/build/src/database/drivers/mysql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/update.js +2 -4
- package/build/src/database/drivers/pgsql/column.d.ts +5 -0
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +19 -1
- package/build/src/database/drivers/pgsql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/sql/update.js +2 -4
- package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +6 -2
- package/build/src/database/drivers/sqlite/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/update.js +2 -4
- package/build/src/database/query/index.d.ts +4 -4
- package/build/src/database/query/index.d.ts.map +1 -1
- package/build/src/database/query/index.js +3 -3
- package/build/src/database/query/model-class-query.d.ts +8 -2
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +25 -11
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +4 -2
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +3 -2
- package/build/src/database/query/update-base.d.ts +7 -0
- package/build/src/database/query/update-base.d.ts.map +1 -1
- package/build/src/database/query/update-base.js +13 -1
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +3 -2
- package/build/src/database/record/acts-as-list.d.ts.map +1 -1
- package/build/src/database/record/acts-as-list.js +35 -14
- package/build/src/database/record/attachments/store.d.ts +67 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +301 -25
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +7 -4
- package/build/src/database/record/index.d.ts +78 -26
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +171 -42
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +3 -2
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +8 -6
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +4 -4
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -12
- package/build/src/frontend-model-controller.d.ts +29 -8
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +122 -51
- package/build/src/frontend-model-resource/base-resource.d.ts +51 -24
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +83 -37
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts +45 -26
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/velocious-attachment-resource.js +89 -31
- package/build/src/frontend-models/base.d.ts +53 -20
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +275 -59
- package/build/src/frontend-models/preloader.d.ts.map +1 -1
- package/build/src/frontend-models/preloader.js +6 -4
- package/build/src/frontend-models/query.d.ts +2 -2
- package/build/src/frontend-models/query.d.ts.map +1 -1
- package/build/src/frontend-models/query.js +14 -7
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -3
- package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
- package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-destroyed-event.js +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-model-class-event.js +3 -3
- package/build/src/frontend-models/use-updated-event.d.ts +1 -1
- package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-updated-event.js +2 -2
- package/build/src/frontend-models/websocket-channel.d.ts +60 -41
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +225 -85
- package/build/src/frontend-models/websocket-publishers.d.ts +9 -0
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +190 -19
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +11 -2
- package/build/src/http-server/websocket-channel.d.ts +20 -8
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +15 -3
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +4 -3
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +3 -2
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
- package/build/src/sync/sync-envelope-replay-service.js +6 -5
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +4 -3
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts +2 -2
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +3 -3
- package/build/src/testing/test-profiler.d.ts +4 -4
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +3 -3
- package/build/src/testing/test-runner.d.ts +271 -117
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +496 -427
- package/build/src/testing/test.d.ts +11 -72
- package/build/src/testing/test.d.ts.map +1 -1
- package/build/src/testing/test.js +131 -284
- package/build/src/testing/velocious-attempt-executor.d.ts +14 -29
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-attempt-executor.js +52 -46
- package/build/src/testing/velocious-runner-reporter.d.ts +29 -34
- package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -1
- package/build/src/testing/velocious-runner-reporter.js +145 -57
- package/build/src/testing/velocious-suite-hook-executor.d.ts +5 -23
- package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-suite-hook-executor.js +22 -47
- package/build/src/testing/velocious-test-arguments.d.ts +11 -6
- package/build/src/testing/velocious-test-arguments.d.ts.map +1 -1
- package/build/src/testing/velocious-test-arguments.js +18 -10
- package/build/src/utils/model-primary-key.d.ts +73 -0
- package/build/src/utils/model-primary-key.d.ts.map +1 -0
- package/build/src/utils/model-primary-key.js +144 -0
- package/build/sync/sync-api-client.js +3 -2
- package/build/sync/sync-client.js +2 -1
- package/build/sync/sync-envelope-replay-service.js +5 -4
- package/build/sync/sync-publisher.js +3 -2
- package/build/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/build/testing/test-profiler.js +2 -2
- package/build/testing/test-runner.js +493 -449
- package/build/testing/test.js +164 -306
- package/build/testing/velocious-attempt-executor.js +55 -45
- package/build/testing/velocious-runner-reporter.js +156 -58
- package/build/testing/velocious-suite-hook-executor.js +22 -50
- package/build/testing/velocious-test-arguments.js +19 -10
- package/build/utils/model-primary-key.js +171 -0
- package/package.json +6 -5
- package/src/authorization/ability.js +36 -13
- package/src/background-jobs/adapter.js +9 -0
- package/src/background-jobs/main.js +35 -10
- package/src/background-jobs/store.js +217 -38
- package/src/background-jobs/types.js +15 -0
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +45 -9
- package/src/database/drivers/base-column.js +8 -0
- package/src/database/drivers/mssql/sql/update.js +1 -3
- package/src/database/drivers/mysql/sql/update.js +1 -3
- package/src/database/drivers/pgsql/column.js +22 -0
- package/src/database/drivers/pgsql/sql/update.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/src/database/drivers/sqlite/sql/update.js +1 -3
- package/src/database/query/index.js +2 -2
- package/src/database/query/model-class-query.js +26 -10
- package/src/database/query/preloader/has-many.js +4 -1
- package/src/database/query/query-data.js +2 -1
- package/src/database/query/update-base.js +14 -0
- package/src/database/query/with-count.js +3 -1
- package/src/database/record/acts-as-list.js +37 -13
- package/src/database/record/attachments/store.js +336 -26
- package/src/database/record/auditing.js +6 -3
- package/src/database/record/index.js +197 -42
- package/src/database/record/instance-relationships/belongs-to.js +2 -1
- package/src/database/record/validators/uniqueness.js +8 -5
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/src/frontend-model-controller.js +136 -53
- package/src/frontend-model-resource/base-resource.js +87 -37
- package/src/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/src/frontend-models/base.js +307 -59
- package/src/frontend-models/preloader.js +6 -3
- package/src/frontend-models/query.js +15 -6
- package/src/frontend-models/resource-definition.js +21 -2
- package/src/frontend-models/use-destroyed-event.js +1 -1
- package/src/frontend-models/use-model-class-event.js +2 -2
- package/src/frontend-models/use-updated-event.js +1 -1
- package/src/frontend-models/websocket-channel.js +256 -93
- package/src/frontend-models/websocket-publishers.js +218 -18
- package/src/http-server/client/websocket-session.js +13 -1
- package/src/http-server/websocket-channel.js +15 -2
- package/src/sync/sync-api-client.js +3 -2
- package/src/sync/sync-client.js +2 -1
- package/src/sync/sync-envelope-replay-service.js +5 -4
- package/src/sync/sync-publisher.js +3 -2
- package/src/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/src/testing/test-profiler.js +2 -2
- package/src/testing/test-runner.js +493 -449
- package/src/testing/test.js +164 -306
- package/src/testing/velocious-attempt-executor.js +55 -45
- package/src/testing/velocious-runner-reporter.js +156 -58
- package/src/testing/velocious-suite-hook-executor.js +22 -50
- package/src/testing/velocious-test-arguments.js +19 -10
- package/src/utils/model-primary-key.js +171 -0
- package/build/src/testing/testing-package-adapter.d.ts +0 -9
- package/build/src/testing/testing-package-adapter.d.ts.map +0 -1
- package/build/src/testing/testing-package-adapter.js +0 -69
- package/build/testing/testing-package-adapter.js +0 -76
- package/build/tsconfig.tsbuildinfo +0 -1
- package/src/testing/testing-package-adapter.js +0 -76
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Concurrent multi threadded web server
|
|
4
4
|
* Database framework with familiar MVC concepts
|
|
5
5
|
* Database models with migrations and validations
|
|
6
|
-
* Database models that work almost the same in frontend and backend
|
|
6
|
+
* Database models that work almost the same in frontend and backend, including online CRUD for [composite primary keys](docs/composite-primary-keys.md)
|
|
7
7
|
* Connection-scoped advisory locks with automatic cleanup before pooled connections are reused or closed (see [docs/advisory-locks.md](docs/advisory-locks.md))
|
|
8
8
|
* Built-in record auditing for model lifecycle changes (see [docs/auditing.md](docs/auditing.md))
|
|
9
9
|
* Declarative state machines for models, with typed event methods generated into the base model (see [docs/state-machine.md](docs/state-machine.md))
|
|
@@ -178,10 +178,10 @@ Baselines are generated against a fresh checkout (no generated dummy `configurat
|
|
|
178
178
|
# Testing
|
|
179
179
|
|
|
180
180
|
Application tests may import the testing DSL from the independent public package.
|
|
181
|
-
`@velocious/testing` `0.0.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
supported.
|
|
181
|
+
`@velocious/testing` `0.0.9` is the declaration and execution engine. Compatible
|
|
182
|
+
installed copies share one protocol-1/schema-3 default registry. Velocious adapts each
|
|
183
|
+
package-owned attempt with its database, request, profiling, and cleanup behavior; the
|
|
184
|
+
existing Velocious facade exports the same declaration DSL and remains supported.
|
|
185
185
|
|
|
186
186
|
```js
|
|
187
187
|
import {describe, expect, it} from "@velocious/testing"
|
|
@@ -851,7 +851,7 @@ attempts each shared digest only once per reconciliation. On-demand resolution
|
|
|
851
851
|
rechecks the backing blob after cleanup and returns `null` instead of a stale
|
|
852
852
|
local URI when concurrent eviction removed it. Cleanup deferred by an active
|
|
853
853
|
cached resolution runs again after that digest's final guard releases.
|
|
854
|
-
Attachment metadata is exposed through the built-in `VelociousAttachment` frontend model with safe fields only: `id`, `recordType`, `recordId`, `name`, `position`, `filename`, `contentType`, `byteSize`, `createdAt`, and `updatedAt`. Storage internals such as `driver`, `storageKey`, and `contentBase64` remain hidden and non-queryable.
|
|
854
|
+
Attachment metadata is exposed through the built-in `VelociousAttachment` frontend model with safe fields only: `id`, `recordType`, `recordId`, `name`, `position`, `filename`, `contentType`, `byteSize`, `createdAt`, and `updatedAt`. Storage internals such as `driver`, `storageKey`, and `contentBase64` remain hidden and non-queryable. Metadata collection queries require owner filters: `resourceName`, `recordType`, `recordId`, and `name`. Composite `recordId` values retain the complete canonical tuple without a 255-character limit, and key-changing saves rekey attachment ownership in the record transaction. `VelociousAttachment.find(id)` uses the member endpoint and authorizes against configured resource aliases backed by the attachment owner type.
|
|
855
855
|
|
|
856
856
|
When your frontend app calls a backend on another host/port (or under a path prefix), configure transport once:
|
|
857
857
|
|
|
@@ -2246,8 +2246,10 @@ If you are using Velocious for an app, Velocious has a built-in testing framewor
|
|
|
2246
2246
|
npx velocious test
|
|
2247
2247
|
```
|
|
2248
2248
|
|
|
2249
|
-
Test declarations can be imported from `@velocious/testing`; the
|
|
2250
|
-
`velocious/build/src/testing/test.js` facade remains
|
|
2249
|
+
Test declarations can be imported from `@velocious/testing`; the compatibility
|
|
2250
|
+
`velocious/build/src/testing/test.js` facade uses the same package registry and remains
|
|
2251
|
+
supported. Under Velocious, ordinary callbacks receive `testArgs`, while `it.each`
|
|
2252
|
+
callbacks receive their row arguments followed by `testArgs`.
|
|
2251
2253
|
|
|
2252
2254
|
If you are developing on Velocious, you can run the tests with:
|
|
2253
2255
|
|
|
@@ -2799,7 +2801,7 @@ backgroundJobs: {
|
|
|
2799
2801
|
}
|
|
2800
2802
|
```
|
|
2801
2803
|
|
|
2802
|
-
A job with no queue runs on `"default"`; a queue with no cap is unlimited. Caps are enforced through the durable per-key concurrency mechanism (the reserved `queue:<name>` key) and hold regardless of how many worker processes run.
|
|
2804
|
+
A job with no queue runs on `"default"`; a queue with no cap is unlimited. Caps are enforced through the durable per-key concurrency mechanism (the reserved `queue:<name>` key) and hold regardless of how many worker processes run. Queue-policy changes are adopted by queued backlog rows when `background-jobs-main` starts; already handed-off jobs drain under their original policy. If they return, reschedule, or retry, the reporting generation releases the original reservation without changing shared policy, and the active generation applies its current queue policy immediately before the next handoff and sends that committed policy to the worker; explicit concurrency remains unchanged. Handoff persistence is fenced against concurrent policy changes. Startup also rebuilds durable active counts under a database advisory lock. The active main then checks those counts every minute, performs no counter writes while they are exact, repairs only locked mismatches, logs a bounded structured repair summary, and immediately retries dispatch so stale capacity cannot require a restart. `db:migrate`, `db:tenants:*`, and routine store/application initialization with an intact jobs table never adopt queued jobs or rebuild global concurrency counts. If schema repair must recreate a missing `background_jobs` table while the migration marker and concurrency table survive, it resets the now-orphaned active counts against that newly empty table. Scheduled jobs honor a job's `static queue` too.
|
|
2803
2805
|
|
|
2804
2806
|
Set `priority` (default `0`) to dispatch a queue ahead of lower-priority ones regardless of enqueue order, so a small time-critical queue is never starved by a flood of low-priority work sharing a worker pool. Unlike Sidekiq's strict queue ordering, priority composes with the caps: a higher-priority queue already at its `maxConcurrent` is skipped and dispatch falls through to the next eligible job. See [docs/background-jobs.md](docs/background-jobs.md#queues-per-queue-concurrency-caps).
|
|
2805
2807
|
|
|
@@ -189,9 +189,10 @@ export default class VelociousAuthorizationAbility {
|
|
|
189
189
|
* @param {string} args.action - Requested action.
|
|
190
190
|
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
191
191
|
* @param {import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} args.query - Query.
|
|
192
|
+
* @param {() => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} [args.ruleQueryFactory] - Optional factory for the queries that evaluate individual conditional rules.
|
|
192
193
|
* @returns {import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} - Authorized query.
|
|
193
194
|
*/
|
|
194
|
-
applyToQuery({action, modelClass, query}) {
|
|
195
|
+
applyToQuery({action, modelClass, query, ruleQueryFactory}) {
|
|
195
196
|
this.loadAbilitiesForModelClass(modelClass)
|
|
196
197
|
|
|
197
198
|
const applicableRules = this.rulesFor({action, modelClass})
|
|
@@ -203,18 +204,18 @@ export default class VelociousAuthorizationAbility {
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
if (allowRules.some((rule) => !rule.conditions)) {
|
|
206
|
-
this.applyDenyRules({action, denyRules, modelClass, query})
|
|
207
|
+
this.applyDenyRules({action, denyRules, modelClass, query, ruleQueryFactory})
|
|
207
208
|
return query
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
const allowSqlParts = this.conditionSqlParts({action, modelClass, query, rules: allowRules})
|
|
211
|
+
const allowSqlParts = this.conditionSqlParts({action, modelClass, query, ruleQueryFactory, rules: allowRules})
|
|
211
212
|
|
|
212
213
|
if (allowSqlParts.length === 0) {
|
|
213
214
|
return query.where("1=0")
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
query.where(`(${allowSqlParts.join(" OR ")})`)
|
|
217
|
-
this.applyDenyRules({action, denyRules, modelClass, query})
|
|
218
|
+
this.applyDenyRules({action, denyRules, modelClass, query, ruleQueryFactory})
|
|
218
219
|
|
|
219
220
|
return query
|
|
220
221
|
}
|
|
@@ -240,19 +241,20 @@ export default class VelociousAuthorizationAbility {
|
|
|
240
241
|
* @param {string} args.action - Action.
|
|
241
242
|
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
242
243
|
* @param {import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} args.query - Base query.
|
|
244
|
+
* @param {() => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} [args.ruleQueryFactory] - Optional conditional-rule query factory.
|
|
243
245
|
* @param {AbilityRuleType[]} args.rules - Rules.
|
|
244
246
|
* @returns {string[]} - SQL condition parts.
|
|
245
247
|
*/
|
|
246
|
-
conditionSqlParts({action, modelClass, query, rules}) {
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const
|
|
248
|
+
conditionSqlParts({action, modelClass, query, ruleQueryFactory, rules}) {
|
|
249
|
+
const primaryKey = modelClass.primaryKey()
|
|
250
|
+
const primaryKeyAttributes = Array.isArray(primaryKey) ? primaryKey : [primaryKey]
|
|
251
|
+
const quotedBaseTable = query.driver.quoteTable(query.getTableReferenceForJoin())
|
|
250
252
|
const sqlParts = []
|
|
251
253
|
|
|
252
254
|
for (const rule of rules) {
|
|
253
255
|
if (!rule.conditions) continue
|
|
254
256
|
|
|
255
|
-
const scopedQuery = modelClass._newQuery()
|
|
257
|
+
const scopedQuery = ruleQueryFactory ? ruleQueryFactory() : modelClass._newQuery()
|
|
256
258
|
const resultQuery = this.applyRuleCondition({
|
|
257
259
|
action,
|
|
258
260
|
conditions: rule.conditions,
|
|
@@ -260,7 +262,16 @@ export default class VelociousAuthorizationAbility {
|
|
|
260
262
|
query: scopedQuery
|
|
261
263
|
})
|
|
262
264
|
const finalQuery = resultQuery || scopedQuery
|
|
263
|
-
|
|
265
|
+
|
|
266
|
+
if (ruleQueryFactory && finalQuery !== scopedQuery) {
|
|
267
|
+
const finalFroms = finalQuery.getFroms()
|
|
268
|
+
|
|
269
|
+
finalFroms.splice(0, finalFroms.length, ...scopedQuery.getFroms())
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const quotedScopedTable = query.driver.quoteTable(finalQuery.getTableReferenceForJoin())
|
|
273
|
+
const primaryKeyColumns = primaryKeyAttributes.map((attributeName) => modelClass.getColumnNameForAttributeName(attributeName))
|
|
274
|
+
const selectedPkSql = primaryKeyColumns.map((columnName) => `${quotedScopedTable}.${query.driver.quoteColumn(columnName)}`)
|
|
264
275
|
|
|
265
276
|
if (finalQuery._distinct) {
|
|
266
277
|
query.distinct(true)
|
|
@@ -268,7 +279,18 @@ export default class VelociousAuthorizationAbility {
|
|
|
268
279
|
|
|
269
280
|
finalQuery.select(selectedPkSql)
|
|
270
281
|
|
|
271
|
-
|
|
282
|
+
if (Array.isArray(primaryKey)) {
|
|
283
|
+
const authorizedRowsAlias = query.driver.quoteTable("velocious_authorized_rows")
|
|
284
|
+
const identitySql = primaryKeyColumns.map((columnName) => {
|
|
285
|
+
const quotedColumn = query.driver.quoteColumn(columnName)
|
|
286
|
+
|
|
287
|
+
return `${authorizedRowsAlias}.${quotedColumn} = ${quotedBaseTable}.${quotedColumn}`
|
|
288
|
+
}).join(" AND ")
|
|
289
|
+
|
|
290
|
+
sqlParts.push(`EXISTS (SELECT 1 FROM (${finalQuery.toSql()}) AS ${authorizedRowsAlias} WHERE ${identitySql})`)
|
|
291
|
+
} else {
|
|
292
|
+
sqlParts.push(`${quotedBaseTable}.${query.driver.quoteColumn(primaryKeyColumns[0])} IN (${finalQuery.toSql()})`)
|
|
293
|
+
}
|
|
272
294
|
}
|
|
273
295
|
|
|
274
296
|
return sqlParts
|
|
@@ -281,9 +303,10 @@ export default class VelociousAuthorizationAbility {
|
|
|
281
303
|
* @param {AbilityRuleType[]} args.denyRules - Deny rules.
|
|
282
304
|
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
283
305
|
* @param {import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} args.query - Query.
|
|
306
|
+
* @param {() => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} [args.ruleQueryFactory] - Optional conditional-rule query factory.
|
|
284
307
|
* @returns {void} - No return value.
|
|
285
308
|
*/
|
|
286
|
-
applyDenyRules({action, denyRules, modelClass, query}) {
|
|
309
|
+
applyDenyRules({action, denyRules, modelClass, query, ruleQueryFactory}) {
|
|
287
310
|
if (denyRules.length === 0) return
|
|
288
311
|
|
|
289
312
|
if (denyRules.some((rule) => !rule.conditions)) {
|
|
@@ -291,7 +314,7 @@ export default class VelociousAuthorizationAbility {
|
|
|
291
314
|
return
|
|
292
315
|
}
|
|
293
316
|
|
|
294
|
-
const denySqlParts = this.conditionSqlParts({action, modelClass, query, rules: denyRules})
|
|
317
|
+
const denySqlParts = this.conditionSqlParts({action, modelClass, query, ruleQueryFactory, rules: denyRules})
|
|
295
318
|
|
|
296
319
|
if (denySqlParts.length > 0) {
|
|
297
320
|
query.where(`NOT (${denySqlParts.join(" OR ")})`)
|
|
@@ -48,6 +48,15 @@ export default class BackgroundJobsAdapter {
|
|
|
48
48
|
*/
|
|
49
49
|
async reconcileQueueConcurrency() { throw new Error("BackgroundJobsAdapter#reconcileQueueConcurrency is not implemented") }
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Repairs drift in adapter-owned durable active concurrency counts. Adapters
|
|
53
|
+
* without duplicate active-count persistence can keep this no-op result.
|
|
54
|
+
* @returns {Promise<import("./types.js").BackgroundJobConcurrencyReconciliation>} - Repair summary.
|
|
55
|
+
*/
|
|
56
|
+
async reconcileActiveConcurrency() {
|
|
57
|
+
return {candidateCount: 0, checkedCount: 0, repairedCount: 0, repairs: [], repairsTruncatedCount: 0}
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
/**
|
|
52
61
|
* Enqueues a job.
|
|
53
62
|
* @param {{jobName: string, args: Array<ReturnType<typeof JSON.parse>>, options?: import("./types.js").BackgroundJobOptions}} _args - Job request.
|
|
@@ -2063,23 +2063,25 @@ export default class BackgroundJobsMain {
|
|
|
2063
2063
|
handoffs.set(job.id, handoff.handoffId)
|
|
2064
2064
|
|
|
2065
2065
|
try {
|
|
2066
|
+
const dispatchedJob = handoff.job || job
|
|
2067
|
+
|
|
2066
2068
|
worker.send({
|
|
2067
2069
|
type: "job",
|
|
2068
2070
|
payload: {
|
|
2069
|
-
id:
|
|
2070
|
-
jobName:
|
|
2071
|
-
args:
|
|
2071
|
+
id: dispatchedJob.id,
|
|
2072
|
+
jobName: dispatchedJob.jobName,
|
|
2073
|
+
args: dispatchedJob.args,
|
|
2072
2074
|
handoffId: handoff.handoffId,
|
|
2073
2075
|
workerId: worker.workerId,
|
|
2074
2076
|
handedOffAtMs: handoff.handedOffAtMs,
|
|
2075
2077
|
options: {
|
|
2076
|
-
concurrencyKey:
|
|
2077
|
-
executionMode:
|
|
2078
|
-
maxConcurrency:
|
|
2079
|
-
maxRetries:
|
|
2080
|
-
queue:
|
|
2081
|
-
scheduledAtMs:
|
|
2082
|
-
...(
|
|
2078
|
+
concurrencyKey: dispatchedJob.concurrencyKey || undefined,
|
|
2079
|
+
executionMode: dispatchedJob.executionMode,
|
|
2080
|
+
maxConcurrency: dispatchedJob.maxConcurrency ?? undefined,
|
|
2081
|
+
maxRetries: dispatchedJob.maxRetries ?? undefined,
|
|
2082
|
+
queue: dispatchedJob.queue,
|
|
2083
|
+
scheduledAtMs: dispatchedJob.scheduledAtMs ?? undefined,
|
|
2084
|
+
...(dispatchedJob.timeoutMs === null ? {} : {timeoutMs: dispatchedJob.timeoutMs})
|
|
2083
2085
|
}
|
|
2084
2086
|
}
|
|
2085
2087
|
})
|
|
@@ -2354,6 +2356,29 @@ export default class BackgroundJobsMain {
|
|
|
2354
2356
|
errorEvents.emit("framework-error", payload)
|
|
2355
2357
|
errorEvents.emit("all-error", {...payload, errorType: "framework-error"})
|
|
2356
2358
|
}
|
|
2359
|
+
|
|
2360
|
+
if (this.lifecycleState === "active") await this._reconcileActiveConcurrency()
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
/**
|
|
2364
|
+
* Repairs durable admission counters on the active main's maintenance cadence
|
|
2365
|
+
* and immediately retries dispatch when capacity was recovered.
|
|
2366
|
+
* @returns {Promise<void>} - Resolves after repair and any resulting drain.
|
|
2367
|
+
*/
|
|
2368
|
+
async _reconcileActiveConcurrency() {
|
|
2369
|
+
try {
|
|
2370
|
+
const result = await this.store.reconcileActiveConcurrency()
|
|
2371
|
+
|
|
2372
|
+
if (result.repairedCount > 0) await this._drain()
|
|
2373
|
+
} catch (error) {
|
|
2374
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error))
|
|
2375
|
+
const payload = {context: {generationId: this.generationId, stage: "background-job-concurrency-reconciliation"}, error: normalizedError}
|
|
2376
|
+
const errorEvents = this.configuration.getErrorEvents()
|
|
2377
|
+
|
|
2378
|
+
this.logger.error(() => ["Failed to reconcile background job active-concurrency counts:", normalizedError])
|
|
2379
|
+
errorEvents.emit("framework-error", payload)
|
|
2380
|
+
errorEvents.emit("all-error", {...payload, errorType: "framework-error"})
|
|
2381
|
+
}
|
|
2357
2382
|
}
|
|
2358
2383
|
|
|
2359
2384
|
/**
|