velocious 1.0.496 → 1.0.497

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/build/configuration-types.js +21 -0
  2. package/build/configuration.js +48 -0
  3. package/build/database/column-types.js +19 -0
  4. package/build/database/record/index.js +9 -0
  5. package/build/src/configuration-types.d.ts +68 -0
  6. package/build/src/configuration-types.d.ts.map +1 -1
  7. package/build/src/configuration-types.js +20 -1
  8. package/build/src/configuration.d.ts +6 -0
  9. package/build/src/configuration.d.ts.map +1 -1
  10. package/build/src/configuration.js +45 -1
  11. package/build/src/database/column-types.d.ts +8 -0
  12. package/build/src/database/column-types.d.ts.map +1 -0
  13. package/build/src/database/column-types.js +18 -0
  14. package/build/src/database/record/index.d.ts +8 -0
  15. package/build/src/database/record/index.d.ts.map +1 -1
  16. package/build/src/database/record/index.js +9 -1
  17. package/build/src/sync/sync-api-client.d.ts +4 -1
  18. package/build/src/sync/sync-api-client.d.ts.map +1 -1
  19. package/build/src/sync/sync-api-client.js +9 -2
  20. package/build/src/sync/sync-client-types.d.ts +88 -10
  21. package/build/src/sync/sync-client-types.d.ts.map +1 -1
  22. package/build/src/sync/sync-client-types.js +1 -1
  23. package/build/src/sync/sync-client.d.ts +38 -11
  24. package/build/src/sync/sync-client.d.ts.map +1 -1
  25. package/build/src/sync/sync-client.js +193 -29
  26. package/build/sync/sync-api-client.js +7 -1
  27. package/build/sync/sync-client-types.js +34 -5
  28. package/build/sync/sync-client.js +223 -27
  29. package/build/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +1 -1
  31. package/src/configuration-types.js +21 -0
  32. package/src/configuration.js +48 -0
  33. package/src/database/column-types.js +19 -0
  34. package/src/database/record/index.js +9 -0
  35. package/src/sync/sync-api-client.js +7 -1
  36. package/src/sync/sync-client-types.js +34 -5
  37. package/src/sync/sync-client.js +223 -27
@@ -1,8 +1,8 @@
1
1
  // @ts-check
2
2
 
3
- import {forcedFunction} from "typanic"
4
-
5
3
  import Configuration from "../configuration.js"
4
+ import {isBooleanColumnType} from "../database/column-types.js"
5
+ import restArgsError from "../utils/rest-args-error.js"
6
6
 
7
7
  import {serializedScopeFromQuery} from "./query-scope.js"
8
8
  import SyncApiClient from "./sync-api-client.js"
@@ -14,44 +14,85 @@ let clientCounter = 0
14
14
  /** @type {{create: "afterCreate", update: "afterUpdate", destroy: "afterDestroy"}} */
15
15
  const TRACKED_CALLBACK_NAMES = {create: "afterCreate", destroy: "afterDestroy", update: "afterUpdate"}
16
16
 
17
+ /** Attribute names treated as client-local sync bookkeeping when deriving localOnlyAttributes. */
18
+ const LOCAL_BOOKKEEPING_ATTRIBUTE_NAMES = ["createdAt", "updatedAt", "lastSyncChangeAt"]
19
+
20
+ /** @type {WeakMap<Configuration, SyncClient>} */
21
+ const syncClientsByConfiguration = new WeakMap()
22
+
17
23
  /**
18
24
  * Declarative client-side sync driver.
19
25
  *
20
- * Apps configure resources, transport, auth, and connectivity once; Velocious
21
- * owns scope persistence, per-scope cursors, pull paging/apply, local queueing,
22
- * and online-gated replay. Declare sync interest from queries:
26
+ * Everything is derived from the app's Velocious configuration: models declare
27
+ * `static sync`, transport/auth/connectivity come from the `sync.client`
28
+ * configuration block, and Velocious owns scope persistence, per-scope cursors,
29
+ * pull paging/apply, local queueing, and online-gated replay. Declare sync
30
+ * interest from queries:
23
31
  *
24
- * const syncClient = new SyncClient({...})
25
- * syncClient.setCurrent()
26
- * await syncClient.sync(Event.where({partnerId}))
32
+ * await syncClient().start()
33
+ * await syncClient().sync(Event.where({partnerId}))
27
34
  */
