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
@@ -4,6 +4,18 @@ import VelociousWebsocketChannel from "../http-server/websocket-channel.js"
4
4
  import Response from "../http-server/client/response.js"
5
5
  import {serializeFrontendModelTransportValue} from "./transport-serialization.js"
6
6
 
7
+ /**
8
+ * Defines this typedef.
9
+ * @typedef {{action?: string, id?: string | number, matchedEventFilterKeys?: string[], record?: import("./query.js").FrontendModelTransportValue, [key: string]: import("./query.js").FrontendModelTransportValue | string[] | undefined}} FrontendModelLifecycleBroadcastBody
10
+ */
11
+ /**
12
+ * Defines this typedef.
13
+ * @typedef {{headers?: () => Record<string, string | string[] | undefined>, remoteAddress?: () => string | undefined}} FrontendModelWebsocketUpgradeRequest
14
+ */
15
+ /**
16
+ * Defines this typedef.
17
+ * @typedef {{headers: () => Record<string, string | string[] | undefined>, header: (name: string) => string | string[] | undefined, metadata: (key?: string) => Record<string, import("./query.js").FrontendModelTransportValue> | import("./query.js").FrontendModelTransportValue | undefined, path: () => string, httpMethod: () => string, remoteAddress: () => string | undefined, origin: () => string | string[] | undefined}} FrontendModelWebsocketSyntheticRequest
18
+ */
7
19
  const EVENT_FILTER_KEYS = new Set(["joins", "key", "searches", "where"])
8
20
 
9
21
  // Mirrors FRONTEND_MODELS_CHANNEL_NAME in ./websocket-publishers.js, duplicated here
@@ -21,19 +33,6 @@ function transportSerializationOptionsForConfiguration(configuration) {
21
33
  }
22
34
  }
23
35
 
24
- /**
25
- * Defines this typedef.
26
- * @typedef {{action?: string, id?: string | number, matchedEventFilterKeys?: string[], record?: import("./query.js").FrontendModelTransportValue, [key: string]: import("./query.js").FrontendModelTransportValue | string[] | undefined}} FrontendModelLifecycleBroadcastBody
27
- */
28
- /**
29
- * Defines this typedef.
30
- * @typedef {{headers?: () => Record<string, string | string[] | undefined>, remoteAddress?: () => string | undefined}} FrontendModelWebsocketUpgradeRequest
31
- */
32
- /**
33
- * Defines this typedef.
34
- * @typedef {{headers: () => Record<string, string | string[] | undefined>, header: (name: string) => string | string[] | undefined, metadata: (key?: string) => Record<string, import("./query.js").FrontendModelTransportValue> | import("./query.js").FrontendModelTransportValue | undefined, path: () => string, httpMethod: () => string, remoteAddress: () => string | undefined, origin: () => string | string[] | undefined}} FrontendModelWebsocketSyntheticRequest
35
- */
36
-
37
36
  /**
38
37
  * Per-session channel subscription for frontend-model lifecycle events.
39
38
  * Replaces the legacy `FrontendModelWebsocketChannel` (Phase 3).
@@ -9,6 +9,10 @@ import WebsocketRequest from "./websocket-request.js"
9
9
  import WebsocketChannel from "../websocket-channel.js"
10
10
  import {websocketEventLogStoreForConfiguration} from "../websocket-event-log-store.js"
11
11
 
12
+ /**
13
+ * Defines this typedef.
14
+ * @typedef {{type: "subscribe", channel: string, lastEventId?: string, params?: Record<string, ?>} | {type: "metadata", data?: Record<string, ?>} | {type?: "request", body?: ?, headers?: Record<string, ?>, id?: string | number | null, method: string, path: string} | Record<string, ?>} WebsocketSessionMessage
15
+ */
12
16
  const WEBSOCKET_FINAL_FRAME = 0x80
13
17
  const WEBSOCKET_OPCODE_CONTINUATION = 0x0
14
18
  const WEBSOCKET_OPCODE_TEXT = 0x1
