velocious 1.0.500 → 1.0.502

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.
@@ -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/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-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-replay-upsert-applier.js","../src/sync/sync-resource-base.js","../src/sync/sync-scope-store.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/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/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-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-realtime-bridge.js","../src/sync/sync-replay-upsert-applier.js","../src/sync/sync-resource-base.js","../src/sync/sync-scope-store.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
@@ -3,7 +3,7 @@
3
3
  "velocious": "build/bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.500",
6
+ "version": "1.0.502",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -369,6 +369,44 @@
369
369
  * @property {(path: string, body?: ?, options?: {headers?: Record<string, string>}) => Promise<{json: () => ?}>} post - Posts one request and resolves a response with a json accessor.
370
370
  */
371
371
 
372
+ /**
373
+ * Websocket client contract required from `sync.client.realtime.createClient`,
374
+ * matching `VelociousWebsocketClient` / snapreq's websocket client.
375
+ * @typedef {object} VelociousSyncRealtimeWebsocketClient
376
+ * @property {() => Promise<?>} connect - Connects the websocket.
377
+ * @property {(channelType: string, options?: {params?: Record<string, ?>, onMessage?: (body: ?) => void, onResume?: () => void, onClose?: (reason: string) => void}) => VelociousSyncRealtimeSubscription} subscribeChannel - Opens one channel subscription.
378
+ * @property {() => Promise<void>} disconnectAndStopReconnect - Closes the socket and stops auto-reconnect.
379
+ */
380
+
381
+ /**
382
+ * Channel subscription handle returned by `subscribeChannel`.
383
+ * @typedef {object} VelociousSyncRealtimeSubscription
384
+ * @property {() => void} close - Closes the subscription.
385
+ * @property {() => boolean} isReady - Whether the subscription is acknowledged and ready.
386
+ * @property {(params?: {timeoutMs?: number}) => Promise<void>} waitForReady - Resolves once the server acknowledges the subscription.
387
+ */
388
+
389
+ /**
390
+ * One realtime channel subscription descriptor.
391
+ * @typedef {object} VelociousSyncRealtimeChannelDescriptor
392
+ * @property {string} channel - Server channel name to subscribe.
393
+ * @property {Record<string, ?>} [params] - Subscribe params (runtime values like eventId). The framework injects `authenticationToken` automatically.
394
+ * @property {string} [resourceType] - Default resource/model name for pushed changes that do not carry their own resourceType.
395
+ */
396
+
397
+ /**
398
+ * Realtime push configuration for the sync client. Only the genuinely app-owned
399
+ * callbacks live here: how to build the websocket client and which channels
400
+ * (with runtime params) to subscribe. Everything else - subscribing, applying
401
+ * pushes through the derived resource applier, echo suppression, and
402
+ * pull-on-reconnect - is derived.
403
+ * @typedef {object} VelociousSyncClientRealtimeConfiguration
404
+ * @property {() => VelociousSyncRealtimeWebsocketClient | Promise<VelociousSyncRealtimeWebsocketClient>} createClient - Builds the (unconnected) websocket client; the framework owns connect/disconnect.
405
+ * @property {(context: ?) => Array<VelociousSyncRealtimeChannelDescriptor> | Promise<Array<VelociousSyncRealtimeChannelDescriptor>>} [channels] - Resolves channel descriptors from the `subscribeRealtime(context)` context (runtime params like eventId). Optional when models declare `static sync = {realtime: {channel}}`.
406
+ * @property {() => string | Promise<string>} [localOrigin] - Resolves this device's echo origin; pushed messages with a matching `echoOrigin` are dropped.
407
+ * @property {boolean} [pullOnReconnect] - Fire a coalesced `pull()` when subscriptions become ready or resume after a drop, closing offline gaps. Defaults to true.
408
+ */
409
+
372
410
  /**
373
411
  * Client-side sync configuration consumed by `SyncClient.fromConfiguration(...)`.
374
412
  * The framework owns the `${mountPath}/changes` and `${mountPath}/replay`
@@ -379,6 +417,7 @@
379
417
  * @property {() => boolean | Promise<boolean>} [isOnline] - Connectivity gate for pulls and replays. Defaults to always online.
380
418
  * @property {string} [mountPath] - Mount path the server serves the sync endpoints under (match the server's `sync.api.mountPath`). Defaults to "/velocious/sync"; normalization strips trailing slashes and always fills in the default.
381
419
  * @property {(error: Error) => void} [onError] - Reports background replay/pull failures. Defaults to rethrowing.
420
+ * @property {VelociousSyncClientRealtimeConfiguration} [realtime] - Realtime push configuration consumed by `subscribeRealtime(...)`.
382
421
  * @property {VelociousSyncClientTransport} transport - Transport posting to the framework sync endpoints (e.g. the frontend-model websocket client).
383
422
  */