28
35
  export default class SyncClient {
29
36
  /**
30
- * Creates a declarative sync client.
31
- * @param {import("./sync-client-types.js").SyncClientConfig} config - Sync client configuration.
37
+ * Builds the sync client by deriving everything from the app's Velocious
38
+ * configuration: every registered model declaring `static sync` becomes a
39
+ * resource with booleanAttributes derived from column types and
40
+ * localOnlyAttributes derived from the primary key, createdAt/updatedAt, and
41
+ * sync bookkeeping columns; the pending-sync model is the registered "Sync"
42
+ * model; transport, auth, connectivity, and error reporting come from the
43
+ * `sync.client` configuration block, with the framework owning the
44
+ * `${mountPath}/changes` and `${mountPath}/replay` POSTers.
45
+ * @param {import("./sync-client-types.js").SyncClientOptions} [options] - Optional overrides.
32
46
  */
33
- constructor(config) {
34
- if (!config || typeof config !== "object") throw new Error("SyncClient requires a configuration object")
47
+ constructor(options = {}) {
48
+ const {configuration = Configuration.current(), legacyCursor, scopeStore, syncModel, ...restOptions} = options
35
49
 
36
- forcedFunction(config.postChanges, "SyncClient config.postChanges")
37
- forcedFunction(config.postReplay, "SyncClient config.postReplay")
38
- forcedFunction(config.authenticationToken, "SyncClient config.authenticationToken")
50
+ restArgsError(restOptions)
39
51
 
40
- if (!config.syncModel) throw new Error("SyncClient requires config.syncModel")
41
- if (!config.resources || typeof config.resources !== "object" || Object.keys(config.resources).length === 0) {
42
- throw new Error("SyncClient requires at least one entry in config.resources")
52
+ const clientConfiguration = configuration.getSyncConfiguration().client
53
+
54
+ if (!clientConfiguration) {
55
+ throw new Error("SyncClient requires a sync.client configuration block: new Configuration({sync: {client: {authenticationToken, transport}}})")
43
56
  }
44
57
 
45
- for (const [resourceType, resource] of Object.entries(config.resources)) {
46
- if (!resource || typeof resource !== "object" || !resource.modelClass) {
47
- throw new Error(`SyncClient resource ${resourceType} must declare a modelClass`)
48
- }
58
+ const modelClasses = configuration.getModelClasses()
59
+ /** @type {Record<string, import("./sync-client-types.js").SyncClientResourceConfig>} */
60
+ const resources = {}
61
+
62
+ for (const modelClass of Object.values(modelClasses)) {
63
+ if (!modelClass.sync) continue
64
+
65
+ const resourceType = modelClass.getModelName()
66
+
67
+ resources[resourceType] = resourceConfigFromSyncDeclaration({declaration: modelClass.sync, modelClass, resourceType})
68
+ }
69
+
70
+ if (Object.keys(resources).length === 0) {
71
+ throw new Error("SyncClient found no registered models declaring static sync - declare `static sync = true` (or a sync declaration object) on the models that should sync")
49
72
  }
50
73
 
51
- this.config = config
74
+ const resolvedSyncModel = syncModel || modelClasses.Sync
75
+
76
+ if (!resolvedSyncModel) {
77
+ throw new Error("SyncClient requires a registered \"Sync\" model for pending local sync rows (or pass options.syncModel)")
78
+ }
79
+
80
+ /** @type {import("./sync-client-types.js").SyncClientConfig} */
81
+ this.config = {
82
+ authenticationToken: clientConfiguration.authenticationToken,
83
+ batchSize: clientConfiguration.batchSize,
84
+ configuration,
85
+ isOnline: clientConfiguration.isOnline,
86
+ legacyCursor,
87
+ onError: clientConfiguration.onError,
88
+ postChanges: transportPoster({path: `${clientConfiguration.mountPath}/changes`, transport: clientConfiguration.transport}),
89
+ postReplay: transportPoster({path: `${clientConfiguration.mountPath}/replay`, transport: clientConfiguration.transport}),
90
+ resources,
91
+ syncModel: resolvedSyncModel
92
+ }
52
93
  this._clientNumber = ++clientCounter
53
94
  /** @type {import("./sync-scope-store.js").default | null} */
54
- this._scopeStore = config.scopeStore || null
95
+ this._scopeStore = scopeStore || null
55
96
  /** @type {Promise<void> | null} */
56
97
  this._scheduledReplay = null
57
98
  /** @type {Record<string, import("./sync-api-client-types.js").SyncResourceConfig> | null} */
@@ -171,6 +212,17 @@ export default class SyncClient {
171
212
  return /** @type {SyncClient} */ (currentSyncClient())
172
213
  }
173
214
 
215
+ /**
216
+ * Builds a sync client derived from the given configuration. Alias for
217
+ * `new SyncClient({configuration, ...options})`.
218
+ * @param {Configuration} [configuration] - Configuration owning the registered models and the sync.client block. Defaults to the current configuration.
219
+ * @param {Omit<import("./sync-client-types.js").SyncClientOptions, "configuration">} [options] - Optional overrides.
220
+ * @returns {SyncClient} Sync client derived from the configuration.
221
+ */
222
+ static fromConfiguration(configuration = Configuration.current(), options = {}) {
223
+ return new SyncClient({...options, configuration})
224
+ }
225
+
174
226
  /**
175
227
  * Declares (or re-activates) a sync scope from a model query and pulls it when online.
176
228
  * @param {import("../database/query/model-class-query.js").default<?>} query - Query declaring the sync scope.
@@ -364,7 +416,7 @@ export default class SyncClient {
364
416
  * @returns {import("./sync-scope-store.js").default} Scope store.
365
417
  */
366
418
  scopeStore() {
367
- this._scopeStore ||= new SyncScopeStore({configuration: this.config.configuration || Configuration.current()})
419
+ this._scopeStore ||= new SyncScopeStore({configuration: this.config.configuration})
368
420
 
369
421
  return this._scopeStore
370
422
  }
@@ -390,13 +442,16 @@ export default class SyncClient {
390
442
  }
391
443
 
392
444
  /**
393
- * Resolves the sync type for a mutation through the resource config.
445
+ * Resolves the sync type for a mutation through the resource config. The
446
+ * "upsert" flag queues creates and updates as "update" rows (the server
447
+ * upserts by resource id) and destroys as "delete" rows.
394
448
  * @param {{operation: "create" | "update" | "destroy", record: ?, resourceConfig: import("./sync-client-types.js").SyncClientResourceConfig}} args - Mutation args.
395
449
  * @returns {string} Sync type.
396
450
  */
397
451
  defaultSyncType({operation, record, resourceConfig}) {
398
- if (resourceConfig.syncType) return resourceConfig.syncType({operation, record})
452
+ if (typeof resourceConfig.syncType === "function") return resourceConfig.syncType({operation, record})
399
453
  if (operation === "destroy") return "delete"
454
+ if (resourceConfig.syncType === "upsert") return "update"
400
455
 
401
456
  return operation
402
457
  }
@@ -423,6 +478,147 @@ export default class SyncClient {
423
478
  }
424
479
  }
425
480
 
481
+ /**
482
+ * Builds one resource config from a model's `static sync` declaration plus its
483
+ * derived column metadata.
484
+ * @param {{declaration: import("./sync-client-types.js").ModelSyncDeclaration, modelClass: ?, resourceType: string}} args - Declaration args.
485
+ * @returns {import("./sync-client-types.js").SyncClientResourceConfig} Derived resource config.
486
+ */
487
+ function resourceConfigFromSyncDeclaration({declaration, modelClass, resourceType}) {
488
+ const normalizedDeclaration = declaration === true ? {} : declaration
489
+
490
+ if (!normalizedDeclaration || typeof normalizedDeclaration !== "object" || Array.isArray(normalizedDeclaration)) {
491
+ throw new Error(`${resourceType} static sync must be true or a sync declaration object, got: ${String(declaration)}`)
492
+ }
493
+
494
+ const {afterApply, attributes, booleanAttributes, findRecord, findRecordForDelete, localOnlyAttributes, syncType, track, trackedData, ...restDeclaration} = normalizedDeclaration
495
+ const unknownKeys = Object.keys(restDeclaration)
496
+
497
+ 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)`)
499
+ }
500
+ if (syncType !== undefined && typeof syncType !== "function" && syncType !== "upsert") {
501
+ throw new Error(`${resourceType} static sync syncType must be a function or the string "upsert", got: ${String(syncType)}`)
502
+ }
503
+
504
+ const derived = derivedSyncAttributes({modelClass, resourceType})
505
+
506
+ return {
507
+ afterApply,
508
+ attributes,
509
+ booleanAttributes: mergedAttributeNames(derived.booleanAttributes, booleanAttributes),
510
+ findRecord,
511
+ findRecordForDelete,
512
+ localOnlyAttributes: mergedAttributeNames(derived.localOnlyAttributes, localOnlyAttributes),
513
+ modelClass,
514
+ syncType,
515
+ track: normalizedTrack(track),
516
+ trackedData
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Derives boolean and local-only attribute names from a model's column metadata:
522
+ * booleans from boolean column types; local-only from the primary key,
523
+ * createdAt/updatedAt, and sync bookkeeping columns.
524
+ * @param {{modelClass: ?, resourceType: string}} args - Derivation args.
525
+ * @returns {{booleanAttributes: string[], localOnlyAttributes: string[]}} Derived attribute names.
526
+ */
527
+ function derivedSyncAttributes({modelClass, resourceType}) {
528
+ if (
529
+ typeof modelClass.getColumnNames !== "function" ||
530
+ typeof modelClass.getColumnNameToAttributeNameMap !== "function" ||
531
+ typeof modelClass.getColumnTypeByName !== "function" ||
532
+ typeof modelClass.primaryKey !== "function" ||
533
+ typeof modelClass.hasPrimaryKey !== "function"
534
+ ) {
535
+ throw new Error(`${resourceType} static sync requires a Velocious model class with column metadata (getColumnNames, getColumnNameToAttributeNameMap, getColumnTypeByName, primaryKey, hasPrimaryKey)`)
536
+ }
537
+
538
+ const columnNameToAttributeName = modelClass.getColumnNameToAttributeNameMap()
539
+ /** @type {string[]} */
540
+ const booleanAttributes = []
541
+ /** @type {string[]} */
542
+ const localOnlyAttributes = []
543
+
544
+ if (modelClass.hasPrimaryKey()) {
545
+ const primaryKeyColumn = modelClass.primaryKey()
546
+
547
+ localOnlyAttributes.push(columnNameToAttributeName[primaryKeyColumn] || primaryKeyColumn)
548
+ }
549
+
550
+ for (const columnName of modelClass.getColumnNames()) {
551
+ const attributeName = columnNameToAttributeName[columnName] || columnName
552
+ const columnType = modelClass.getColumnTypeByName(columnName)
553
+
554
+ if (LOCAL_BOOKKEEPING_ATTRIBUTE_NAMES.includes(attributeName) && !localOnlyAttributes.includes(attributeName)) {
555
+ localOnlyAttributes.push(attributeName)
556
+ }
557
+ if (columnType && isBooleanColumnType(columnType)) {
558
+ booleanAttributes.push(attributeName)
559
+ }
560
+ }
561
+
562
+ return {booleanAttributes, localOnlyAttributes}
563
+ }
564
+
565
+ /**
566
+ * Merges derived attribute names with declared extras into a sorted, duplicate-free list.
567
+ * @param {string[]} derived - Derived attribute names.
568
+ * @param {string[] | undefined} declared - Declared extra attribute names.
569
+ * @returns {string[]} Merged attribute names.
570
+ */
571
+ function mergedAttributeNames(derived, declared) {
572
+ return [...new Set([...derived, ...(declared || [])])].sort()
573
+ }
574
+
575
+ /**
576
+ * Normalizes a declaration's track value: an operations array is shorthand for
577
+ * the {operations} form.
578
+ * @param {import("./sync-client-types.js").ModelSyncDeclarationConfig["track"]} track - Declared track value.
579
+ * @returns {import("./sync-client-types.js").SyncClientResourceConfig["track"]} Normalized track value.
580
+ */
581
+ function normalizedTrack(track) {
582
+ if (Array.isArray(track)) return {operations: track}
583
+
584
+ return track
585
+ }
586
+
587
+ /**
588
+ * Builds a framework-owned sync endpoint POSTer over the configured transport.
589
+ * @param {{path: string, transport: import("../configuration-types.js").VelociousSyncClientTransport}} args - Poster args.
590
+ * @returns {(payload: Record<string, ?>) => Promise<?>} Sync endpoint POSTer.
591
+ */
592
+ function transportPoster({path, transport}) {
593
+ return async (payload) => {
594
+ const response = await transport.post(path, payload)
595
+
596
+ if (!response || typeof response.json !== "function") {
597
+ throw new Error(`sync.client transport.post must resolve to a response with a json() method for ${path} (like the frontend-model websocket client)`)
598
+ }
599
+
600
+ return await response.json()
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Lazily builds (and memoizes per configuration) the sync client derived from the
606
+ * app's Velocious configuration and registers it as the current sync client.
607
+ * @param {Configuration} [configuration] - Configuration owning the registered models and the sync.client block. Defaults to the current configuration.
608
+ * @returns {SyncClient} Memoized sync client for the configuration.
609
+ */
610
+ export function syncClient(configuration = Configuration.current()) {
611
+ let client = syncClientsByConfiguration.get(configuration)
612
+
613
+ if (!client) {
614
+ client = SyncClient.fromConfiguration(configuration)
615
+ syncClientsByConfiguration.set(configuration, client)
616
+ client.setCurrent()
617
+ }
618
+
619
+ return client
620
+ }
621
+
426
622
  /**
427
623
  * Declares a sync scope on the current sync client.
428
624
  * @param {import("../database/query/model-class-query.js").default<?>} query - Query declaring the sync scope.
@@ -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/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/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/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"}
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.496",
6
+ "version": "1.0.497",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -362,10 +362,31 @@
362
362
  * @property {FrontendModelResourceClassType} resourceClass - App sync resource class served by the auto-mounted sync endpoints.
363
363
  */
364
364
 
365
+ /**
366
+ * Client-side sync transport owning HTTP POSTs to the framework sync endpoints,
367
+ * matching the frontend-model websocket client post contract.
368
+ * @typedef {object} VelociousSyncClientTransport
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
+ */
371
+
372
+ /**
373
+ * Client-side sync configuration consumed by `SyncClient.fromConfiguration(...)`.
374
+ * The framework owns the `${mountPath}/changes` and `${mountPath}/replay`
375
+ * POSTers over the given transport.
376
+ * @typedef {object} VelociousSyncClientConfiguration
377
+ * @property {() => string | Promise<string>} authenticationToken - Resolves the auth token sent with sync requests.
378
+ * @property {number} [batchSize] - Max syncs per request.
379
+ * @property {() => boolean | Promise<boolean>} [isOnline] - Connectivity gate for pulls and replays. Defaults to always online.
380
+ * @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
+ * @property {(error: Error) => void} [onError] - Reports background replay/pull failures. Defaults to rethrowing.
382
+ * @property {VelociousSyncClientTransport} transport - Transport posting to the framework sync endpoints (e.g. the frontend-model websocket client).
383
+ */
384
+
365
385
  /**
366
386
  * Velocious sync configuration.
367
387
  * @typedef {object} VelociousSyncConfiguration
368
388
  * @property {VelociousSyncApiConfiguration} [api] - Auto-mounts the Velocious sync changes/replay endpoints for this resource class.
389
+ * @property {VelociousSyncClientConfiguration} [client] - Client-side sync configuration consumed by `SyncClient.fromConfiguration(...)`.
369
390
  * @property {import("./sync/device-identity.js").SyncJsonWebKey | null} [deviceCertificateBackendPublicKey] - Public backend key used to verify offline device certificates for sync replay.
370
391
  * @property {number} [changeFeedRetentionSize] - Number of accepted server changes retained before clients must refresh from snapshot.
371
392
  * @property {Array<import("./sync/offline-grant.js").OfflineGrantSigningKey>} offlineGrantSigningKeys - Signing keys used to issue and verify offline grants. Secrets must never be exposed to clients.
@@ -444,12 +444,60 @@ export default class VelociousConfiguration {
444
444
  return {
445
445
  api: this._normalizeSyncApiConfiguration(api),
446
446
  changeFeedRetentionSize: changeFeedRetentionSize || 10000,
447
+ client: this._normalizeSyncClientConfiguration(sync?.client),
447
448
  deviceCertificateBackendPublicKey,
448
449
  offlineGrantSigningKeys: offlineGrantSigningKeys.map((key) => normalizeOfflineGrantSigningKey(key)),
449
450
  offlineGrantTtlMs: offlineGrantTtlMs || 24 * 60 * 60 * 1000
450
451
  }
451
452
  }
452
453
 
454
+ /**
455
+ * Normalizes client-side sync configuration consumed by `SyncClient.fromConfiguration(...)`.
456
+ * @param {import("./configuration-types.js").VelociousSyncClientConfiguration | undefined} client - Client-side sync configuration.
457
+ * @returns {import("./configuration-types.js").VelociousSyncClientConfiguration | undefined} - Normalized client-side sync configuration.
458
+ */
459
+ _normalizeSyncClientConfiguration(client) {
460
+ if (client === undefined || client === null) return undefined
461
+
462
+ if (typeof client !== "object" || Array.isArray(client)) {
463
+ throw new Error("sync.client must be an object with transport and authenticationToken")
464
+ }
465
+
466
+ const {authenticationToken, batchSize, isOnline, mountPath, onError, transport, ...restClient} = client
467
+ const restClientKeys = Object.keys(restClient)
468
+
469
+ if (restClientKeys.length > 0) {
470
+ throw new Error(`sync.client received unknown keys: ${restClientKeys.join(", ")} (supported: authenticationToken, batchSize, isOnline, mountPath, onError, transport)`)
471
+ }
472
+ if (!transport || typeof transport !== "object" || typeof transport.post !== "function") {
473
+ throw new Error("sync.client.transport must be an object with a post(path, body) method (like the frontend-model websocket client)")
474
+ }
475
+ if (typeof authenticationToken !== "function") {
476
+ throw new Error("sync.client.authenticationToken must be a function resolving the auth token sent with sync requests")
477
+ }
478
+ if (isOnline !== undefined && typeof isOnline !== "function") {
479
+ throw new Error("sync.client.isOnline must be a function resolving connectivity")
480
+ }
481
+ if (onError !== undefined && typeof onError !== "function") {
482
+ throw new Error("sync.client.onError must be a function reporting background sync failures")
483
+ }
484
+ if (batchSize !== undefined && (!Number.isInteger(batchSize) || batchSize <= 0)) {
485
+ throw new Error("sync.client.batchSize must be a positive integer")
486
+ }
487
+ if (mountPath !== undefined && (typeof mountPath !== "string" || !mountPath.startsWith("/"))) {
488
+ throw new Error(`sync.client.mountPath must start with '/', got: ${String(mountPath)}`)
489
+ }
490
+
491
+ return {
492
+ authenticationToken,
493
+ batchSize,
494
+ isOnline,
495
+ mountPath: (mountPath || "/velocious/sync").replace(/\/+$/u, "") || "/",
496
+ onError,
497
+ transport
498
+ }
499
+ }
500
+
453
501
  /**
454
502
  * Normalizes sync API endpoint configuration.
455
503
  * @param {import("./configuration-types.js").VelociousSyncApiConfiguration | undefined} api - Sync API configuration.
@@ -0,0 +1,19 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Driver column types that store boolean values. Drivers report storage types,
5
+ * which diverge for booleans (Postgres/SQLite `boolean`, some drivers `bool`,
6
+ * MSSQL `bit`), so cross-driver consumers must match them uniformly.
7
+ * @type {Set<string>}
8
+ */
9
+ const BOOLEAN_COLUMN_TYPES = new Set(["bit", "bool", "boolean"])
10
+
11
+ /**
12
+ * Whether a driver-reported (or cast) column type stores boolean values,
13
+ * uniformly across database drivers.
14
+ * @param {string} columnType - Driver-reported (or cast) column type.
15
+ * @returns {boolean} Whether the column type is boolean-backed.
16
+ */
17
+ export function isBooleanColumnType(columnType) {
18
+ return BOOLEAN_COLUMN_TYPES.has(columnType.toLowerCase())
19
+ }
@@ -239,6 +239,15 @@ class VelociousDatabaseRecord {
239
239
  * @type {string | undefined} */
240
240
  static modelName
241
241
 
242
+ /**
243
+ * Opt-in client sync declaration consumed by `SyncClient.fromConfiguration(...)`.
244
+ * Declare `static sync = true` (all defaults) or a declaration object like
245
+ * `static sync = {track: ["create", "update"], syncType: "upsert"}` to have the
246
+ * sync client auto-discover this model and derive its resource config from
247
+ * column metadata.
248
+ * @type {import("../../sync/sync-client-types.js").ModelSyncDeclaration | undefined} */
249
+ static sync
250
+
242
251
  /**
243
252
  * Narrows the runtime value to the documented type.
244
253
  * @type {Promise<void> | null | undefined} */
@@ -485,7 +485,10 @@ export default class SyncApiClient {
485
485
  }
486
486
 
487
487
  /**
488
- * Builds backend-safe queued sync data without mutating caller data.
488
+ * Builds backend-safe queued sync data without mutating caller data. The default
489
+ * (no explicit `data`) is the resource's attributes minus local-only attributes,
490
+ * with booleans coerced and Date values serialized to ISO strings, so apps don't
491
+ * need per-model tracked-payload builders.
489
492
  * @param {{resource: ?, data?: Record<string, unknown>, localOnlyAttributes?: string[], booleanAttributes?: string[], normalizeData?: (data: Record<string, unknown>) => Record<string, unknown>}} args - Data args.
490
493
  * @returns {Record<string, unknown>} Queued data.
491
494
  */
@@ -498,6 +501,9 @@ export default class SyncApiClient {
498
501
  for (const attributeName of args.booleanAttributes || []) {
499
502
  if (Object.hasOwn(syncData, attributeName)) syncData[attributeName] = this.optionalBooleanSyncValue(syncData[attributeName], attributeName)
500
503
  }
504
+ for (const [attributeName, value] of Object.entries(syncData)) {
505
+ if (value instanceof Date) syncData[attributeName] = value.toISOString()
506
+ }
501
507
 
502
508
  return syncData
503
509
  }