@pylonsync/functions 0.3.68 → 0.3.69

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pylonsync/functions",
3
- "version": "0.3.68",
3
+ "version": "0.3.69",
4
4
  "description": "TypeScript function runtime for pylon — defines server-side queries, mutations, and actions.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/runtime.ts CHANGED
@@ -398,6 +398,16 @@ function buildDbWriter(callId: string): DbWriter {
398
398
  })) as { unlinked: boolean };
399
399
  return r.unlinked;
400
400
  },
401
+ async advisoryLock(key) {
402
+ // The lock key rides on `entity` to avoid carving a new field
403
+ // for a single op. The Rust dispatcher matches on `op:
404
+ // "advisory_lock"` and treats `entity` as the key string.
405
+ await rpcDb(callId, {
406
+ type: "db",
407
+ op: "advisory_lock",
408
+ entity: key,
409
+ });
410
+ },
401
411
  };
402
412
  }
403
413
 
package/src/types.ts CHANGED
@@ -134,6 +134,27 @@ export interface DbWriter extends DbReader {
134
134
 
135
135
  /** Unlink a relation (set FK to null). */
136
136
  unlink(entity: string, id: string, relation: string): Promise<boolean>;
137
+
138
+ /**
139
+ * Acquire a transaction-scoped advisory lock on `key`. Held until
140
+ * the mutation tx commits or rolls back. Two concurrent mutations
141
+ * holding the same key serialize on Postgres; on SQLite this is a
142
+ * noop because writers are already serialized at the connection
143
+ * level.
144
+ *
145
+ * Use this to close TOCTOU windows on quota / uniqueness checks:
146
+ * call `advisoryLock` BEFORE the count query so the second tx
147
+ * blocks on the first's commit before observing state.
148
+ *
149
+ * Example:
150
+ * ```ts
151
+ * await ctx.db.advisoryLock(`org_count:${ctx.auth.userId}`);
152
+ * const orgs = await ctx.db.query("Organization", { createdBy: userId });
153
+ * if (orgs.length >= cap) throw ctx.error("QUOTA_EXCEEDED", "...");
154
+ * await ctx.db.insert("Organization", { ... });
155
+ * ```
156
+ */
157
+ advisoryLock(key: string): Promise<void>;
137
158
  }
138
159
 
139
160
  // ---------------------------------------------------------------------------