384
423
 
@@ -463,11 +463,11 @@ export default class VelociousConfiguration {
463
463
  throw new Error("sync.client must be an object with transport and authenticationToken")
464
464
  }
465
465
 
466
- const {authenticationToken, batchSize, isOnline, mountPath, onError, transport, ...restClient} = client
466
+ const {authenticationToken, batchSize, isOnline, mountPath, onError, realtime, transport, ...restClient} = client
467
467
  const restClientKeys = Object.keys(restClient)
468
468
 
469
469
  if (restClientKeys.length > 0) {
470
- throw new Error(`sync.client received unknown keys: ${restClientKeys.join(", ")} (supported: authenticationToken, batchSize, isOnline, mountPath, onError, transport)`)
470
+ throw new Error(`sync.client received unknown keys: ${restClientKeys.join(", ")} (supported: authenticationToken, batchSize, isOnline, mountPath, onError, realtime, transport)`)
471
471
  }
472
472
  if (!transport || typeof transport !== "object" || typeof transport.post !== "function") {
473
473
  throw new Error("sync.client.transport must be an object with a post(path, body) method (like the frontend-model websocket client)")
@@ -494,6 +494,7 @@ export default class VelociousConfiguration {
494
494
  isOnline,
495
495
  mountPath: (mountPath || "/velocious/sync").replace(/\/+$/u, "") || "/",
496
496
  onError,
497
+ realtime,
497
498
  transport
498
499
  }
499
500
  }
@@ -7,6 +7,15 @@
7
7
  * @property {string} resourceType - Resource/model name the scope was declared for.
8
8
  */
9
9
 
10
+ /**
11
+ * Static realtime channel declaration on a model's `static sync`, for channels
12
+ * whose name and params are static. Channels needing runtime params (like
13
+ * eventId) belong in the `sync.client.realtime.channels` callback instead.
14
+ * @typedef {object} ModelSyncRealtimeDeclaration
15
+ * @property {string} channel - Server channel name to subscribe.
16
+ * @property {Record<string, ?>} [params] - Static subscribe params. The framework injects `authenticationToken` automatically.
17
+ */
18
+
10
19
  /**
11
20
  * Declarative per-resource sync policy.
12
21
  * @typedef {object} SyncClientResourceConfig
@@ -19,7 +28,8 @@
19
28
  * @property {string[]} [localOnlyAttributes] - Attributes stripped from queued payloads.
20
29
  * @property {"upsert" | ((args: {operation: "create" | "update" | "destroy", record: ?}) => string)} [syncType] - Maps a mutation operation to a sync type. The "upsert" flag queues creates and updates as "update" rows (the server upserts by resource id) and destroys as "delete". Defaults to the operation name with destroy mapped to "delete".
21
30
  * @property {(args: {operation: "create" | "update" | "destroy", record: ?}) => Record<string, ?>} [trackedData] - Custom queued-payload builder for tracked mutations.
22
- * @property {boolean | {operations: Array<"create" | "update" | "destroy">}} [track] - Enables automatic mutation tracking through model lifecycle callbacks.
31
+ * @property {boolean | {operations: Array<"create" | "update" | "destroy">}} [track] - Automatic mutation tracking policy. On by default (creates and updates queue automatically); `false` opts the resource out, `true` adds destroys, `{operations}` narrows the tracked operations.
32
+ * @property {ModelSyncRealtimeDeclaration} [realtime] - Static realtime channel this resource subscribes through `subscribeRealtime(...)`.
23
33
  */
