velocious 1.0.568 → 1.0.570

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 (152) hide show
  1. package/README.md +30 -0
  2. package/build/configuration.js +36 -0
  3. package/build/database/drivers/base.js +244 -93
  4. package/build/database/drivers/mssql/index.js +10 -7
  5. package/build/database/drivers/mysql/index.js +10 -5
  6. package/build/database/drivers/pgsql/index.js +4 -4
  7. package/build/database/drivers/sqlite/base.js +2 -2
  8. package/build/database/drivers/sqlite/connection-sql-js.js +62 -2
  9. package/build/database/drivers/sqlite/index.web.js +74 -0
  10. package/build/database/operation-connection.js +121 -0
  11. package/build/database/operation-lease.js +47 -0
  12. package/build/database/operation.js +150 -0
  13. package/build/database/pool/base.js +26 -1
  14. package/build/database/pool/single-multi-use.js +49 -0
  15. package/build/database/query/model-class-query.js +38 -7
  16. package/build/database/query/preloader/belongs-to.js +3 -2
  17. package/build/database/query/preloader/has-many.js +4 -3
  18. package/build/database/query/preloader/has-one.js +2 -1
  19. package/build/database/query/preloader/query-for-model.js +17 -0
  20. package/build/database/query/query-data.js +12 -3
  21. package/build/database/query/with-count.js +5 -3
  22. package/build/database/record/acts-as-list.js +11 -8
  23. package/build/database/record/attachments/store.js +15 -11
  24. package/build/database/record/auditing.js +12 -9
  25. package/build/database/record/counter-cache-magnitude.js +5 -3
  26. package/build/database/record/index.js +95 -14
  27. package/build/database/record/instance-relationships/belongs-to.js +4 -2
  28. package/build/database/record/instance-relationships/has-many.js +7 -5
  29. package/build/database/record/instance-relationships/has-one.js +4 -2
  30. package/build/database/record/validators/uniqueness.js +3 -2
  31. package/build/frontend-models/websocket-publishers.js +2 -2
  32. package/build/src/configuration.d.ts +12 -0
  33. package/build/src/configuration.d.ts.map +1 -1
  34. package/build/src/configuration.js +35 -1
  35. package/build/src/database/drivers/base.d.ts +96 -23
  36. package/build/src/database/drivers/base.d.ts.map +1 -1
  37. package/build/src/database/drivers/base.js +230 -90
  38. package/build/src/database/drivers/mssql/index.d.ts +4 -1
  39. package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
  40. package/build/src/database/drivers/mssql/index.js +11 -8
  41. package/build/src/database/drivers/mysql/index.d.ts +2 -1
  42. package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
  43. package/build/src/database/drivers/mysql/index.js +10 -6
  44. package/build/src/database/drivers/pgsql/index.d.ts +2 -1
  45. package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
  46. package/build/src/database/drivers/pgsql/index.js +5 -5
  47. package/build/src/database/drivers/sqlite/base.d.ts +1 -1
  48. package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
  49. package/build/src/database/drivers/sqlite/base.js +3 -3
  50. package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +20 -0
  51. package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
  52. package/build/src/database/drivers/sqlite/connection-sql-js.js +56 -3
  53. package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
  54. package/build/src/database/drivers/sqlite/index.web.js +64 -1
  55. package/build/src/database/operation-connection.d.ts +67 -0
  56. package/build/src/database/operation-connection.d.ts.map +1 -0
  57. package/build/src/database/operation-connection.js +102 -0
  58. package/build/src/database/operation-lease.d.ts +27 -0
  59. package/build/src/database/operation-lease.d.ts.map +1 -0
  60. package/build/src/database/operation-lease.js +44 -0
  61. package/build/src/database/operation.d.ts +85 -0
  62. package/build/src/database/operation.d.ts.map +1 -0
  63. package/build/src/database/operation.js +132 -0
  64. package/build/src/database/pool/base.d.ts +14 -0
  65. package/build/src/database/pool/base.d.ts.map +1 -1
  66. package/build/src/database/pool/base.js +24 -2
  67. package/build/src/database/pool/single-multi-use.d.ts +1 -0
  68. package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
  69. package/build/src/database/pool/single-multi-use.js +49 -1
  70. package/build/src/database/query/model-class-query.d.ts +14 -0
  71. package/build/src/database/query/model-class-query.d.ts.map +1 -1
  72. package/build/src/database/query/model-class-query.js +32 -8
  73. package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
  74. package/build/src/database/query/preloader/belongs-to.js +4 -3
  75. package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
  76. package/build/src/database/query/preloader/has-many.js +5 -4
  77. package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
  78. package/build/src/database/query/preloader/has-one.js +3 -2
  79. package/build/src/database/query/preloader/query-for-model.d.ts +10 -0
  80. package/build/src/database/query/preloader/query-for-model.d.ts.map +1 -0
  81. package/build/src/database/query/preloader/query-for-model.js +16 -0
  82. package/build/src/database/query/query-data.d.ts.map +1 -1
  83. package/build/src/database/query/query-data.js +13 -4
  84. package/build/src/database/query/with-count.d.ts.map +1 -1
  85. package/build/src/database/query/with-count.js +6 -4
  86. package/build/src/database/record/acts-as-list.js +12 -9
  87. package/build/src/database/record/attachments/attachment-record.d.ts +9 -0
  88. package/build/src/database/record/attachments/attachment-record.d.ts.map +1 -1
  89. package/build/src/database/record/attachments/store.d.ts +4 -2
  90. package/build/src/database/record/attachments/store.d.ts.map +1 -1
  91. package/build/src/database/record/attachments/store.js +16 -12
  92. package/build/src/database/record/auditing.d.ts.map +1 -1
  93. package/build/src/database/record/auditing.js +13 -10
  94. package/build/src/database/record/counter-cache-magnitude.js +6 -4
  95. package/build/src/database/record/index.d.ts +42 -2
  96. package/build/src/database/record/index.d.ts.map +1 -1
  97. package/build/src/database/record/index.js +87 -15
  98. package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
  99. package/build/src/database/record/instance-relationships/belongs-to.js +4 -3
  100. package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
  101. package/build/src/database/record/instance-relationships/has-many.js +7 -5
  102. package/build/src/database/record/instance-relationships/has-one.d.ts.map +1 -1
  103. package/build/src/database/record/instance-relationships/has-one.js +4 -3
  104. package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
  105. package/build/src/database/record/validators/uniqueness.js +4 -3
  106. package/build/src/frontend-models/websocket-publishers.js +3 -3
  107. package/build/src/sync/server-sequence-allocator.d.ts +16 -4
  108. package/build/src/sync/server-sequence-allocator.d.ts.map +1 -1
  109. package/build/src/sync/server-sequence-allocator.js +37 -11
  110. package/build/src/sync/sync-client.d.ts.map +1 -1
  111. package/build/src/sync/sync-client.js +7 -3
  112. package/build/src/sync/sync-publisher.d.ts +2 -1
  113. package/build/src/sync/sync-publisher.d.ts.map +1 -1
  114. package/build/src/sync/sync-publisher.js +11 -6
  115. package/build/sync/server-sequence-allocator.js +43 -10
  116. package/build/sync/sync-client.js +6 -2
  117. package/build/sync/sync-publisher.js +10 -5
  118. package/build/tsconfig.tsbuildinfo +1 -1
  119. package/package.json +1 -1
  120. package/src/configuration.js +36 -0
  121. package/src/database/drivers/base.js +244 -93
  122. package/src/database/drivers/mssql/index.js +10 -7
  123. package/src/database/drivers/mysql/index.js +10 -5
  124. package/src/database/drivers/pgsql/index.js +4 -4
  125. package/src/database/drivers/sqlite/base.js +2 -2
  126. package/src/database/drivers/sqlite/connection-sql-js.js +62 -2
  127. package/src/database/drivers/sqlite/index.web.js +74 -0
  128. package/src/database/operation-connection.js +121 -0
  129. package/src/database/operation-lease.js +47 -0
  130. package/src/database/operation.js +150 -0
  131. package/src/database/pool/base.js +26 -1
  132. package/src/database/pool/single-multi-use.js +49 -0
  133. package/src/database/query/model-class-query.js +38 -7
  134. package/src/database/query/preloader/belongs-to.js +3 -2
  135. package/src/database/query/preloader/has-many.js +4 -3
  136. package/src/database/query/preloader/has-one.js +2 -1
  137. package/src/database/query/preloader/query-for-model.js +17 -0
  138. package/src/database/query/query-data.js +12 -3
  139. package/src/database/query/with-count.js +5 -3
  140. package/src/database/record/acts-as-list.js +11 -8
  141. package/src/database/record/attachments/store.js +15 -11
  142. package/src/database/record/auditing.js +12 -9
  143. package/src/database/record/counter-cache-magnitude.js +5 -3
  144. package/src/database/record/index.js +95 -14
  145. package/src/database/record/instance-relationships/belongs-to.js +4 -2
  146. package/src/database/record/instance-relationships/has-many.js +7 -5
  147. package/src/database/record/instance-relationships/has-one.js +4 -2
  148. package/src/database/record/validators/uniqueness.js +3 -2
  149. package/src/frontend-models/websocket-publishers.js +2 -2
  150. package/src/sync/server-sequence-allocator.js +43 -10
  151. package/src/sync/sync-client.js +6 -2
  152. package/src/sync/sync-publisher.js +10 -5
