@unbrained/pm-web 2026.9.23 → 2026.10.5

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.
@@ -0,0 +1,361 @@
1
+ import crypto from "node:crypto";
2
+ import { pool } from "./db.js";
3
+ import { extractToken, verifyToken } from "./auth.js";
4
+ import { isUnsafeMethod } from "./rate-limit.js";
5
+ /**
6
+ * Minimum length an `Idempotency-Key` header value may have.
7
+ *
8
+ * Keys are generated by the offline mutation queue as 128-bit UUID-shaped
9
+ * strings, so anything shorter than this is a client bug rather than a key,
10
+ * and accepting it would let a truncated or hand-typed value collide with a
11
+ * future real key.
12
+ */
13
+ export const IDEMPOTENCY_KEY_MIN_LENGTH = 8;
14
+ /**
15
+ * Maximum length an `Idempotency-Key` header value may have.
16
+ *
17
+ * Bounds the stored key so a hostile client cannot stuff multi-kilobyte
18
+ * strings into the unique index; real keys are 36-character UUID strings.
19
+ */
20
+ export const IDEMPOTENCY_KEY_MAX_LENGTH = 200;
21
+ /**
22
+ * How long a duplicate waits for the in-flight original to settle, in
23
+ * milliseconds, before answering 409. Override: `PM_WEB_IDEMPOTENCY_WAIT_MS`.
24
+ *
25
+ * The default covers the longest mutating route (pm CLI commands time out at
26
+ * 30 s), so a concurrent duplicate normally observes the original's stored
27
+ * outcome instead of being told to retry.
28
+ */
29
+ const DEFAULT_WAIT_MS = 30_000;
30
+ /** How often a waiting duplicate re-checks the store, in milliseconds. */
31
+ const DEFAULT_POLL_MS = 25;
32
+ /** Age after which an unfinished execution is reported as outcome unknown. */
33
+ const DEFAULT_PENDING_TIMEOUT_MS = 300_000;
34
+ /**
35
+ * How long a settled idempotency record is kept before it is swept, in
36
+ * milliseconds. Override: `PM_WEB_IDEMPOTENCY_RETENTION_MS`.
37
+ *
38
+ * Completed outcomes are deduplicated within this window. A retry after expiry
39
+ * can execute again. Unknown outcomes have the same bounded retention, measured
40
+ * from settlement; stale pending intents must settle before they can expire.
41
+ */
42
+ const DEFAULT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
43
+ /** Handler assertions are process-local and cannot be forged through request headers. */
44
+ const preCommitFailures = new WeakSet();
45
+ /** Mark a failure only after proving no mutation committed (for example, confirmed rollback before COMMIT was attempted). */
46
+ export function markIdempotencyPreCommitFailure(res) {
47
+ preCommitFailures.add(res);
48
+ }
49
+ /** Machine-readable refusal reused by failure settlement and stale-intent recovery. */
50
+ const UNKNOWN_OUTCOME = JSON.stringify({
51
+ code: "PM_IDEMPOTENCY_OUTCOME_UNKNOWN",
52
+ error: "Idempotency outcome unknown; reconcile the original mutation before submitting new work",
53
+ });
54
+ /**
55
+ * Compute the fingerprint that binds one idempotency key to one request.
56
+ *
57
+ * The fingerprint covers the method, the full path with query string, and the
58
+ * parsed JSON body, so a key can only ever be replayed for the identical
59
+ * request it was created for. Re-serializing the parsed body is stable here
60
+ * because a retry sends the same bytes (the offline queue persists the exact
61
+ * body string), which parses to the same property order; a different body —
62
+ * different properties or different values — produces a different fingerprint
63
+ * and is rejected rather than silently deduplicated.
64
+ *
65
+ * @param method - The HTTP method of the request.
66
+ * @param path - The request path including any query string.
67
+ * @param body - The parsed request body, or `null` when the request has none.
68
+ * @returns A hex SHA-256 digest over the request identity.
69
+ */
70
+ export function requestFingerprint(method, path, body) {
71
+ return crypto
72
+ .createHash("sha256")
73
+ .update(`${method}\n${path}\n${JSON.stringify(body ?? null)}`)
74
+ .digest("hex");
75
+ }
76
+ /**
77
+ * Resolve a positive millisecond duration from the environment.
78
+ *
79
+ * Accepts only finite positive numbers; anything else (unset, empty, zero,
80
+ * negative, non-numeric) falls back to the shipped default so a typo in an
81
+ * environment variable degrades to the documented behaviour instead of
82
+ * disabling a timeout entirely.
83
+ *
84
+ * @param name - The environment variable to read.
85
+ * @param fallback - The default used when the variable is unusable.
86
+ * @returns The resolved duration in milliseconds.
87
+ */
88
+ function envDurationMs(name, fallback) {
89
+ const parsed = Number(process.env[name]);
90
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
91
+ }
92
+ /**
93
+ * Insert the intent to execute one keyed request, or report a conflict.
94
+ *
95
+ * `ON CONFLICT DO NOTHING` makes the insert atomic against a concurrent
96
+ * duplicate: exactly one request can own a (user, key) pair at a time, and
97
+ * the loser learns it lost by receiving no row rather than by an error it
98
+ * would have to disambiguate from a genuine failure.
99
+ *
100
+ * @returns The id of the inserted row when this call owns the execution, or
101
+ * `null` when the key is already held.
102
+ */
103
+ async function insertPendingKey(userId, key, method, path, fingerprint) {
104
+ const result = await pool.query(`INSERT INTO pm_idempotency_keys (user_id, idempotency_key, method, path, request_fingerprint)
105
+ SELECT id, $2, $3, $4, $5 FROM pm_users WHERE id = $1 FOR KEY SHARE
106
+ ON CONFLICT (user_id, idempotency_key) DO NOTHING
107
+ RETURNING id`, [userId, key, method, path, fingerprint]);
108
+ const row = result.rows[0];
109
+ return row?.id ?? null;
110
+ }
111
+ /** Check that a verified token still names an existing account before keying. */
112
+ async function accountExists(userId) {
113
+ if (typeof userId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId))
114
+ return false;
115
+ const result = await pool.query("SELECT id FROM pm_users WHERE id = $1", [userId]);
116
+ return result.rows.length > 0;
117
+ }
118
+ /**
119
+ * Read the current idempotency record for one (account, key) pair.
120
+ *
121
+ * @returns The record, or `null` when no row exists (which can only happen
122
+ * transiently: a settled record is swept only by retention, and a pending
123
+ * one only by an explicit transient refusal).
124
+ */
125
+ async function selectKey(userId, key) {
126
+ const result = await pool.query(`SELECT id, request_fingerprint, status_code, response_body, response_content_type, created_at, outcome_state
127
+ FROM pm_idempotency_keys
128
+ WHERE user_id = $1 AND idempotency_key = $2`, [userId, key]);
129
+ return result.rows[0] ?? null;
130
+ }
131
+ /**
132
+ * Sweep idempotency records past the retention window.
133
+ *
134
+ * Called at most once per minute per guard instance. Stale pending records first
135
+ * become terminal unknown outcomes; retention starts at that settlement time.
136
+ */
137
+ async function deleteExpiredKeys() {
138
+ await pool.query(`UPDATE pm_idempotency_keys SET outcome_state = 'outcome_unknown', status_code = 409,
139
+ response_body = $2, response_content_type = 'application/json', updated_at = NOW()
140
+ WHERE status_code IS NULL AND outcome_state = 'pending'
141
+ AND created_at < NOW() - ($1::double precision * interval '1 ms')`, [envDurationMs("PM_WEB_IDEMPOTENCY_PENDING_TIMEOUT_MS", DEFAULT_PENDING_TIMEOUT_MS), UNKNOWN_OUTCOME]);
142
+ await pool.query(`DELETE FROM pm_idempotency_keys
143
+ WHERE status_code IS NOT NULL AND updated_at < NOW() - ($1::double precision * interval '1 ms')`, [envDurationMs("PM_WEB_IDEMPOTENCY_RETENTION_MS", DEFAULT_RETENTION_MS)]);
144
+ }
145
+ /**
146
+ * Store a definitive outcome. Release only explicit pre-mutation refusals
147
+ * (425, 429, or handler-proven pre-commit failures). All other server failures
148
+ * settle as terminal unknown outcomes because they may follow a commit.
149
+ */
150
+ async function persistOutcome(rowId, statusCode, body, contentType, preCommit = false) {
151
+ if (statusCode === 425 || statusCode === 429 || (statusCode >= 500 && preCommit)) {
152
+ await pool.query(`DELETE FROM pm_idempotency_keys WHERE id = $1 AND outcome_state = 'pending'`, [rowId]);
153
+ return;
154
+ }
155
+ const unknown = statusCode >= 500;
156
+ await pool.query(`UPDATE pm_idempotency_keys
157
+ SET status_code = $2, response_body = $3, response_content_type = $4, outcome_state = $5, updated_at = NOW()
158
+ WHERE id = $1 AND outcome_state = 'pending'`, [rowId, unknown ? 409 : statusCode, unknown ? UNKNOWN_OUTCOME : body,
159
+ unknown ? "application/json" : contentType, unknown ? "outcome_unknown" : "completed"]);
160
+ }
161
+ /**
162
+ * Answer a duplicate from the stored outcome of the original request.
163
+ *
164
+ * The `Idempotency-Replayed` header lets the client (and the tests) tell a
165
+ * replay from a fresh execution without diffing bodies. Responses that had no
166
+ * body keep having none; responses that had one are served with the content
167
+ * type they originally carried.
168
+ */
169
+ function replayStored(record, res) {
170
+ res.setHeader("Idempotency-Replayed", "true");
171
+ if (record.response_body === null) {
172
+ res.status(record.status_code ?? 500).end();
173
+ return;
174
+ }
175
+ res.setHeader("Content-Type", record.response_content_type ?? "application/json");
176
+ res.status(record.status_code ?? 500).send(record.response_body);
177
+ }
178
+ /**
179
+ * Answer a settled record, or reject a key recycled onto a different request.
180
+ *
181
+ * @param record - The settled record held under the client's key.
182
+ * @param fingerprint - The fingerprint of the request being answered.
183
+ * @param res - The response to answer on.
184
+ */
185
+ function finishSettled(record, fingerprint, res) {
186
+ if (record.request_fingerprint !== fingerprint) {
187
+ res.status(422).json({
188
+ error: "Idempotency-Key was already used for a different request",
189
+ });
190
+ return;
191
+ }
192
+ replayStored(record, res);
193
+ }
194
+ /**
195
+ * Capture the executing request's response and persist it when it finishes.
196
+ *
197
+ * `res.json`/`res.send` are wrapped once to record the body the handler
198
+ * produced, and the `finish` listener persists whatever status the response
199
+ * left with — so handlers that never call `json`/`send` (204s, `end()`) are
200
+ * still memoized, just without a body.
201
+ *
202
+ * @param rowId - The id of this execution's idempotency record.
203
+ * @param res - The executing response.
204
+ */
205
+ function captureOutcome(rowId, res) {
206
+ let capturedBody = null;
207
+ const originalJson = res.json.bind(res);
208
+ const originalSend = res.send.bind(res);
209
+ /** Capture the first response serialization without recording request content. */
210
+ const capture = (body) => {
211
+ if (capturedBody !== null)
212
+ return;
213
+ capturedBody = typeof body === "string" ? body : JSON.stringify(body ?? null);
214
+ };
215
+ res.json = (body) => {
216
+ capture(body);
217
+ return originalJson(body);
218
+ };
219
+ res.send = (body) => {
220
+ capture(body);
221
+ return originalSend(body);
222
+ };
223
+ let finished = false;
224
+ /** Finish and premature close share one persistence path, avoiding conflicting writes. */
225
+ const settle = (statusCode) => {
226
+ const contentType = res.getHeader("Content-Type");
227
+ void persistOutcome(rowId, statusCode, capturedBody, typeof contentType === "string" ? contentType : null, preCommitFailures.has(res)).catch((error) => {
228
+ // Do not log response bodies, tokens or database error details.
229
+ console.error("Failed to persist idempotency outcome; key remains pending", error instanceof Error ? error.name : "unknown error");
230
+ });
231
+ };
232
+ res.on("finish", () => {
233
+ finished = true;
234
+ settle(res.statusCode);
235
+ });
236
+ res.on("close", () => {
237
+ if (!finished)
238
+ settle(500);
239
+ });
240
+ }
241
+ /**
242
+ * Build the middleware that makes keyed mutating requests at-most-once.
243
+ *
244
+ * Scope: keyed mutating `/api` requests outside `/api/auth`. Auth responses
245
+ * always execute live so session cookies are never lost to body-only replay.
246
+ * An expected-account header is checked against an existing authenticated user
247
+ * before execution or claiming, including recovery probes to `/api/auth/me`.
248
+ *
249
+ * Exactly-once per account: the key is stored together with the authenticated
250
+ * user, so two accounts using the same key never interfere. The first
251
+ * execution atomically claims the (account, key) pair, stores its outcome
252
+ * when the response finishes, and any duplicate — a retry after an ambiguous
253
+ * response the client never saw, or a concurrent duplicate of an in-flight
254
+ * request — is answered from that stored outcome without re-executing. A key
255
+ * reused for a *different* request is rejected with 422 rather than silently
256
+ * deduplicated; an unfinished record older than the pending timeout is
257
+ * answered with 409 outcome unknown without re-execution; an in-flight
258
+ * duplicate waits for it to settle and answers 409 only at the
259
+ * deadline.
260
+ *
261
+ * @returns An Express middleware enforcing the contract on its mount.
262
+ */
263
+ export function idempotencyGuard() {
264
+ let nextSweepAt = 0;
265
+ return async (req, res, next) => {
266
+ const authRoute = /^\/api\/auth(?:\/|$)/i.test(req.originalUrl.split("?")[0]);
267
+ const expectedAccount = req.get("x-pm-expected-account");
268
+ const checkExpected = expectedAccount !== undefined
269
+ && (!authRoute || /^\/api\/auth\/me\/?(?:\?|$)/i.test(req.originalUrl));
270
+ const header = req.get("idempotency-key");
271
+ const keyed = isUnsafeMethod(req.method) && !authRoute && header !== undefined;
272
+ if (!keyed && !checkExpected) {
273
+ next();
274
+ return;
275
+ }
276
+ // Only record requests we can attribute to an account; an unauthenticated
277
+ // or invalid-session request is passed through for the route to refuse,
278
+ // so an anonymous probe can never occupy a key.
279
+ let userId = null;
280
+ const token = extractToken(req);
281
+ if (token) {
282
+ try {
283
+ userId = verifyToken(token).userId;
284
+ }
285
+ catch {
286
+ userId = null;
287
+ }
288
+ }
289
+ if (userId !== null && !await accountExists(userId))
290
+ userId = null;
291
+ if (checkExpected && (userId === null || expectedAccount !== userId)) {
292
+ res.status(409).json({ code: "PM_EXPECTED_ACCOUNT_MISMATCH", error: "The signed-in account differs from the account that approved this work" });
293
+ return;
294
+ }
295
+ if (!keyed || userId === null) {
296
+ next();
297
+ return;
298
+ }
299
+ const key = header.trim();
300
+ if (key.length < IDEMPOTENCY_KEY_MIN_LENGTH || key.length > IDEMPOTENCY_KEY_MAX_LENGTH) {
301
+ res.status(400).json({
302
+ error: `Idempotency-Key must be between ${IDEMPOTENCY_KEY_MIN_LENGTH} and ${IDEMPOTENCY_KEY_MAX_LENGTH} characters`,
303
+ });
304
+ return;
305
+ }
306
+ if (Date.now() >= nextSweepAt) {
307
+ // Cleanup can fail only before an intent exists, leaving the key retryable.
308
+ nextSweepAt = Date.now() + 60_000;
309
+ await deleteExpiredKeys();
310
+ }
311
+ const fingerprint = requestFingerprint(req.method, req.originalUrl, req.body);
312
+ const deadline = Date.now() + envDurationMs("PM_WEB_IDEMPOTENCY_WAIT_MS", DEFAULT_WAIT_MS);
313
+ let rowId = await insertPendingKey(userId, key, req.method, req.originalUrl, fingerprint);
314
+ while (rowId === null) {
315
+ const record = await selectKey(userId, key);
316
+ if (record && record.status_code !== null) {
317
+ // The original settled before this duplicate arrived: replay it.
318
+ finishSettled(record, fingerprint, res);
319
+ return;
320
+ }
321
+ if (record && record.request_fingerprint !== fingerprint) {
322
+ res.status(422).json({
323
+ error: "Idempotency-Key was already used for a different request",
324
+ });
325
+ return;
326
+ }
327
+ if (record === null) {
328
+ // Deletion can race token validation; the locked INSERT SELECT refuses
329
+ // a missing user without a foreign-key failure or a permanent wait.
330
+ if (!await accountExists(userId)) {
331
+ next();
332
+ return;
333
+ }
334
+ rowId = await insertPendingKey(userId, key, req.method, req.originalUrl, fingerprint);
335
+ continue;
336
+ }
337
+ if (isStalePending(record)) {
338
+ await persistOutcome(record.id, 500, null, null);
339
+ continue;
340
+ }
341
+ if (Date.now() >= deadline) {
342
+ res.status(409).json({ code: "PM_IDEMPOTENCY_IN_FLIGHT", error: "The request with this Idempotency-Key is still executing; retry later" });
343
+ return;
344
+ }
345
+ await new Promise((resolve) => { setTimeout(resolve, envDurationMs("PM_WEB_IDEMPOTENCY_POLL_MS", DEFAULT_POLL_MS)); });
346
+ }
347
+ captureOutcome(rowId, res);
348
+ next();
349
+ };
350
+ }
351
+ /**
352
+ * Report whether a pending record is old enough to be a crashed execution.
353
+ *
354
+ * @param record - The still-pending record to age-check.
355
+ * @returns `true` when the record predates the pending timeout.
356
+ */
357
+ function isStalePending(record) {
358
+ const timeoutMs = envDurationMs("PM_WEB_IDEMPOTENCY_PENDING_TIMEOUT_MS", DEFAULT_PENDING_TIMEOUT_MS);
359
+ return Date.now() - record.created_at.getTime() > timeoutMs;
360
+ }
361
+ //# sourceMappingURL=idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -262,7 +262,7 @@ function processAlive(pid) {
262
262
  }
