velocious 1.0.596 → 1.0.597

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 (106) hide show
  1. package/README.md +16 -0
  2. package/build/background-jobs/pooled-runner-broker-identity.js +22 -4
  3. package/build/background-jobs/store.js +383 -9
  4. package/build/background-jobs/types.js +1 -0
  5. package/build/cli/commands/db/tenants/migrations/pending.js +45 -0
  6. package/build/configuration-types.js +3 -0
  7. package/build/configuration.js +5 -2
  8. package/build/database/drivers/sqlite/base.js +3 -3
  9. package/build/database/tenants/migration-pending-inspector.js +77 -0
  10. package/build/environment-handlers/base.js +8 -0
  11. package/build/environment-handlers/node.js +14 -0
  12. package/build/mailer/backends/resend-smtp.js +121 -0
  13. package/build/mailer/base.js +52 -22
  14. package/build/mailer/delivery-operation-store.js +152 -0
  15. package/build/mailer/delivery-operation.js +205 -0
  16. package/build/mailer/delivery.js +9 -4
  17. package/build/mailer/index.js +5 -1
  18. package/build/mailer.js +6 -1
  19. package/build/src/background-jobs/pooled-runner-broker-identity.d.ts +10 -1
  20. package/build/src/background-jobs/pooled-runner-broker-identity.d.ts.map +1 -1
  21. package/build/src/background-jobs/pooled-runner-broker-identity.js +23 -5
  22. package/build/src/background-jobs/store.d.ts +154 -0
  23. package/build/src/background-jobs/store.d.ts.map +1 -1
  24. package/build/src/background-jobs/store.js +350 -10
  25. package/build/src/background-jobs/types.d.ts +5 -0
  26. package/build/src/background-jobs/types.d.ts.map +1 -1
  27. package/build/src/background-jobs/types.js +2 -1
  28. package/build/src/cli/commands/db/tenants/migrations/pending.d.ts +15 -0
  29. package/build/src/cli/commands/db/tenants/migrations/pending.d.ts.map +1 -0
  30. package/build/src/cli/commands/db/tenants/migrations/pending.js +40 -0
  31. package/build/src/configuration-types.d.ts +20 -0
  32. package/build/src/configuration-types.d.ts.map +1 -1
  33. package/build/src/configuration-types.js +4 -1
  34. package/build/src/configuration.d.ts +2 -2
  35. package/build/src/configuration.d.ts.map +1 -1
  36. package/build/src/configuration.js +6 -3
  37. package/build/src/database/drivers/sqlite/base.js +4 -4
  38. package/build/src/database/tenants/migration-pending-inspector.d.ts +38 -0
  39. package/build/src/database/tenants/migration-pending-inspector.d.ts.map +1 -0
  40. package/build/src/database/tenants/migration-pending-inspector.js +70 -0
  41. package/build/src/environment-handlers/base.d.ts +10 -0
  42. package/build/src/environment-handlers/base.d.ts.map +1 -1
  43. package/build/src/environment-handlers/base.js +8 -1
  44. package/build/src/environment-handlers/node.d.ts +11 -0
  45. package/build/src/environment-handlers/node.d.ts.map +1 -1
  46. package/build/src/environment-handlers/node.js +14 -1
  47. package/build/src/mailer/backends/resend-smtp.d.ts +43 -0
  48. package/build/src/mailer/backends/resend-smtp.d.ts.map +1 -0
  49. package/build/src/mailer/backends/resend-smtp.js +106 -0
  50. package/build/src/mailer/base.d.ts +9 -2
  51. package/build/src/mailer/base.d.ts.map +1 -1
  52. package/build/src/mailer/base.js +41 -19
  53. package/build/src/mailer/delivery-operation-store.d.ts +52 -0
  54. package/build/src/mailer/delivery-operation-store.d.ts.map +1 -0
  55. package/build/src/mailer/delivery-operation-store.js +130 -0
  56. package/build/src/mailer/delivery-operation.d.ts +73 -0
  57. package/build/src/mailer/delivery-operation.d.ts.map +1 -0
  58. package/build/src/mailer/delivery-operation.js +185 -0
  59. package/build/src/mailer/delivery.d.ts +4 -2
  60. package/build/src/mailer/delivery.d.ts.map +1 -1
  61. package/build/src/mailer/delivery.js +9 -5
  62. package/build/src/mailer/index.d.ts +24 -1
  63. package/build/src/mailer/index.d.ts.map +1 -1
  64. package/build/src/mailer/index.js +6 -2
  65. package/build/src/mailer.d.ts +11 -13
  66. package/build/src/mailer.d.ts.map +1 -1
  67. package/build/src/mailer.js +7 -2
  68. package/build/src/sync/local-mutation-log.d.ts +5 -5
  69. package/build/src/sync/local-mutation-log.d.ts.map +1 -1
  70. package/build/src/sync/local-mutation-log.js +34 -10
  71. package/build/src/sync/stable-json.d.ts +1 -8
  72. package/build/src/sync/stable-json.d.ts.map +1 -1
  73. package/build/src/sync/stable-json.js +2 -27
  74. package/build/src/sync/sync-envelope-replay-service.d.ts +3 -2
  75. package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
  76. package/build/src/sync/sync-envelope-replay-service.js +12 -8
  77. package/build/src/utils/stable-json.d.ts +7 -0
  78. package/build/src/utils/stable-json.d.ts.map +1 -0
  79. package/build/src/utils/stable-json.js +25 -0
  80. package/build/sync/local-mutation-log.js +37 -6
  81. package/build/sync/stable-json.js +1 -28
  82. package/build/sync/sync-envelope-replay-service.js +11 -7
  83. package/build/tsconfig.tsbuildinfo +1 -1
  84. package/build/utils/stable-json.js +26 -0
  85. package/package.json +1 -1
  86. package/src/background-jobs/pooled-runner-broker-identity.js +22 -4
  87. package/src/background-jobs/store.js +383 -9
  88. package/src/background-jobs/types.js +1 -0
  89. package/src/cli/commands/db/tenants/migrations/pending.js +45 -0
  90. package/src/configuration-types.js +3 -0
  91. package/src/configuration.js +5 -2
  92. package/src/database/drivers/sqlite/base.js +3 -3
  93. package/src/database/tenants/migration-pending-inspector.js +77 -0
  94. package/src/environment-handlers/base.js +8 -0
  95. package/src/environment-handlers/node.js +14 -0
  96. package/src/mailer/backends/resend-smtp.js +121 -0
  97. package/src/mailer/base.js +52 -22
  98. package/src/mailer/delivery-operation-store.js +152 -0
  99. package/src/mailer/delivery-operation.js +205 -0
  100. package/src/mailer/delivery.js +9 -4
  101. package/src/mailer/index.js +5 -1
  102. package/src/mailer.js +6 -1
  103. package/src/sync/local-mutation-log.js +37 -6
  104. package/src/sync/stable-json.js +1 -28
  105. package/src/sync/sync-envelope-replay-service.js +11 -7
  106. package/src/utils/stable-json.js +26 -0
