velocious 1.0.563 → 1.0.565

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 (219) hide show
  1. package/README.md +3 -1
  2. package/build/background-jobs/cron-expression.js +12 -13
  3. package/build/background-jobs/main.js +6 -6
  4. package/build/background-jobs/runner-graceful-shutdown.js +2 -3
  5. package/build/background-jobs/scheduler.js +3 -3
  6. package/build/background-jobs/worker.js +8 -9
  7. package/build/beacon/client.js +3 -4
  8. package/build/database/drivers/sqlite/web-persistence.js +2 -5
  9. package/build/database/pool/async-tracked-multi-connection.js +65 -15
  10. package/build/database/pool/base.js +23 -7
  11. package/build/database/query/index.js +21 -22
  12. package/build/database/query/model-class-query.js +5 -6
  13. package/build/database/record/acts-as-list.js +37 -10
  14. package/build/database/record/attachments/normalize-input.js +9 -10
  15. package/build/database/record/attachments/store.js +2 -2
  16. package/build/database/record/auditing.js +7 -8
  17. package/build/database/record/index.js +4 -5
  18. package/build/environment-handlers/node/cli/commands/generate/base-models.js +4 -12
  19. package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +1 -2
  20. package/build/frontend-model-controller.js +49 -56
  21. package/build/frontend-models/base.js +17 -19
  22. package/build/frontend-models/outgoing-event-buffer.js +3 -4
  23. package/build/frontend-models/query.js +40 -44
  24. package/build/frontend-models/resource-definition.js +7 -8
  25. package/build/frontend-models/transport-serialization.js +10 -12
  26. package/build/frontend-models/websocket-channel.js +12 -13
  27. package/build/http-server/client/websocket-session.js +4 -5
  28. package/build/http-server/client-delivery-queue.js +7 -8
  29. package/build/http-server/websocket-event-log-store.js +4 -6
  30. package/build/plugins/sqljs-wasm-route.js +3 -5
  31. package/build/src/background-jobs/cron-expression.d.ts +0 -12
  32. package/build/src/background-jobs/cron-expression.d.ts.map +1 -1
  33. package/build/src/background-jobs/cron-expression.js +13 -13
  34. package/build/src/background-jobs/main.d.ts.map +1 -1
  35. package/build/src/background-jobs/main.js +7 -7
  36. package/build/src/background-jobs/runner-graceful-shutdown.d.ts +0 -6
  37. package/build/src/background-jobs/runner-graceful-shutdown.d.ts.map +1 -1
  38. package/build/src/background-jobs/runner-graceful-shutdown.js +3 -3
  39. package/build/src/background-jobs/scheduler.d.ts +0 -3
  40. package/build/src/background-jobs/scheduler.d.ts.map +1 -1
  41. package/build/src/background-jobs/scheduler.js +4 -4
  42. package/build/src/background-jobs/worker.d.ts +0 -8
  43. package/build/src/background-jobs/worker.d.ts.map +1 -1
  44. package/build/src/background-jobs/worker.js +9 -9
  45. package/build/src/beacon/client.d.ts +0 -4
  46. package/build/src/beacon/client.d.ts.map +1 -1
  47. package/build/src/beacon/client.js +4 -4
  48. package/build/src/database/drivers/sqlite/web-persistence.d.ts +0 -14
  49. package/build/src/database/drivers/sqlite/web-persistence.d.ts.map +1 -1
  50. package/build/src/database/drivers/sqlite/web-persistence.js +3 -3
  51. package/build/src/database/pool/async-tracked-multi-connection.d.ts +29 -1
  52. package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
  53. package/build/src/database/pool/async-tracked-multi-connection.js +57 -15
  54. package/build/src/database/pool/base.d.ts +53 -4
  55. package/build/src/database/pool/base.d.ts.map +1 -1
  56. package/build/src/database/pool/base.js +23 -6
  57. package/build/src/database/query/index.d.ts +18 -39
  58. package/build/src/database/query/index.d.ts.map +1 -1
  59. package/build/src/database/query/index.js +22 -22
  60. package/build/src/database/query/model-class-query.d.ts +0 -5
  61. package/build/src/database/query/model-class-query.d.ts.map +1 -1
  62. package/build/src/database/query/model-class-query.js +6 -6
  63. package/build/src/database/record/acts-as-list.d.ts.map +1 -1
  64. package/build/src/database/record/acts-as-list.js +34 -12
  65. package/build/src/database/record/attachments/normalize-input.d.ts +0 -9
  66. package/build/src/database/record/attachments/normalize-input.d.ts.map +1 -1
  67. package/build/src/database/record/attachments/normalize-input.js +10 -10
  68. package/build/src/database/record/attachments/store.d.ts.map +1 -1
  69. package/build/src/database/record/attachments/store.js +2 -2
  70. package/build/src/database/record/auditing.d.ts +17 -24
  71. package/build/src/database/record/auditing.d.ts.map +1 -1
  72. package/build/src/database/record/auditing.js +8 -8
  73. package/build/src/database/record/index.d.ts +4 -4
  74. package/build/src/database/record/index.d.ts.map +1 -1
  75. package/build/src/database/record/index.js +5 -5
  76. package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
  77. package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +5 -13
  78. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +0 -39
  79. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
  80. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +2 -2
  81. package/build/src/frontend-model-controller.d.ts +1 -1
  82. package/build/src/frontend-model-controller.d.ts.map +1 -1
  83. package/build/src/frontend-model-controller.js +50 -50
  84. package/build/src/frontend-models/base.d.ts.map +1 -1
  85. package/build/src/frontend-models/base.js +18 -18
  86. package/build/src/frontend-models/outgoing-event-buffer.d.ts.map +1 -1
  87. package/build/src/frontend-models/outgoing-event-buffer.js +3 -3
  88. package/build/src/frontend-models/query.d.ts +56 -37
  89. package/build/src/frontend-models/query.d.ts.map +1 -1
  90. package/build/src/frontend-models/query.js +39 -39
  91. package/build/src/frontend-models/resource-definition.d.ts +0 -7
  92. package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
  93. package/build/src/frontend-models/resource-definition.js +8 -8
  94. package/build/src/frontend-models/transport-serialization.d.ts +0 -10
  95. package/build/src/frontend-models/transport-serialization.d.ts.map +1 -1
  96. package/build/src/frontend-models/transport-serialization.js +11 -11
  97. package/build/src/frontend-models/websocket-channel.d.ts +0 -12
  98. package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
  99. package/build/src/frontend-models/websocket-channel.js +13 -13
  100. package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
  101. package/build/src/http-server/client/websocket-session.js +5 -5
  102. package/build/src/http-server/client-delivery-queue.d.ts +7 -7
  103. package/build/src/http-server/client-delivery-queue.d.ts.map +1 -1
  104. package/build/src/http-server/client-delivery-queue.js +8 -8
  105. package/build/src/http-server/websocket-event-log-store.d.ts +0 -14
  106. package/build/src/http-server/websocket-event-log-store.d.ts.map +1 -1
  107. package/build/src/http-server/websocket-event-log-store.js +5 -5
  108. package/build/src/plugins/sqljs-wasm-route.d.ts.map +1 -1
  109. package/build/src/plugins/sqljs-wasm-route.js +4 -4
  110. package/build/src/sync/conflict-strategy.d.ts +0 -15
  111. package/build/src/sync/conflict-strategy.d.ts.map +1 -1
  112. package/build/src/sync/conflict-strategy.js +3 -3
  113. package/build/src/sync/device-identity.d.ts +0 -45
  114. package/build/src/sync/device-identity.d.ts.map +1 -1
  115. package/build/src/sync/device-identity.js +4 -4
  116. package/build/src/sync/local-mutation-log.d.ts +0 -41
  117. package/build/src/sync/local-mutation-log.d.ts.map +1 -1
  118. package/build/src/sync/local-mutation-log.js +9 -9
  119. package/build/src/sync/offline-grant.d.ts +0 -26
  120. package/build/src/sync/offline-grant.d.ts.map +1 -1
  121. package/build/src/sync/offline-grant.js +4 -4
  122. package/build/src/sync/peer-mutation-bundle.d.ts +0 -14
  123. package/build/src/sync/peer-mutation-bundle.d.ts.map +1 -1
  124. package/build/src/sync/peer-mutation-bundle.js +3 -3
  125. package/build/src/sync/server-change-feed.d.ts.map +1 -1
  126. package/build/src/sync/server-change-feed.js +33 -33
  127. package/build/src/sync/sync-api-client.d.ts +0 -9
  128. package/build/src/sync/sync-api-client.d.ts.map +1 -1
  129. package/build/src/sync/sync-api-client.js +2 -2
  130. package/build/src/sync/sync-envelope-replay-service.d.ts +38 -21
  131. package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
  132. package/build/src/sync/sync-envelope-replay-service.js +18 -18
  133. package/build/src/sync/sync-resource-base.d.ts +0 -7
  134. package/build/src/sync/sync-resource-base.d.ts.map +1 -1
  135. package/build/src/sync/sync-resource-base.js +2 -2
  136. package/build/src/sync/sync-scope-store.d.ts +0 -9
  137. package/build/src/sync/sync-scope-store.d.ts.map +1 -1
  138. package/build/src/sync/sync-scope-store.js +10 -10
  139. package/build/src/testing/test-filter-parser.d.ts +0 -10
  140. package/build/src/testing/test-filter-parser.d.ts.map +1 -1
  141. package/build/src/testing/test-filter-parser.js +11 -11
  142. package/build/src/testing/test-runner.d.ts +25 -91
  143. package/build/src/testing/test-runner.d.ts.map +1 -1
  144. package/build/src/testing/test-runner.js +113 -98
  145. package/build/src/testing/test.d.ts +8 -0
  146. package/build/src/testing/test.d.ts.map +1 -1
  147. package/build/src/testing/test.js +9 -9
  148. package/build/src/utils/backtrace-cleaner.d.ts.map +1 -1
  149. package/build/src/utils/backtrace-cleaner.js +9 -9
  150. package/build/src/utils/model-scope.d.ts +0 -8
  151. package/build/src/utils/model-scope.d.ts.map +1 -1
  152. package/build/src/utils/model-scope.js +3 -3
  153. package/build/src/utils/ransack.d.ts +52 -19
  154. package/build/src/utils/ransack.d.ts.map +1 -1
  155. package/build/src/utils/ransack.js +26 -26
  156. package/build/sync/conflict-strategy.js +3 -6
  157. package/build/sync/device-identity.js +4 -9
  158. package/build/sync/local-mutation-log.js +9 -14
  159. package/build/sync/offline-grant.js +4 -7
  160. package/build/sync/peer-mutation-bundle.js +2 -4
  161. package/build/sync/server-change-feed.js +32 -34
  162. package/build/sync/sync-api-client.js +1 -2
  163. package/build/sync/sync-envelope-replay-service.js +17 -19
  164. package/build/sync/sync-resource-base.js +1 -2
  165. package/build/sync/sync-scope-store.js +9 -10
  166. package/build/testing/test-filter-parser.js +10 -11
  167. package/build/testing/test-runner.js +116 -108
  168. package/build/testing/test.js +8 -9
  169. package/build/utils/backtrace-cleaner.js +8 -9
  170. package/build/utils/model-scope.js +3 -4
  171. package/build/utils/ransack.js +27 -34
  172. package/package.json +2 -1
  173. package/scripts/eslint-rules/typedefs-first.js +67 -0
  174. package/scripts/prewarm-chromedriver.js +26 -0
  175. package/src/background-jobs/cron-expression.js +12 -13
  176. package/src/background-jobs/main.js +6 -6
  177. package/src/background-jobs/runner-graceful-shutdown.js +2 -3
  178. package/src/background-jobs/scheduler.js +3 -3
  179. package/src/background-jobs/worker.js +8 -9
  180. package/src/beacon/client.js +3 -4
  181. package/src/database/drivers/sqlite/web-persistence.js +2 -5
  182. package/src/database/pool/async-tracked-multi-connection.js +65 -15
  183. package/src/database/pool/base.js +23 -7
  184. package/src/database/query/index.js +21 -22
  185. package/src/database/query/model-class-query.js +5 -6
  186. package/src/database/record/acts-as-list.js +37 -10
  187. package/src/database/record/attachments/normalize-input.js +9 -10
  188. package/src/database/record/attachments/store.js +2 -2
  189. package/src/database/record/auditing.js +7 -8
  190. package/src/database/record/index.js +4 -5
  191. package/src/environment-handlers/node/cli/commands/generate/base-models.js +4 -12
  192. package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +1 -2
  193. package/src/frontend-model-controller.js +49 -56
  194. package/src/frontend-models/base.js +17 -19
  195. package/src/frontend-models/outgoing-event-buffer.js +3 -4
  196. package/src/frontend-models/query.js +40 -44
  197. package/src/frontend-models/resource-definition.js +7 -8
  198. package/src/frontend-models/transport-serialization.js +10 -12
  199. package/src/frontend-models/websocket-channel.js +12 -13
  200. package/src/http-server/client/websocket-session.js +4 -5
  201. package/src/http-server/client-delivery-queue.js +7 -8
  202. package/src/http-server/websocket-event-log-store.js +4 -6
  203. package/src/plugins/sqljs-wasm-route.js +3 -5
  204. package/src/sync/conflict-strategy.js +3 -6
  205. package/src/sync/device-identity.js +4 -9
  206. package/src/sync/local-mutation-log.js +9 -14
  207. package/src/sync/offline-grant.js +4 -7
  208. package/src/sync/peer-mutation-bundle.js +2 -4
  209. package/src/sync/server-change-feed.js +32 -34
  210. package/src/sync/sync-api-client.js +1 -2
  211. package/src/sync/sync-envelope-replay-service.js +17 -19
  212. package/src/sync/sync-resource-base.js +1 -2
  213. package/src/sync/sync-scope-store.js +9 -10
  214. package/src/testing/test-filter-parser.js +10 -11
  215. package/src/testing/test-runner.js +116 -108
  216. package/src/testing/test.js +8 -9
  217. package/src/utils/backtrace-cleaner.js +8 -9
  218. package/src/utils/model-scope.js +3 -4
  219. package/src/utils/ransack.js +27 -34
