@objectstack/service-messaging 10.2.0 → 10.3.0

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.
package/dist/index.cjs CHANGED
@@ -963,6 +963,19 @@ function classifyDeliveryAttempt(result, errorClass, attemptsSoFar, now = Date.n
963
963
  return { success: false, error: result.error, nextAttemptAt: now + delay };
964
964
  }
965
965
 
966
+ // src/audit-timestamp.ts
967
+ function toEpochMs(value) {
968
+ if (typeof value === "number") return value;
969
+ if (value instanceof Date) return value.getTime();
970
+ if (typeof value === "string") {
971
+ const parsed = Date.parse(value);
972
+ if (Number.isFinite(parsed)) return parsed;
973
+ const numeric = Number(value);
974
+ if (Number.isFinite(numeric)) return numeric;
975
+ }
976
+ return 0;
977
+ }
978
+
966
979
  // src/sql-outbox.ts
967
980
  var DELIVERY_OBJECT = "sys_notification_delivery";
968
981
  var SqlNotificationOutbox = class {
@@ -981,7 +994,7 @@ var SqlNotificationOutbox = class {
981
994
  const existing = await this.engine.findOne(this.objectName, { where: dedup, fields: ["id"] });
982
995
  if (existing?.id) return String(existing.id);
983
996
  const id = (0, import_node_crypto.randomUUID)();
984
- const now = Date.now();
997
+ const now = /* @__PURE__ */ new Date();
985
998
  const row = {
986
999
  id,
987
1000
  notification_id: input.notificationId,
@@ -1142,8 +1155,8 @@ var SqlNotificationOutbox = class {
1142
1155
  lastAttemptedAt: r.last_attempted_at ?? void 0,
1143
1156
  error: r.error ?? void 0,
1144
1157
  digestKey: r.digest_key ?? void 0,
1145
- createdAt: r.created_at,
1146
- updatedAt: r.updated_at
1158
+ createdAt: toEpochMs(r.created_at),
1159
+ updatedAt: toEpochMs(r.updated_at)
1147
1160
  };
1148
1161
  }
1149
1162
  };
@@ -1272,8 +1285,11 @@ var HttpDelivery = import_data.ObjectSchema.create({
1272
1285
  response_code: import_data.Field.number({ label: "HTTP Status", required: false }),
1273
1286
  response_body: import_data.Field.textarea({ label: "Response Body (capped)", required: false }),
1274
1287
  error: import_data.Field.textarea({ label: "Error", required: false }),
1275
- created_at: import_data.Field.number({ label: "Created At (ms)", required: true }),
1276
- updated_at: import_data.Field.number({ label: "Updated At (ms)", required: true })
1288
+ // Builtin audit columns are native TIMESTAMP columns (Postgres/MySQL),
1289
+ // so declare them `datetime` and write `Date`s (not epoch-ms numbers,
1290
+ // which a real timestamp column rejects). See SqlHttpOutbox.
1291
+ created_at: import_data.Field.datetime({ label: "Created At", required: true }),
1292
+ updated_at: import_data.Field.datetime({ label: "Updated At", required: true })
1277
1293
  },
1278
1294
  indexes: [
1279
1295
  { fields: ["source", "dedup_key"], unique: true },
@@ -1303,7 +1319,7 @@ var SqlHttpOutbox = class {
1303
1319
  });
1304
1320
  if (existing?.id) return existing.id;
1305
1321
  const id = (0, import_node_crypto2.randomUUID)();
1306
- const now = Date.now();
1322
+ const now = /* @__PURE__ */ new Date();
1307
1323
  const row = {
1308
1324
  id,
1309
1325
  source: input.source,
@@ -1472,8 +1488,8 @@ var SqlHttpOutbox = class {
1472
1488
  responseCode: r.response_code ?? void 0,
1473
1489
  responseBody: r.response_body ?? void 0,
1474
1490
  error: r.error ?? void 0,
1475
- createdAt: r.created_at,
1476
- updatedAt: r.updated_at
1491
+ createdAt: toEpochMs(r.created_at),
1492
+ updatedAt: toEpochMs(r.updated_at)
1477
1493
  };
1478
1494
  }
1479
1495
  };
@@ -1947,10 +1963,10 @@ function stableNodeOffset2(nodeId, partitionCount) {
1947
1963
  // src/retention.ts
1948
1964
  var DEFAULT_NOTIFICATION_RETENTION_DAYS = 90;
1949
1965
  var DEFAULT_RETENTION_TARGETS = [
1950
- { object: RECEIPT_OBJECT, tsField: "created_at", format: "iso" },
1951
- { object: INBOX_OBJECT, tsField: "created_at", format: "iso" },
1952
- { object: DELIVERY_OBJECT, tsField: "created_at", format: "epoch" },
1953
- { object: NOTIFICATION_EVENT_OBJECT, tsField: "created_at", format: "iso" }
1966
+ { object: RECEIPT_OBJECT, tsField: "created_at" },
1967
+ { object: INBOX_OBJECT, tsField: "created_at" },
1968
+ { object: DELIVERY_OBJECT, tsField: "created_at" },
1969
+ { object: NOTIFICATION_EVENT_OBJECT, tsField: "created_at" }
1954
1970
  ];
1955
1971
  var NotificationRetention = class {
1956
1972
  constructor(opts) {
@@ -1973,14 +1989,15 @@ var NotificationRetention = class {
1973
1989
  this.opts.logger.warn(`[messaging] retention: invalid retentionDays=${retentionDays}; prune skipped`);
1974
1990
  return [];
1975
1991
  }
1976
- const cutoffMs = this.now() - retentionDays * 864e5;
1977
- const cutoffIso = new Date(cutoffMs).toISOString();
1992
+ const cutoffIso = new Date(this.now() - retentionDays * 864e5).toISOString();
1978
1993
  const outcomes = [];
1979
1994
  for (const t of this.targets) {
1980
- const cutoff = t.format === "epoch" ? cutoffMs : cutoffIso;
1981
1995
  try {
1982
1996
  const res = await data.delete(t.object, {
1983
- where: { [t.tsField]: { $lt: cutoff } },
1997
+ // ISO-8601 cutoff for every target: `created_at` is a native
1998
+ // timestamp column, which rejects a bare epoch-ms number on
1999
+ // Postgres. The driver coerces this per dialect on the way down.
2000
+ where: { [t.tsField]: { $lt: cutoffIso } },
1984
2001
  multi: true,
1985
2002
  // System context: retention is an operator policy that spans
1986
2003
  // tenants, so it must not be scoped by the caller's RLS.
@@ -2332,8 +2349,12 @@ var NotificationDelivery = import_data4.ObjectSchema.create({
2332
2349
  next_attempt_at: import_data4.Field.number({ label: "Next Attempt At (ms)" }),
2333
2350
  last_attempted_at: import_data4.Field.number({ label: "Last Attempted At (ms)" }),
2334
2351
  error: import_data4.Field.textarea({ label: "Error" }),
2335
- created_at: import_data4.Field.number({ label: "Created At (ms)", readonly: true }),
2336
- updated_at: import_data4.Field.number({ label: "Updated At (ms)" })
2352
+ // Builtin audit columns: the SQL driver provisions `created_at` /
2353
+ // `updated_at` as native TIMESTAMP columns (Postgres/MySQL), so they are
2354
+ // declared `datetime` and written as `Date`s — a bare epoch-ms number is
2355
+ // rejected by a real timestamp column. See SqlNotificationOutbox.
2356
+ created_at: import_data4.Field.datetime({ label: "Created At", readonly: true }),
2357
+ updated_at: import_data4.Field.datetime({ label: "Updated At" })
2337
2358
  },
2338
2359
  indexes: [
2339
2360
  // Dedup: one delivery per (event, recipient, channel).