package/src/mailer.js CHANGED
@@ -1,6 +1,10 @@
1
1
  // @ts-check
2
2
 
3
- /** @typedef {{to: ReturnType<typeof JSON.parse>, subject: string, from?: ReturnType<typeof JSON.parse>, cc?: ReturnType<typeof JSON.parse>, bcc?: ReturnType<typeof JSON.parse>, replyTo?: ReturnType<typeof JSON.parse>, headers?: Record<string, string>, html: string, mailer: string, action: string}} MailerDeliveryPayload */
3
+ /** @typedef {import("./mailer/index.js").MailerDeliveryOperationRequest} MailerDeliveryOperationRequest */
4
+ /** @typedef {import("./mailer/index.js").MailerDeliveryOperation} MailerDeliveryOperation */
5
+ /** @typedef {import("./mailer/index.js").MailerDeliveryIdempotencyCapability} MailerDeliveryIdempotencyCapability */
6
+ /** @typedef {import("./mailer/index.js").MailerDeliveryLaterOptions} MailerDeliveryLaterOptions */
7
+ /** @typedef {import("./mailer/index.js").MailerDeliveryPayload} MailerDeliveryPayload */
4
8
 
5
9
  export {
6
10
  VelociousMailerBase,
@@ -12,4 +16,5 @@ export {
12
16
  setDeliveryHandler
13
17
  } from "./mailer/index.js"
14
18
  export {default as SmtpMailerBackend} from "./mailer/backends/smtp.js"
19
+ export {default as ResendSmtpMailerBackend} from "./mailer/backends/resend-smtp.js"
15
20
  export {default} from "./mailer/index.js"
@@ -37,11 +37,12 @@
37
37
  * @property {import("./device-identity.js").SignedSyncMutation} [signedMutation] - Original signed mutation envelope, retained for peer-forwarded mutations.
38
38
  * @property {number} sequence - Monotonic local sequence.
39
39
  * @property {LocalMutationStatus} status - Local replay/apply status.
40
- * @property {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} [syncResult] - Backend replay/result metadata.
40
+ * @property {Record<string, import("../frontend-models/base.js").FrontendModelTransportValue>} [syncResult] - Backend replay/result metadata. Stored as transport markers so Date (and other typed) values survive the durable JSON round trip and are restored on readback.
41
41
  * @property {string} updatedAt - ISO timestamp when the record was last changed.
42
42
  */
43
43
  // @ts-check
44
44
 
45
+ import {deserializeFrontendModelTransportValue, serializeFrontendModelTransportValue} from "../frontend-models/transport-serialization.js"
45
46
  import stableJsonStringify from "./stable-json.js"
46
47
 
47
48
  const DEFAULT_STORAGE_KEY = "velocious.sync.localMutationLog"
@@ -126,7 +127,7 @@ export default class LocalMutationLog {
126
127
  * @param {object} args - Arguments.
127
128
  * @param {string} args.id - Record id.
128
129
  * @param {LocalMutationStatus} args.status - New status.
129
- * @param {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} [args.syncResult] - Result metadata.
130
+ * @param {Record<string, import("../frontend-models/base.js").FrontendModelTransportValue>} [args.syncResult] - Result metadata (may carry transport-restored typed values).
130
131
  * @returns {Promise<LocalMutationLogRecord>} - Updated record.
131
132
  */
132
133
  async updateStatus({id, status, syncResult}) {
@@ -140,11 +141,16 @@ export default class LocalMutationLog {
140
141
  const record = normalizeRecord(rawRecord)
141
142
 
142
143
  record.status = /** @type {LocalMutationStatus} */ (status)
143
- if (syncResult !== undefined) record.syncResult = cloneJsonObject(syncResult, "syncResult")
144
+ if (syncResult !== undefined) {
145
+ // Encode transport-restored typed values (e.g. Date attributes in a
146
+ // conflict serverModel) as markers before the JSON clone so the
147
+ // durable persistence round trip cannot stringify them.
148
+ record.syncResult = cloneJsonObject(serializeFrontendModelTransportValue(syncResult), "syncResult")
149
+ }
144
150
  record.updatedAt = this.currentTimestamp()
145
151
  await this.storage.updateRecord(this.storageKey, cloneRecord(record))
146
152
 
147
- return cloneRecord(record)
153
+ return restoreSyncResultTypes(cloneRecord(record))
148
154
  })
149
155
  }
150
156
 
@@ -168,7 +174,7 @@ export default class LocalMutationLog {
168
174
  record.updatedAt = this.currentTimestamp()
169
175
  await this.storage.updateRecord(this.storageKey, cloneRecord(record))
170
176
 
171
- return cloneRecord(record)
177
+ return restoreSyncResultTypes(cloneRecord(record))
172
178
  })