263
263
  export default defineExtension({
264
264
  name: "pm-web",
265
- version: "2026.9.23",
265
+ version: "2026.10.5",
266
266
  activate(api) {
267
267
  // -----------------------------------------------------------------------
268
268
  // Command: pm web [--port <port>] [--detach]
@@ -3,6 +3,7 @@ import { pool } from "../db.js";
3
3
  import { requireAuth } from "../middleware/auth.js";
4
4
  import { routeParam, uuidParamGuard } from "./route-params.js";
5
5
  import { findUserByEmail, notFoundWhenEmpty, parseGroupInput } from "./route-helpers.js";
6
+ import { markIdempotencyPreCommitFailure } from "../idempotency.js";
6
7
  const router = Router();
7
8
  router.use(requireAuth);
8
9
  // Both group ids and the member :userId are UUIDs; a malformed one is a bad
@@ -31,7 +32,11 @@ router.post("/", async (req, res) => {
31
32
  if (!input)
32
33
  return;
33
34
  const { name, description } = input;
34
- const client = await pool.connect();
35
+ const client = await pool.connect().catch((error) => {
36
+ markIdempotencyPreCommitFailure(res);
37
+ throw error;
38
+ });
39
+ let commitAttempted = false;
35
40
  try {
36
41
  await client.query("BEGIN");
37
42
  const groupResult = await client.query(`INSERT INTO pm_groups (owner_id, name, description)
@@ -40,11 +45,15 @@ router.post("/", async (req, res) => {
40
45
  const group = groupResult.rows[0];
41
46
  // Add owner as a member with role 'owner'
42
47
  await client.query(`INSERT INTO pm_group_members (group_id, user_id, role) VALUES ($1, $2, 'owner')`, [group.id, req.user.userId]);
48
+ commitAttempted = true;
43
49
  await client.query("COMMIT");
44
50
  res.status(201).json({ group: { ...groupResult.rows[0], role: "owner", member_count: "1" } });
45
51
  }
46
52
  catch (err) {
47
53
  await client.query("ROLLBACK");
54
+ // A failed COMMIT can have succeeded remotely; later ROLLBACK cannot prove otherwise.
55
+ if (!commitAttempted)
56
+ markIdempotencyPreCommitFailure(res);
48
57
  console.error("Create group error:", err);
49
58
  res.status(500).json({ error: "Failed to create group" });
50
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/routes/groups.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/routes/groups.ts"],"names":[],"mappings":"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"}