velocious 1.0.510 → 1.0.512
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 +1 -0
- package/build/database/advisory-lock-runner.js +192 -0
- package/build/database/drivers/mysql/index.js +3 -0
- package/build/database/live-query.js +244 -0
- package/build/database/record/index.js +48 -111
- package/build/database/record-changes.js +135 -0
- package/build/database/use-live-query.js +118 -0
- package/build/src/configuration-types.d.ts +1 -1
- package/build/src/configuration.d.ts +1 -1
- package/build/src/database/advisory-lock-runner.d.ts +124 -0
- package/build/src/database/advisory-lock-runner.d.ts.map +1 -0
- package/build/src/database/advisory-lock-runner.js +176 -0
- package/build/src/database/drivers/mysql/index.d.ts +2 -2
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +3 -1
- package/build/src/database/live-query.d.ts +168 -0
- package/build/src/database/live-query.d.ts.map +1 -0
- package/build/src/database/live-query.js +211 -0
- package/build/src/database/record/attachments/handle.d.ts +1 -1
- package/build/src/database/record/index.d.ts +24 -61
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +46 -108
- package/build/src/database/record-changes.d.ts +111 -0
- package/build/src/database/record-changes.d.ts.map +1 -0
- package/build/src/database/record-changes.js +115 -0
- package/build/src/database/use-live-query.d.ts +44 -0
- package/build/src/database/use-live-query.d.ts.map +1 -0
- package/build/src/database/use-live-query.js +97 -0
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +17 -10
- package/build/src/sync/sync-envelope-replay-service.d.ts +1 -1
- package/build/src/sync/sync-realtime-bridge.d.ts.map +1 -1
- package/build/src/sync/sync-realtime-bridge.js +9 -5
- package/build/src/sync/sync-replay-upsert-applier.d.ts +2 -2
- package/build/sync/sync-api-client.js +19 -11
- package/build/sync/sync-realtime-bridge.js +9 -4
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/database/advisory-lock-runner.js +192 -0
- package/src/database/drivers/mysql/index.js +3 -0
- package/src/database/live-query.js +244 -0
- package/src/database/record/index.js +48 -111
- package/src/database/record-changes.js +135 -0
- package/src/database/use-live-query.js +118 -0
- package/src/sync/sync-api-client.js +19 -11
- package/src/sync/sync-realtime-bridge.js +9 -4
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import recordChanges from "../database/record-changes.js"
|
|
4
|
+
|
|
3
5
|
import SyncApiClient from "./sync-api-client.js"
|
|
4
6
|
import {VELOCIOUS_SYNC_CHANNEL} from "./sync-channel-name.js"
|
|
5
7
|
|
|
@@ -350,11 +352,14 @@ export default class SyncRealtimeBridge {
|
|
|
350
352
|
const syncPayloads = Array.isArray(body.syncs) ? body.syncs : [body]
|
|
351
353
|
const applySync = this.syncClient.remoteApplySync({source: "remote change"})
|
|
352
354
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
+
// Coalesce record-change events across the pushed batch so it triggers one live-query re-run.
|
|
356
|
+
await recordChanges.batch(async () => {
|
|
357
|
+
for (const syncPayload of syncPayloads) {
|
|
358
|
+
const sync = SyncApiClient.syncEnvelopeFromPayload({resourceType, ...syncPayload})
|
|
355
359
|
|
|
356
|
-
|
|
357
|
-
|
|
360
|
+
await applySync(sync)
|
|
361
|
+
}
|
|
362
|
+
})
|
|
358
363
|
}
|
|
359
364
|
|
|
360
365
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../index.js","../bin/velocious.js","../src/application.js","../src/configuration-resolver.js","../src/configuration-types.js","../src/configuration.js","../src/controller.js","../src/current-configuration.js","../src/current.js","../src/error-logger.js","../src/frontend-model-controller.js","../src/initializer.js","../src/logger.js","../src/mailer.js","../src/record-payload-values.js","../src/time-zone.js","../src/velocious-error.js","../src/authorization/ability.js","../src/authorization/base-resource.js","../src/background-jobs/client.js","../src/background-jobs/cron-expression.js","../src/background-jobs/forked-runner-child.js","../src/background-jobs/job-record.js","../src/background-jobs/job-registry.js","../src/background-jobs/job-runner.js","../src/background-jobs/job.js","../src/background-jobs/json-socket.js","../src/background-jobs/main.js","../src/background-jobs/normalize-error.js","../src/background-jobs/scheduler.js","../src/background-jobs/socket-request.js","../src/background-jobs/status-reporter.js","../src/background-jobs/store.js","../src/background-jobs/types.js","../src/background-jobs/worker.js","../src/background-jobs/web/authorization.js","../src/background-jobs/web/controller.js","../src/background-jobs/web/index.js","../src/background-jobs/web/path-matcher.js","../src/background-jobs/web/registry.js","../src/beacon/client.js","../src/beacon/in-process-broker.js","../src/beacon/in-process-client.js","../src/beacon/server.js","../src/beacon/types.js","../src/cli/base-command.js","../src/cli/browser-cli.js","../src/cli/index.js","../src/cli/tenant-database-command-helper.js","../src/cli/use-browser-cli.js","../src/cli/commands/background-jobs-main.js","../src/cli/commands/background-jobs-runner.js","../src/cli/commands/background-jobs-worker.js","../src/cli/commands/beacon.js","../src/cli/commands/console.js","../src/cli/commands/init.js","../src/cli/commands/routes.js","../src/cli/commands/run-script.js","../src/cli/commands/runner.js","../src/cli/commands/server.js","../src/cli/commands/test.js","../src/cli/commands/db/base-command.js","../src/cli/commands/db/create.js","../src/cli/commands/db/drop.js","../src/cli/commands/db/migrate.js","../src/cli/commands/db/reset.js","../src/cli/commands/db/rollback.js","../src/cli/commands/db/seed.js","../src/cli/commands/db/schema/dump.js","../src/cli/commands/db/schema/load.js","../src/cli/commands/db/tenants/check.js","../src/cli/commands/db/tenants/create.js","../src/cli/commands/db/tenants/drop.js","../src/cli/commands/db/tenants/migrate.js","../src/cli/commands/destroy/migration.js","../src/cli/commands/generate/base-models.js","../src/cli/commands/generate/frontend-models.js","../src/cli/commands/generate/migration.js","../src/cli/commands/generate/model.js","../src/cli/commands/lint/relationships.js","../src/database/annotations-async-hooks.js","../src/database/annotations.js","../src/database/column-types.js","../src/database/datetime-storage.js","../src/database/handler.js","../src/database/initializer-from-require-context.js","../src/database/migrations-ledger.js","../src/database/migrator.js","../src/database/use-database.js","../src/database/drivers/base-column.js","../src/database/drivers/base-columns-index.js","../src/database/drivers/base-foreign-key.js","../src/database/drivers/base-table.js","../src/database/drivers/base.js","../src/database/drivers/mssql/column.js","../src/database/drivers/mssql/columns-index.js","../src/database/drivers/mssql/connect-connection.js","../src/database/drivers/mssql/foreign-key.js","../src/database/drivers/mssql/index.js","../src/database/drivers/mssql/options.js","../src/database/drivers/mssql/query-parser.js","../src/database/drivers/mssql/structure-sql.js","../src/database/drivers/mssql/table.js","../src/database/drivers/mssql/sql/alter-table.js","../src/database/drivers/mssql/sql/create-database.js","../src/database/drivers/mssql/sql/create-index.js","../src/database/drivers/mssql/sql/create-table.js","../src/database/drivers/mssql/sql/delete.js","../src/database/drivers/mssql/sql/drop-database.js","../src/database/drivers/mssql/sql/drop-table.js","../src/database/drivers/mssql/sql/insert.js","../src/database/drivers/mssql/sql/remove-index.js","../src/database/drivers/mssql/sql/update.js","../src/database/drivers/mssql/sql/upsert.js","../src/database/drivers/mysql/column.js","../src/database/drivers/mysql/columns-index.js","../src/database/drivers/mysql/foreign-key.js","../src/database/drivers/mysql/index.js","../src/database/drivers/mysql/options.js","../src/database/drivers/mysql/query-parser.js","../src/database/drivers/mysql/query.js","../src/database/drivers/mysql/structure-sql.js","../src/database/drivers/mysql/table.js","../src/database/drivers/mysql/sql/alter-table.js","../src/database/drivers/mysql/sql/create-database.js","../src/database/drivers/mysql/sql/create-index.js","../src/database/drivers/mysql/sql/create-table.js","../src/database/drivers/mysql/sql/delete.js","../src/database/drivers/mysql/sql/drop-database.js","../src/database/drivers/mysql/sql/drop-table.js","../src/database/drivers/mysql/sql/insert.js","../src/database/drivers/mysql/sql/remove-index.js","../src/database/drivers/mysql/sql/update.js","../src/database/drivers/mysql/sql/upsert.js","../src/database/drivers/pgsql/column.js","../src/database/drivers/pgsql/columns-index.js","../src/database/drivers/pgsql/foreign-key.js","../src/database/drivers/pgsql/index.js","../src/database/drivers/pgsql/options.js","../src/database/drivers/pgsql/query-parser.js","../src/database/drivers/pgsql/structure-sql.js","../src/database/drivers/pgsql/table.js","../src/database/drivers/pgsql/sql/alter-table.js","../src/database/drivers/pgsql/sql/create-database.js","../src/database/drivers/pgsql/sql/create-index.js","../src/database/drivers/pgsql/sql/create-table.js","../src/database/drivers/pgsql/sql/delete.js","../src/database/drivers/pgsql/sql/drop-database.js","../src/database/drivers/pgsql/sql/drop-table.js","../src/database/drivers/pgsql/sql/insert.js","../src/database/drivers/pgsql/sql/remove-index.js","../src/database/drivers/pgsql/sql/update.js","../src/database/drivers/pgsql/sql/upsert.js","../src/database/drivers/sqlite/base.js","../src/database/drivers/sqlite/column.js","../src/database/drivers/sqlite/columns-index.js","../src/database/drivers/sqlite/connection-sql-js.js","../src/database/drivers/sqlite/foreign-key.js","../src/database/drivers/sqlite/index.js","../src/database/drivers/sqlite/index.native.js","../src/database/drivers/sqlite/index.web.js","../src/database/drivers/sqlite/options.js","../src/database/drivers/sqlite/query-parser.js","../src/database/drivers/sqlite/query.js","../src/database/drivers/sqlite/query.native.js","../src/database/drivers/sqlite/query.web.js","../src/database/drivers/sqlite/structure-sql.js","../src/database/drivers/sqlite/table-rebuilder.js","../src/database/drivers/sqlite/table.js","../src/database/drivers/sqlite/web-persistence.js","../src/database/drivers/sqlite/sql/alter-table.js","../src/database/drivers/sqlite/sql/create-index.js","../src/database/drivers/sqlite/sql/create-table.js","../src/database/drivers/sqlite/sql/delete.js","../src/database/drivers/sqlite/sql/drop-table.js","../src/database/drivers/sqlite/sql/insert.js","../src/database/drivers/sqlite/sql/remove-index.js","../src/database/drivers/sqlite/sql/update.js","../src/database/drivers/sqlite/sql/upsert.js","../src/database/drivers/structure-sql/utils.js","../src/database/migration/index.js","../src/database/migrator/files-finder.js","../src/database/migrator/types.js","../src/database/pool/async-tracked-multi-connection.js","../src/database/pool/base-methods-forward.js","../src/database/pool/base.js","../src/database/pool/single-multi-use.js","../src/database/query/alter-table-base.js","../src/database/query/base.js","../src/database/query/create-database-base.js","../src/database/query/create-index-base.js","../src/database/query/create-table-base.js","../src/database/query/delete-base.js","../src/database/query/drop-database-base.js","../src/database/query/drop-table-base.js","../src/database/query/from-base.js","../src/database/query/from-plain.js","../src/database/query/from-table.js","../src/database/query/index.js","../src/database/query/insert-base.js","../src/database/query/join-base.js","../src/database/query/join-object.js","../src/database/query/join-plain.js","../src/database/query/join-tracker.js","../src/database/query/model-class-query.js","../src/database/query/order-base.js","../src/database/query/order-column.js","../src/database/query/order-plain.js","../src/database/query/preloader.js","../src/database/query/query-data.js","../src/database/query/remove-index-base.js","../src/database/query/select-base.js","../src/database/query/select-plain.js","../src/database/query/select-table-and-column.js","../src/database/query/update-base.js","../src/database/query/upsert-base.js","../src/database/query/where-base.js","../src/database/query/where-combinator.js","../src/database/query/where-hash.js","../src/database/query/where-model-class-hash.js","../src/database/query/where-not.js","../src/database/query/where-plain.js","../src/database/query/with-count.js","../src/database/query/preloader/belongs-to.js","../src/database/query/preloader/ensure-model-class-initialized.js","../src/database/query/preloader/has-many.js","../src/database/query/preloader/has-one.js","../src/database/query/preloader/selection.js","../src/database/query-parser/base-query-parser.js","../src/database/query-parser/from-parser.js","../src/database/query-parser/group-parser.js","../src/database/query-parser/joins-parser.js","../src/database/query-parser/limit-parser.js","../src/database/query-parser/options.js","../src/database/query-parser/order-parser.js","../src/database/query-parser/select-parser.js","../src/database/query-parser/where-parser.js","../src/database/record/acts-as-list.js","../src/database/record/auditing.js","../src/database/record/counter-cache-magnitude.js","../src/database/record/index.js","../src/database/record/record-not-found-error.js","../src/database/record/state-machine.js","../src/database/record/user-module.js","../src/database/record/validation-messages.js","../src/database/record/attachments/attachment-record.js","../src/database/record/attachments/download.js","../src/database/record/attachments/handle.js","../src/database/record/attachments/normalize-input.js","../src/database/record/attachments/store.js","../src/database/record/attachments/storage-drivers/filesystem.js","../src/database/record/attachments/storage-drivers/native.js","../src/database/record/attachments/storage-drivers/s3.js","../src/database/record/instance-relationships/base.js","../src/database/record/instance-relationships/belongs-to.js","../src/database/record/instance-relationships/has-many.js","../src/database/record/instance-relationships/has-one.js","../src/database/record/relationships/base.js","../src/database/record/relationships/belongs-to.js","../src/database/record/relationships/has-many.js","../src/database/record/relationships/has-one.js","../src/database/record/validators/base.js","../src/database/record/validators/format.js","../src/database/record/validators/length.js","../src/database/record/validators/presence.js","../src/database/record/validators/uniqueness.js","../src/database/table-data/index.js","../src/database/table-data/table-column.js","../src/database/table-data/table-foreign-key.js","../src/database/table-data/table-index.js","../src/database/table-data/table-reference.js","../src/database/tenants/data-copier.js","../src/database/tenants/schema-cloner.js","../src/database/tenants/tenant-table-plan.js","../src/environment-handlers/base.js","../src/environment-handlers/browser.js","../src/environment-handlers/node.js","../src/environment-handlers/node/cli/commands/background-jobs-main.js","../src/environment-handlers/node/cli/commands/background-jobs-runner.js","../src/environment-handlers/node/cli/commands/background-jobs-worker.js","../src/environment-handlers/node/cli/commands/beacon.js","../src/environment-handlers/node/cli/commands/cli-command-context.js","../src/environment-handlers/node/cli/commands/console.js","../src/environment-handlers/node/cli/commands/init.js","../src/environment-handlers/node/cli/commands/routes.js","../src/environment-handlers/node/cli/commands/run-script.js","../src/environment-handlers/node/cli/commands/runner.js","../src/environment-handlers/node/cli/commands/server.js","../src/environment-handlers/node/cli/commands/test.js","../src/environment-handlers/node/cli/commands/db/seed.js","../src/environment-handlers/node/cli/commands/db/schema/dump.js","../src/environment-handlers/node/cli/commands/db/schema/load.js","../src/environment-handlers/node/cli/commands/destroy/migration.js","../src/environment-handlers/node/cli/commands/generate/base-models.js","../src/environment-handlers/node/cli/commands/generate/frontend-models.js","../src/environment-handlers/node/cli/commands/generate/generated-file-banner.js","../src/environment-handlers/node/cli/commands/generate/migration.js","../src/environment-handlers/node/cli/commands/generate/model.js","../src/environment-handlers/node/cli/commands/lint/relationships.js","../src/error-reporting/request-details.js","../src/frontend-model-resource/base-resource.js","../src/frontend-model-resource/velocious-attachment-resource.js","../src/frontend-models/base.js","../src/frontend-models/built-in-resources.js","../src/frontend-models/clear-pending-debounced-callback.js","../src/frontend-models/event-hook-models.js","../src/frontend-models/model-registry.js","../src/frontend-models/outgoing-event-buffer.js","../src/frontend-models/preloader.js","../src/frontend-models/query.js","../src/frontend-models/resource-config-validation.js","../src/frontend-models/resource-definition.js","../src/frontend-models/transport-serialization.js","../src/frontend-models/use-created-event.js","../src/frontend-models/use-destroyed-event.js","../src/frontend-models/use-model-class-event.js","../src/frontend-models/use-updated-event.js","../src/frontend-models/websocket-channel.js","../src/frontend-models/websocket-publishers.js","../src/http-client/header.js","../src/http-client/index.js","../src/http-client/request.js","../src/http-client/response.js","../src/http-client/websocket-client.js","../src/http-server/cookie.js","../src/http-server/development-reloader.js","../src/http-server/index.js","../src/http-server/remote-address.js","../src/http-server/server-client.js","../src/http-server/server-lock.js","../src/http-server/websocket-channel-subscribers.js","../src/http-server/websocket-channel.js","../src/http-server/websocket-connection.js","../src/http-server/websocket-event-log-store.js","../src/http-server/websocket-events-host.js","../src/http-server/websocket-events.js","../src/http-server/client/index.js","../src/http-server/client/params-to-object.js","../src/http-server/client/request-parser.js","../src/http-server/client/request-runner.js","../src/http-server/client/request-timing.js","../src/http-server/client/request.js","../src/http-server/client/response.js","../src/http-server/client/websocket-request.js","../src/http-server/client/websocket-session.js","../src/http-server/client/request-buffer/form-data-part.js","../src/http-server/client/request-buffer/header.js","../src/http-server/client/request-buffer/index.js","../src/http-server/client/uploaded-file/memory-uploaded-file.js","../src/http-server/client/uploaded-file/temporary-uploaded-file.js","../src/http-server/client/uploaded-file/uploaded-file.js","../src/http-server/worker-handler/channel-subscriber-dispatch.js","../src/http-server/worker-handler/in-process.js","../src/http-server/worker-handler/index.js","../src/http-server/worker-handler/worker-script.js","../src/http-server/worker-handler/worker-thread.js","../src/jobs/mail-delivery.js","../src/logger/base-logger.js","../src/logger/console-logger.js","../src/logger/file-logger.js","../src/logger/outputs/array-output.js","../src/logger/outputs/console-output.js","../src/logger/outputs/file-output.js","../src/logger/outputs/stdout-output.js","../src/mailer/base.js","../src/mailer/delivery.js","../src/mailer/index.js","../src/mailer/backends/smtp.js","../src/packages/velocious-package.js","../src/plugins/sqljs-wasm-route-controller.js","../src/plugins/sqljs-wasm-route.js","../src/routes/app-routes.js","../src/routes/base-route.js","../src/routes/basic-route.js","../src/routes/get-route.js","../src/routes/index.js","../src/routes/namespace-route.js","../src/routes/plugin-routes.js","../src/routes/post-route.js","../src/routes/resolver.js","../src/routes/resource-route.js","../src/routes/root-route.js","../src/routes/built-in/api-manifest/controller.js","../src/routes/built-in/debug/controller.js","../src/routes/built-in/errors/controller.js","../src/routes/hooks/frontend-model-command-route-hook.js","../src/sync/conflict-strategy.js","../src/sync/device-identity.js","../src/sync/local-mutation-log.js","../src/sync/offline-grant.js","../src/sync/peer-mutation-bundle.js","../src/sync/query-scope.js","../src/sync/server-change-feed.js","../src/sync/server-sequence-allocator.js","../src/sync/stable-json.js","../src/sync/sync-api-client-types.js","../src/sync/sync-api-client.js","../src/sync/sync-api-controller.js","../src/sync/sync-change-fanout.js","../src/sync/sync-channel-name.js","../src/sync/sync-client-registry.js","../src/sync/sync-client-types.js","../src/sync/sync-client.js","../src/sync/sync-envelope-replay-service.js","../src/sync/sync-model-change-feed-service.js","../src/sync/sync-publish-suppression.js","../src/sync/sync-publisher-types.js","../src/sync/sync-publisher.js","../src/sync/sync-realtime-bridge.js","../src/sync/sync-replay-upsert-applier.js","../src/sync/sync-resource-base.js","../src/sync/sync-scope-attributes.js","../src/sync/sync-scope-store.js","../src/sync/sync-websocket-channel.js","../src/tenants/default-tenant-database-provisioning.js","../src/tenants/tenant-iterator.js","../src/tenants/tenant.js","../src/testing/base-expect.js","../src/testing/browser-frontend-model-event-hook-scenarios.js","../src/testing/browser-test-app.js","../src/testing/expect-to-change.js","../src/testing/expect-utils.js","../src/testing/expect.js","../src/testing/request-client.js","../src/testing/test-files-finder.js","../src/testing/test-filter-parser.js","../src/testing/test-runner.js","../src/testing/test-suite-splitter.js","../src/testing/test.js","../src/types/external-modules.d.ts","../src/utils/backtrace-cleaner-node.js","../src/utils/backtrace-cleaner.js","../src/utils/deburr-column-name.js","../src/utils/event-emitter.js","../src/utils/file-exists.js","../src/utils/format-value.js","../src/utils/is-date.js","../src/utils/model-scope.js","../src/utils/nest-callbacks.js","../src/utils/plain-object.js","../src/utils/ransack.js","../src/utils/rest-args-error.js","../src/utils/singularize-model-name.js","../src/utils/split-sql-statements.js","../src/utils/to-import-specifier.js","../src/utils/with-tracked-stack-async-hooks.js","../src/utils/with-tracked-stack.js"],"version":"6.0.3"}
|
|
1
|
+
{"root":["../index.js","../bin/velocious.js","../src/application.js","../src/configuration-resolver.js","../src/configuration-types.js","../src/configuration.js","../src/controller.js","../src/current-configuration.js","../src/current.js","../src/error-logger.js","../src/frontend-model-controller.js","../src/initializer.js","../src/logger.js","../src/mailer.js","../src/record-payload-values.js","../src/time-zone.js","../src/velocious-error.js","../src/authorization/ability.js","../src/authorization/base-resource.js","../src/background-jobs/client.js","../src/background-jobs/cron-expression.js","../src/background-jobs/forked-runner-child.js","../src/background-jobs/job-record.js","../src/background-jobs/job-registry.js","../src/background-jobs/job-runner.js","../src/background-jobs/job.js","../src/background-jobs/json-socket.js","../src/background-jobs/main.js","../src/background-jobs/normalize-error.js","../src/background-jobs/scheduler.js","../src/background-jobs/socket-request.js","../src/background-jobs/status-reporter.js","../src/background-jobs/store.js","../src/background-jobs/types.js","../src/background-jobs/worker.js","../src/background-jobs/web/authorization.js","../src/background-jobs/web/controller.js","../src/background-jobs/web/index.js","../src/background-jobs/web/path-matcher.js","../src/background-jobs/web/registry.js","../src/beacon/client.js","../src/beacon/in-process-broker.js","../src/beacon/in-process-client.js","../src/beacon/server.js","../src/beacon/types.js","../src/cli/base-command.js","../src/cli/browser-cli.js","../src/cli/index.js","../src/cli/tenant-database-command-helper.js","../src/cli/use-browser-cli.js","../src/cli/commands/background-jobs-main.js","../src/cli/commands/background-jobs-runner.js","../src/cli/commands/background-jobs-worker.js","../src/cli/commands/beacon.js","../src/cli/commands/console.js","../src/cli/commands/init.js","../src/cli/commands/routes.js","../src/cli/commands/run-script.js","../src/cli/commands/runner.js","../src/cli/commands/server.js","../src/cli/commands/test.js","../src/cli/commands/db/base-command.js","../src/cli/commands/db/create.js","../src/cli/commands/db/drop.js","../src/cli/commands/db/migrate.js","../src/cli/commands/db/reset.js","../src/cli/commands/db/rollback.js","../src/cli/commands/db/seed.js","../src/cli/commands/db/schema/dump.js","../src/cli/commands/db/schema/load.js","../src/cli/commands/db/tenants/check.js","../src/cli/commands/db/tenants/create.js","../src/cli/commands/db/tenants/drop.js","../src/cli/commands/db/tenants/migrate.js","../src/cli/commands/destroy/migration.js","../src/cli/commands/generate/base-models.js","../src/cli/commands/generate/frontend-models.js","../src/cli/commands/generate/migration.js","../src/cli/commands/generate/model.js","../src/cli/commands/lint/relationships.js","../src/database/advisory-lock-runner.js","../src/database/annotations-async-hooks.js","../src/database/annotations.js","../src/database/column-types.js","../src/database/datetime-storage.js","../src/database/handler.js","../src/database/initializer-from-require-context.js","../src/database/live-query.js","../src/database/migrations-ledger.js","../src/database/migrator.js","../src/database/record-changes.js","../src/database/use-database.js","../src/database/use-live-query.js","../src/database/drivers/base-column.js","../src/database/drivers/base-columns-index.js","../src/database/drivers/base-foreign-key.js","../src/database/drivers/base-table.js","../src/database/drivers/base.js","../src/database/drivers/mssql/column.js","../src/database/drivers/mssql/columns-index.js","../src/database/drivers/mssql/connect-connection.js","../src/database/drivers/mssql/foreign-key.js","../src/database/drivers/mssql/index.js","../src/database/drivers/mssql/options.js","../src/database/drivers/mssql/query-parser.js","../src/database/drivers/mssql/structure-sql.js","../src/database/drivers/mssql/table.js","../src/database/drivers/mssql/sql/alter-table.js","../src/database/drivers/mssql/sql/create-database.js","../src/database/drivers/mssql/sql/create-index.js","../src/database/drivers/mssql/sql/create-table.js","../src/database/drivers/mssql/sql/delete.js","../src/database/drivers/mssql/sql/drop-database.js","../src/database/drivers/mssql/sql/drop-table.js","../src/database/drivers/mssql/sql/insert.js","../src/database/drivers/mssql/sql/remove-index.js","../src/database/drivers/mssql/sql/update.js","../src/database/drivers/mssql/sql/upsert.js","../src/database/drivers/mysql/column.js","../src/database/drivers/mysql/columns-index.js","../src/database/drivers/mysql/foreign-key.js","../src/database/drivers/mysql/index.js","../src/database/drivers/mysql/options.js","../src/database/drivers/mysql/query-parser.js","../src/database/drivers/mysql/query.js","../src/database/drivers/mysql/structure-sql.js","../src/database/drivers/mysql/table.js","../src/database/drivers/mysql/sql/alter-table.js","../src/database/drivers/mysql/sql/create-database.js","../src/database/drivers/mysql/sql/create-index.js","../src/database/drivers/mysql/sql/create-table.js","../src/database/drivers/mysql/sql/delete.js","../src/database/drivers/mysql/sql/drop-database.js","../src/database/drivers/mysql/sql/drop-table.js","../src/database/drivers/mysql/sql/insert.js","../src/database/drivers/mysql/sql/remove-index.js","../src/database/drivers/mysql/sql/update.js","../src/database/drivers/mysql/sql/upsert.js","../src/database/drivers/pgsql/column.js","../src/database/drivers/pgsql/columns-index.js","../src/database/drivers/pgsql/foreign-key.js","../src/database/drivers/pgsql/index.js","../src/database/drivers/pgsql/options.js","../src/database/drivers/pgsql/query-parser.js","../src/database/drivers/pgsql/structure-sql.js","../src/database/drivers/pgsql/table.js","../src/database/drivers/pgsql/sql/alter-table.js","../src/database/drivers/pgsql/sql/create-database.js","../src/database/drivers/pgsql/sql/create-index.js","../src/database/drivers/pgsql/sql/create-table.js","../src/database/drivers/pgsql/sql/delete.js","../src/database/drivers/pgsql/sql/drop-database.js","../src/database/drivers/pgsql/sql/drop-table.js","../src/database/drivers/pgsql/sql/insert.js","../src/database/drivers/pgsql/sql/remove-index.js","../src/database/drivers/pgsql/sql/update.js","../src/database/drivers/pgsql/sql/upsert.js","../src/database/drivers/sqlite/base.js","../src/database/drivers/sqlite/column.js","../src/database/drivers/sqlite/columns-index.js","../src/database/drivers/sqlite/connection-sql-js.js","../src/database/drivers/sqlite/foreign-key.js","../src/database/drivers/sqlite/index.js","../src/database/drivers/sqlite/index.native.js","../src/database/drivers/sqlite/index.web.js","../src/database/drivers/sqlite/options.js","../src/database/drivers/sqlite/query-parser.js","../src/database/drivers/sqlite/query.js","../src/database/drivers/sqlite/query.native.js","../src/database/drivers/sqlite/query.web.js","../src/database/drivers/sqlite/structure-sql.js","../src/database/drivers/sqlite/table-rebuilder.js","../src/database/drivers/sqlite/table.js","../src/database/drivers/sqlite/web-persistence.js","../src/database/drivers/sqlite/sql/alter-table.js","../src/database/drivers/sqlite/sql/create-index.js","../src/database/drivers/sqlite/sql/create-table.js","../src/database/drivers/sqlite/sql/delete.js","../src/database/drivers/sqlite/sql/drop-table.js","../src/database/drivers/sqlite/sql/insert.js","../src/database/drivers/sqlite/sql/remove-index.js","../src/database/drivers/sqlite/sql/update.js","../src/database/drivers/sqlite/sql/upsert.js","../src/database/drivers/structure-sql/utils.js","../src/database/migration/index.js","../src/database/migrator/files-finder.js","../src/database/migrator/types.js","../src/database/pool/async-tracked-multi-connection.js","../src/database/pool/base-methods-forward.js","../src/database/pool/base.js","../src/database/pool/single-multi-use.js","../src/database/query/alter-table-base.js","../src/database/query/base.js","../src/database/query/create-database-base.js","../src/database/query/create-index-base.js","../src/database/query/create-table-base.js","../src/database/query/delete-base.js","../src/database/query/drop-database-base.js","../src/database/query/drop-table-base.js","../src/database/query/from-base.js","../src/database/query/from-plain.js","../src/database/query/from-table.js","../src/database/query/index.js","../src/database/query/insert-base.js","../src/database/query/join-base.js","../src/database/query/join-object.js","../src/database/query/join-plain.js","../src/database/query/join-tracker.js","../src/database/query/model-class-query.js","../src/database/query/order-base.js","../src/database/query/order-column.js","../src/database/query/order-plain.js","../src/database/query/preloader.js","../src/database/query/query-data.js","../src/database/query/remove-index-base.js","../src/database/query/select-base.js","../src/database/query/select-plain.js","../src/database/query/select-table-and-column.js","../src/database/query/update-base.js","../src/database/query/upsert-base.js","../src/database/query/where-base.js","../src/database/query/where-combinator.js","../src/database/query/where-hash.js","../src/database/query/where-model-class-hash.js","../src/database/query/where-not.js","../src/database/query/where-plain.js","../src/database/query/with-count.js","../src/database/query/preloader/belongs-to.js","../src/database/query/preloader/ensure-model-class-initialized.js","../src/database/query/preloader/has-many.js","../src/database/query/preloader/has-one.js","../src/database/query/preloader/selection.js","../src/database/query-parser/base-query-parser.js","../src/database/query-parser/from-parser.js","../src/database/query-parser/group-parser.js","../src/database/query-parser/joins-parser.js","../src/database/query-parser/limit-parser.js","../src/database/query-parser/options.js","../src/database/query-parser/order-parser.js","../src/database/query-parser/select-parser.js","../src/database/query-parser/where-parser.js","../src/database/record/acts-as-list.js","../src/database/record/auditing.js","../src/database/record/counter-cache-magnitude.js","../src/database/record/index.js","../src/database/record/record-not-found-error.js","../src/database/record/state-machine.js","../src/database/record/user-module.js","../src/database/record/validation-messages.js","../src/database/record/attachments/attachment-record.js","../src/database/record/attachments/download.js","../src/database/record/attachments/handle.js","../src/database/record/attachments/normalize-input.js","../src/database/record/attachments/store.js","../src/database/record/attachments/storage-drivers/filesystem.js","../src/database/record/attachments/storage-drivers/native.js","../src/database/record/attachments/storage-drivers/s3.js","../src/database/record/instance-relationships/base.js","../src/database/record/instance-relationships/belongs-to.js","../src/database/record/instance-relationships/has-many.js","../src/database/record/instance-relationships/has-one.js","../src/database/record/relationships/base.js","../src/database/record/relationships/belongs-to.js","../src/database/record/relationships/has-many.js","../src/database/record/relationships/has-one.js","../src/database/record/validators/base.js","../src/database/record/validators/format.js","../src/database/record/validators/length.js","../src/database/record/validators/presence.js","../src/database/record/validators/uniqueness.js","../src/database/table-data/index.js","../src/database/table-data/table-column.js","../src/database/table-data/table-foreign-key.js","../src/database/table-data/table-index.js","../src/database/table-data/table-reference.js","../src/database/tenants/data-copier.js","../src/database/tenants/schema-cloner.js","../src/database/tenants/tenant-table-plan.js","../src/environment-handlers/base.js","../src/environment-handlers/browser.js","../src/environment-handlers/node.js","../src/environment-handlers/node/cli/commands/background-jobs-main.js","../src/environment-handlers/node/cli/commands/background-jobs-runner.js","../src/environment-handlers/node/cli/commands/background-jobs-worker.js","../src/environment-handlers/node/cli/commands/beacon.js","../src/environment-handlers/node/cli/commands/cli-command-context.js","../src/environment-handlers/node/cli/commands/console.js","../src/environment-handlers/node/cli/commands/init.js","../src/environment-handlers/node/cli/commands/routes.js","../src/environment-handlers/node/cli/commands/run-script.js","../src/environment-handlers/node/cli/commands/runner.js","../src/environment-handlers/node/cli/commands/server.js","../src/environment-handlers/node/cli/commands/test.js","../src/environment-handlers/node/cli/commands/db/seed.js","../src/environment-handlers/node/cli/commands/db/schema/dump.js","../src/environment-handlers/node/cli/commands/db/schema/load.js","../src/environment-handlers/node/cli/commands/destroy/migration.js","../src/environment-handlers/node/cli/commands/generate/base-models.js","../src/environment-handlers/node/cli/commands/generate/frontend-models.js","../src/environment-handlers/node/cli/commands/generate/generated-file-banner.js","../src/environment-handlers/node/cli/commands/generate/migration.js","../src/environment-handlers/node/cli/commands/generate/model.js","../src/environment-handlers/node/cli/commands/lint/relationships.js","../src/error-reporting/request-details.js","../src/frontend-model-resource/base-resource.js","../src/frontend-model-resource/velocious-attachment-resource.js","../src/frontend-models/base.js","../src/frontend-models/built-in-resources.js","../src/frontend-models/clear-pending-debounced-callback.js","../src/frontend-models/event-hook-models.js","../src/frontend-models/model-registry.js","../src/frontend-models/outgoing-event-buffer.js","../src/frontend-models/preloader.js","../src/frontend-models/query.js","../src/frontend-models/resource-config-validation.js","../src/frontend-models/resource-definition.js","../src/frontend-models/transport-serialization.js","../src/frontend-models/use-created-event.js","../src/frontend-models/use-destroyed-event.js","../src/frontend-models/use-model-class-event.js","../src/frontend-models/use-updated-event.js","../src/frontend-models/websocket-channel.js","../src/frontend-models/websocket-publishers.js","../src/http-client/header.js","../src/http-client/index.js","../src/http-client/request.js","../src/http-client/response.js","../src/http-client/websocket-client.js","../src/http-server/cookie.js","../src/http-server/development-reloader.js","../src/http-server/index.js","../src/http-server/remote-address.js","../src/http-server/server-client.js","../src/http-server/server-lock.js","../src/http-server/websocket-channel-subscribers.js","../src/http-server/websocket-channel.js","../src/http-server/websocket-connection.js","../src/http-server/websocket-event-log-store.js","../src/http-server/websocket-events-host.js","../src/http-server/websocket-events.js","../src/http-server/client/index.js","../src/http-server/client/params-to-object.js","../src/http-server/client/request-parser.js","../src/http-server/client/request-runner.js","../src/http-server/client/request-timing.js","../src/http-server/client/request.js","../src/http-server/client/response.js","../src/http-server/client/websocket-request.js","../src/http-server/client/websocket-session.js","../src/http-server/client/request-buffer/form-data-part.js","../src/http-server/client/request-buffer/header.js","../src/http-server/client/request-buffer/index.js","../src/http-server/client/uploaded-file/memory-uploaded-file.js","../src/http-server/client/uploaded-file/temporary-uploaded-file.js","../src/http-server/client/uploaded-file/uploaded-file.js","../src/http-server/worker-handler/channel-subscriber-dispatch.js","../src/http-server/worker-handler/in-process.js","../src/http-server/worker-handler/index.js","../src/http-server/worker-handler/worker-script.js","../src/http-server/worker-handler/worker-thread.js","../src/jobs/mail-delivery.js","../src/logger/base-logger.js","../src/logger/console-logger.js","../src/logger/file-logger.js","../src/logger/outputs/array-output.js","../src/logger/outputs/console-output.js","../src/logger/outputs/file-output.js","../src/logger/outputs/stdout-output.js","../src/mailer/base.js","../src/mailer/delivery.js","../src/mailer/index.js","../src/mailer/backends/smtp.js","../src/packages/velocious-package.js","../src/plugins/sqljs-wasm-route-controller.js","../src/plugins/sqljs-wasm-route.js","../src/routes/app-routes.js","../src/routes/base-route.js","../src/routes/basic-route.js","../src/routes/get-route.js","../src/routes/index.js","../src/routes/namespace-route.js","../src/routes/plugin-routes.js","../src/routes/post-route.js","../src/routes/resolver.js","../src/routes/resource-route.js","../src/routes/root-route.js","../src/routes/built-in/api-manifest/controller.js","../src/routes/built-in/debug/controller.js","../src/routes/built-in/errors/controller.js","../src/routes/hooks/frontend-model-command-route-hook.js","../src/sync/conflict-strategy.js","../src/sync/device-identity.js","../src/sync/local-mutation-log.js","../src/sync/offline-grant.js","../src/sync/peer-mutation-bundle.js","../src/sync/query-scope.js","../src/sync/server-change-feed.js","../src/sync/server-sequence-allocator.js","../src/sync/stable-json.js","../src/sync/sync-api-client-types.js","../src/sync/sync-api-client.js","../src/sync/sync-api-controller.js","../src/sync/sync-change-fanout.js","../src/sync/sync-channel-name.js","../src/sync/sync-client-registry.js","../src/sync/sync-client-types.js","../src/sync/sync-client.js","../src/sync/sync-envelope-replay-service.js","../src/sync/sync-model-change-feed-service.js","../src/sync/sync-publish-suppression.js","../src/sync/sync-publisher-types.js","../src/sync/sync-publisher.js","../src/sync/sync-realtime-bridge.js","../src/sync/sync-replay-upsert-applier.js","../src/sync/sync-resource-base.js","../src/sync/sync-scope-attributes.js","../src/sync/sync-scope-store.js","../src/sync/sync-websocket-channel.js","../src/tenants/default-tenant-database-provisioning.js","../src/tenants/tenant-iterator.js","../src/tenants/tenant.js","../src/testing/base-expect.js","../src/testing/browser-frontend-model-event-hook-scenarios.js","../src/testing/browser-test-app.js","../src/testing/expect-to-change.js","../src/testing/expect-utils.js","../src/testing/expect.js","../src/testing/request-client.js","../src/testing/test-files-finder.js","../src/testing/test-filter-parser.js","../src/testing/test-runner.js","../src/testing/test-suite-splitter.js","../src/testing/test.js","../src/types/external-modules.d.ts","../src/utils/backtrace-cleaner-node.js","../src/utils/backtrace-cleaner.js","../src/utils/deburr-column-name.js","../src/utils/event-emitter.js","../src/utils/file-exists.js","../src/utils/format-value.js","../src/utils/is-date.js","../src/utils/model-scope.js","../src/utils/nest-callbacks.js","../src/utils/plain-object.js","../src/utils/ransack.js","../src/utils/rest-args-error.js","../src/utils/singularize-model-name.js","../src/utils/split-sql-statements.js","../src/utils/to-import-specifier.js","../src/utils/with-tracked-stack-async-hooks.js","../src/utils/with-tracked-stack.js"],"version":"6.0.3"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import timeout from "awaitery/build/timeout.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when an advisory lock could not be acquired before `timeoutMs` elapsed.
|
|
7
|
+
*/
|
|
8
|
+
class AdvisoryLockTimeoutError extends Error {
|
|
9
|
+
/**
|
|
10
|
+
* Runs constructor.
|
|
11
|
+
* @param {string} message - Error message.
|
|
12
|
+
* @param {{name: string}} args - The advisory lock name that timed out.
|
|
13
|
+
*/
|
|
14
|
+
constructor(message, {name}) {
|
|
15
|
+
super(message)
|
|
16
|
+
this.name = "AdvisoryLockTimeoutError"
|
|
17
|
+
this.lockName = name
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Thrown when `withAdvisoryLockOrFail` finds the lock already held.
|
|
23
|
+
*/
|
|
24
|
+
class AdvisoryLockBusyError extends Error {
|
|
25
|
+
/**
|
|
26
|
+
* Runs constructor.
|
|
27
|
+
* @param {string} message - Error message.
|
|
28
|
+
* @param {{name: string}} args - The advisory lock name that was already held.
|
|
29
|
+
*/
|
|
30
|
+
constructor(message, {name}) {
|
|
31
|
+
super(message)
|
|
32
|
+
this.name = "AdvisoryLockBusyError"
|
|
33
|
+
this.lockName = name
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when a callback holds an advisory lock longer than `holdTimeoutMs`.
|
|
39
|
+
*/
|
|
40
|
+
class AdvisoryLockHoldTimeoutError extends Error {
|
|
41
|
+
/**
|
|
42
|
+
* Runs constructor.
|
|
43
|
+
* @param {string} message - Error message.
|
|
44
|
+
* @param {{name: string}} args - The advisory lock name whose hold timed out.
|
|
45
|
+
*/
|
|
46
|
+
constructor(message, {name}) {
|
|
47
|
+
super(message)
|
|
48
|
+
this.name = "AdvisoryLockHoldTimeoutError"
|
|
49
|
+
this.lockName = name
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Runs advisory locks on the caller connection by default, using a dedicated
|
|
55
|
+
* lock connection only when a positive hold timeout needs separate ownership.
|
|
56
|
+
*/
|
|
57
|
+
export default class AdvisoryLockRunner {
|
|
58
|
+
/**
|
|
59
|
+
* Creates an advisory-lock runner for one model database identifier.
|
|
60
|
+
* @param {{configuration: import("../configuration.js").default, connectionProvider: () => import("./drivers/base.js").default, databaseIdentifier: string}} args - Runner dependencies.
|
|
61
|
+
*/
|
|
62
|
+
constructor({configuration, connectionProvider, databaseIdentifier}) {
|
|
63
|
+
this.configuration = configuration
|
|
64
|
+
this.connectionProvider = connectionProvider
|
|
65
|
+
this.databaseIdentifier = databaseIdentifier
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Runs a callback after acquiring the advisory lock, waiting up to `timeoutMs`.
|
|
70
|
+
* @template T
|
|
71
|
+
* @param {string} name - Lock name.
|
|
72
|
+
* @param {() => Promise<T>} callback - Callback to invoke while the lock is held.
|
|
73
|
+
* @param {{timeoutMs?: number | null, holdTimeoutMs?: number | null}} [args] - Lock and hold timeout options.
|
|
74
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
75
|
+
*/
|
|
76
|
+
async withAdvisoryLock(name, callback, args = {}) {
|
|
77
|
+
return await this.withLockConnection(args.holdTimeoutMs, async (connection) => {
|
|
78
|
+
const acquired = await connection.acquireAdvisoryLock(name, args)
|
|
79
|
+
|
|
80
|
+
if (!acquired) {
|
|
81
|
+
throw new AdvisoryLockTimeoutError(`Timed out waiting for advisory lock ${JSON.stringify(name)}`, {name})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return await this.runLockedCallback({callback, connection, holdTimeoutMs: args.holdTimeoutMs, name})
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Runs a callback only if the advisory lock can be acquired immediately.
|
|
90
|
+
* @template T
|
|
91
|
+
* @param {string} name - Lock name.
|
|
92
|
+
* @param {() => Promise<T>} callback - Callback to invoke while the lock is held.
|
|
93
|
+
* @param {{holdTimeoutMs?: number | null}} [args] - Hold timeout options.
|
|
94
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
95
|
+
*/
|
|
96
|
+
async withAdvisoryLockOrFail(name, callback, args = {}) {
|
|
97
|
+
return await this.withLockConnection(args.holdTimeoutMs, async (connection) => {
|
|
98
|
+
const acquired = await connection.tryAcquireAdvisoryLock(name)
|
|
99
|
+
|
|
100
|
+
if (!acquired) {
|
|
101
|
+
throw new AdvisoryLockBusyError(`Advisory lock ${JSON.stringify(name)} is already held`, {name})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return await this.runLockedCallback({callback, connection, holdTimeoutMs: args.holdTimeoutMs, name})
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Runs the lock holder callback and releases the lock from its owning connection.
|
|
110
|
+
* @template T
|
|
111
|
+
* @param {{callback: () => Promise<T>, connection: import("./drivers/base.js").default, holdTimeoutMs?: number | null, name: string}} args - Locked callback args.
|
|
112
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
113
|
+
*/
|
|
114
|
+
async runLockedCallback({callback, connection, holdTimeoutMs, name}) {
|
|
115
|
+
try {
|
|
116
|
+
return await AdvisoryLockRunner.runWithAdvisoryLockHoldTimeout(name, callback, holdTimeoutMs)
|
|
117
|
+
} finally {
|
|
118
|
+
await connection.releaseAdvisoryLock(name)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Runs lock work on the caller connection unless a positive hold timeout needs
|
|
124
|
+
* its own lock connection.
|
|
125
|
+
* @template T
|
|
126
|
+
* @param {number | null | undefined} holdTimeoutMs - Max hold time; positive values use a dedicated lock connection.
|
|
127
|
+
* @param {(connection: import("./drivers/base.js").default) => Promise<T>} callback - Callback receiving the connection that owns the advisory lock.
|
|
128
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
129
|
+
*/
|
|
130
|
+
async withLockConnection(holdTimeoutMs, callback) {
|
|
131
|
+
if (holdTimeoutMs && holdTimeoutMs > 0) {
|
|
132
|
+
return await this.withDedicatedConnection(callback)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return await callback(this.connectionProvider())
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Spawns a hold-timeout lock connection and closes it after lock work completes when
|
|
140
|
+
* the spawned driver owns the underlying physical connection.
|
|
141
|
+
* @template T
|
|
142
|
+
* @param {(connection: import("./drivers/base.js").default) => Promise<T>} callback - Callback that receives the dedicated lock connection.
|
|
143
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
144
|
+
*/
|
|
145
|
+
async withDedicatedConnection(callback) {
|
|
146
|
+
const connection = await this.configuration.getDatabasePool(this.databaseIdentifier).spawnConnection()
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
return await callback(connection)
|
|
150
|
+
} finally {
|
|
151
|
+
if (!connection.getArgs().getConnection) {
|
|
152
|
+
await connection.close()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Runs `callback`, rejecting with `AdvisoryLockHoldTimeoutError` if it has
|
|
159
|
+
* not settled within `holdTimeoutMs`. The callback is not cancelled; callers
|
|
160
|
+
* use a dedicated advisory-lock connection so the lock can still be released.
|
|
161
|
+
* @template T
|
|
162
|
+
* @param {string} name - Lock name (for the error message).
|
|
163
|
+
* @param {() => Promise<T>} callback - Callback holding the lock.
|
|
164
|
+
* @param {number | null} [holdTimeoutMs] - Max hold time; falsy disables the timeout.
|
|
165
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
166
|
+
*/
|
|
167
|
+
static async runWithAdvisoryLockHoldTimeout(name, callback, holdTimeoutMs) {
|
|
168
|
+
if (!holdTimeoutMs || holdTimeoutMs <= 0) {
|
|
169
|
+
return await callback()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let callbackSettled = false
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
return await timeout({timeout: holdTimeoutMs}, async () => {
|
|
176
|
+
try {
|
|
177
|
+
return await callback()
|
|
178
|
+
} finally {
|
|
179
|
+
callbackSettled = true
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (!callbackSettled) {
|
|
184
|
+
throw new AdvisoryLockHoldTimeoutError(`Advisory lock ${JSON.stringify(name)} held longer than ${holdTimeoutMs}ms`, {name})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
throw error
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export {AdvisoryLockBusyError, AdvisoryLockHoldTimeoutError, AdvisoryLockTimeoutError}
|
|
@@ -31,6 +31,9 @@ import Update from "./sql/update.js"
|
|
|
31
31
|
const MYSQL_INDEFINITE_LOCK_TIMEOUT_SECONDS = 60 * 60 * 24 * 365
|
|
32
32
|
|
|
33
33
|
export default class VelociousDatabaseDriversMysql extends Base{
|
|
34
|
+
/** @type {import("mysql").Pool | undefined} */
|
|
35
|
+
pool = undefined
|
|
36
|
+
|
|
34
37
|
/** @type {string | null} */
|
|
35
38
|
_desiredSessionTimeZone = "+00:00"
|
|
36
39
|
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import debounceFunction from "debounce"
|
|
4
|
+
|
|
5
|
+
import recordChanges from "./record-changes.js"
|
|
6
|
+
import restArgsError from "../utils/rest-args-error.js"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* RecordModelClass type.
|
|
10
|
+
* @typedef {typeof import("./record/index.js").default} RecordModelClass */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The minimal query contract a live query needs: a root model class to observe
|
|
14
|
+
* for committed changes and a way to run the query and return the current rows.
|
|
15
|
+
* `Model.where({...})` (a model-class query) satisfies this directly.
|
|
16
|
+
* @template T
|
|
17
|
+
* @typedef {object} LiveQuerySource
|
|
18
|
+
* @property {() => RecordModelClass} getModelClass - Root model class the query reads.
|
|
19
|
+
* @property {() => Promise<T[]>} toArray - Runs the query and resolves the current rows.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* LiveQueryState type.
|
|
24
|
+
* @template T
|
|
25
|
+
* @typedef {object} LiveQueryState
|
|
26
|
+
* @property {T[]} results - Current query results.
|
|
27
|
+
* @property {boolean} loading - Whether the initial results are still loading.
|
|
28
|
+
* @property {Error | null} error - The last run error, or null when the last run succeeded.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A reactive query controller: fetches once, subscribes to committed changes of
|
|
33
|
+
* its model class(es), and re-runs whenever a watched model changes. Re-runs are
|
|
34
|
+
* coalesced (microtask by default, or a trailing debounce) and protected against
|
|
35
|
+
* stale responses by a monotonically increasing request id, so an in-flight run
|
|
36
|
+
* superseded by a newer change never overwrites fresher results. Framework-level
|
|
37
|
+
* and React-free so it can be unit tested and wrapped by `useLiveQuery`.
|
|
38
|
+
*
|
|
39
|
+
* Cost model: invalidation is by model class. A change to model M schedules one
|
|
40
|
+
* re-run of every live query observing M (no per-condition matching); a batch of
|
|
41
|
+
* changes coalesces into a single re-run.
|
|
42
|
+
* @template T
|
|
43
|
+
*/
|
|
44
|
+
class LiveQuery {
|
|
45
|
+
/**
|
|
46
|
+
* Builds a live query controller for a query source.
|
|
47
|
+
* @param {object} args - Options.
|
|
48
|
+
* @param {LiveQuerySource<T>} args.query - Query source providing model class and `toArray`.
|
|
49
|
+
* @param {RecordModelClass[]} [args.models] - Model classes to observe. Defaults to `[query.getModelClass()]`; pass this to also react to joined models.
|
|
50
|
+
* @param {number} [args.debounce] - Trailing debounce in ms for re-runs. Defaults to microtask coalescing.
|
|
51
|
+
*/
|
|
52
|
+
constructor({query, ...restArgs}) {
|
|
53
|
+
const {debounce, models, ...unknownArgs} = restArgs
|
|
54
|
+
|
|
55
|
+
restArgsError(unknownArgs)
|
|
56
|
+
|
|
57
|
+
if (!query) throw new Error("No query given to LiveQuery")
|
|
58
|
+
|
|
59
|
+
/** @type {LiveQuerySource<T>} */
|
|
60
|
+
this._query = query
|
|
61
|
+
|
|
62
|
+
/** @type {RecordModelClass[]} */
|
|
63
|
+
this._modelClasses = models ?? [query.getModelClass()]
|
|
64
|
+
|
|
65
|
+
/** @type {LiveQueryState<T>} */
|
|
66
|
+
this._state = {error: null, loading: true, results: []}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* State-change listeners notified after every state transition.
|
|
70
|
+
* @type {Set<() => void>} */
|
|
71
|
+
this._listeners = new Set()
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Record-change unsubscribe callbacks registered on `start`.
|
|
75
|
+
* @type {Array<() => void>} */
|
|
76
|
+
this._unsubscribes = []
|
|
77
|
+
|
|
78
|
+
/** @type {number} */
|
|
79
|
+
this._requestId = 0
|
|
80
|
+
|
|
81
|
+
/** @type {boolean} */
|
|
82
|
+
this._closed = false
|
|
83
|
+
|
|
84
|
+
/** @type {boolean} */
|
|
85
|
+
this._started = false
|
|
86
|
+
|
|
87
|
+
/** @type {boolean} */
|
|
88
|
+
this._runScheduled = false
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Promise for the currently in-flight run, or null when idle.
|
|
92
|
+
* @type {Promise<void> | null} */
|
|
93
|
+
this._runningPromise = null
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Schedules a coalesced re-run: a trailing debounce when configured, else microtask coalescing.
|
|
97
|
+
* @type {(() => void) & {clear?: () => void}} */
|
|
98
|
+
this._scheduleRun = typeof debounce === "number"
|
|
99
|
+
? debounceFunction(() => this._run(), debounce)
|
|
100
|
+
: () => this._scheduleMicrotaskRun()
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Record-change listener scheduling a re-run while the controller is open.
|
|
104
|
+
* @type {() => void} */
|
|
105
|
+
this._onRecordChange = () => {
|
|
106
|
+
if (!this._closed) this._scheduleRun()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Subscribes to record changes and runs the initial query. Idempotent.
|
|
112
|
+
* @returns {void}
|
|
113
|
+
*/
|
|
114
|
+
start() {
|
|
115
|
+
if (this._closed || this._started) return
|
|
116
|
+
|
|
117
|
+
this._started = true
|
|
118
|
+
|
|
119
|
+
for (const modelClass of this._modelClasses) {
|
|
120
|
+
this._unsubscribes.push(recordChanges.subscribe(modelClass, this._onRecordChange))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this._run()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns the current state. The reference only changes when the state changes,
|
|
128
|
+
* so it is safe to use as a React external-store snapshot.
|
|
129
|
+
* @returns {LiveQueryState<T>} Current live-query state.
|
|
130
|
+
*/
|
|
131
|
+
getState() {
|
|
132
|
+
return this._state
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Subscribes a listener notified after every state change.
|
|
137
|
+
* @param {() => void} listener - State-change listener.
|
|
138
|
+
* @returns {() => void} Unsubscribe callback.
|
|
139
|
+
*/
|
|
140
|
+
subscribe(listener) {
|
|
141
|
+
this._listeners.add(listener)
|
|
142
|
+
|
|
143
|
+
return () => {
|
|
144
|
+
this._listeners.delete(listener)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Unsubscribes from record changes, drops listeners, and prevents further runs.
|
|
150
|
+
* @returns {void}
|
|
151
|
+
*/
|
|
152
|
+
close() {
|
|
153
|
+
if (this._closed) return
|
|
154
|
+
|
|
155
|
+
this._closed = true
|
|
156
|
+
|
|
157
|
+
for (const unsubscribe of this._unsubscribes) {
|
|
158
|
+
unsubscribe()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this._unsubscribes = []
|
|
162
|
+
this._listeners.clear()
|
|
163
|
+
|
|
164
|
+
if (this._scheduleRun.clear) this._scheduleRun.clear()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Awaits any scheduled or in-flight run so callers (tests) can observe settled
|
|
169
|
+
* results. Bounded so a continuous change stream cannot loop forever.
|
|
170
|
+
* @returns {Promise<void>}
|
|
171
|
+
*/
|
|
172
|
+
async whenSettled() {
|
|
173
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
174
|
+
if (this._closed) return
|
|
175
|
+
if (!this._runScheduled && !this._runningPromise) return
|
|
176
|
+
|
|
177
|
+
if (this._runningPromise) await this._runningPromise
|
|
178
|
+
|
|
179
|
+
await new Promise((resolve) => queueMicrotask(() => resolve(undefined)))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Schedules a microtask-coalesced re-run, collapsing a synchronous burst of
|
|
185
|
+
* change events into a single run.
|
|
186
|
+
* @returns {void}
|
|
187
|
+
*/
|
|
188
|
+
_scheduleMicrotaskRun() {
|
|
189
|
+
if (this._runScheduled) return
|
|
190
|
+
|
|
191
|
+
this._runScheduled = true
|
|
192
|
+
|
|
193
|
+
queueMicrotask(() => {
|
|
194
|
+
this._runScheduled = false
|
|
195
|
+
|
|
196
|
+
if (!this._closed) this._run()
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Runs the query and applies its results unless a newer run superseded it or the
|
|
202
|
+
* controller was closed. A run error surfaces in state (with the previous
|
|
203
|
+
* results kept) rather than rejecting a background promise.
|
|
204
|
+
* @returns {Promise<void>}
|
|
205
|
+
*/
|
|
206
|
+
_run() {
|
|
207
|
+
const requestId = ++this._requestId
|
|
208
|
+
const runningPromise = (async () => {
|
|
209
|
+
try {
|
|
210
|
+
const results = await this._query.toArray()
|
|
211
|
+
|
|
212
|
+
if (this._closed || requestId !== this._requestId) return
|
|
213
|
+
|
|
214
|
+
this._setState({error: null, loading: false, results})
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (this._closed || requestId !== this._requestId) return
|
|
217
|
+
|
|
218
|
+
this._setState({error: /** @type {Error} */ (error), loading: false, results: this._state.results})
|
|
219
|
+
}
|
|
220
|
+
})()
|
|
221
|
+
|
|
222
|
+
this._runningPromise = runningPromise
|
|
223
|
+
void runningPromise.then(() => {
|
|
224
|
+
if (this._runningPromise === runningPromise) this._runningPromise = null
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
return runningPromise
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Replaces the state and notifies listeners.
|
|
232
|
+
* @param {LiveQueryState<T>} state - Next state.
|
|
233
|
+
* @returns {void}
|
|
234
|
+
*/
|
|
235
|
+
_setState(state) {
|
|
236
|
+
this._state = state
|
|
237
|
+
|
|
238
|
+
for (const listener of Array.from(this._listeners)) {
|
|
239
|
+
listener()
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export default LiveQuery
|