24
34
 
25
35
  /**
@@ -34,8 +44,9 @@
34
44
  * @property {import("./sync-api-client-types.js").SyncResourceConfig["findRecordForDelete"]} [findRecordForDelete] - Custom pull-apply delete resolver.
35
45
  * @property {string[]} [localOnlyAttributes] - Extra local-only attributes merged with the derived primary key, createdAt/updatedAt, and sync bookkeeping attributes.
36
46
  * @property {"upsert" | ((args: {operation: "create" | "update" | "destroy", record: ?}) => string)} [syncType] - Sync type flag or mapper (see SyncClientResourceConfig).
37
- * @property {boolean | Array<"create" | "update" | "destroy"> | {operations: Array<"create" | "update" | "destroy">}} [track] - Enables automatic mutation tracking; an array is shorthand for {operations}.
47
+ * @property {boolean | Array<"create" | "update" | "destroy"> | {operations: Array<"create" | "update" | "destroy">}} [track] - Automatic mutation tracking policy; an array is shorthand for {operations}. On by default (creates and updates queue automatically); `false` opts the model out (for models written by non-user flows), `true` adds destroys.
38
48
  * @property {(args: {operation: "create" | "update" | "destroy", record: ?}) => Record<string, ?>} [trackedData] - Custom queued-payload builder for tracked mutations.
49
+ * @property {ModelSyncRealtimeDeclaration} [realtime] - Static realtime channel this resource subscribes through `subscribeRealtime(...)`; use the `sync.client.realtime.channels` callback for channels needing runtime params.
39
50
  */
40
51
 
41
52
  /** @typedef {boolean | ModelSyncDeclarationConfig} ModelSyncDeclaration */
@@ -63,6 +74,7 @@
63
74
  * @property {(error: Error) => void} [onError] - Reports background replay/pull failures. Defaults to rethrowing.
64
75
  * @property {(payload: import("./sync-api-client-types.js").SyncChangesRequest & {scope: SerializedSyncScope}) => Promise<import("./sync-api-client-types.js").SyncChangesResponse>} postChanges - Posts one changes request.
65
76
  * @property {(payload: {authenticationToken: string, syncs: Array<Record<string, ?>>}) => Promise<import("./sync-api-client-types.js").SyncReplayResponse>} postReplay - Posts one replay request.
77
+ * @property {import("../configuration-types.js").VelociousSyncClientRealtimeConfiguration} [realtime] - Realtime push configuration consumed by `subscribeRealtime(...)`.
66
78
  * @property {Record<string, SyncClientResourceConfig>} resources - Derived resource policies keyed by resource/model name.
67
79
  * @property {?} syncModel - Local pending-sync model class.
68
80
  */
@@ -2,10 +2,12 @@
2
2
 
3
3
  import Configuration from "../configuration.js"
4
4
  import {isBooleanColumnType} from "../database/column-types.js"
5
+ import Logger from "../logger.js"
5
6
  import restArgsError from "../utils/rest-args-error.js"
6
7
 
7
8
  import {serializedScopeFromQuery} from "./query-scope.js"
8
9
  import SyncApiClient from "./sync-api-client.js"
10
+ import SyncRealtimeBridge from "./sync-realtime-bridge.js"
9
11
  import SyncScopeStore from "./sync-scope-store.js"
10
12
  import {currentSyncClient, setCurrentSyncClient} from "./sync-client-registry.js"
11
13
 
@@ -14,6 +16,14 @@ let clientCounter = 0
14
16
  /** @type {{create: "afterCreate", update: "afterUpdate", destroy: "afterDestroy"}} */
15
17
  const TRACKED_CALLBACK_NAMES = {create: "afterCreate", destroy: "afterDestroy", update: "afterUpdate"}
16
18
 
