@everystack/api 0.2.11 → 0.2.12

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": "@everystack/api",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "PostgREST-compatible HTTP handler + typed fetch client for Drizzle ORM",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "Scalable Technology, Inc. <licensing@scalable.technology>",
@@ -156,6 +156,23 @@ export interface CacheConfig {
156
156
  private?: string;
157
157
  }
158
158
 
159
+ /** Global post-RPC hook — fired after a SUCCESSFUL RPC dispatch, fire-and-forget.
160
+ * The RPC counterpart to the per-table after* hooks (afterCreate/afterUpdate/
161
+ * afterDelete). RPCs are function-scoped, not table-scoped, so this is global
162
+ * rather than keyed by table. Receives the RPC's return value (the row(s) it
163
+ * produced), the function name, the request body, the verified user/client, and
164
+ * the trace-id ctx. Errors are swallowed and never affect the response — a
165
+ * side-effect failure (cache purge, webhook) must not fail the write. Runs only
166
+ * on the success path. See docs/plans/afterrpc-cache-purge.md. */
167
+ export type AfterRpcHook = (
168
+ result: unknown,
169
+ fnName: string,
170
+ body: unknown,
171
+ user: Record<string, unknown> | null,
172
+ client: Record<string, unknown> | null,
173
+ ctx: RequestContext,
174
+ ) => Promise<void>;
175
+
159
176
  /** RPC handler — plain function (backward compatible).
160
177
  * Plain handlers dispatch directly: no transaction, no pgSettings — they own
161
178
  * their DB access through whatever they close over, same privilege model as
@@ -212,6 +229,10 @@ export interface HandlerOptions {
212
229
  pgSettings?: (user: Record<string, unknown> | null, client?: Record<string, unknown> | null) => Record<string, string>;
213
230
  /** Per-table lifecycle hooks for mutations. */
214
231
  hooks?: Record<string, HookConfig>;
232
+ /** Global hook fired after a successful RPC. Fire-and-forget; errors swallowed.
233
+ * RPC writes fire no per-table hooks, so this is the only post-RPC side-effect
234
+ * seam (cache purge, webhooks, audit enrichment). See docs/plans/afterrpc-cache-purge.md. */
235
+ afterRpc?: AfterRpcHook;
215
236
  /** API naming convention for query params, request bodies, and responses.
216
237
  * 'snake_case': accept snake_case inbound, return snake_case outbound.
217
238
  * Default: use Drizzle property names as-is (typically camelCase). */
@@ -639,6 +660,12 @@ export function createHandler(db: DrizzleDb, schema: Schema, options?: HandlerOp
639
660
  kind: 'rpc', name: fnName, user, client,
640
661
  result: 'allow', requestId, body: body as Record<string, unknown>,
641
662
  });
663
+ // Global post-RPC hook: fire-and-forget side effects (cache purge,
664
+ // webhooks). Swallow errors — a side-effect failure must not fail the
665
+ // write, exactly like the after* REST hooks. Success path only.
666
+ if (options?.afterRpc) {
667
+ try { await options.afterRpc(result, fnName, body, user, client, ctx); } catch {}
668
+ }
642
669
  // Same cache discipline as table GETs: with pgSettings configured the
643
670
  // response can vary by identity (opportunistic auth on public RPCs),
644
671
  // so it must forbid shared caching ITSELF — never rely on the CDN