experimental-a2 0.0.0 → 0.2.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/CHANGELOG.md +43 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.d.ts +19 -7
- package/dist/ai-server.js +730 -96
- package/dist/ai.d.ts +32 -11
- package/dist/ai.js +253 -75
- package/dist/client.d.ts +1 -1
- package/dist/client.js +4 -4
- package/dist/{contract-B0kAXoaL.js → contract-CG_adnu_.js} +2 -1
- package/dist/{contract-DL8btVd9.d.ts → contract-C_3dIIEU.d.ts} +4 -1
- package/dist/devtools-server.browser.js +2 -2
- package/dist/devtools-server.js +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.js +4 -3
- package/dist/idempotent-replay-BMyHrP0L.js +19 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{internal-Dm8Ejnud.js → internal-D6wNxTck.js} +3 -3
- package/dist/{log-Dg1I8NRr.d.ts → log-ldf5g8Cx.d.ts} +74 -56
- package/dist/log-memory.d.ts +1 -1
- package/dist/log-memory.js +173 -96
- package/dist/{log-polling-RO7kclzR.js → log-polling-6COoN60V.js} +1 -1
- package/dist/log-postgres.d.ts +1 -1
- package/dist/log-postgres.js +235 -192
- package/dist/log-redis.d.ts +1 -1
- package/dist/log-redis.js +453 -263
- package/dist/log-sqlite.d.ts +1 -1
- package/dist/log-sqlite.js +216 -127
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/recovery-vercel.d.ts +2 -2
- package/dist/recovery-vercel.js +9 -10
- package/dist/server-DJgD2YWP.js +877 -0
- package/dist/server.browser.js +4 -4
- package/dist/server.d.ts +46 -27
- package/dist/server.js +1 -1
- package/dist/{telemetry-C78al20p.d.ts → telemetry-Cso0qyHQ.d.ts} +1 -1
- package/dist/{wire-2QpU1EtJ.js → wire-BVsgR8o9.js} +1 -1
- package/docs/01-quickstart.mdx +7 -7
- package/docs/concepts/01-contracts.mdx +22 -22
- package/docs/concepts/02-handlers.mdx +223 -89
- package/docs/concepts/03-durability.mdx +199 -112
- package/docs/concepts/04-state.mdx +27 -1
- package/docs/guides/01-timers.mdx +4 -4
- package/docs/guides/02-cancellation.mdx +32 -4
- package/docs/guides/05-production.mdx +61 -27
- package/docs/guides/06-ai-agents.mdx +151 -70
- package/docs/guides/07-devtools.mdx +6 -3
- package/docs/guides/08-application-data.mdx +5 -6
- package/docs/index.mdx +30 -14
- package/docs/reference/01-api.mdx +305 -70
- package/package.json +31 -31
- package/dist/server-DYsnKTTy.js +0 -780
package/dist/log-memory.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { t as A2Error } from "./errors-BJRMd-h6.js";
|
|
2
|
+
import { t as idempotentReplay } from "./idempotent-replay-BMyHrP0L.js";
|
|
2
3
|
import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./log-yJbXUf72.js";
|
|
3
4
|
//#region src/log-memory.ts
|
|
4
5
|
/**
|
|
5
|
-
* a2/log-memory — in-memory log backend (the test default).
|
|
6
|
+
* experimental-a2/log-memory — in-memory log backend (the test default).
|
|
6
7
|
*
|
|
7
8
|
* Implements the A2Log interface with zero dependencies. See
|
|
8
9
|
* specs/a2-implementation.md §3 for the contract; the conformance suite
|
|
@@ -16,11 +17,15 @@ const toStored = (row) => ({
|
|
|
16
17
|
sessionId: row.sessionId,
|
|
17
18
|
createdAt: new Date(row.createdAt),
|
|
18
19
|
cause: row.cause ? { ...row.cause } : null,
|
|
20
|
+
lane: row.lane,
|
|
19
21
|
processedAt: row.processedAt ? new Date(row.processedAt) : null,
|
|
20
22
|
processedByAttempt: row.processedByAttempt,
|
|
23
|
+
returnedEventIds: row.returnedEventIds ? [...row.returnedEventIds] : null,
|
|
21
24
|
firstClaimedAt: row.firstClaimedAt ? new Date(row.firstClaimedAt) : null,
|
|
22
25
|
lastClaimedAt: row.lastClaimedAt ? new Date(row.lastClaimedAt) : null,
|
|
23
26
|
attemptCount: row.attemptCount,
|
|
27
|
+
claimHolder: row.claimHolder,
|
|
28
|
+
claimExpiresAt: row.claimExpiresAt ? new Date(row.claimExpiresAt) : null,
|
|
24
29
|
failureCount: row.failureCount,
|
|
25
30
|
lastFailedAt: row.lastFailedAt ? new Date(row.lastFailedAt) : null,
|
|
26
31
|
lastFailedAttempt: row.lastFailedAttempt,
|
|
@@ -39,11 +44,9 @@ function memory(options = {}) {
|
|
|
39
44
|
const clock = options.clock ?? SYSTEM_CLOCK;
|
|
40
45
|
const generateId = options.ids ?? RANDOM_IDS;
|
|
41
46
|
const sessions = /* @__PURE__ */ new Map();
|
|
47
|
+
const dispatch = /* @__PURE__ */ new Map();
|
|
42
48
|
/** Global unique index on event ids, like the SQL schema's. */
|
|
43
49
|
const byEventId = /* @__PURE__ */ new Map();
|
|
44
|
-
const leases = /* @__PURE__ */ new Map();
|
|
45
|
-
/** Array offset of the first row not known to be processed. */
|
|
46
|
-
const pendingHeads = /* @__PURE__ */ new Map();
|
|
47
50
|
/** Keyed by `${sessionId}\u0000${reducerName}` — a pure cache. */
|
|
48
51
|
const snapshots = /* @__PURE__ */ new Map();
|
|
49
52
|
const streamSubscribers = /* @__PURE__ */ new Map();
|
|
@@ -60,117 +63,206 @@ function memory(options = {}) {
|
|
|
60
63
|
}
|
|
61
64
|
return rows;
|
|
62
65
|
};
|
|
66
|
+
const dispatchOf = (sessionId) => {
|
|
67
|
+
let state = dispatch.get(sessionId);
|
|
68
|
+
if (!state) {
|
|
69
|
+
state = {
|
|
70
|
+
ready: /* @__PURE__ */ new Map(),
|
|
71
|
+
lanes: /* @__PURE__ */ new Map()
|
|
72
|
+
};
|
|
73
|
+
dispatch.set(sessionId, state);
|
|
74
|
+
}
|
|
75
|
+
return state;
|
|
76
|
+
};
|
|
77
|
+
const enqueue = (sessionId, row) => {
|
|
78
|
+
if (row.processedAt !== null) return;
|
|
79
|
+
const state = dispatchOf(sessionId);
|
|
80
|
+
if (row.lane === null) {
|
|
81
|
+
state.ready.set(row.index, row);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
let queue = state.lanes.get(row.lane);
|
|
85
|
+
if (!queue) {
|
|
86
|
+
queue = {
|
|
87
|
+
rows: [],
|
|
88
|
+
head: 0
|
|
89
|
+
};
|
|
90
|
+
state.lanes.set(row.lane, queue);
|
|
91
|
+
}
|
|
92
|
+
queue.rows.push(row);
|
|
93
|
+
if (queue.rows.length === 1) state.ready.set(row.index, row);
|
|
94
|
+
};
|
|
95
|
+
const settle = (sessionId, row) => {
|
|
96
|
+
const state = dispatchOf(sessionId);
|
|
97
|
+
state.ready.delete(row.index);
|
|
98
|
+
if (row.lane === null) return;
|
|
99
|
+
const queue = state.lanes.get(row.lane);
|
|
100
|
+
if (queue.rows[queue.head] !== row) return;
|
|
101
|
+
queue.head += 1;
|
|
102
|
+
if (queue.head === queue.rows.length) {
|
|
103
|
+
state.lanes.delete(row.lane);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const next = queue.rows[queue.head];
|
|
107
|
+
if (next.failedAt === null) state.ready.set(next.index, next);
|
|
108
|
+
};
|
|
63
109
|
const find = (sessionId, index) => {
|
|
64
|
-
const row = sessions.get(sessionId)?.
|
|
110
|
+
const row = sessions.get(sessionId)?.[index - 1];
|
|
65
111
|
if (!row) throw new TypeError(`no event at index ${index} in session '${sessionId}'`);
|
|
66
112
|
return row;
|
|
67
113
|
};
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return rows[offset];
|
|
74
|
-
};
|
|
75
|
-
const claim = (row, now) => {
|
|
114
|
+
const appendResult = (sessionId, events) => ({
|
|
115
|
+
events,
|
|
116
|
+
hasPending: (sessions.get(sessionId) ?? []).some((row) => row.processedAt === null)
|
|
117
|
+
});
|
|
118
|
+
const claim = (row, holder, now, expiresAt) => {
|
|
76
119
|
row.attemptCount += 1;
|
|
77
120
|
row.firstClaimedAt ??= new Date(now);
|
|
78
121
|
row.lastClaimedAt = new Date(now);
|
|
122
|
+
row.claimHolder = holder;
|
|
123
|
+
row.claimExpiresAt = new Date(expiresAt);
|
|
79
124
|
return toStored(row);
|
|
80
125
|
};
|
|
126
|
+
const checkBatchIds = (sessionId, events) => {
|
|
127
|
+
const supplied = events.filter((candidate) => candidate.id !== void 0);
|
|
128
|
+
if (new Set(supplied.map((candidate) => candidate.id)).size !== supplied.length) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "batch contains the same event id more than once");
|
|
129
|
+
const existing = supplied.map((candidate) => byEventId.get(candidate.id)).filter((row) => row !== void 0);
|
|
130
|
+
const foreign = existing.find((row) => row.sessionId !== sessionId);
|
|
131
|
+
if (foreign) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${foreign.id}' already exists in another session`);
|
|
132
|
+
return existing;
|
|
133
|
+
};
|
|
134
|
+
const insert = (sessionId, events, now, cause) => {
|
|
135
|
+
const rows = rowsOf(sessionId);
|
|
136
|
+
const base = rows.length === 0 ? 0 : rows[rows.length - 1].index;
|
|
137
|
+
const inserted = events.map((candidate, offset) => ({
|
|
138
|
+
id: candidate.id ?? generateId(),
|
|
139
|
+
type: candidate.type,
|
|
140
|
+
payload: structuredClone(candidate.payload),
|
|
141
|
+
index: base + 1 + offset,
|
|
142
|
+
sessionId,
|
|
143
|
+
createdAt: new Date(now),
|
|
144
|
+
cause: cause ? { ...cause } : candidate.cause ? { ...candidate.cause } : null,
|
|
145
|
+
lane: candidate.lane ?? null,
|
|
146
|
+
processedAt: candidate.settled ? new Date(now) : null,
|
|
147
|
+
processedByAttempt: null,
|
|
148
|
+
returnedEventIds: null,
|
|
149
|
+
firstClaimedAt: null,
|
|
150
|
+
lastClaimedAt: null,
|
|
151
|
+
attemptCount: 0,
|
|
152
|
+
claimHolder: null,
|
|
153
|
+
claimExpiresAt: null,
|
|
154
|
+
failureCount: 0,
|
|
155
|
+
lastFailedAt: null,
|
|
156
|
+
lastFailedAttempt: null,
|
|
157
|
+
lastError: null,
|
|
158
|
+
failedAt: null
|
|
159
|
+
}));
|
|
160
|
+
for (const row of inserted) if (byEventId.has(row.id)) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${row.id}' already exists`);
|
|
161
|
+
for (const row of inserted) {
|
|
162
|
+
rows.push(row);
|
|
163
|
+
byEventId.set(row.id, row);
|
|
164
|
+
enqueue(sessionId, row);
|
|
165
|
+
}
|
|
166
|
+
return inserted;
|
|
167
|
+
};
|
|
81
168
|
return {
|
|
82
169
|
async append(sessionId, events) {
|
|
83
|
-
if (events.length === 0) return [];
|
|
84
|
-
const
|
|
85
|
-
if (new Set(supplied.map((e) => e.id)).size !== supplied.length) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "batch contains the same event id more than once");
|
|
86
|
-
const existing = supplied.map((e) => byEventId.get(e.id)).filter((row) => row !== void 0);
|
|
170
|
+
if (events.length === 0) return appendResult(sessionId, []);
|
|
171
|
+
const existing = checkBatchIds(sessionId, events);
|
|
87
172
|
if (existing.length > 0) {
|
|
88
|
-
|
|
89
|
-
if (foreign) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${foreign.id}' already exists in another session`);
|
|
90
|
-
if (existing.length === events.length) return existing.toSorted((a, b) => a.index - b.index).map(toStored);
|
|
173
|
+
if (existing.length === events.length) return appendResult(sessionId, idempotentReplay(events, existing.map(toStored)));
|
|
91
174
|
throw new A2Error("PARTIAL_DUPLICATE_BATCH", `batch mixes ${existing.length} already-appended and ${events.length - existing.length} fresh events`);
|
|
92
175
|
}
|
|
93
|
-
const rows = rowsOf(sessionId);
|
|
94
176
|
const now = clock.now();
|
|
95
|
-
const
|
|
96
|
-
const inserted = events.map((e, i) => {
|
|
97
|
-
return {
|
|
98
|
-
id: e.id ?? generateId(),
|
|
99
|
-
type: e.type,
|
|
100
|
-
payload: structuredClone(e.payload),
|
|
101
|
-
index: base + 1 + i,
|
|
102
|
-
sessionId,
|
|
103
|
-
createdAt: new Date(now),
|
|
104
|
-
cause: e.cause ? { ...e.cause } : null,
|
|
105
|
-
processedAt: null,
|
|
106
|
-
processedByAttempt: null,
|
|
107
|
-
firstClaimedAt: null,
|
|
108
|
-
lastClaimedAt: null,
|
|
109
|
-
attemptCount: 0,
|
|
110
|
-
failureCount: 0,
|
|
111
|
-
lastFailedAt: null,
|
|
112
|
-
lastFailedAttempt: null,
|
|
113
|
-
lastError: null,
|
|
114
|
-
failedAt: null
|
|
115
|
-
};
|
|
116
|
-
});
|
|
117
|
-
for (const row of inserted) if (byEventId.has(row.id)) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${row.id}' already exists`);
|
|
118
|
-
for (const row of inserted) {
|
|
119
|
-
rows.push(row);
|
|
120
|
-
byEventId.set(row.id, row);
|
|
121
|
-
}
|
|
177
|
+
const inserted = insert(sessionId, events, now);
|
|
122
178
|
notifyStreams(sessionId, inserted);
|
|
123
|
-
return inserted.map(toStored);
|
|
179
|
+
return appendResult(sessionId, inserted.map(toStored));
|
|
124
180
|
},
|
|
125
181
|
async read(sessionId, opts) {
|
|
126
182
|
return (sessions.get(sessionId) ?? []).filter((row) => {
|
|
127
183
|
if (opts?.afterIndex !== void 0 && row.index <= opts.afterIndex) return false;
|
|
128
|
-
if (opts?.unprocessedOnly && row.processedAt !== null) return false;
|
|
129
184
|
return true;
|
|
130
185
|
}).map(toStored);
|
|
131
186
|
},
|
|
132
|
-
async
|
|
133
|
-
const next = pendingHead(sessionId);
|
|
134
|
-
if (!next || next.failedAt || maxIndex !== void 0 && next.index > maxIndex) return { outcome: "settled" };
|
|
187
|
+
async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes }) {
|
|
135
188
|
const claimedAt = clock.now();
|
|
136
189
|
const now = claimedAt.getTime();
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
190
|
+
const expiresAt = new Date(expiresAtMs ?? now + ttlMs);
|
|
191
|
+
const excluded = new Set(excludeIndexes ?? []);
|
|
192
|
+
const eligible = [];
|
|
193
|
+
let retryAt = null;
|
|
194
|
+
for (const row of dispatchOf(sessionId).ready.values()) {
|
|
195
|
+
if (row.failedAt !== null || excluded.has(row.index)) continue;
|
|
196
|
+
if (row.claimExpiresAt && row.claimExpiresAt.getTime() > now) {
|
|
197
|
+
if (!retryAt || row.claimExpiresAt < retryAt) retryAt = new Date(row.claimExpiresAt);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
eligible.push(row);
|
|
201
|
+
}
|
|
202
|
+
if (eligible.length === 0) return retryAt ? {
|
|
203
|
+
outcome: "busy",
|
|
204
|
+
retryAt
|
|
205
|
+
} : { outcome: "settled" };
|
|
206
|
+
eligible.sort((a, b) => a.index - b.index);
|
|
143
207
|
return {
|
|
144
208
|
outcome: "claimed",
|
|
145
|
-
|
|
209
|
+
events: eligible.map((row) => claim(row, holder, claimedAt, expiresAt))
|
|
146
210
|
};
|
|
147
211
|
},
|
|
148
|
-
async
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
212
|
+
async renewClaims({ sessionId, holder, indexes, ttlMs, expiresAtMs }) {
|
|
213
|
+
const now = clock.now();
|
|
214
|
+
const expiresAt = new Date(expiresAtMs ?? now.getTime() + ttlMs);
|
|
215
|
+
const requested = new Set(indexes);
|
|
216
|
+
const renewed = [];
|
|
217
|
+
for (const row of sessions.get(sessionId) ?? []) if (requested.has(row.index) && row.processedAt === null && row.failedAt === null && row.claimHolder === holder && row.claimExpiresAt !== null && row.claimExpiresAt > now) {
|
|
218
|
+
row.claimExpiresAt = new Date(expiresAt);
|
|
219
|
+
renewed.push(row.index);
|
|
220
|
+
}
|
|
221
|
+
return renewed;
|
|
222
|
+
},
|
|
223
|
+
async completeAttempt({ sessionId, index, attempt, events }) {
|
|
224
|
+
const parent = find(sessionId, index);
|
|
225
|
+
const cause = {
|
|
226
|
+
index,
|
|
227
|
+
attempt
|
|
228
|
+
};
|
|
229
|
+
if (parent.processedAt !== null) {
|
|
230
|
+
if (parent.processedByAttempt !== attempt) return { outcome: "superseded" };
|
|
231
|
+
const requestedIds = events.map((event) => event.id);
|
|
232
|
+
if (parent.returnedEventIds === null || parent.returnedEventIds.length !== requestedIds.length || parent.returnedEventIds.some((id, offset) => id !== requestedIds[offset])) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "completed attempt does not match the returned event batch");
|
|
233
|
+
const existing = checkBatchIds(sessionId, events);
|
|
234
|
+
if (existing.length !== events.length || existing.some((row) => row.cause?.index !== index || row.cause.attempt !== attempt)) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "completed attempt does not match the returned event batch");
|
|
235
|
+
return {
|
|
236
|
+
outcome: "completed",
|
|
237
|
+
events: idempotentReplay(events, existing.map(toStored))
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
if (parent.attemptCount !== attempt || parent.failedAt !== null || parent.claimHolder === null) return { outcome: "superseded" };
|
|
241
|
+
if (checkBatchIds(sessionId, events).length > 0) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "returned event batch contains already-appended ids");
|
|
242
|
+
const now = clock.now();
|
|
243
|
+
const inserted = insert(sessionId, events, now, cause);
|
|
244
|
+
parent.processedAt = new Date(now);
|
|
245
|
+
parent.processedByAttempt = attempt;
|
|
246
|
+
parent.returnedEventIds = events.map((event) => event.id);
|
|
247
|
+
parent.claimHolder = null;
|
|
248
|
+
parent.claimExpiresAt = null;
|
|
249
|
+
settle(sessionId, parent);
|
|
250
|
+
notifyStreams(sessionId, inserted);
|
|
159
251
|
return {
|
|
160
|
-
outcome: "
|
|
161
|
-
|
|
252
|
+
outcome: "completed",
|
|
253
|
+
events: inserted.map(toStored)
|
|
162
254
|
};
|
|
163
255
|
},
|
|
164
|
-
async markProcessed(sessionId, index) {
|
|
165
|
-
const row = find(sessionId, index);
|
|
166
|
-
row.processedAt ??= clock.now();
|
|
167
|
-
},
|
|
168
256
|
async failAttempt({ sessionId, index, attempt, error, maxFailures }) {
|
|
169
257
|
const row = find(sessionId, index);
|
|
170
|
-
if (row.processedAt || row.attemptCount !== attempt) return {
|
|
258
|
+
if (row.processedAt || row.attemptCount !== attempt || row.claimHolder === null && row.lastFailedAttempt !== attempt) return {
|
|
171
259
|
outcome: "superseded",
|
|
172
260
|
failureCount: row.failureCount
|
|
173
261
|
};
|
|
262
|
+
if (row.lastFailedAttempt === attempt && row.claimHolder === null) return {
|
|
263
|
+
outcome: row.failedAt ? "dead_lettered" : "failed",
|
|
264
|
+
failureCount: row.failureCount
|
|
265
|
+
};
|
|
174
266
|
if (row.failedAt) return {
|
|
175
267
|
outcome: "dead_lettered",
|
|
176
268
|
failureCount: row.failureCount
|
|
@@ -180,8 +272,11 @@ function memory(options = {}) {
|
|
|
180
272
|
const failedAt = clock.now();
|
|
181
273
|
row.lastFailedAt = new Date(failedAt);
|
|
182
274
|
row.lastFailedAttempt = attempt;
|
|
275
|
+
row.claimHolder = null;
|
|
276
|
+
row.claimExpiresAt = null;
|
|
183
277
|
if (row.failureCount >= maxFailures) {
|
|
184
278
|
row.failedAt = new Date(failedAt);
|
|
279
|
+
dispatchOf(sessionId).ready.delete(row.index);
|
|
185
280
|
return {
|
|
186
281
|
outcome: "dead_lettered",
|
|
187
282
|
failureCount: row.failureCount
|
|
@@ -192,9 +287,6 @@ function memory(options = {}) {
|
|
|
192
287
|
failureCount: row.failureCount
|
|
193
288
|
};
|
|
194
289
|
},
|
|
195
|
-
async markFailed(sessionId, index) {
|
|
196
|
-
find(sessionId, index).failedAt = clock.now();
|
|
197
|
-
},
|
|
198
290
|
async readState(sessionId, reducerName) {
|
|
199
291
|
const snap = snapshots.get(`${sessionId}\u0000${reducerName}`);
|
|
200
292
|
const snapshot = snap ? {
|
|
@@ -323,21 +415,6 @@ function memory(options = {}) {
|
|
|
323
415
|
}
|
|
324
416
|
};
|
|
325
417
|
} };
|
|
326
|
-
},
|
|
327
|
-
lease: {
|
|
328
|
-
async acquire({ sessionId, holder, ttlMs, expiresAtMs }) {
|
|
329
|
-
const now = clock.now().getTime();
|
|
330
|
-
const current = leases.get(sessionId);
|
|
331
|
-
if (current && current.expiresAt > now && current.holder !== holder) return false;
|
|
332
|
-
leases.set(sessionId, {
|
|
333
|
-
holder,
|
|
334
|
-
expiresAt: expiresAtMs ?? now + ttlMs
|
|
335
|
-
});
|
|
336
|
-
return true;
|
|
337
|
-
},
|
|
338
|
-
async release({ sessionId, holder }) {
|
|
339
|
-
if (leases.get(sessionId)?.holder === holder) leases.delete(sessionId);
|
|
340
|
-
}
|
|
341
418
|
}
|
|
342
419
|
};
|
|
343
420
|
}
|
package/dist/log-postgres.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as IdSource, i as Clock, t as A2Log } from "./log-
|
|
1
|
+
import { c as IdSource, i as Clock, t as A2Log } from "./log-ldf5g8Cx.js";
|
|
2
2
|
//#region src/log-postgres.d.ts
|
|
3
3
|
/** The result shape this backend reads: just rows. */
|
|
4
4
|
type PostgresQueryResult = {
|