173
179
  }
174
180
 
@@ -270,6 +276,25 @@ function normalizeRecordList(records) {
270
276
  .map(normalizeRecord)
271
277
  .sort((left, right) => left.sequence - right.sequence)
272
278
  .map((record) => cloneRecord(record))
279
+ .map(restoreSyncResultTypes)
280
+ }
281
+
282
+ /**
283
+ * Restores transport-restored typed values in a record's syncResult after the
284
+ * final JSON clone, so callers see Date (and other typed) values on durable
285
+ * readback instead of marker-encoded ISO strings.
286
+ * @param {LocalMutationLogRecord} record - Cloned record.
287
+ * @returns {LocalMutationLogRecord} - Record with restored syncResult types.
288
+ */
289
+ function restoreSyncResultTypes(record) {
290
+ if (record.syncResult === undefined) return record
291
+
292
+ return {
293
+ ...record,
294
+ syncResult: /** @type {Record<string, import("../frontend-models/base.js").FrontendModelTransportValue>} */ (
295
+ deserializeFrontendModelTransportValue(record.syncResult)
296
+ )
297
+ }
273
298
  }
274
299
 
275
300
  /**
@@ -328,7 +353,13 @@ function normalizeRecord(value) {
328
353
  if (record.signedMutation !== undefined) {
329
354
  normalizedRecord.signedMutation = /** @type {import("./device-identity.js").SignedSyncMutation} */ (cloneJsonObject(record.signedMutation, "signedMutation"))
