@ai-matrx/messaging 0.11.1 → 0.11.3
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/CHANGELOG.md +45 -0
- package/dist/index.cjs +42 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +42 -14
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +46 -14
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -2
- package/dist/react.d.ts +8 -2
- package/dist/react.js +46 -14
- package/dist/react.js.map +1 -1
- package/dist/styles.css +9 -0
- package/package.json +3 -3
package/dist/react.cjs
CHANGED
|
@@ -203,14 +203,15 @@ var MessagingError = class extends Error {
|
|
|
203
203
|
this.remedy = remedy;
|
|
204
204
|
if (cause !== void 0) this.cause = cause;
|
|
205
205
|
}
|
|
206
|
-
/** True when
|
|
206
|
+
/** True when re-running the same call is the fix. */
|
|
207
207
|
get isRetryable() {
|
|
208
|
-
return this.code === "session-unavailable" || this.code === "transport";
|
|
208
|
+
return this.code === "session-unavailable" || this.code === "contention" || this.code === "transport";
|
|
209
209
|
}
|
|
210
210
|
};
|
|
211
211
|
var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["42501", "PGRST301", "PGRST302"]);
|
|
212
212
|
var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["PGRST116", "PGRST205"]);
|
|
213
213
|
var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505"]);
|
|
214
|
+
var CONTENTION_CODES = /* @__PURE__ */ new Set(["40P01", "40001", "55P03"]);
|
|
214
215
|
var SESSION_MARKERS = [
|
|
215
216
|
"auth session missing",
|
|
216
217
|
"jwt expired",
|
|
@@ -232,6 +233,14 @@ function normalizeMessagingError(error, operation) {
|
|
|
232
233
|
error
|
|
233
234
|
);
|
|
234
235
|
}
|
|
236
|
+
if (code !== void 0 && CONTENTION_CODES.has(code)) {
|
|
237
|
+
return new MessagingError(
|
|
238
|
+
"contention",
|
|
239
|
+
`${operation}: the database resolved a lock collision by cancelling this call (${code}: ${message})`,
|
|
240
|
+
"Nothing of this call committed, so it is safe to re-run \u2014 the package re-runs it a bounded number of times and announces each heal. Seeing it repeatedly means real contention on the messaging tables (usually DDL applied against live traffic); look at what else held a lock at that moment, never at the caller.",
|
|
241
|
+
error
|
|
242
|
+
);
|
|
243
|
+
}
|
|
235
244
|
if (code !== void 0 && FORBIDDEN_CODES.has(code)) {
|
|
236
245
|
return new MessagingError(
|
|
237
246
|
"forbidden",
|
|
@@ -259,7 +268,7 @@ function normalizeMessagingError(error, operation) {
|
|
|
259
268
|
return new MessagingError(
|
|
260
269
|
"transport",
|
|
261
270
|
`${operation}: ${message}`,
|
|
262
|
-
"Transient transport failure.
|
|
271
|
+
"Transient transport failure. A typed message is surfaced to the outbox so it is never lost; a read is the caller's to re-issue (only `contention` and `session-unavailable` are re-run inside the package).",
|
|
263
272
|
error
|
|
264
273
|
);
|
|
265
274
|
}
|
|
@@ -1564,6 +1573,12 @@ function requireOrg(organizationId, operation) {
|
|
|
1564
1573
|
}
|
|
1565
1574
|
return organizationId;
|
|
1566
1575
|
}
|
|
1576
|
+
var CONTENTION_ATTEMPTS = 3;
|
|
1577
|
+
var CONTENTION_BACKOFF_MS = [60, 180];
|
|
1578
|
+
function sleep(ms) {
|
|
1579
|
+
const jittered = ms + Math.random() * ms * 0.5;
|
|
1580
|
+
return new Promise((resolve) => setTimeout(resolve, jittered));
|
|
1581
|
+
}
|
|
1567
1582
|
function createMessagingRepository(options) {
|
|
1568
1583
|
const { identity } = options;
|
|
1569
1584
|
const org = requireOrg(identity.organizationId, "createMessagingRepository");
|
|
@@ -1575,18 +1590,31 @@ function createMessagingRepository(options) {
|
|
|
1575
1590
|
const db = () => client.schema(MESSAGING_SCHEMA);
|
|
1576
1591
|
const rpcDb = () => client.schema(MESSAGING_RPC_SCHEMA);
|
|
1577
1592
|
async function withSessionRetry(operation, run) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
} catch (error) {
|
|
1581
|
-
const normalized = normalizeMessagingError(error, operation);
|
|
1582
|
-
if (normalized.code !== "session-unavailable" || options.resolveSession === void 0) {
|
|
1583
|
-
throw normalized;
|
|
1584
|
-
}
|
|
1585
|
-
await options.resolveSession();
|
|
1593
|
+
let sessionRetried = false;
|
|
1594
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
1586
1595
|
try {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1596
|
+
const value = await run();
|
|
1597
|
+
if (attempt > 1) {
|
|
1598
|
+
options.onDiagnostic?.(
|
|
1599
|
+
`${operation}: healed on attempt ${attempt} after a database lock collision (rolled back, safe to re-run).`
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
return value;
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
const normalized = normalizeMessagingError(error, operation);
|
|
1605
|
+
if (normalized.code === "session-unavailable" && options.resolveSession !== void 0 && !sessionRetried) {
|
|
1606
|
+
sessionRetried = true;
|
|
1607
|
+
await options.resolveSession();
|
|
1608
|
+
continue;
|
|
1609
|
+
}
|
|
1610
|
+
if (normalized.code === "contention" && attempt < CONTENTION_ATTEMPTS) {
|
|
1611
|
+
options.onDiagnostic?.(
|
|
1612
|
+
`${operation}: attempt ${attempt}/${CONTENTION_ATTEMPTS} hit a database lock collision (${normalized.message}) \u2014 re-running in ${CONTENTION_BACKOFF_MS[attempt - 1] ?? 0}ms.`
|
|
1613
|
+
);
|
|
1614
|
+
await sleep(CONTENTION_BACKOFF_MS[attempt - 1] ?? 0);
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
throw normalized;
|
|
1590
1618
|
}
|
|
1591
1619
|
}
|
|
1592
1620
|
}
|
|
@@ -1970,6 +1998,10 @@ function MessagingRuntime(props) {
|
|
|
1970
1998
|
// contract, which `supabase-shape.test.ts` proves a real client honors.
|
|
1971
1999
|
client,
|
|
1972
2000
|
identity,
|
|
2001
|
+
// An automatic heal ANNOUNCES itself: a lock collision the package
|
|
2002
|
+
// re-ran is a warning in the host's diagnostics, never a banner on the
|
|
2003
|
+
// inbox and never silence.
|
|
2004
|
+
onDiagnostic: (message) => report({ level: "warn", message }),
|
|
1973
2005
|
...props.resolveSession !== void 0 ? { resolveSession: props.resolveSession } : {}
|
|
1974
2006
|
});
|
|
1975
2007
|
let built = null;
|