package/README.md CHANGED
@@ -34,6 +34,7 @@
34
34
  * In-process driver schema metadata caching (see [docs/schema-metadata-cache.md](docs/schema-metadata-cache.md))
35
35
  * Planned local-first shared-resource sync architecture (see [docs/offline-sync.md](docs/offline-sync.md))
36
36
  * Selective named database connection checkouts, bounded pool waits, and debugging held connections (see [docs/database-connections.md](docs/database-connections.md))
37
+ * Explicit singular-database operation transactions whose model scopes preserve ownership through records, relationships, lifecycle work, nested savepoints, pre-commit guards, and commit callbacks (see [docs/operation-scoped-transactions.md](docs/operation-scoped-transactions.md))
37
38
  * AbortSignal-driven MySQL/MariaDB query cancellation for raw, model, and cross-tenant aggregate queries (see [docs/database-query-cancellation.md](docs/database-query-cancellation.md))
38
39
  * Optional built-in debug endpoint for inspecting server and database connection state (see [docs/debug-endpoint.md](docs/debug-endpoint.md))
39
40
  * Optional built-in API manifest endpoint describing every registered frontend-model resource as human- and machine-readable JSON (see [docs/api-manifest-endpoint.md](docs/api-manifest-endpoint.md))
@@ -51,6 +52,35 @@ npx velocious init
51
52
 