330
355
  }
331
- if (record.syncResult !== undefined) normalizedRecord.syncResult = cloneJsonObject(record.syncResult, "syncResult")
356
+ if (record.syncResult !== undefined) {
357
+ // Persisted syncResult is marker-encoded so it survives the JSON clone;
358
+ // typed values are restored by restoreSyncResultTypes at the read boundary.
359
+ normalizedRecord.syncResult = /** @type {Record<string, import("../frontend-models/base.js").FrontendModelTransportValue>} */ (
360
+ cloneJsonObject(record.syncResult, "syncResult")
361
+ )
362
+ }
332
363
 
333
364
  return normalizedRecord
334
365
  }
@@ -1,28 +1 @@
1
- // @ts-check
2
-
3
- /**
4
- * Serializes a JSON-compatible value with recursively sorted object keys, so
5
- * equal values always produce byte-identical strings (used for sync scope and
6
- * change-feed identity comparisons).
7
- * @param {ReturnType<typeof JSON.parse>} value - JSON-compatible value.
8
- * @returns {string} - Stable JSON string.
9
- */
10
- export default function stableJsonStringify(value) {
11
- return JSON.stringify(stableJsonValue(value))
12
- }
13
-
14
- /**
15
- * Produces a recursively key-sorted JSON value.
16
- * @param {ReturnType<typeof JSON.parse>} value - JSON-compatible value.
17
- * @returns {ReturnType<typeof JSON.parse>} - Stable JSON-compatible value.
18
- */
19
- function stableJsonValue(value) {
20
- if (Array.isArray(value)) return value.map((item) => stableJsonValue(item))
21
- if (!value || typeof value !== "object") return value
22
-
23
- return Object.keys(value).sort().reduce((memo, key) => {
24
- memo[key] = stableJsonValue(value[key])
25
-
26
- return memo
27
- }, /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ ({}))
28
- }
1
+ export {default} from "../utils/stable-json.js"
@@ -841,8 +841,9 @@ export default class SyncEnvelopeReplayService {
841
841
  * Projects affected mutation fields through the resource's readable
842
842
  * attribute contract. Writable-but-hidden fields are omitted, while custom
843
843
  * `<attribute>Attribute(model)` serializers and model accessors remain the
844
- * source of frontend-visible values. The full model attribute hash is never
845
- * exposed.
844
+ * source of frontend-visible values (Date values are kept raw so the normal
845
+ * frontend-model transport serializer can emit its date marker). The full
846
+ * model attribute hash is never exposed.
846
847
  * @param {object} args - Projection args.
847
848
  * @param {Record<string, ReturnType<typeof JSON.parse>>} args.attributes - Permitted affected mutation attributes.
848
849
  * @param {import("../database/record/index.js").default} args.existingRecord - Authorized server record.
@@ -885,7 +886,7 @@ export default class SyncEnvelopeReplayService {
885
886
  const resourceAttribute = resource.resourceMethod(`${attributeName}Attribute`)
886
887
 
887
888
  if (resourceAttribute) {
888
- serializedAttributes[affectedField] = normalizeConflictValue(await resourceAttribute.method.call(resourceAttribute.resource, existingRecord))
889
+ serializedAttributes[affectedField] = await resourceAttribute.method.call(resourceAttribute.resource, existingRecord)
889
890
  continue
890
891
  }
891
892
 
@@ -893,9 +894,9 @@ export default class SyncEnvelopeReplayService {
893
894
  const attributeMethod = recordMethods[attributeName]
894
895
 
895
896
  if (typeof attributeMethod === "function") {
896
- serializedAttributes[affectedField] = normalizeConflictValue(await attributeMethod.call(existingRecord))
897
+ serializedAttributes[affectedField] = await attributeMethod.call(existingRecord)
897
898
  } else {
898
- serializedAttributes[affectedField] = normalizeConflictValue(existingRecord.readAttribute(attributeName))
899
+ serializedAttributes[affectedField] = existingRecord.readAttribute(attributeName)
899
900
  }
900
901
  }
901
902
 
@@ -1166,8 +1167,11 @@ export function syncReplayConflictLockName({resourceId, resourceType}) {
1166
1167
  }
1167
1168
 
1168
1169
  /**
1169
- * Normalizes an authoritative conflict value for JSON transport and deterministic comparison.
1170
- * @param {ReturnType<typeof JSON.parse>} value - Raw value from a database record.
1170
+ * Normalizes a version value for deterministic comparison and transport.
1171
+ * Only version values participate in stable-JSON comparison against client
1172
+ * `baseVersion` strings; resource serializer/accessor results must stay raw so
1173
+ * the frontend-model transport serializer can retain Date markers.
1174
+ * @param {ReturnType<typeof JSON.parse>} value - Raw version value from a database record.
1171
1175
  * @returns {ReturnType<typeof JSON.parse>} - Normalized value (Date values become ISO strings).
1172
1176
  */
1173
1177
  function normalizeConflictValue(value) {
@@ -0,0 +1,26 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Serializes a JSON-compatible value with recursively sorted object keys.
5
+ * @param {ReturnType<typeof JSON.parse>} value - JSON-compatible value.
6
+ * @returns {string} - Stable JSON string.
7
+ */
8
+ export default function stableJsonStringify(value) {
9
+ return JSON.stringify(stableJsonValue(value))
10
+ }
11
+
12
+ /**
13
+ * Produces a recursively key-sorted JSON value.
14
+ * @param {ReturnType<typeof JSON.parse>} value - JSON-compatible value.
15
+ * @returns {ReturnType<typeof JSON.parse>} - Stable JSON-compatible value.
16
+ */
17
+ function stableJsonValue(value) {
18
+ if (Array.isArray(value)) return value.map((item) => stableJsonValue(item))
19
+ if (!value || typeof value !== "object") return value
20
+
21
+ return Object.keys(value).sort().reduce((memo, key) => {
22
+ memo[key] = stableJsonValue(value[key])
23
+
24
+ return memo
25
+ }, /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ ({}))
26
+ }