@@ -50,6 +50,7 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
50
50
  const position = record.readAttribute(positionColumn)
51
51
 
52
52
  if (position != null) {
53
+ assertPositivePosition({position, positionColumn})
53
54
  await shiftPositionsUp({record, positionColumn, scope, fromPosition: position})
54
55
  } else {
55
56
  const nextPosition = await highestPositionInScope({record, positionColumn, scope})
@@ -77,12 +78,19 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
77
78
 
78
79
  if (!posChanged && !scopeChanged) return
79
80
 
81
+ assertPositivePosition({
82
+ position: rawAttributes[posColumn],
83
+ positionColumn,
84
+ persisted: true
85
+ })
86
+
80
87
  const oldPosition = posChanged ? /** @type {number} */ (rawAttributes[posColumn]) : /** @type {number} */ (record.readAttribute(positionColumn))
81
88
  const newPosition = posChanged ? /** @type {number} */ (changes[posColumn]) : /** @type {number} */ (record.readAttribute(positionColumn))
82
89
  const oldScopeValue = scopeChanged ? /** @type {number} */ (rawAttributes[scopeCol]) : /** @type {number} */ (record.readAttribute(scope))
83
90
  const newScopeValue = scopeChanged ? /** @type {number} */ (changes[scopeCol]) : /** @type {number} */ (record.readAttribute(scope))
84
91
 
85
- if (oldPosition == null || newPosition == null) return
92
+ assertPositivePosition({position: newPosition, positionColumn})
93
+ if (oldPosition == null) return
86
94
  if (newPosition === oldPosition && newScopeValue === oldScopeValue) return
87
95
 
88
96
  if (scopeChanged && oldScopeValue !== newScopeValue) {
@@ -100,7 +108,7 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
100
108
  return
101
109
  }
102
110
 
103
- await moveOutOfWay({record, positionColumn, scope, scopeValue: oldScopeValue, targetScopeValue: newScopeValue})
111
+ await moveOutOfWay({record, positionColumn, scope, scopeValue: oldScopeValue})
104
112
  setShiftingFlag(record, false)
105
113
  await shiftPositionsDown({record, positionColumn, scope, scopeValue: oldScopeValue, fromPosition: oldPosition + 1})
106
114
  await shiftPositionsUp({record, positionColumn, scope, scopeValue: newScopeValue, fromPosition: newPosition, excludeRecordId: record.id()})
@@ -121,7 +129,14 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
121
129
  modelClass.beforeDestroy(async (record) => {
122
130
  const position = record.readAttribute(positionColumn)
123
131
 
124
- if (position == null) return
132
+ if (position == null) {
133
+ const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
134
+ const posColumn = modelClass.getColumnNameForAttributeName(positionColumn)
135
+
136
+ if (posColumn in record._attributes) assertPositivePosition({position, positionColumn, persisted: true})
137
+ return
138
+ }
139
+ assertPositivePosition({position, positionColumn, persisted: true})
125
140
 
126
141
  await moveOutOfWay({record, positionColumn, scope})
127
142
  setShiftingFlag(record, false)
@@ -130,6 +145,22 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
130
145
  })
131
146
  }