@@ -31,11 +35,6 @@ const WEBSOCKET_MAX_INBOUND_FRAME_BYTES_BIGINT = BigInt(WEBSOCKET_MAX_FINAL_FRAM
31
35
  /** Cap on fragment count for a single fragmented message. */
32
36
  const WEBSOCKET_MAX_FRAGMENTED_MESSAGE_FRAGMENTS = 1024
33
37
 
34
- /**
35
- * Defines this typedef.
36
- * @typedef {{type: "subscribe", channel: string, lastEventId?: string, params?: Record<string, ?>} | {type: "metadata", data?: Record<string, ?>} | {type?: "request", body?: ?, headers?: Record<string, ?>, id?: string | number | null, method: string, path: string} | Record<string, ?>} WebsocketSessionMessage
37
- */
38
-
39
38
  /**
40
39
  * Runs subscribe message.
41
40
  * @param {WebsocketSessionMessage} message - Raw websocket message.
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @typedef {object} DeliveryTask
3
+ * @property {number} byteLength - Retained complete-buffer bytes.
4
+ * @property {boolean} countedFrame - Whether this task is an outbound frame.
5
+ * @property {() => Promise<void>} delivery - Delivery operation.
6
+ * @property {(error?: Error) => void} settle - Settles the enqueue promise.
7
+ */
1
8
  // @ts-check
2
9
 
3
10
  export class ClientDeliveryQueueOverflowError extends Error {
@@ -17,14 +24,6 @@ export class ClientDeliveryQueueOverflowError extends Error {
17
24
  }
18
25
  }
19
26
 
20
- /**
21
- * @typedef {object} DeliveryTask
22
- * @property {number} byteLength - Retained complete-buffer bytes.
23
- * @property {boolean} countedFrame - Whether this task is an outbound frame.
24
- * @property {() => Promise<void>} delivery - Delivery operation.
25
- * @property {(error?: Error) => void} settle - Settles the enqueue promise.
26
- */
27
-
28
27
  export default class ClientDeliveryQueue {
29
28
  /**
30
29
  * Builds a per-client delivery queue.
@@ -4,11 +4,6 @@ import {randomUUID} from "crypto"
4
4
  import TableData from "../database/table-data/index.js"
5
5
  import Logger from "../logger.js"
6
6
 
7
- const EVENTS_TABLE = "websocket_channel_events"
8
- const REPLAY_CHANNELS_TABLE = "websocket_replay_channels"
9
- const DEFAULT_RETENTION_MS = 10 * 60 * 1000
10
- const stores = new WeakMap()
11
-
12
7
  /**
13
8
  * WebsocketEventRow type.
14
9
  * @typedef {object} WebsocketEventRow
@@ -18,12 +13,15 @@ const stores = new WeakMap()
18
13
  * @property {string} payload_json - Serialized payload.
19
14
  * @property {number | string} sequence - Sequence number.
20
15
  */
21
-
22
16
  /**
23
17
  * WebsocketReplayChannelRow type.
24
18
  * @typedef {object} WebsocketReplayChannelRow
25
19
  * @property {string} channel - Channel name.
26
20
  */
21
+ const EVENTS_TABLE = "websocket_channel_events"
22
+ const REPLAY_CHANNELS_TABLE = "websocket_replay_channels"
23
+ const DEFAULT_RETENTION_MS = 10 * 60 * 1000
24
+ const stores = new WeakMap()
27
25
 
28
26
  /**
29
27
  * Runs the websocketEventLogStoreForConfiguration helper.
@@ -2,23 +2,21 @@ import {createRequire} from "node:module"
2
2
  import path from "node:path"
3
3
  import SqlJsWasmRouteController from "./sqljs-wasm-route-controller.js"
4
4
 
5
- const require = createRequire(import.meta.url)
6
- const sqlJsEntryPath = require.resolve("sql.js")
7
- const sqlJsDistDirectory = path.dirname(sqlJsEntryPath)
8
-
9
5
  /**
10
6
  * InstallSqlJsWasmRouteArgs type.
11
7
  * @typedef {object} InstallSqlJsWasmRouteArgs
12
8
  * @property {import("../configuration.js").default} configuration - Velocious configuration instance.
13
9
  * @property {string} [routePrefix] - Route prefix used for sql.js asset serving.
14
10
  */
15
-
16
11
  /**
17
12
  * SqlJsLocateFileFromBackendArgs type.
18
13
  * @typedef {object} SqlJsLocateFileFromBackendArgs
19
14
  * @property {string} backendBaseUrl - Backend base URL (for example `https://api.example.com`).
20
15
  * @property {string} [routePrefix] - Route prefix used for sql.js asset serving.
21
16
  */
17
+ const require = createRequire(import.meta.url)
18
+ const sqlJsEntryPath = require.resolve("sql.js")
19
+ const sqlJsDistDirectory = path.dirname(sqlJsEntryPath)
22
20
 
23
21
  /**
24
22
  * Runs normalize route prefix.
@@ -1,17 +1,11 @@
1
- // @ts-check
2
-
3
- const CONFLICT_STRATEGIES = new Set(["optimisticVersion", "serverWins", "lastWriterWins", "fieldThreeWay", "appendOnly"])
4
-
5
1
  /**
6
2
  * @typedef {null | string | number | boolean | unknown[] | Record<string, unknown>} SyncJsonValue
7
3
  */
8
-
9
4
  /**
10
5
  * @typedef {object} SyncConflictRecord
11
6
  * @property {Record<string, SyncJsonValue>} attributes - Record attributes.
12
7
  * @property {string | number | boolean | null} [version] - Record version value.
13
8
  */
14
-
15
9
  /**
16
10
  * @typedef {object} SyncConflictResult
17
11
  * @property {Record<string, SyncJsonValue>} [attributes] - Attributes to apply when replay may continue.
@@ -19,6 +13,9 @@ const CONFLICT_STRATEGIES = new Set(["optimisticVersion", "serverWins", "lastWri
19
13
  * @property {"applied" | "conflict" | "rejected"} status - Conflict decision.
20
14
  * @property {string} strategy - Strategy that produced the decision.
21
15
  */
16
+ // @ts-check
17
+
18
+ const CONFLICT_STRATEGIES = new Set(["optimisticVersion", "serverWins", "lastWriterWins", "fieldThreeWay", "appendOnly"])
22
19
 
23
20
  /**
24
21
  * Evaluates a replay mutation against server/base state using a sync conflict strategy.
@@ -1,13 +1,7 @@
1
- // @ts-check
2
-
3
- const ECDSA_P256_SHA256_ALGORITHM = "ECDSA-P256-SHA256"
4
- const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-"
5
-
6
1
  /**
7
2
  * JSON Web Key used by the sync signing helpers.
8
3
  * @typedef {import("node:crypto").webcrypto.JsonWebKey} SyncJsonWebKey
9
4
  */
10
-
11
5
  /**
12
6
  * Backend-signed device certificate payload.
13
7
  * @typedef {object} DeviceCertificatePayload
@@ -18,7 +12,6 @@ const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-"
18
12
  * @property {string} expiresAt - ISO timestamp after which the certificate is invalid.
19
13
  * @property {string} issuedAt - ISO timestamp when the backend issued the certificate.
20
14
  */
21
-
22
15
  /**
23
16
  * Backend-signed device certificate envelope.
24
17
  * @typedef {object} DeviceCertificate
@@ -26,7 +19,6 @@ const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-"
26
19
  * @property {DeviceCertificatePayload} certificate - Certificate payload.
27
20
  * @property {string} signature - Backend signature over the certificate payload.
28
21
  */
29
-
30
22
  /**
31
23
  * Sync mutation payload signed by a device.
32
24
  * @typedef {object} SyncMutation
@@ -43,7 +35,6 @@ const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-"
43
35
  * @property {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} [payload] - Domain command payload.
44
36
  * @property {string} policyHash - Sync policy hash the mutation was checked against.
45
37
  */
46
-
47
38
  /**
48
39
  * Device-signed mutation envelope.
49
40
  * @typedef {object} SignedSyncMutation
@@ -52,6 +43,10 @@ const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-"
52
43
  * @property {SyncMutation} mutation - Mutation payload.
53
44
  * @property {string} signature - Device signature over the mutation envelope.
54
45
  */
46
+ // @ts-check
47
+
48
+ const ECDSA_P256_SHA256_ALGORITHM = "ECDSA-P256-SHA256"
49
+ const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-"
55
50
 
56
51
  /**
57
52
  * Generates an ECDSA P-256 keypair exported as JWKs.
@@ -1,19 +1,8 @@
1
- // @ts-check
2
-
3
- const DEFAULT_STORAGE_KEY = "velocious.sync.localMutationLog"
4
- const PENDING_STATUS_VALUES = /** @type {LocalMutationStatus[]} */ (["pending", "applied-locally", "peer-applied"])
5
- const PENDING_STATUSES = new Set(PENDING_STATUS_VALUES)
6
- const MUTATION_STATUSES = new Set([...PENDING_STATUSES, "conflict", "rejected", "synced"])
7
- const TERMINAL_STATUSES = new Set(["rejected", "synced"])
8
- /** @type {Map<string, Promise<unknown>>} */
9
- const STORAGE_KEY_LOCKS = new Map()
10
-
11
1
  /**
12
2
  * Local mutation log record query options.
13
3
  * @typedef {object} LocalMutationLogRecordsOptions
14
4
  * @property {LocalMutationStatus[]} [statuses] - Optional status filter.
15
5
  */
16
-
17
6
  /**
18
7
  * Local mutation log row-oriented storage adapter.
19
8
  *
@@ -28,19 +17,16 @@ const STORAGE_KEY_LOCKS = new Map()
28
17
  * @property {(storageKey: string, options?: LocalMutationLogRecordsOptions) => Promise<LocalMutationLogRecord[]> | LocalMutationLogRecord[]} records - Reads log records.
29
18
  * @property {(storageKey: string, record: LocalMutationLogRecord) => Promise<void> | void} updateRecord - Replaces one log record.
30
19
  */
31
-
32
20
  /**
33
21
  * Local sync mutation dependency metadata.
34
22
  * @typedef {object} LocalMutationDependency
35
23
  * @property {string} clientMutationId - Client mutation id this mutation depends on.
36
24
  * @property {string} model - Dependent model/resource name.
37
25
  */
38
-
39
26
  /**
40
27
  * Local mutation log status.
41
28
  * @typedef {"pending" | "applied-locally" | "peer-applied" | "conflict" | "rejected" | "synced"} LocalMutationStatus
42
29
  * */
43
-
44
30
  /**
45
31
  * Local mutation log record.
46
32
  * @typedef {object} LocalMutationLogRecord
@@ -53,6 +39,15 @@ const STORAGE_KEY_LOCKS = new Map()
53
39
  * @property {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} [syncResult] - Backend replay/result metadata.
54
40
  * @property {string} updatedAt - ISO timestamp when the record was last changed.
55
41
  */
42
+ // @ts-check
43
+
44
+ const DEFAULT_STORAGE_KEY = "velocious.sync.localMutationLog"
45
+ const PENDING_STATUS_VALUES = /** @type {LocalMutationStatus[]} */ (["pending", "applied-locally", "peer-applied"])
46
+ const PENDING_STATUSES = new Set(PENDING_STATUS_VALUES)
47
+ const MUTATION_STATUSES = new Set([...PENDING_STATUSES, "conflict", "rejected", "synced"])
48
+ const TERMINAL_STATUSES = new Set(["rejected", "synced"])
49
+ /** @type {Map<string, Promise<unknown>>} */
50
+ const STORAGE_KEY_LOCKS = new Map()
56
51
 
57
52
  /** Client-side append-only sync mutation log with pluggable persistent storage. */
58
53
  export default class LocalMutationLog {
@@ -1,8 +1,3 @@
1
- // @ts-check
2
-
3
- const OFFLINE_GRANT_SIGNATURE_ALGORITHM = "HS256"
4
- const OFFLINE_GRANT_SIGNATURE_PREFIX = "hmac-sha256-"
5
-
6
1
  /**
7
2
  * Signed offline grant envelope.
8
3
  * @typedef {object} SignedOfflineGrant
@@ -11,7 +6,6 @@ const OFFLINE_GRANT_SIGNATURE_PREFIX = "hmac-sha256-"
11
6
  * @property {string} keyId - Signing key id.
12
7
  * @property {string} signature - Hex HMAC signature with a hmac-sha256 prefix.
13
8
  */
14
-
15
9
  /**
16
10
  * Backend-issued offline grant payload.
17
11
  * @typedef {object} OfflineGrant
@@ -23,7 +17,6 @@ const OFFLINE_GRANT_SIGNATURE_PREFIX = "hmac-sha256-"
23
17
  * @property {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} scopes - Materialized grant scopes.
24
18
  * @property {string} userId - Actor user id allowed to use the grant.
25
19
  */
26
-
27
20
  /**
28
21
  * Offline grant signing key.
29
22
  * @typedef {object} OfflineGrantSigningKey
@@ -31,6 +24,10 @@ const OFFLINE_GRANT_SIGNATURE_PREFIX = "hmac-sha256-"
31
24
  * @property {string} id - Public key id included in signed grant envelopes.
32
25
  * @property {string} secret - Private HMAC secret. Never expose this to clients.
33
26
  */
27
+ // @ts-check
28
+
29
+ const OFFLINE_GRANT_SIGNATURE_ALGORITHM = "HS256"
30
+ const OFFLINE_GRANT_SIGNATURE_PREFIX = "hmac-sha256-"
34
31
 
35
32
  /**
36
33
  * Creates a signed offline grant envelope.
@@ -2,9 +2,6 @@
2
2
 
3
3
  import {createSignedMutation, mutationIdempotencyKey, verifySignedMutation} from "./device-identity.js"
4
4
 
5
- const PEER_MUTATION_BUNDLE_FORMAT = "velocious.sync.peer-mutation-bundle.v1"
6
- const EXPORTABLE_STATUSES = new Set(["pending", "applied-locally", "conflict"])
7
-
8
5
  /**
9
6
  * Peer mutation bundle exported by a device for offline/P2P transfer.
10
7
  * @typedef {object} PeerMutationBundle
@@ -12,7 +9,6 @@ const EXPORTABLE_STATUSES = new Set(["pending", "applied-locally", "conflict"])
12
9
  * @property {"velocious.sync.peer-mutation-bundle.v1"} format - Bundle format identifier.
13
10
  * @property {PeerMutationBundleEntry[]} mutations - Signed mutations in local sequence order.
14
11
  */
15
-
16
12
  /**
17
13
  * One peer mutation bundle entry.
18
14
  * @typedef {object} PeerMutationBundleEntry
@@ -20,6 +16,8 @@ const EXPORTABLE_STATUSES = new Set(["pending", "applied-locally", "conflict"])
20
16
  * @property {number} [localSequence] - Exporting device's local mutation sequence.
21
17
  * @property {import("./device-identity.js").SignedSyncMutation} signedMutation - Device-signed mutation envelope.
22
18
  */
19
+ const PEER_MUTATION_BUNDLE_FORMAT = "velocious.sync.peer-mutation-bundle.v1"
20
+ const EXPORTABLE_STATUSES = new Set(["pending", "applied-locally", "conflict"])
23
21
 
24
22
  /**
25
23
  * Exports local non-terminal mutations as a signed peer-transfer bundle.
@@ -4,6 +4,38 @@ import {randomUUID} from "crypto"
4
4
  import TableData from "../database/table-data/index.js"
5
5
  import stableJsonStringify from "./stable-json.js"
6
6
 
7
+ /**
8
+ * @typedef {object} ServerChangeFeedEntry
9
+ * @property {string | null} actorDeviceId - Signed mutation actor device id when available.
10
+ * @property {string | null} actorUserId - Signed mutation actor user id when available.
11
+ * @property {Record<string, ?> | null} attributes - Serialized mutation attributes.
12
+ * @property {string} createdAt - Server change creation timestamp.
13
+ * @property {string} id - Server change id.
14
+ * @property {string | null} idempotencyKey - Mutation idempotency key when available.
15
+ * @property {string} model - Frontend model name.
16
+ * @property {string} operation - Mutation operation.
17
+ * @property {Record<string, ?> | null} payload - Serialized mutation payload.
18
+ * @property {string | null} recordId - Changed record id when known.
19
+ * @property {Record<string, ?> | null} response - Command response payload.
20
+ * @property {Record<string, ?> | null} scope - Offline grant scope.
21
+ * @property {number} serverSequence - Monotonic server sequence.
22
+ */
23
+ /**
24
+ * @typedef {object} ServerChangeFeedRow
25
+ * @property {string | null} actor_device_id - Actor device id.
26
+ * @property {string | null} actor_user_id - Actor user id.
27
+ * @property {string | null} attributes_json - Attributes JSON.
28
+ * @property {Date | string} created_at - Creation time.
29
+ * @property {string} id - Entry id.
30
+ * @property {string | null} idempotency_key - Mutation idempotency key.
31
+ * @property {string} model - Frontend model name.
32
+ * @property {string} operation - Mutation operation.
33
+ * @property {string | null} payload_json - Mutation payload JSON.
34
+ * @property {string | null} record_id - Record id.
35
+ * @property {string | null} response_json - Response JSON.
36
+ * @property {string | null} scope_json - Scope JSON.
37
+ * @property {number | string} server_sequence - Server sequence.
38
+ */
7
39
  const DEFAULT_RETENTION_SIZE = 10000
8
40
  const DEFAULT_PAGE_SIZE = 100
9
41
  const MAX_PAGE_SIZE = 1000
@@ -464,37 +496,3 @@ function scopesEqual(changeScope, requestedScope) {
464
496
 
465
497
  return stableJsonStringify(changeScope || null) === stableJsonStringify(requestedScope)
466
498
  }
467
-
468
- /**
469
- * @typedef {object} ServerChangeFeedEntry
470
- * @property {string | null} actorDeviceId - Signed mutation actor device id when available.
471
- * @property {string | null} actorUserId - Signed mutation actor user id when available.
472
- * @property {Record<string, ?> | null} attributes - Serialized mutation attributes.
473
- * @property {string} createdAt - Server change creation timestamp.
474
- * @property {string} id - Server change id.
475
- * @property {string | null} idempotencyKey - Mutation idempotency key when available.
476
- * @property {string} model - Frontend model name.
477
- * @property {string} operation - Mutation operation.
478
- * @property {Record<string, ?> | null} payload - Serialized mutation payload.
479
- * @property {string | null} recordId - Changed record id when known.
480
- * @property {Record<string, ?> | null} response - Command response payload.
481
- * @property {Record<string, ?> | null} scope - Offline grant scope.
482
- * @property {number} serverSequence - Monotonic server sequence.
483
- */
484
-
485
- /**
486
- * @typedef {object} ServerChangeFeedRow
487
- * @property {string | null} actor_device_id - Actor device id.
488
- * @property {string | null} actor_user_id - Actor user id.
489
- * @property {string | null} attributes_json - Attributes JSON.
490
- * @property {Date | string} created_at - Creation time.
491
- * @property {string} id - Entry id.
492
- * @property {string | null} idempotency_key - Mutation idempotency key.
493
- * @property {string} model - Frontend model name.
494
- * @property {string} operation - Mutation operation.
495
- * @property {string | null} payload_json - Mutation payload JSON.
496
- * @property {string | null} record_id - Record id.
497
- * @property {string | null} response_json - Response JSON.
498
- * @property {string | null} scope_json - Scope JSON.
499
- * @property {number | string} server_sequence - Server sequence.
500
- */
@@ -4,8 +4,6 @@ import {optionalBoolean, optionalInteger} from "typanic"
4
4
 
5
5
  import recordChanges from "../database/record-changes.js"
6
6
 
7
- const syncTaskPromises = new Map()
8
-
9
7
  /** @typedef {import("./sync-api-client-types.js").SyncChangeApplyResult} SyncChangeApplyResult */
10
8
  /** @typedef {import("./sync-api-client-types.js").SyncChangeEnvelope} SyncChangeEnvelope */
11
9
  /** @typedef {import("./sync-api-client-types.js").SyncChangesRequest} SyncChangesRequest */
@@ -15,6 +13,7 @@ const syncTaskPromises = new Map()
15
13
  /** @typedef {import("./sync-api-client-types.js").SyncReplayItem} SyncReplayItem */
16
14
  /** @typedef {import("./sync-api-client-types.js").SyncReplayResponse} SyncReplayResponse */
17
15
  /** @typedef {import("./sync-api-client-types.js").SyncResourceConfig} SyncResourceConfig */
16
+ const syncTaskPromises = new Map()
18
17
 
19
18
  /**
20
19
  * Generic client-side helper for replaying pending sync envelopes through the
@@ -7,6 +7,23 @@ import SyncReplayUpsertApplier from "./sync-replay-upsert-applier.js"
7
7
  import {ValidationError} from "../database/record/index.js"
8
8
  import VelociousError from "../velocious-error.js"
9
9
 
10
+ /**
11
+ * Resolved routed-resource registration for one replay resource type.
12
+ * @typedef {object} SyncReplayResourceRegistration
13
+ * @property {string} modelName - Effective frontend model name.
14
+ * @property {import("../configuration-types.js").FrontendModelResourceClassType} resourceClass - Routed resource class.
15
+ * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration | null} resourceConfiguration - Normalized resource configuration when registry-resolved.
16
+ */
17
+ /**
18
+ * @typedef {object} SyncReplayMutation
19
+ * @property {Date} clientUpdatedAt - Client-side mutation timestamp.
20
+ * @property {Record<string, ?>} data - Parsed mutation payload.
21
+ * @property {?} id - Client sync row id for per-sync responses.
22
+ * @property {string} resourceId - Resource id as a string.
23
+ * @property {string} resourceType - Resource/model name.
24
+ * @property {string} serializedData - JSON serialized mutation payload.
25
+ * @property {string} syncType - Sync operation type.
26
+ */
10
27
  /**
11
28
  * One declarative broadcast fanned out after a mutation applies.
12
29
  * @typedef {object} SyncReplayBroadcast
@@ -778,22 +795,3 @@ export default class SyncEnvelopeReplayService {
778
795
  await deliverDeclaredBroadcasts({args, broadcaster: this.broadcaster, broadcasts: this.broadcasts})
779
796
  }
780
797
  }
781
-
782
- /**
783
- * Resolved routed-resource registration for one replay resource type.
784
- * @typedef {object} SyncReplayResourceRegistration
785
- * @property {string} modelName - Effective frontend model name.
786
- * @property {import("../configuration-types.js").FrontendModelResourceClassType} resourceClass - Routed resource class.
787
- * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration | null} resourceConfiguration - Normalized resource configuration when registry-resolved.
788
- */
789
-
790
- /**
791
- * @typedef {object} SyncReplayMutation
792
- * @property {Date} clientUpdatedAt - Client-side mutation timestamp.
793
- * @property {Record<string, ?>} data - Parsed mutation payload.
794
- * @property {?} id - Client sync row id for per-sync responses.
795
- * @property {string} resourceId - Resource id as a string.
796
- * @property {string} resourceType - Resource/model name.
797
- * @property {string} serializedData - JSON serialized mutation payload.
798
- * @property {string} syncType - Sync operation type.
799
- */
@@ -7,8 +7,6 @@ import SyncEnvelopeReplayService from "./sync-envelope-replay-service.js"
7
7
  import SyncModelChangeFeedService from "./sync-model-change-feed-service.js"
8
8
  import VelociousError from "../velocious-error.js"
9
9
 
10
- const QUICK_SEARCH_COLUMN = "quickSearch"
11
-
12
10
  /**
13
11
  * Optional client-declared sync scope carried on a changes request.
14
12
  * @typedef {object} SerializedChangesScope
@@ -16,6 +14,7 @@ const QUICK_SEARCH_COLUMN = "quickSearch"
16
14
  * @property {string | null} resourceType - Client resource/model name the scope was declared for, or null for the all-types (user) scope: one scope covering every resource type this resource authorizes for the caller, so a sync authorizes once however many types it serves.
17
15
  * @property {string[] | null} resourceTypes - For the all-types scope, the resource types the client can apply. A cheap delivery/type filter only - it narrows, never widens, what the app's authorization already allows. Null for a type-declared scope.
18
16
  */
17
+ const QUICK_SEARCH_COLUMN = "quickSearch"
19
18
 
20
19
  /**
21
20
  * Base resource for Velocious sync endpoints.
@@ -6,6 +6,15 @@ import UUID from "pure-uuid"
6
6
  import {scopeKey} from "./query-scope.js"
7
7
  import stableJsonStringify from "./stable-json.js"
8
8
 
9
+ /**
10
+ * @typedef {object} SyncScopeRow
11
+ * @property {Record<string, ?>} conditions - Scope attribute conditions.
12
+ * @property {string | null} cursorPayload - Persisted cursor JSON payload.
13
+ * @property {string} id - Scope row id.
14
+ * @property {string | null} resourceType - Scope resource/model name, or null for the all-types (user) scope.
15
+ * @property {string} scopeDigest - Fixed-size deterministic digest of the canonical scope key.
16
+ * @property {string} state - Scope state ("active" or "removed").
17
+ */
9
18
  const TABLE_NAME = "velocious_sync_scopes"
10
19
  const SCOPE_DIGEST_PREFIX = "velocious-sync-scope:"
11
20
 
@@ -19,16 +28,6 @@ function scopeDigestForScope(scope) {
19
28
  return new UUID(5, "ns:URL", `${SCOPE_DIGEST_PREFIX}${scopeKey(scope)}`).format()
20
29
  }
21
30
 
22
- /**
23
- * @typedef {object} SyncScopeRow
24
- * @property {Record<string, ?>} conditions - Scope attribute conditions.
25
- * @property {string | null} cursorPayload - Persisted cursor JSON payload.
26
- * @property {string} id - Scope row id.
27
- * @property {string | null} resourceType - Scope resource/model name, or null for the all-types (user) scope.
28
- * @property {string} scopeDigest - Fixed-size deterministic digest of the canonical scope key.
29
- * @property {string} state - Scope state ("active" or "removed").
30
- */
31
-
32
31
  /**
33
32
  * Framework-owned local persistence for declared sync scopes and their cursors.
34
33
  *
@@ -1,3 +1,13 @@
1
+ /**
2
+ * ParseFiltersResult type.
3
+ * @typedef {object} ParseFiltersResult
4
+ * @property {string[]} includeTags - Tags to include.
5
+ * @property {string[]} excludeTags - Tags to exclude.
6
+ * @property {string[]} examplePatterns - Example name patterns.
7
+ * @property {string[]} filteredProcessArgs - Remaining process args with filter flags removed.
8
+ * @property {number | undefined} groups - Total number of groups for test splitting.
9
+ * @property {number | undefined} groupNumber - Which group to run (1-indexed).
10
+ */
1
11
  // @ts-check
2
12
 
3
13
  const INCLUDE_TAG_FLAGS = new Set(["--tag", "--include-tag", "-t"])
@@ -50,17 +60,6 @@ export function normalizeExamplePatterns(patterns) {
50
60
  return normalized
51
61
  }
52
62
 
53
- /**
54
- * ParseFiltersResult type.
55
- * @typedef {object} ParseFiltersResult
56
- * @property {string[]} includeTags - Tags to include.
57
- * @property {string[]} excludeTags - Tags to exclude.
58
- * @property {string[]} examplePatterns - Example name patterns.
59
- * @property {string[]} filteredProcessArgs - Remaining process args with filter flags removed.
60
- * @property {number | undefined} groups - Total number of groups for test splitting.
61
- * @property {number | undefined} groupNumber - Which group to run (1-indexed).
62
- */
63
-
64
63
  /**
65
64
  * Runs the parseFilters helper.
66
65
  * @param {string[]} processArgs - Process args.