19
+ /**
20
+ * Operations tracked by default for models declaring `static sync` without a
21
+ * `track` key: local creates and updates queue automatically. Destroys are not
22
+ * tracked by default because a local destroy is often cache eviction rather
23
+ * than a server delete; opt in with `track: true` or an operations list.
24
+ * @type {Array<"create" | "update" | "destroy">} */
25
+ const DEFAULT_TRACKED_OPERATIONS = ["create", "update"]
26
+
17
27
  /** Attribute names treated as client-local sync bookkeeping when deriving localOnlyAttributes. */
18
28
  const LOCAL_BOOKKEEPING_ATTRIBUTE_NAMES = ["createdAt", "updatedAt", "lastSyncChangeAt"]
19
29
 
@@ -87,10 +97,13 @@ export default class SyncClient {
87
97
  onError: clientConfiguration.onError,
88
98
  postChanges: transportPoster({path: `${clientConfiguration.mountPath}/changes`, transport: clientConfiguration.transport}),
89
99
  postReplay: transportPoster({path: `${clientConfiguration.mountPath}/replay`, transport: clientConfiguration.transport}),
100
+ realtime: clientConfiguration.realtime,
90
101
  resources,
91
102
  syncModel: resolvedSyncModel
92
103
  }
93
104
  this._clientNumber = ++clientCounter
105
+ /** @type {SyncRealtimeBridge | null} */
106
+ this._realtimeBridge = null
94
107
  /** @type {import("./sync-scope-store.js").default | null} */
95
108
  this._scopeStore = scopeStore || null
96
109
  /** @type {Promise<void> | null} */
@@ -101,13 +114,18 @@ export default class SyncClient {
101
114
  this._trackedCallbacks = []
102
115
  /** @type {WeakSet<object>} */
103
116
  this._remoteApplyRecords = new WeakSet()
117
+ this._withoutTrackingDepth = 0
118
+ /** @type {Logger | {error: (...messages: Array<?>) => Promise<void>} | null} */
119
+ this._logger = null
104
120
  this._started = false
105
121
  }
106
122
 
107
123
  /**
108
- * Registers automatic mutation tracking for every resource with `track` enabled:
109
- * local model creates/updates/destroys queue pending sync rows and schedule an
110
- * immediate replay attempt, without app-side queue calls.
124
+ * Registers automatic mutation tracking for every declared resource (on by
125
+ * default: local creates and updates queue pending sync rows once their
126
+ * transaction commits and schedule an immediate replay attempt, without
127
+ * app-side queue calls). `track: false` resources are skipped; `track: true`
128
+ * adds destroys; an operations list narrows the tracked operations.
111
129
  * @returns {Promise<void>}
112
130
  */