132
147
 
148
+ /**
149
+ * Enforces the public gap-less list position invariant before any shifting.
150
+ * @param {object} args - Arguments.
151
+ * @param {number | null | undefined} args.position - Position to validate.
152
+ * @param {string} args.positionColumn - Position attribute name.
153
+ * @param {boolean} [args.persisted] - Whether the invalid value came from persisted state.
154
+ * @returns {void}
155
+ */
156
+ function assertPositivePosition({position, positionColumn, persisted = false}) {
157
+ if (typeof position === "number" && Number.isInteger(position) && position > 0) return
158
+
159
+ const source = persisted ? "Persisted" : "Requested"
160
+
161
+ throw new Error(`${source} actsAsList ${positionColumn} must be a positive integer`)
162
+ }
163
+
133
164
  /**
134
165
  * Places a moved row after surrounding rows have shifted.
135
166
  * @param {object} args - Arguments.
@@ -380,30 +411,26 @@ function resolveScopeValue(record, scope) {
380
411
  * @param {string} args.positionColumn - camelCase position attribute.
381
412
  * @param {string} args.scope - camelCase scope attribute.
382
413
  * @param {string | number | null} [args.scopeValue] - Scope containing the record before move-out.
383
- * @param {string | number | null} [args.targetScopeValue] - Temporary scope value to assign.
384
414
  * @returns {Promise<void>}
385
415
  */