52
53
  By default, Velocious looks for your configuration in `src/config/configuration.js`. If you keep the configuration elsewhere, make sure your app imports it early and calls `configuration.setCurrent()`.
53
54
 
55
+ # Operation-scoped transactions
56
+
57
+ Use `configuration.withTransaction` for an atomic unit of model work on one database:
58
+
59
+ ```js
60
+ await configuration.withTransaction({databaseIdentifier: "default", name: "accept ticket"}, async (operation) => {
61
+ const ticket = await operation.forModel(Ticket).find(ticketId)
62
+
63
+ ticket.setAccepted(true)
64
+ await ticket.save()
65
+
66
+ await operation.beforeCommit(async ({operation: guardedOperation}) => {
67
+ const currentTicket = await guardedOperation
68
+ .forModel(Ticket)
69
+ .findByOrFail({id: ticketId})
70
+
71
+ if (!currentTicket.acceptanceStillOwnedBy(workerId)) {
72
+ throw new Error("Ticket acceptance ownership changed")
73
+ }
74
+ })
75
+
76
+ await operation.afterCommit(async () => {
77
+ await publishAcceptedTicket(ticket.id())
78
+ })
79
+ })
80
+ ```
81
+
82
+ Use operation-bound model scopes and their loaded records throughout the callback. `operation.beforeCommit` runs a final operation-owned guard after callback success but before outer commit or nested savepoint release; a rejection rolls back that frame. `operation.transaction` adds a nested savepoint, and `operation.connection()` is the deliberate escape hatch for owned raw SQL. Cross-database models, same-identifier tenant switches to another physical database, and operation handles used after the callback are rejected. On shared SQLite/SQL.js pools, unrelated work waits for the operation lease, while admission during an already-open ordinary transaction is rejected. See [operation-scoped transactions](docs/operation-scoped-transactions.md) for guard, pool, after-commit failure, and migration semantics.
83
+
54
84
  # Development