113
131
  async start() {
@@ -143,13 +161,17 @@ export default class SyncClient {
143
161
 
144
162
  /**
145
163
  * Resolves and validates the tracked operations for a resource config.
164
+ * Tracking is on by default: models declaring `static sync` without a `track`
165
+ * key queue local creates and updates automatically; `track: false` opts a
166
+ * model out (for models written by non-user flows).
146
167
  * @param {{resourceConfig: import("./sync-client-types.js").SyncClientResourceConfig, resourceType: string}} args - Resource config and name.
147
168
  * @returns {Array<"create" | "update" | "destroy">} Tracked operations.
148
169
  */
149
170
  trackedOperations({resourceConfig, resourceType}) {
150
171
  const track = resourceConfig.track
151
172
 
152
- if (track === undefined || track === false) return []
173
+ if (track === false) return []
174
+ if (track === undefined) return DEFAULT_TRACKED_OPERATIONS
153
175
  if (track === true) return ["create", "update", "destroy"]
154
176
 
155
177
  if (!track || typeof track !== "object" || !Array.isArray(track.operations) || track.operations.length === 0) {
@@ -166,27 +188,78 @@ export default class SyncClient {
166
188
  }
167
189
 
168
190
  /**
169
- * Builds the lifecycle callback queueing one tracked mutation.
191
+ * Builds the lifecycle callback queueing one tracked mutation. The queued
192
+ * payload and sync type are snapshotted at mutation-callback time, so
193
+ * afterSave hooks assigning unsaved attributes (or any later drift on the
194
+ * record) cannot change what gets queued vs what was committed. Queueing is
195
+ * deferred through the model connection's afterCommit hook so it only runs
196
+ * once the mutation's transaction has committed (immediately when no
197
+ * transaction is open) - queued syncs never reference rolled-back rows.
198
+ * Post-commit queue failures are reported without rethrowing into the
199
+ * driver's afterCommit chain (see reportAfterCommitError).
170
200
  * @param {{operation: "create" | "update" | "destroy", resourceConfig: import("./sync-client-types.js").SyncClientResourceConfig}} args - Operation and resource config.
171
201
  * @returns {(record: ?) => Promise<void>} Lifecycle callback.
172
202
  */
173
203
  trackedMutationCallback({operation, resourceConfig}) {
174
204
  return async (record) => {
175
- if (this.isRemoteApply(record)) return
205
+ if (this.isTrackingSuppressed(record)) return
176
206
 
177
- await SyncApiClient.queueLocalSync({
207
+ const data = SyncApiClient.queuedSyncData({
178
208
  booleanAttributes: resourceConfig.booleanAttributes || [],
179
209
  data: resourceConfig.trackedData ? resourceConfig.trackedData({operation, record}) : undefined,
180
210
  localOnlyAttributes: resourceConfig.localOnlyAttributes || [],
181
- resource: record,
182
- syncModel: this.config.syncModel,
183
- syncType: this.defaultSyncType({operation, record, resourceConfig})
211
+ resource: record
184
212
  })
213
+ const syncType = this.defaultSyncType({operation, record, resourceConfig})
214
+
215
+ await resourceConfig.modelClass.connection().afterCommit(async () => {
216
+ try {
217
+ await SyncApiClient.queueLocalSync({
218
+ data,
219
+ resource: record,
220
+ syncModel: this.config.syncModel,
221
+ syncType
222
+ })
223
+ } catch (error) {
224
+ await this.reportAfterCommitError(/** @type {Error} */ (error))
225
+
226
+ return
227
+ }
185
228
 
186
- this.scheduleReplay()
229
+ this.scheduleReplay()
230
+ })
187
231
  }
188
232
  }
189
233
 
234
+ /**
235
+ * Reports a post-commit tracked-queueing failure. The transaction has already
236
+ * committed when afterCommit callbacks run, so rethrowing here would poison
237
+ * the driver's awaited afterCommit chain (breaking unrelated callbacks) -
238
+ * instead the failure goes to the configured sync.client.onError hook, or is
239
+ * logged loudly through the client's logger when none is configured.
240
+ * @param {Error} error - Post-commit queueing failure.
241
+ * @returns {Promise<void>}
242
+ */
243
+ async reportAfterCommitError(error) {
244
+ if (this.config.onError) {
245
+ this.config.onError(error)
246
+
247
+ return
248
+ }
249
+
250
+ await this.logger().error("SyncClient failed to queue a tracked mutation after commit", error)
251
+ }
252
+
253
+ /**
254
+ * Returns the lazily built client logger.
255
+ * @returns {Logger | {error: (...messages: Array<?>) => Promise<void>}} Client logger.
256
+ */
257
+ logger() {
258
+ this._logger ||= new Logger("SyncClient", {configuration: this.config.configuration})
259
+
260
+ return this._logger
261
+ }
262
+
190
263
  /**
191
264
  * Whether a record is currently being written by pull-apply (echo suppression).
192
265
  * @param {?} record - Local model record.
@@ -196,6 +269,54 @@ export default class SyncClient {
196
269
  return this._remoteApplyRecords.has(record)
197
270
  }
198
271
 
272
+ /**
273
+ * Whether tracked mutation queueing is currently suppressed for a record:
274
+ * either the record was marked as a remote apply (`markRemoteApply`, used by
275
+ * pull and realtime applies) or a `withoutTracking` callback is running on
276
+ * this client.
277
+ * @param {?} record - Local model record.
278
+ * @returns {boolean} Whether tracked queueing is suppressed for the record.
279
+ */
280
+ isTrackingSuppressed(record) {
281
+ return this._withoutTrackingDepth > 0 || this.isRemoteApply(record)
282
+ }
283
+
284
+ /**
285
+ * Runs a callback with tracked mutation queueing suppressed on this client -
286
+ * for code applying server-originated data outside the derived pull/realtime
287
+ * appliers (legacy pull paths, importers, sign-in backfills), so their writes
288
+ * are not echoed back to the server as device changes. Suppression covers the
289
+ * whole async duration of the callback (nested calls stack) and is
290
+ * client-wide while it runs: mutations from concurrently running tasks are
291
+ * also suppressed for that window, so prefer `markRemoteApply(record)` when
292
+ * writes from other flows can interleave.
293
+ * @template T
294
+ * @param {() => Promise<T> | T} callback - Work whose model writes should not queue tracked syncs.
295
+ * @returns {Promise<T>} The callback result.
296
+ */
297
+ async withoutTracking(callback) {
298
+ this._withoutTrackingDepth++
299
+
300
+ try {
301
+ return await callback()
302
+ } finally {
303
+ this._withoutTrackingDepth--
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Marks one record as being written from server-originated data so tracked
309
+ * mutation queueing skips it (record-precise suppression). The derived pull
310
+ * and realtime appliers use this internally around every applied write.
311
+ * @param {?} record - Local model record about to be written.
312
+ * @returns {() => void} Release callback re-enabling tracking for the record.
313
+ */
314
+ markRemoteApply(record) {
315
+ this._remoteApplyRecords.add(record)
316
+
317
+ return () => this._remoteApplyRecords.delete(record)
318
+ }
319
+
199
320
  /**
200
321
  * Registers this client as the app's current sync client.
201
322
  * @returns {void}
@@ -265,26 +386,7 @@ export default class SyncClient {
265
386
  await SyncApiClient.singleFlight(`velocious-sync-client-pull-${this._clientNumber}`, async () => {
266
387
  const authenticationToken = await this.config.authenticationToken()
267
388
  const scopeStore = this.scopeStore()
268
- const pullResourceConfigs = this.pullResourceConfigs()
269
- const applier = SyncApiClient.resourceApplier(pullResourceConfigs, (record) => {
270
- this._remoteApplyRecords.add(record)
271
-
272
- return () => this._remoteApplyRecords.delete(record)
273
- })
274
- /**
275
- * Applies one pulled change, failing loudly instead of silently skipping unconfigured resources.
276
- * @param {import("./sync-api-client-types.js").SyncChangeEnvelope} sync - Pulled change.
277
- * @returns {Promise<import("./sync-api-client-types.js").SyncChangeApplyResult>} Apply result.
278
- */
279
- const applySync = async (sync) => {
280
- const resourceType = sync.resourceType()
281
-
282
- if (!resourceType || !pullResourceConfigs[resourceType]) {
283
- throw new Error(`No sync resource with pull attributes configured for pulled change: ${String(resourceType)}`)
284
- }
285
-
286
- return await applier(sync)
287
- }
389
+ const applySync = this.remoteApplySync()
288
390
  const result = {
289
391
  changed: false,
290
392
  pages: 0,
@@ -324,6 +426,74 @@ export default class SyncClient {
324
426
  return combinedResult
325
427
  }
326
428
 
429
+ /**
430
+ * Builds the derived remote-change applier shared by pulls and realtime pushes:
431
+ * applies through the declared resource configs, registers each written record
432
+ * for echo suppression (tracked resources do not re-queue applied changes), and
433
+ * fails loudly instead of silently skipping unconfigured resources.
434
+ * @param {{source?: string}} [args] - Error context describing where the change came from.
435
+ * @returns {(sync: import("./sync-api-client-types.js").SyncChangeEnvelope) => Promise<import("./sync-api-client-types.js").SyncChangeApplyResult>} Loud remote-change applier.
436
+ */
437
+ remoteApplySync({source = "pulled change"} = {}) {
438
+ const pullResourceConfigs = this.pullResourceConfigs()
439
+ const applier = SyncApiClient.resourceApplier(pullResourceConfigs, (record) => this.markRemoteApply(record))
440
+
441
+ return async (sync) => {
442
+ const resourceType = sync.resourceType()
443
+
444
+ if (!resourceType || !pullResourceConfigs[resourceType]) {
445
+ throw new Error(`No sync resource with pull attributes configured for ${source}: ${String(resourceType)}`)
446
+ }
447
+
448
+ return await applier(sync)
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Subscribes the derived realtime channels so pushed websocket changes apply
454
+ * through the same derived applier as pulls (idempotent, single-flighted).
455
+ * @param {?} [context] - App context passed to the `sync.client.realtime.channels` callback (runtime values like eventId).
456
+ * @returns {Promise<void>}
457
+ */
458
+ async subscribeRealtime(context) {
459
+ await this.realtimeBridge().subscribe(context)
460
+ }
461
+
462
+ /**
463
+ * Unsubscribes the realtime channels and disconnects the websocket client (idempotent).
464
+ * @returns {Promise<void>}
465
+ */
466
+ async unsubscribeRealtime() {
467
+ await this.realtimeBridge().unsubscribe()
468
+ }
469
+
470
+ /**
471
+ * Reports the realtime subscription state and per-channel readiness.
472
+ * @returns {ReturnType<SyncRealtimeBridge["status"]>} Realtime status.
473
+ */
474
+ realtimeStatus() {
475
+ return this.realtimeBridge().status()
476
+ }
477
+
478
+ /**
479
+ * Awaits all pending realtime message applies and any scheduled
480
+ * pull-on-reconnect (useful in tests and shutdown flows).
481
+ * @returns {Promise<void>}
482
+ */
483
+ async waitForRealtimeApplied() {
484
+ await this.realtimeBridge().waitForApplied()
485
+ }
486
+
487
+ /**
488
+ * Returns the lazily built realtime bridge.
489
+ * @returns {SyncRealtimeBridge} Realtime bridge.
490
+ */
491
+ realtimeBridge() {
492
+ this._realtimeBridge ||= new SyncRealtimeBridge({syncClient: this})
493
+
494
+ return this._realtimeBridge
495
+ }
496
+
327
497
  /**
328
498
  * Queues a local model change as a pending sync row and schedules an immediate
329
499
  * replay attempt (kept pending while offline or when the backend rejects it).
@@ -491,11 +661,11 @@ function resourceConfigFromSyncDeclaration({declaration, modelClass, resourceTyp
491
661
  throw new Error(`${resourceType} static sync must be true or a sync declaration object, got: ${String(declaration)}`)
492
662
  }
493
663
 
494
- const {afterApply, attributes, booleanAttributes, findRecord, findRecordForDelete, localOnlyAttributes, syncType, track, trackedData, ...restDeclaration} = normalizedDeclaration
664
+ const {afterApply, attributes, booleanAttributes, findRecord, findRecordForDelete, localOnlyAttributes, realtime, syncType, track, trackedData, ...restDeclaration} = normalizedDeclaration
495
665
  const unknownKeys = Object.keys(restDeclaration)
496
666
 
497
667
  if (unknownKeys.length > 0) {
498
- throw new Error(`${resourceType} static sync received unknown keys: ${unknownKeys.join(", ")} (supported: afterApply, attributes, booleanAttributes, findRecord, findRecordForDelete, localOnlyAttributes, syncType, track, trackedData)`)
668
+ throw new Error(`${resourceType} static sync received unknown keys: ${unknownKeys.join(", ")} (supported: afterApply, attributes, booleanAttributes, findRecord, findRecordForDelete, localOnlyAttributes, realtime, syncType, track, trackedData)`)
499
669
  }
500
670
  if (syncType !== undefined && typeof syncType !== "function" && syncType !== "upsert") {
501
671
  throw new Error(`${resourceType} static sync syncType must be a function or the string "upsert", got: ${String(syncType)}`)
@@ -511,6 +681,7 @@ function resourceConfigFromSyncDeclaration({declaration, modelClass, resourceTyp
511
681
  findRecordForDelete,
512
682
  localOnlyAttributes: mergedAttributeNames(derived.localOnlyAttributes, localOnlyAttributes),
513
683
  modelClass,
684
+ realtime,
514
685
  syncType,
515
686
  track: normalizedTrack(track),
516
687
  trackedData