386
- async function moveOutOfWay({record, positionColumn, scope, scopeValue, targetScopeValue}) {
416
+ async function moveOutOfWay({record, positionColumn, scope, scopeValue}) {
387
417
  const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
388
418
  const connection = modelClass.connection()
389
419
  const tableName = modelClass._getTable().getName()
390
420
  const resolvedScopeValue = scopeValue != null ? scopeValue : resolveScopeValue(record, scope)
391
- const resolvedTargetScopeValue = targetScopeValue != null ? targetScopeValue : resolvedScopeValue
392
421
 
393
422
  if (resolvedScopeValue == null) return
394
- if (resolvedTargetScopeValue == null) return
395
423
 
396
- const tempPosition = -record.id()
397
424
  const positionColumnSql = connection.quoteColumn(modelClass.getColumnNameForAttributeName(positionColumn))
398
425
  const scopeColumnSql = connection.quoteColumn(modelClass.getColumnNameForAttributeName(scope))
399
426
  const tableSql = connection.quoteTable(tableName)
400
- const pkSql = connection.quoteColumn("id")
427
+ const pkSql = connection.quoteColumn(modelClass.primaryKey())
401
428
 
402
429
  setShiftingFlag(record, true)
403
430
 
404
431
  try {
405
432
  await connection.query(
406
- `UPDATE ${tableSql} SET ${scopeColumnSql} = ${connection.quote(resolvedTargetScopeValue)}, ${positionColumnSql} = ${connection.quote(tempPosition)} WHERE ${scopeColumnSql} = ${connection.quote(resolvedScopeValue)} AND ${pkSql} = ${connection.quote(record.id())}`
433
+ `UPDATE ${tableSql} SET ${positionColumnSql} = -${positionColumnSql} WHERE ${scopeColumnSql} = ${connection.quote(resolvedScopeValue)} AND ${pkSql} = ${connection.quote(record.id())}`
407
434
  )
408
435
  } finally {
409
436
  // Don't clear the flag here — the caller will do that after shifts
@@ -2,6 +2,15 @@
2
2
 
3
3
  import UploadedFile from "../../../http-server/client/uploaded-file/uploaded-file.js"
4
4
 
5
+ /**
6
+ * NormalizedAttachmentInput type.
7
+ * @typedef {object} NormalizedAttachmentInput
8
+ * @property {number} byteSize - File size in bytes.
9
+ * @property {Buffer} contentBuffer - Raw content bytes.
10
+ * @property {string} contentBase64 - Base64 encoded content.
11
+ * @property {string | null} contentType - Content type.
12
+ * @property {string} filename - Filename.
13
+ */
5
14
  /**
6
15
  * Runs base name.
7
16
  * @param {string} value - Path-like value.
@@ -96,16 +105,6 @@ async function uploadedFileBuffer(uploadedFile, environmentHandler) {
96
105
  throw new Error("Unsupported uploaded file type")
97
106
  }
98
107
 
99
- /**
100
- * NormalizedAttachmentInput type.
101
- * @typedef {object} NormalizedAttachmentInput
102
- * @property {number} byteSize - File size in bytes.
103
- * @property {Buffer} contentBuffer - Raw content bytes.
104
- * @property {string} contentBase64 - Base64 encoded content.
105
- * @property {string | null} contentType - Content type.
106
- * @property {string} filename - Filename.
107
- */
108
-
109
108
  /**
110
109
  * Runs normalize record attachment input.
111
110
  * @param {?} input - Attachment input.
@@ -4,11 +4,11 @@ import UUID from "pure-uuid"
4
4
  import TableData from "../../table-data/index.js"
5
5
  import normalizeRecordAttachmentInput from "./normalize-input.js"
6
6
 
7
- const ATTACHMENTS_TABLE = "velocious_attachments"
8
-
9
7
  /**
10
8
  * AttachmentDriverConstructor type.
11
9
  * @typedef {import("../../../configuration-types.js").AttachmentDriverConstructor} AttachmentDriverConstructor */
10
+ const ATTACHMENTS_TABLE = "velocious_attachments"
11
+
12
12
  /**
13
13
  * Stores by configuration.
14
14
  * @type {WeakMap<import("../../../configuration.js").default, Map<string, RecordAttachmentsStore>>} */
@@ -2,6 +2,13 @@
2
2
 
3
3
  import UUID from "pure-uuid"
4
4
 
5
+ /**
6
+ * Global audit event bus matching ActiveRecordAuditable::Events.
7
+ * @typedef {object} AuditEventsType
8
+ * @property {(type: string, action: string, args: AuditEventPayload) => void} call - Fire all callbacks for a model type + action.
9
+ * @property {(type: string, action: string, callback: (args: AuditEventPayload) => void) => () => void} connect - Register a callback for a model type + action. Returns an unsubscribe function.
10
+ * @property {() => void} reset - Clear all registered callbacks.
11
+ */
5
12
  /**
6
13
  * AuditChanges type.
7
14
  * @typedef {Record<string, ?>} AuditChanges
@@ -64,14 +71,6 @@ const generatedAuditRelationships = new WeakSet()
64
71
  /** @type {Record<string, Record<string, Array<(args: AuditEventPayload) => void>>>} */
65
72
  let globalEventConnections = {}
66
73
 
67
- /**
68
- * Global audit event bus matching ActiveRecordAuditable::Events.
69
- * @typedef {object} AuditEventsType
70
- * @property {(type: string, action: string, args: AuditEventPayload) => void} call - Fire all callbacks for a model type + action.
71
- * @property {(type: string, action: string, callback: (args: AuditEventPayload) => void) => () => void} connect - Register a callback for a model type + action. Returns an unsubscribe function.
72
- * @property {() => void} reset - Clear all registered callbacks.
73
- */
74
-
75
74
  /** @type {AuditEventsType} */
76
75
  const AuditEvents = {
77
76
  /**
@@ -57,6 +57,10 @@ import ValidatorsUniqueness from "./validators/uniqueness.js"
57
57
  import registerActsAsListCallbacks from "./acts-as-list.js"
58
58
  import UUID from "pure-uuid"
59
59
 
60
+ /**
61
+ * Translation record shape used by translated attributes.
62
+ * @typedef {VelociousDatabaseRecord & {locale: function(): string}} TranslationBase
63
+ */
60
64
  /**
61
65
  * AttachmentDriverConstructor type.
62
66
  * @typedef {import("../../configuration-types.js").AttachmentDriverConstructor} AttachmentDriverConstructor
@@ -4452,11 +4456,6 @@ class VelociousDatabaseRecord {
4452
4456
  }
4453
4457
  }
4454
4458
 
4455
- /**
4456
- * Translation record shape used by translated attributes.
4457
- * @typedef {VelociousDatabaseRecord & {locale: function(): string}} TranslationBase
4458
- */
4459
-
4460
4459
  VelociousDatabaseRecord.registerValidatorType("format", ValidatorsFormat)
4461
4460
  VelociousDatabaseRecord.registerValidatorType("length", ValidatorsLength)
4462
4461
  VelociousDatabaseRecord.registerValidatorType("presence", ValidatorsPresence)
@@ -154,16 +154,12 @@ export default class DbGenerateModel extends BaseCommand {
154
154
  // --- getModelClass() override (fixes polymorphic typing in JS/JSDoc) ---
155
155
  if (await fileExists(sourceModelFullFilePath)) {
156
156
  // Model file exists (e.g. src/models/ticket.js) → return typeof Ticket
157
- fileContent += " /**\n"
158
- fileContent += ` * @returns {typeof import("${sourceModelFilePath}").default}\n`
159
- fileContent += " */\n"
157
+ fileContent += ` /** @returns {typeof import("${sourceModelFilePath}").default} */\n`
160
158
  fileContent += " // @ts-ignore - override narrows return type for better IntelliSense in generated model bases\n"
161
159
  fileContent += ` getModelClass() { return /** @type {typeof import("${sourceModelFilePath}").default} */ (this.constructor) }\n\n`
162
160
  } else {
163
161
  // No model file yet → fall back to typeof TicketBase
164
- fileContent += " /**\n"
165
- fileContent += ` * @returns {typeof ${modelNameCamelized}Base}\n`
166
- fileContent += " */\n"
162
+ fileContent += ` /** @returns {typeof ${modelNameCamelized}Base} */\n`
167
163
  fileContent += " // @ts-ignore - override narrows return type for better IntelliSense in generated model bases\n"
168
164
  fileContent += ` getModelClass() { return /** @type {typeof ${modelNameCamelized}Base} */ (this.constructor) }\n\n`
169
165
  }
@@ -181,9 +177,7 @@ export default class DbGenerateModel extends BaseCommand {
181
177
  }
182
178
 
183
179
  if (jsdocType) {
184
- fileContent += " /**\n"
185
- fileContent += ` * @returns {${jsdocType}${column.getNull() ? " | null" : ""}}\n`
186
- fileContent += " */\n"
180
+ fileContent += ` /** @returns {${jsdocType}${column.getNull() ? " | null" : ""}} */\n`
187
181
  }
188
182
 
189
183
  fileContent += ` ${camelizedColumnName}() { return this.readAttribute("${camelizedColumnName}") }\n\n`
@@ -199,9 +193,7 @@ export default class DbGenerateModel extends BaseCommand {
199
193
 
200
194
  fileContent += ` set${camelizedColumnNameBigFirst}(newValue) { return this._setColumnAttribute("${camelizedColumnName}", newValue) }\n\n`
201
195
 
202
- fileContent += " /**\n"
203
- fileContent += " * @returns {boolean}\n"
204
- fileContent += " */\n"
196
+ fileContent += " /** @returns {boolean} */\n"
205
197
  fileContent += ` has${camelizedColumnNameBigFirst}() { return this._hasAttribute(this.${camelizedColumnName}()) }\n`
206
198
 
207
199
  methodsCount++
@@ -6,8 +6,6 @@ import * as inflection from "inflection"
6
6
  import {frontendModelResourceIsBuiltIn, frontendModelResourcesWithBuiltInsForBackendProject} from "../../../../../frontend-models/built-in-resources.js"
7
7
  import {frontendModelResourceClassFromDefinition, frontendModelResourceConfigurationFromDefinition} from "../../../../../frontend-models/resource-definition.js"
8
8
 
9
- const FRONTEND_MODELS_REGENERATE_COMMAND = "velocious generate:frontend-models"
10
-
11
9
  /**
12
10
  * Attribute metadata used for generated frontend-model JSDoc.
13
11
  * @typedef {object} FrontendAttributeConfig
@@ -47,6 +45,7 @@ const FRONTEND_MODELS_REGENERATE_COMMAND = "velocious generate:frontend-models"
47
45
  * @property {string | null} sourceFile - Source file that declared the method.
48
46
  * @property {string} type - JSDoc parameter type.
49
47
  */
48
+ const FRONTEND_MODELS_REGENERATE_COMMAND = "velocious generate:frontend-models"
50
49
 
51
50
  /** Node CLI command that generates frontend model classes from backend project resource config. */
52
51
  export default class DbGenerateFrontendModels extends BaseCommand {
@@ -20,6 +20,55 @@ import isDate from "./utils/is-date.js"
20
20
  import isPlainObject from "./utils/plain-object.js"
21
21
  import {RansackQueryError, normalizeRansackGroup, parseRansackSort} from "./utils/ransack.js"
22
22
 
23
+ /**
24
+ * FrontendModelSearch type.
25
+ * @typedef {object} FrontendModelSearch
26
+ * @property {string[]} path - Relationship path.
27
+ * @property {string} column - Column or attribute name.
28
+ * @property {"eq" | "like" | "notEq" | "gt" | "gteq" | "lt" | "lteq"} operator - Search operator.
29
+ * @property {?} value - Search value.
30
+ */
31
+ /**
32
+ * FrontendModelSort type.
33
+ * @typedef {object} FrontendModelSort
34
+ * @property {string} column - Attribute name to sort by.
35
+ * @property {"asc" | "desc"} direction - Sort direction.
36
+ * @property {string[]} path - Relationship path from root model.
37
+ */
38
+ /**
39
+ * FrontendModelGroup type.
40
+ * @typedef {object} FrontendModelGroup
41
+ * @property {string} column - Attribute name to group by.
42
+ * @property {string[]} path - Relationship path from root model.
43
+ */
44
+ /**
45
+ * FrontendModelPluck type.
46
+ * @typedef {object} FrontendModelPluck
47
+ * @property {string} column - Attribute name to pluck.
48
+ * @property {string[]} path - Relationship path from root model.
49
+ */
50
+ /**
51
+ * FrontendModelPagination type.
52
+ * @typedef {object} FrontendModelPagination
53
+ * @property {number | null} limit - Maximum number of records.
54
+ * @property {number | null} offset - Number of records to skip.
55
+ * @property {number | null} page - 1-based page number.
56
+ * @property {number | null} perPage - Page size.
57
+ */
58
+ /**
59
+ * @typedef {import("./configuration-types.js").ClientErrorPayloadContext & {
60
+ * action: string,
61
+ * expectedError: boolean,
62
+ * frontendModelEndpoint: true
63
+ * }} FrontendModelEndpointErrorContext
64
+ */
65
+ /**
66
+ * FrontendModelIndexQueryOptions type.
67
+ * @typedef {object} FrontendModelIndexQueryOptions
68
+ * @property {boolean} [includePagination] - Whether frontend-model pagination params should be applied.
69
+ * @property {boolean} [includeSort] - Whether frontend-model sort params should be applied.
70
+ * @property {import("./frontend-model-resource/base-resource.js").default} [resource] - Resource providing query hooks.
71
+ */
23
72
  /** @typedef {import("./database/query/model-class-query.js").default & Record<symbol, Set<string> | undefined>} FrontendModelQueryMetadata */
24
73
 
25
74
  /**
@@ -114,62 +163,6 @@ function normalizeFrontendModelSelect(select, rootModelName = null) {
114
163
  return normalized
115
164
  }
116
165
 
117
- /**
118
- * FrontendModelSearch type.
119
- * @typedef {object} FrontendModelSearch
120
- * @property {string[]} path - Relationship path.
121
- * @property {string} column - Column or attribute name.
122
- * @property {"eq" | "like" | "notEq" | "gt" | "gteq" | "lt" | "lteq"} operator - Search operator.
123
- * @property {?} value - Search value.
124
- */
125
-
126
- /**
127
- * FrontendModelSort type.
128
- * @typedef {object} FrontendModelSort
129
- * @property {string} column - Attribute name to sort by.
130
- * @property {"asc" | "desc"} direction - Sort direction.
131
- * @property {string[]} path - Relationship path from root model.
132
- */
133
-
134
- /**
135
- * FrontendModelGroup type.
136
- * @typedef {object} FrontendModelGroup
137
- * @property {string} column - Attribute name to group by.
138
- * @property {string[]} path - Relationship path from root model.
139
- */
140
-
141
- /**
142
- * FrontendModelPluck type.
143
- * @typedef {object} FrontendModelPluck
144
- * @property {string} column - Attribute name to pluck.
145
- * @property {string[]} path - Relationship path from root model.
146
- */
147
-
148
- /**
149
- * FrontendModelPagination type.
150
- * @typedef {object} FrontendModelPagination
151
- * @property {number | null} limit - Maximum number of records.
152
- * @property {number | null} offset - Number of records to skip.
153
- * @property {number | null} page - 1-based page number.
154
- * @property {number | null} perPage - Page size.
155
- */
156
-
157
- /**
158
- * @typedef {import("./configuration-types.js").ClientErrorPayloadContext & {
159
- * action: string,
160
- * expectedError: boolean,
161
- * frontendModelEndpoint: true
162
- * }} FrontendModelEndpointErrorContext
163
- */
164
-
165
- /**
166
- * FrontendModelIndexQueryOptions type.
167
- * @typedef {object} FrontendModelIndexQueryOptions
168
- * @property {boolean} [includePagination] - Whether frontend-model pagination params should be applied.
169
- * @property {boolean} [includeSort] - Whether frontend-model sort params should be applied.
170
- * @property {import("./frontend-model-resource/base-resource.js").default} [resource] - Resource providing query hooks.
171
- */
172
-
173
166
  const frontendModelJoinedPathsSymbol = Symbol("frontendModelJoinedPaths")
174
167
  const frontendModelGroupedColumnsSymbol = Symbol("frontendModelGroupedColumns")
175
168
  const frontendModelWhereNoMatchSymbol = Symbol("frontendModelWhereNoMatch")
@@ -17,6 +17,23 @@ import {defineModelScope} from "../utils/model-scope.js"
17
17
  import isPlainObject from "../utils/plain-object.js"
18
18
  import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQueryData, setPayloadAssociationCount, setPayloadComputedAbility, setPayloadQueryData} from "../record-payload-values.js"
19
19
 
20
+ /**
21
+ * Frontend model relationship helper type. Returned by `getRelationshipByName`,
22
+ * which generated models immediately cast to their concrete relationship type
23
+ * (e.g. `FrontendModelSingularRelationship<Owner, Target, TargetCreateAttributes>`).
24
+ * The members use `any` type args so that cast is allowed regardless of the
25
+ * target model's typed-attribute generics — a concrete `FrontendModelBase` member
26
+ * here makes the cast a non-overlapping (TS2352) error for every typed model.
27
+ * @typedef {FrontendModelHasManyRelationship<any, any, any> | FrontendModelSingularRelationship<any, any, any>} FrontendModelRelationship
28
+ */
29
+ /**
30
+ * Defines this typedef.
31
+ * @typedef {{callback: (payload: {id: string, model: FrontendModelBase}) => void, eventFilterKey: string | null, eventFilterPayload: import("./query.js").FrontendModelEventFilterPayload | null, projectionPayload: import("./query.js").FrontendModelProjectionPayload}} FrontendModelModelEventCallbackEntry
32
+ */
33
+ /**
34
+ * Defines this typedef.
35
+ * @typedef {{callback: (payload: {id: string}) => void}} FrontendModelDestroyEventCallbackEntry
36
+ */
20
37
  /**
21
38
  * FrontendModelCommandType type.
22
39
  * @typedef {"create" | "find" | "index" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url"} FrontendModelCommandType */
@@ -658,16 +675,6 @@ export class FrontendModelHasManyRelationship {
658
675
  }
659
676
  }
660
677
 
661
- /**
662
- * Frontend model relationship helper type. Returned by `getRelationshipByName`,
663
- * which generated models immediately cast to their concrete relationship type
664
- * (e.g. `FrontendModelSingularRelationship<Owner, Target, TargetCreateAttributes>`).
665
- * The members use `any` type args so that cast is allowed regardless of the
666
- * target model's typed-attribute generics — a concrete `FrontendModelBase` member
667
- * here makes the cast a non-overlapping (TS2352) error for every typed model.
668
- * @typedef {FrontendModelHasManyRelationship<any, any, any> | FrontendModelSingularRelationship<any, any, any>} FrontendModelRelationship
669
- */
670
-
671
678
  /**
672
679
  * Copies loaded relationship state between helpers of the same relationship shape.
673
680
  * @param {object} args - Arguments.
@@ -1279,15 +1286,6 @@ function cloneFrontendModelAttributes(value) {
1279
1286
  */
1280
1287
  const FRONTEND_MODELS_CHANNEL_NAME = "frontend-models"
1281
1288
 
1282
- /**
1283
- * Defines this typedef.
1284
- * @typedef {{callback: (payload: {id: string, model: FrontendModelBase}) => void, eventFilterKey: string | null, eventFilterPayload: import("./query.js").FrontendModelEventFilterPayload | null, projectionPayload: import("./query.js").FrontendModelProjectionPayload}} FrontendModelModelEventCallbackEntry
1285
- */
1286
- /**
1287
- * Defines this typedef.
1288
- * @typedef {{callback: (payload: {id: string}) => void}} FrontendModelDestroyEventCallbackEntry
1289
- */
1290
-
1291
1289
  /**
1292
1290
  * Runs merge frontend model event preload.
1293
1291
  * @param {Record<string, import("./query.js").FrontendModelTransportValue>} target - Target preload payload.
@@ -1,13 +1,12 @@
1
- // @ts-check
2
-
3
- const MAX_BUFFERED_OUTGOING_EVENTS = 50
4
-
5
1
  /**
6
2
  * BufferedOutgoingEvent type.
7
3
  * @typedef {object} BufferedOutgoingEvent
8
4
  * @property {string} customPath - Request path.
9
5
  * @property {Record<string, ?>} payload - Command payload.
10
6
  */
7
+ // @ts-check
8
+
9
+ const MAX_BUFFERED_OUTGOING_EVENTS = 50
11
10
 
12
11
  /**
13
12
  * Buffer.
@@ -5,28 +5,6 @@ import {normalizeRansackGroup, parseRansackSort} from "../utils/ransack.js"
5
5
  import {isModelScopeDescriptor} from "../utils/model-scope.js"
6
6
  import isPlainObject from "../utils/plain-object.js"
7
7
 
8
- /** Error raised when a frontend-model query descriptor is malformed. */
9
- export class FrontendModelQueryError extends Error {
10
- /**
11
- * Creates a frontend-model query error.
12
- * @param {string} message - Error message.
13
- */
14
- constructor(message) {
15
- super(message)
16
-
17
- this.name = "FrontendModelQueryError"
18
- }
19
- }
20
-
21
- /**
22
- * Builds a query descriptor error.
23
- * @param {string} message - Error message.
24
- * @returns {FrontendModelQueryError} - Query descriptor error.
25
- */
26
- function frontendModelQueryError(message) {
27
- return new FrontendModelQueryError(message)
28
- }
29
-
30
8
  /**
31
9
  * FrontendModelSearch type.
32
10
  * @typedef {object} FrontendModelSearch
@@ -97,6 +75,46 @@ function frontendModelQueryError(message) {
97
75
  * @property {FrontendModelEventFilterPayload | null} eventFilterPayload - Normalized event filter payload, or null when unfiltered.
98
76
  * @property {FrontendModelProjectionPayload} projectionPayload - Normalized event serialization projection payload.
99
77
  */
78
+ /**
79
+ * FrontendModelSort type.
80
+ * @typedef {object} FrontendModelSort
81
+ * @property {string} column - Attribute name to sort by.
82
+ * @property {"asc" | "desc"} direction - Sort direction.
83
+ * @property {string[]} path - Relationship path from root model.
84
+ */
85
+ /**
86
+ * FrontendModelGroup type.
87
+ * @typedef {object} FrontendModelGroup
88
+ * @property {string} column - Attribute name to group by.
89
+ * @property {string[]} path - Relationship path from root model.
90
+ */
91
+ /**
92
+ * FrontendModelPluck type.
93
+ * @typedef {object} FrontendModelPluck
94
+ * @property {string} column - Attribute name to pluck.
95
+ * @property {string[]} path - Relationship path from root model.
96
+ */
97
+ /** Error raised when a frontend-model query descriptor is malformed. */
98
+ export class FrontendModelQueryError extends Error {
99
+ /**
100
+ * Creates a frontend-model query error.
101
+ * @param {string} message - Error message.
102
+ */
103
+ constructor(message) {
104
+ super(message)
105
+
106
+ this.name = "FrontendModelQueryError"
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Builds a query descriptor error.
112
+ * @param {string} message - Error message.
113
+ * @returns {FrontendModelQueryError} - Query descriptor error.
114
+ */
115
+ function frontendModelQueryError(message) {
116
+ return new FrontendModelQueryError(message)
117
+ }
100
118
 
101
119
  /**
102
120
  * Runs the normalizePreload helper.
@@ -490,28 +508,6 @@ export function normalizeJoins(joins) {
490
508
  return normalized
491
509
  }
492
510
 
493
- /**
494
- * FrontendModelSort type.
495
- * @typedef {object} FrontendModelSort
496
- * @property {string} column - Attribute name to sort by.
497
- * @property {"asc" | "desc"} direction - Sort direction.
498
- * @property {string[]} path - Relationship path from root model.
499
- */
500
-
501
- /**
502
- * FrontendModelGroup type.
503
- * @typedef {object} FrontendModelGroup
504
- * @property {string} column - Attribute name to group by.
505
- * @property {string[]} path - Relationship path from root model.
506
- */
507
-
508
- /**
509
- * FrontendModelPluck type.
510
- * @typedef {object} FrontendModelPluck
511
- * @property {string} column - Attribute name to pluck.
512
- * @property {string[]} path - Relationship path from root model.
513
- */
514
-
515
511
  /**
516
512
  * Runs normalize sort direction.
517
513
  * @param {?} direction - Direction value.
@@ -5,6 +5,13 @@ import FrontendModelBaseResource from "../frontend-model-resource/base-resource.
5
5
  import restArgsError from "../utils/rest-args-error.js"
6
6
  import {validateFrontendModelResourceCommandName} from "./resource-config-validation.js"
7
7
 
8
+ /**
9
+ * Resolved frontend-model resource registration for a replay resource type.
10
+ * @typedef {object} FrontendModelResolvedResourceRegistration
11
+ * @property {string} modelName - Effective frontend model name (modelName override or registry key).
12
+ * @property {import("../configuration-types.js").FrontendModelResourceClassType} resourceClass - Registered resource class.
13
+ * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration} resourceConfiguration - Normalized resource configuration.
14
+ */
8
15
  const BASE_FRONTEND_MODEL_ABILITY_ACTIONS = ["create", "destroy", "read", "update"]
9
16
  const SHA256_INITIAL_HASH = [
10
17
  0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
@@ -1027,14 +1034,6 @@ function normalizeFrontendModelResourcePathForMatch(path) {
1027
1034
  return withLeadingSlash
1028
1035
  }
1029
1036
 
1030
- /**
1031
- * Resolved frontend-model resource registration for a replay resource type.
1032
- * @typedef {object} FrontendModelResolvedResourceRegistration
1033
- * @property {string} modelName - Effective frontend model name (modelName override or registry key).
1034
- * @property {import("../configuration-types.js").FrontendModelResourceClassType} resourceClass - Registered resource class.
1035
- * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration} resourceConfiguration - Normalized resource configuration.
1036
- */
1037
-
1038
1037
  /**
1039
1038
  * Resolves the registered frontend-model resource class for a resource type
1040
1039
  * across all backend projects. A resource's effective name is its
@@ -4,28 +4,26 @@ import {resolveFrontendModelClass} from "./model-registry.js"
4
4
  import isPlainObject from "../utils/plain-object.js"
5
5
  import {formatDateInTimeZone, validateTimeZone} from "../time-zone.js"
6
6
 
7
- const TYPE_KEY = "__velocious_type"
8
- const TYPE_DATE = "date"
9
- const TYPE_UNDEFINED = "undefined"
10
- const TYPE_BIGINT = "bigint"
11
- const TYPE_NUMBER = "number"
12
- const TYPE_FRONTEND_MODEL = "frontend_model"
13
- const NUMBER_NAN = "NaN"
14
- const NUMBER_POSITIVE_INFINITY = "Infinity"
15
- const NUMBER_NEGATIVE_INFINITY = "-Infinity"
16
- const PRELOADED_RELATIONSHIPS_KEY = "__preloadedRelationships"
17
-
18
7
  /**
19
8
  * Frontend model transport serialization options.
20
9
  * @typedef {object} FrontendModelTransportSerializationOptions
21
10
  * @property {string | undefined} [timeZone] - IANA timezone used when serializing Date instants.
22
11
  */
23
-
24
12
  /**
25
13
  * Normalized frontend model transport serialization options.
26
14
  * @typedef {object} NormalizedFrontendModelTransportSerializationOptions
27
15
  * @property {string | undefined} timeZone - Validated IANA timezone used when serializing Date instants.
28
16
  */
17
+ const TYPE_KEY = "__velocious_type"
18
+ const TYPE_DATE = "date"
19
+ const TYPE_UNDEFINED = "undefined"
20
+ const TYPE_BIGINT = "bigint"
21
+ const TYPE_NUMBER = "number"
22
+ const TYPE_FRONTEND_MODEL = "frontend_model"
23
+ const NUMBER_NAN = "NaN"
24
+ const NUMBER_POSITIVE_INFINITY = "Infinity"
25
+ const NUMBER_NEGATIVE_INFINITY = "-Infinity"
26
+ const PRELOADED_RELATIONSHIPS_KEY = "__preloadedRelationships"
29
27
 
30
28
  /**
31
29
  * Assign a key to a plain object without triggering the `__proto__` setter.