55
85
 
56
86
  When working on Velocious itself, npm scripts are cross-platform (Windows `cmd`/PowerShell and POSIX shells):
@@ -16,6 +16,7 @@ import {digg} from "diggerize"
16
16
  import gettextConfig from "gettext-universal/build/src/config.js"
17
17
  import translate from "gettext-universal/build/src/translate.js"
18
18
  import Ability from "./authorization/ability.js"
19
+ import DatabaseOperation from "./database/operation.js"
19
20
  import {initializeAuditedModelRelationships} from "./database/record/auditing.js"
20
21
  import EventEmitter from "./utils/event-emitter.js"
21
22
  import VelociousWebsocketChannelSubscribers from "./http-server/websocket-channel-subscribers.js"
@@ -2896,6 +2897,41 @@ export default class VelociousConfiguration {
2896
2897
  })
2897
2898
  }
2898
2899
 
2900
+ /**
2901
+ * Runs explicit model work in a transaction pinned to one database connection.
2902
+ * @template T
2903
+ * @param {{databaseIdentifier: string, name?: string}} options - Operation options.
2904
+ * @param {(operation: DatabaseOperation) => Promise<T>} callback - Operation callback.
2905
+ * @returns {Promise<T>} - Resolves with the callback result.
2906
+ */
2907
+ async withTransaction({databaseIdentifier, name = "Configuration.withTransaction", ...restArgs}, callback) {
2908
+ restArgsError(restArgs)
2909
+
2910
+ if (!databaseIdentifier) throw new Error("Configuration.withTransaction requires a databaseIdentifier")
2911
+ if (typeof callback != "function") throw new Error("Configuration.withTransaction requires a callback")
2912
+ if (!this.getDatabaseIdentifiers().includes(databaseIdentifier)) {
2913
+ throw new Error(`Unknown or inactive database identifier: ${databaseIdentifier}`)
2914
+ }
2915
+
2916
+ const pool = this.getDatabasePool(databaseIdentifier)
2917
+
2918
+ return await pool.withOperationConnection({name}, async (connection, owner) => {
2919
+ const operation = new DatabaseOperation({
2920
+ configuration: this,
2921
+ configurationReuseKey: pool.getConnectionConfigurationReuseKey(connection),
2922
+ connection,
2923
+ databaseIdentifier,
2924
+ owner
2925
+ })
2926
+
2927
+ try {
2928
+ return await operation.transaction(async () => await callback(operation))
2929
+ } finally {
2930
+ operation.complete()
2931
+ }
2932
+ })
2933
+ }
2934
+
2899
2935
  /**
2900
2936
  * Runs callback with database connections for the requested identifiers.
2901
2937
  * @template T