From 279eccc55a2960e1bc8aef1f05bbf25083180804 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 01:47:21 +0000 Subject: [PATCH 01/16] Add finally callback to customFunction Co-Authored-By: Ian Macartney --- .../server/customFunctions.test.ts | 61 +++++++++++++++++++ .../convex-helpers/server/customFunctions.ts | 32 +++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 8b3156ce..dc596b8a 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -379,6 +379,10 @@ const testApi: ApiFromModules<{ create: typeof create; outerAdds: typeof outerAdds; outerRemoves: typeof outerRemoves; + successFn: typeof successFn; + errorFn: typeof errorFn; + mutationFn: typeof mutationFn; + actionFn: typeof actionFn; }; }>["fns"] = anyApi["customFunctions.test"] as any; @@ -560,3 +564,60 @@ describe("nested custom functions", () => { ).rejects.toThrow("Validator error: Expected `string`"); }); }); + +describe("finally callback", () => { + test("finally callback is called with result and context", async () => { + const finallyMock = vi.fn(); + const ctx = { foo: "bar" }; + const args = { test: "value" }; + const result = { success: true }; + + const handler = async () => result; + + const mod = { + args: {}, + input: async () => ({ ctx: {}, args: {} }), + finally: finallyMock + }; + + let actualResult; + let actualError; + try { + actualResult = await handler(ctx, args); + } catch (e) { + actualError = e; + throw e; + } finally { + if (mod.finally) { + await mod.finally({ ctx, result: actualResult, error: actualError }); + } + } + + expect(finallyMock).toHaveBeenCalledWith({ + ctx, + result, + error: undefined + }); + + finallyMock.mockClear(); + const testError = new Error("Test error"); + const errorHandler = async () => { + throw testError; + }; + + try { + await errorHandler(); + } catch (e) { + } finally { + if (mod.finally) { + await mod.finally({ ctx, result: undefined, error: testError }); + } + } + + expect(finallyMock).toHaveBeenCalledWith({ + ctx, + result: undefined, + error: testError + }); + }); +}); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 3e24bd26..e12fcc82 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -60,6 +60,7 @@ export type Mod< ) => | Promise<{ ctx: ModCtx; args: ModMadeArgs }> | { ctx: ModCtx; args: ModMadeArgs }; + finally?: (params: { ctx: Ctx & ModCtx; result?: any; error?: any }) => void | Promise; }; /** @@ -88,6 +89,7 @@ export const NoOp = { input() { return { args: {}, ctx: {} }; }, + finally() {}, }; /** @@ -339,7 +341,20 @@ function customFnBuilder( pick(allArgs, Object.keys(inputArgs)) as any, ); const args = omit(allArgs, Object.keys(inputArgs)); - return handler({ ...ctx, ...added.ctx }, { ...args, ...added.args }); + const finalCtx = { ...ctx, ...added.ctx }; + let result; + let error; + try { + result = await handler(finalCtx, { ...args, ...added.args }); + return result; + } catch (e) { + error = e; + throw e; + } finally { + if (mod.finally) { + await mod.finally({ ctx: finalCtx, result, error }); + } + } }, }); } @@ -353,7 +368,20 @@ function customFnBuilder( returns: fn.returns, handler: async (ctx: any, args: any) => { const added = await inputMod(ctx, args); - return handler({ ...ctx, ...added.ctx }, { ...args, ...added.args }); + const finalCtx = { ...ctx, ...added.ctx }; + let result; + let error; + try { + result = await handler(finalCtx, { ...args, ...added.args }); + return result; + } catch (e) { + error = e; + throw e; + } finally { + if (mod.finally) { + await mod.finally({ ctx: finalCtx, result, error }); + } + } }, }); }; From e402a8dab102f3fb76e457286dc991f110c784fc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 01:50:42 +0000 Subject: [PATCH 02/16] Fix TypeScript errors in test file Co-Authored-By: Ian Macartney --- .../server/customFunctions.test.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index dc596b8a..f559caa9 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -379,10 +379,7 @@ const testApi: ApiFromModules<{ create: typeof create; outerAdds: typeof outerAdds; outerRemoves: typeof outerRemoves; - successFn: typeof successFn; - errorFn: typeof errorFn; - mutationFn: typeof mutationFn; - actionFn: typeof actionFn; + }; }>["fns"] = anyApi["customFunctions.test"] as any; @@ -567,7 +564,9 @@ describe("nested custom functions", () => { describe("finally callback", () => { test("finally callback is called with result and context", async () => { - const finallyMock = vi.fn(); + let finallyCalled = false; + let finallyParams = null; + const ctx = { foo: "bar" }; const args = { test: "value" }; const result = { success: true }; @@ -577,7 +576,10 @@ describe("finally callback", () => { const mod = { args: {}, input: async () => ({ ctx: {}, args: {} }), - finally: finallyMock + finally: (params) => { + finallyCalled = true; + finallyParams = params; + } }; let actualResult; @@ -593,13 +595,16 @@ describe("finally callback", () => { } } - expect(finallyMock).toHaveBeenCalledWith({ + expect(finallyCalled).toBe(true); + expect(finallyParams).toEqual({ ctx, result, error: undefined }); - finallyMock.mockClear(); + finallyCalled = false; + finallyParams = null; + const testError = new Error("Test error"); const errorHandler = async () => { throw testError; @@ -614,7 +619,8 @@ describe("finally callback", () => { } } - expect(finallyMock).toHaveBeenCalledWith({ + expect(finallyCalled).toBe(true); + expect(finallyParams).toEqual({ ctx, result: undefined, error: testError From 35ccd49e99aad14a3c86df89ebc20ce22f11bc6e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 01:52:02 +0000 Subject: [PATCH 03/16] Fix handler function in test to accept arguments Co-Authored-By: Ian Macartney --- packages/convex-helpers/server/customFunctions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index f559caa9..8685e328 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -571,7 +571,7 @@ describe("finally callback", () => { const args = { test: "value" }; const result = { success: true }; - const handler = async () => result; + const handler = async (_ctx, _args) => result; const mod = { args: {}, From d94142d74108bda06816d331020bee78294b4473 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 01:53:40 +0000 Subject: [PATCH 04/16] Fix formatting issues Co-Authored-By: Ian Macartney --- .../server/customFunctions.test.ts | 25 +++++++++---------- .../convex-helpers/server/customFunctions.ts | 6 ++++- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 8685e328..abaf3910 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -379,7 +379,6 @@ const testApi: ApiFromModules<{ create: typeof create; outerAdds: typeof outerAdds; outerRemoves: typeof outerRemoves; - }; }>["fns"] = anyApi["customFunctions.test"] as any; @@ -566,22 +565,22 @@ describe("finally callback", () => { test("finally callback is called with result and context", async () => { let finallyCalled = false; let finallyParams = null; - + const ctx = { foo: "bar" }; const args = { test: "value" }; const result = { success: true }; - + const handler = async (_ctx, _args) => result; - + const mod = { args: {}, input: async () => ({ ctx: {}, args: {} }), finally: (params) => { finallyCalled = true; finallyParams = params; - } + }, }; - + let actualResult; let actualError; try { @@ -594,22 +593,22 @@ describe("finally callback", () => { await mod.finally({ ctx, result: actualResult, error: actualError }); } } - + expect(finallyCalled).toBe(true); expect(finallyParams).toEqual({ ctx, result, - error: undefined + error: undefined, }); - + finallyCalled = false; finallyParams = null; - + const testError = new Error("Test error"); const errorHandler = async () => { throw testError; }; - + try { await errorHandler(); } catch (e) { @@ -618,12 +617,12 @@ describe("finally callback", () => { await mod.finally({ ctx, result: undefined, error: testError }); } } - + expect(finallyCalled).toBe(true); expect(finallyParams).toEqual({ ctx, result: undefined, - error: testError + error: testError, }); }); }); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index e12fcc82..8d5b39be 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -60,7 +60,11 @@ export type Mod< ) => | Promise<{ ctx: ModCtx; args: ModMadeArgs }> | { ctx: ModCtx; args: ModMadeArgs }; - finally?: (params: { ctx: Ctx & ModCtx; result?: any; error?: any }) => void | Promise; + finally?: (params: { + ctx: Ctx & ModCtx; + result?: any; + error?: any; + }) => void | Promise; }; /** From fb599bb06e68dbc28cc69edfdb69b48970f73776 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 02:04:27 +0000 Subject: [PATCH 05/16] Update finally callback API based on PR feedback Co-Authored-By: Ian Macartney --- .../server/customFunctions.finally.test.ts | 135 ++++++++++++++++++ .../convex-helpers/server/customFunctions.ts | 13 +- 2 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 packages/convex-helpers/server/customFunctions.finally.test.ts diff --git a/packages/convex-helpers/server/customFunctions.finally.test.ts b/packages/convex-helpers/server/customFunctions.finally.test.ts new file mode 100644 index 00000000..c7f89c26 --- /dev/null +++ b/packages/convex-helpers/server/customFunctions.finally.test.ts @@ -0,0 +1,135 @@ +import { describe, expect, test, vi } from "vitest"; +import { customQuery, customMutation, customAction } from "./customFunctions.js"; +import { convexTest } from "convex-test"; +import { + actionGeneric, + anyApi, + DataModelFromSchemaDefinition, + defineSchema, + defineTable, + MutationBuilder, + mutationGeneric, + QueryBuilder, + queryGeneric, +} from "convex/server"; +import { v } from "convex/values"; +import { modules } from "./setup.test.js"; + +const schema = defineSchema({ + users: defineTable({ + tokenIdentifier: v.string(), + }).index("tokenIdentifier", ["tokenIdentifier"]), +}); + +type DataModel = DataModelFromSchemaDefinition; +const query = queryGeneric as QueryBuilder; +const mutation = mutationGeneric as MutationBuilder; +const action = actionGeneric; + +describe("finally callback", () => { + test("finally callback with query", async () => { + const t = convexTest(schema, modules); + const finallyMock = vi.fn(); + + const withFinally = customQuery(query, { + args: {}, + input: async () => ({ ctx: { foo: "bar" }, args: {} }), + finally: (ctx, params) => { + finallyMock(ctx, params); + } + }); + + const successFn = withFinally({ + args: {}, + handler: async (ctx) => { + return { success: true, foo: ctx.foo }; + }, + }); + + const result = await t.query(successFn, {}); + expect(result).toEqual({ success: true, foo: "bar" }); + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { result: { success: true, foo: "bar" }, error: undefined } + ); + + finallyMock.mockClear(); + + const errorFn = withFinally({ + args: {}, + handler: async () => { + throw new Error("Test error"); + }, + }); + + try { + await t.query(errorFn, {}); + } catch (e) { + expect(e.message).toContain("Test error"); + } + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { + result: undefined, + error: expect.objectContaining({ message: expect.stringContaining("Test error") }) + } + ); + }); + + test("finally callback with mutation", async () => { + const t = convexTest(schema, modules); + const finallyMock = vi.fn(); + + const withFinally = customMutation(mutation, { + args: {}, + input: async () => ({ ctx: { foo: "bar" }, args: {} }), + finally: (ctx, params) => { + finallyMock(ctx, params); + } + }); + + const mutationFn = withFinally({ + args: {}, + handler: async (ctx) => { + return { updated: true, foo: ctx.foo }; + }, + }); + + const result = await t.mutation(mutationFn, {}); + expect(result).toEqual({ updated: true, foo: "bar" }); + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { result: { updated: true, foo: "bar" }, error: undefined } + ); + }); + + test("finally callback with action", async () => { + const t = convexTest(schema, modules); + const finallyMock = vi.fn(); + + const withFinally = customAction(action, { + args: {}, + input: async () => ({ ctx: { foo: "bar" }, args: {} }), + finally: (ctx, params) => { + finallyMock(ctx, params); + } + }); + + const actionFn = withFinally({ + args: {}, + handler: async (ctx) => { + return { executed: true, foo: ctx.foo }; + }, + }); + + const result = await t.action(actionFn, {}); + expect(result).toEqual({ executed: true, foo: "bar" }); + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { result: { executed: true, foo: "bar" }, error: undefined } + ); + }); +}); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 8d5b39be..0524194e 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -60,10 +60,9 @@ export type Mod< ) => | Promise<{ ctx: ModCtx; args: ModMadeArgs }> | { ctx: ModCtx; args: ModMadeArgs }; - finally?: (params: { - ctx: Ctx & ModCtx; - result?: any; - error?: any; + finally?: (ctx: Ctx & ModCtx, params: { + result?: unknown; + error?: unknown; }) => void | Promise; }; @@ -93,7 +92,7 @@ export const NoOp = { input() { return { args: {}, ctx: {} }; }, - finally() {}, + }; /** @@ -356,7 +355,7 @@ function customFnBuilder( throw e; } finally { if (mod.finally) { - await mod.finally({ ctx: finalCtx, result, error }); + await mod.finally(finalCtx, { result, error }); } } }, @@ -383,7 +382,7 @@ function customFnBuilder( throw e; } finally { if (mod.finally) { - await mod.finally({ ctx: finalCtx, result, error }); + await mod.finally(finalCtx, { result, error }); } } }, From ad26e41e4c332480c9c9a8e48fc80e78fedbc76e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 20:04:59 +0000 Subject: [PATCH 06/16] Address PR feedback: Update finally callback implementation and tests Co-Authored-By: Ian Macartney --- .../server/customFunctions.finally.test.ts | 135 --------------- .../server/customFunctions.test.ts | 159 ++++++++++++------ .../convex-helpers/server/customFunctions.ts | 20 +-- 3 files changed, 114 insertions(+), 200 deletions(-) delete mode 100644 packages/convex-helpers/server/customFunctions.finally.test.ts diff --git a/packages/convex-helpers/server/customFunctions.finally.test.ts b/packages/convex-helpers/server/customFunctions.finally.test.ts deleted file mode 100644 index c7f89c26..00000000 --- a/packages/convex-helpers/server/customFunctions.finally.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { describe, expect, test, vi } from "vitest"; -import { customQuery, customMutation, customAction } from "./customFunctions.js"; -import { convexTest } from "convex-test"; -import { - actionGeneric, - anyApi, - DataModelFromSchemaDefinition, - defineSchema, - defineTable, - MutationBuilder, - mutationGeneric, - QueryBuilder, - queryGeneric, -} from "convex/server"; -import { v } from "convex/values"; -import { modules } from "./setup.test.js"; - -const schema = defineSchema({ - users: defineTable({ - tokenIdentifier: v.string(), - }).index("tokenIdentifier", ["tokenIdentifier"]), -}); - -type DataModel = DataModelFromSchemaDefinition; -const query = queryGeneric as QueryBuilder; -const mutation = mutationGeneric as MutationBuilder; -const action = actionGeneric; - -describe("finally callback", () => { - test("finally callback with query", async () => { - const t = convexTest(schema, modules); - const finallyMock = vi.fn(); - - const withFinally = customQuery(query, { - args: {}, - input: async () => ({ ctx: { foo: "bar" }, args: {} }), - finally: (ctx, params) => { - finallyMock(ctx, params); - } - }); - - const successFn = withFinally({ - args: {}, - handler: async (ctx) => { - return { success: true, foo: ctx.foo }; - }, - }); - - const result = await t.query(successFn, {}); - expect(result).toEqual({ success: true, foo: "bar" }); - expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), - { result: { success: true, foo: "bar" }, error: undefined } - ); - - finallyMock.mockClear(); - - const errorFn = withFinally({ - args: {}, - handler: async () => { - throw new Error("Test error"); - }, - }); - - try { - await t.query(errorFn, {}); - } catch (e) { - expect(e.message).toContain("Test error"); - } - - expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), - { - result: undefined, - error: expect.objectContaining({ message: expect.stringContaining("Test error") }) - } - ); - }); - - test("finally callback with mutation", async () => { - const t = convexTest(schema, modules); - const finallyMock = vi.fn(); - - const withFinally = customMutation(mutation, { - args: {}, - input: async () => ({ ctx: { foo: "bar" }, args: {} }), - finally: (ctx, params) => { - finallyMock(ctx, params); - } - }); - - const mutationFn = withFinally({ - args: {}, - handler: async (ctx) => { - return { updated: true, foo: ctx.foo }; - }, - }); - - const result = await t.mutation(mutationFn, {}); - expect(result).toEqual({ updated: true, foo: "bar" }); - - expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), - { result: { updated: true, foo: "bar" }, error: undefined } - ); - }); - - test("finally callback with action", async () => { - const t = convexTest(schema, modules); - const finallyMock = vi.fn(); - - const withFinally = customAction(action, { - args: {}, - input: async () => ({ ctx: { foo: "bar" }, args: {} }), - finally: (ctx, params) => { - finallyMock(ctx, params); - } - }); - - const actionFn = withFinally({ - args: {}, - handler: async (ctx) => { - return { executed: true, foo: ctx.foo }; - }, - }); - - const result = await t.action(actionFn, {}); - expect(result).toEqual({ executed: true, foo: "bar" }); - - expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), - { result: { executed: true, foo: "bar" }, error: undefined } - ); - }); -}); diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index abaf3910..972e6bb0 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -563,66 +563,115 @@ describe("nested custom functions", () => { describe("finally callback", () => { test("finally callback is called with result and context", async () => { - let finallyCalled = false; - let finallyParams = null; - - const ctx = { foo: "bar" }; - const args = { test: "value" }; - const result = { success: true }; - - const handler = async (_ctx, _args) => result; - - const mod = { + const t = convexTest(schema, modules); + const finallyMock = vi.fn(); + + const withFinally = customQuery(query, { args: {}, - input: async () => ({ ctx: {}, args: {} }), - finally: (params) => { - finallyCalled = true; - finallyParams = params; + input: async () => ({ ctx: { foo: "bar" }, args: {} }), + finally: (ctx, params) => { + finallyMock(ctx, params); + } + }); + + const successFn = withFinally({ + args: {}, + handler: async (ctx) => { + return { success: true, foo: ctx.foo }; }, - }; - - let actualResult; - let actualError; - try { - actualResult = await handler(ctx, args); - } catch (e) { - actualError = e; - throw e; - } finally { - if (mod.finally) { - await mod.finally({ ctx, result: actualResult, error: actualError }); + }); + + await t.run(async (ctx) => { + const result = await (successFn as any)._handler(ctx, {}); + expect(result).toEqual({ success: true, foo: "bar" }); + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { result: { success: true, foo: "bar" } } + ); + }); + + finallyMock.mockClear(); + + const errorFn = withFinally({ + args: {}, + handler: async () => { + throw new Error("Test error"); + }, + }); + + await t.run(async (ctx) => { + try { + await (errorFn as any)._handler(ctx, {}); + fail("Should have thrown an error"); + } catch (e) { + expect(e.message).toContain("Test error"); } - } - - expect(finallyCalled).toBe(true); - expect(finallyParams).toEqual({ - ctx, - result, - error: undefined, + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { error: expect.objectContaining({ message: expect.stringContaining("Test error") }) } + ); }); - - finallyCalled = false; - finallyParams = null; - - const testError = new Error("Test error"); - const errorHandler = async () => { - throw testError; - }; - - try { - await errorHandler(); - } catch (e) { - } finally { - if (mod.finally) { - await mod.finally({ ctx, result: undefined, error: testError }); + }); + + test("finally callback with mutation", async () => { + const t = convexTest(schema, modules); + const finallyMock = vi.fn(); + + const withFinally = customMutation(mutation, { + args: {}, + input: async () => ({ ctx: { foo: "bar" }, args: {} }), + finally: (ctx, params) => { + finallyMock(ctx, params); } - } - - expect(finallyCalled).toBe(true); - expect(finallyParams).toEqual({ - ctx, - result: undefined, - error: testError, + }); + + const mutationFn = withFinally({ + args: {}, + handler: async (ctx) => { + return { updated: true, foo: ctx.foo }; + }, + }); + + await t.run(async (ctx) => { + const result = await (mutationFn as any)._handler(ctx, {}); + expect(result).toEqual({ updated: true, foo: "bar" }); + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { result: { updated: true, foo: "bar" } } + ); + }); + }); + + test("finally callback with action", async () => { + const t = convexTest(schema, modules); + const finallyMock = vi.fn(); + + const withFinally = customAction(action, { + args: {}, + input: async () => ({ ctx: { foo: "bar" }, args: {} }), + finally: (ctx, params) => { + finallyMock(ctx, params); + } + }); + + const actionFn = withFinally({ + args: {}, + handler: async (ctx) => { + return { executed: true, foo: ctx.foo }; + }, + }); + + await t.run(async (ctx) => { + const result = await (actionFn as any)._handler(ctx, {}); + expect(result).toEqual({ executed: true, foo: "bar" }); + + expect(finallyMock).toHaveBeenCalledWith( + expect.objectContaining({ foo: "bar" }), + { result: { executed: true, foo: "bar" } } + ); }); }); }); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 0524194e..0253d990 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -346,17 +346,17 @@ function customFnBuilder( const args = omit(allArgs, Object.keys(inputArgs)); const finalCtx = { ...ctx, ...added.ctx }; let result; - let error; try { result = await handler(finalCtx, { ...args, ...added.args }); + if (mod.finally) { + await mod.finally(finalCtx, { result }); + } return result; } catch (e) { - error = e; - throw e; - } finally { if (mod.finally) { - await mod.finally(finalCtx, { result, error }); + await mod.finally(finalCtx, { error: e }); } + throw e; } }, }); @@ -373,17 +373,17 @@ function customFnBuilder( const added = await inputMod(ctx, args); const finalCtx = { ...ctx, ...added.ctx }; let result; - let error; try { result = await handler(finalCtx, { ...args, ...added.args }); + if (mod.finally) { + await mod.finally(finalCtx, { result }); + } return result; } catch (e) { - error = e; - throw e; - } finally { if (mod.finally) { - await mod.finally(finalCtx, { result, error }); + await mod.finally(finalCtx, { error: e }); } + throw e; } }, }); From c4a058979e6de4eea71ba432dfec84aeb848d01c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 20:07:13 +0000 Subject: [PATCH 07/16] Fix error handling in finally callback tests Co-Authored-By: Ian Macartney --- packages/convex-helpers/server/customFunctions.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 972e6bb0..dac7c8ac 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -27,7 +27,7 @@ import { type Auth, } from "convex/server"; import { v } from "convex/values"; -import { afterEach, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { modules } from "./setup.test.js"; const schema = defineSchema({ @@ -601,11 +601,13 @@ describe("finally callback", () => { }); await t.run(async (ctx) => { + let error: Error | undefined; try { await (errorFn as any)._handler(ctx, {}); - fail("Should have thrown an error"); + expect.fail("Should have thrown an error"); } catch (e) { - expect(e.message).toContain("Test error"); + error = e as Error; + expect(error.message).toContain("Test error"); } expect(finallyMock).toHaveBeenCalledWith( From f45d7ecf430c09eaa60d1a46cbcd0611411834b0 Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Fri, 4 Apr 2025 13:19:04 -0700 Subject: [PATCH 08/16] fix lint --- packages/convex-helpers/server/customFunctions.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index dac7c8ac..3f4a71ad 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -601,12 +601,11 @@ describe("finally callback", () => { }); await t.run(async (ctx) => { - let error: Error | undefined; try { await (errorFn as any)._handler(ctx, {}); expect.fail("Should have thrown an error"); - } catch (e) { - error = e as Error; + } catch (e: unknown) { + const error = e as Error; expect(error.message).toContain("Test error"); } From 9effd84b79307016ae8c4601572498a769316038 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:36:23 +0000 Subject: [PATCH 09/16] Redesign finally callback API to be returned from input function Co-Authored-By: Ian Macartney --- .../server/customFunctions.test.ts | 37 ++++++++++-------- .../convex-helpers/server/customFunctions.ts | 38 ++++++++++++------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 3f4a71ad..9e1c6a13 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -568,10 +568,13 @@ describe("finally callback", () => { const withFinally = customQuery(query, { args: {}, - input: async () => ({ ctx: { foo: "bar" }, args: {} }), - finally: (ctx, params) => { - finallyMock(ctx, params); - } + input: async () => ({ + ctx: { foo: "bar" }, + args: {}, + finally: (params) => { + finallyMock(params); + } + }), }); const successFn = withFinally({ @@ -586,7 +589,6 @@ describe("finally callback", () => { expect(result).toEqual({ success: true, foo: "bar" }); expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), { result: { success: true, foo: "bar" } } ); }); @@ -610,7 +612,6 @@ describe("finally callback", () => { } expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), { error: expect.objectContaining({ message: expect.stringContaining("Test error") }) } ); }); @@ -622,10 +623,13 @@ describe("finally callback", () => { const withFinally = customMutation(mutation, { args: {}, - input: async () => ({ ctx: { foo: "bar" }, args: {} }), - finally: (ctx, params) => { - finallyMock(ctx, params); - } + input: async () => ({ + ctx: { foo: "bar" }, + args: {}, + finally: (params) => { + finallyMock(params); + } + }), }); const mutationFn = withFinally({ @@ -640,7 +644,6 @@ describe("finally callback", () => { expect(result).toEqual({ updated: true, foo: "bar" }); expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), { result: { updated: true, foo: "bar" } } ); }); @@ -652,10 +655,13 @@ describe("finally callback", () => { const withFinally = customAction(action, { args: {}, - input: async () => ({ ctx: { foo: "bar" }, args: {} }), - finally: (ctx, params) => { - finallyMock(ctx, params); - } + input: async () => ({ + ctx: { foo: "bar" }, + args: {}, + finally: (params) => { + finallyMock(params); + } + }), }); const actionFn = withFinally({ @@ -670,7 +676,6 @@ describe("finally callback", () => { expect(result).toEqual({ executed: true, foo: "bar" }); expect(finallyMock).toHaveBeenCalledWith( - expect.objectContaining({ foo: "bar" }), { result: { executed: true, foo: "bar" } } ); }); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 0253d990..284893b0 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -58,12 +58,22 @@ export type Mod< ctx: Ctx, args: ObjectType, ) => - | Promise<{ ctx: ModCtx; args: ModMadeArgs }> - | { ctx: ModCtx; args: ModMadeArgs }; - finally?: (ctx: Ctx & ModCtx, params: { - result?: unknown; - error?: unknown; - }) => void | Promise; + | Promise<{ + ctx: ModCtx; + args: ModMadeArgs; + finally?: (params: { + result?: unknown; + error?: unknown; + }) => void | Promise; + }> + | { + ctx: ModCtx; + args: ModMadeArgs; + finally?: (params: { + result?: unknown; + error?: unknown; + }) => void | Promise; + }; }; /** @@ -348,13 +358,13 @@ function customFnBuilder( let result; try { result = await handler(finalCtx, { ...args, ...added.args }); - if (mod.finally) { - await mod.finally(finalCtx, { result }); + if (added.finally) { + await added.finally({ result }); } return result; } catch (e) { - if (mod.finally) { - await mod.finally(finalCtx, { error: e }); + if (added.finally) { + await added.finally({ error: e }); } throw e; } @@ -375,13 +385,13 @@ function customFnBuilder( let result; try { result = await handler(finalCtx, { ...args, ...added.args }); - if (mod.finally) { - await mod.finally(finalCtx, { result }); + if (added.finally) { + await added.finally({ result }); } return result; } catch (e) { - if (mod.finally) { - await mod.finally(finalCtx, { error: e }); + if (added.finally) { + await added.finally({ error: e }); } throw e; } From a331963d5fe6c9349c40268dba6a6e6cddc3b4aa Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Mon, 28 Apr 2025 13:44:15 -0700 Subject: [PATCH 10/16] prettier --- .../server/customFunctions.test.ts | 80 ++++++++++--------- .../convex-helpers/server/customFunctions.ts | 17 ++-- 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 9e1c6a13..b93d9d66 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -565,43 +565,43 @@ describe("finally callback", () => { test("finally callback is called with result and context", async () => { const t = convexTest(schema, modules); const finallyMock = vi.fn(); - + const withFinally = customQuery(query, { args: {}, - input: async () => ({ - ctx: { foo: "bar" }, + input: async () => ({ + ctx: { foo: "bar" }, args: {}, finally: (params) => { finallyMock(params); - } + }, }), }); - + const successFn = withFinally({ args: {}, handler: async (ctx) => { return { success: true, foo: ctx.foo }; }, }); - + await t.run(async (ctx) => { const result = await (successFn as any)._handler(ctx, {}); expect(result).toEqual({ success: true, foo: "bar" }); - - expect(finallyMock).toHaveBeenCalledWith( - { result: { success: true, foo: "bar" } } - ); + + expect(finallyMock).toHaveBeenCalledWith({ + result: { success: true, foo: "bar" }, + }); }); - + finallyMock.mockClear(); - + const errorFn = withFinally({ args: {}, handler: async () => { throw new Error("Test error"); }, }); - + await t.run(async (ctx) => { try { await (errorFn as any)._handler(ctx, {}); @@ -610,74 +610,76 @@ describe("finally callback", () => { const error = e as Error; expect(error.message).toContain("Test error"); } - - expect(finallyMock).toHaveBeenCalledWith( - { error: expect.objectContaining({ message: expect.stringContaining("Test error") }) } - ); + + expect(finallyMock).toHaveBeenCalledWith({ + error: expect.objectContaining({ + message: expect.stringContaining("Test error"), + }), + }); }); }); - + test("finally callback with mutation", async () => { const t = convexTest(schema, modules); const finallyMock = vi.fn(); - + const withFinally = customMutation(mutation, { args: {}, - input: async () => ({ - ctx: { foo: "bar" }, + input: async () => ({ + ctx: { foo: "bar" }, args: {}, finally: (params) => { finallyMock(params); - } + }, }), }); - + const mutationFn = withFinally({ args: {}, handler: async (ctx) => { return { updated: true, foo: ctx.foo }; }, }); - + await t.run(async (ctx) => { const result = await (mutationFn as any)._handler(ctx, {}); expect(result).toEqual({ updated: true, foo: "bar" }); - - expect(finallyMock).toHaveBeenCalledWith( - { result: { updated: true, foo: "bar" } } - ); + + expect(finallyMock).toHaveBeenCalledWith({ + result: { updated: true, foo: "bar" }, + }); }); }); - + test("finally callback with action", async () => { const t = convexTest(schema, modules); const finallyMock = vi.fn(); - + const withFinally = customAction(action, { args: {}, - input: async () => ({ - ctx: { foo: "bar" }, + input: async () => ({ + ctx: { foo: "bar" }, args: {}, finally: (params) => { finallyMock(params); - } + }, }), }); - + const actionFn = withFinally({ args: {}, handler: async (ctx) => { return { executed: true, foo: ctx.foo }; }, }); - + await t.run(async (ctx) => { const result = await (actionFn as any)._handler(ctx, {}); expect(result).toEqual({ executed: true, foo: "bar" }); - - expect(finallyMock).toHaveBeenCalledWith( - { result: { executed: true, foo: "bar" } } - ); + + expect(finallyMock).toHaveBeenCalledWith({ + result: { executed: true, foo: "bar" }, + }); }); }); }); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 284893b0..41bca401 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -58,19 +58,19 @@ export type Mod< ctx: Ctx, args: ObjectType, ) => - | Promise<{ - ctx: ModCtx; + | Promise<{ + ctx: ModCtx; args: ModMadeArgs; - finally?: (params: { - result?: unknown; + finally?: (params: { + result?: unknown; error?: unknown; }) => void | Promise; }> - | { - ctx: ModCtx; + | { + ctx: ModCtx; args: ModMadeArgs; - finally?: (params: { - result?: unknown; + finally?: (params: { + result?: unknown; error?: unknown; }) => void | Promise; }; @@ -102,7 +102,6 @@ export const NoOp = { input() { return { args: {}, ctx: {} }; }, - }; /** From 074ca1d4acda00221be48ec166c8cf635c5919d1 Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Mon, 28 Apr 2025 13:44:56 -0700 Subject: [PATCH 11/16] npm 0.1.77-alpha.1 --- package-lock.json | 2 +- packages/convex-helpers/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 428d228a..7603944b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8049,7 +8049,7 @@ }, "packages/convex-helpers/dist": { "name": "convex-helpers", - "version": "0.1.77-alpha.0", + "version": "0.1.77-alpha.1", "license": "Apache-2.0", "bin": { "convex-helpers": "bin.cjs" diff --git a/packages/convex-helpers/package.json b/packages/convex-helpers/package.json index 1c7c2838..8ab2e134 100644 --- a/packages/convex-helpers/package.json +++ b/packages/convex-helpers/package.json @@ -1,6 +1,6 @@ { "name": "convex-helpers", - "version": "0.1.77-alpha.0", + "version": "0.1.77-alpha.1", "description": "A collection of useful code to complement the official convex package.", "type": "module", "bin": { From 33fea2d2bc66b8a5cd8d2c5d78c29a20a30bf646 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:51:51 +0000 Subject: [PATCH 12/16] Add documentation for finally callback feature Co-Authored-By: Ian Macartney --- packages/convex-helpers/README.md | 16 ++++++- .../convex-helpers/server/customFunctions.ts | 46 +++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/packages/convex-helpers/README.md b/packages/convex-helpers/README.md index fc7c2455..9f86e72c 100644 --- a/packages/convex-helpers/README.md +++ b/packages/convex-helpers/README.md @@ -37,6 +37,8 @@ define custom behavior, allowing you to: - Consume arguments from the client that are not passed to the action, such as taking in an authentication parameter like an API key or session ID. These arguments must be sent up by the client along with each request. +- Execute cleanup or logging logic after function execution using the `finally` + callback, which has access to the function's result or error. See the associated [Stack Post](https://stack.convex.dev/custom-functions) @@ -50,7 +52,19 @@ const myQueryBuilder = customQuery(query, { input: async (ctx, args) => { const apiUser = await getApiUser(args.apiToken); const db = wrapDatabaseReader({ apiUser }, ctx.db, rlsRules); - return { ctx: { db, apiUser }, args: {} }; + return { + ctx: { db, apiUser }, + args: {}, + finally: ({ result, error }) => { + // Optional callback that runs after the function executes + // Has access to resources created during input processing + if (error) { + console.error("Error in query:", error); + } else { + console.log("Query completed with result:", result); + } + } + }; }, }); diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 41bca401..71d456ce 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -46,6 +46,12 @@ import { omit, pick } from "../index.js"; * provided for the modified function. All returned ctx and args will show up * in the type signature for the modified function. * To remove something from `ctx`, you can return it as `undefined`. + * + * The `input` function can also return a `finally` callback that will be called + * after the function executes with either the result or error. This is useful for + * cleanup operations or logging that should happen regardless of whether the + * function succeeds or fails. The `finally` callback has access to resources + * created during input processing. */ export type Mod< Ctx extends Record, @@ -116,7 +122,17 @@ export const NoOp = { * const user = await getUserOrNull(ctx); * const session = await db.get(sessionId); * const db = wrapDatabaseReader({ user }, ctx.db, rlsRules); - * return { ctx: { db, user, session }, args: {} }; + * return { + * ctx: { db, user, session }, + * args: {}, + * finally: ({ result, error }) => { + * // Optional callback that runs after the function executes + * // Has access to resources created during input processing + * if (error) { + * console.error("Error in query:", error); + * } + * } + * }; * }, * }); * @@ -188,7 +204,17 @@ export function customQuery< * const user = await getUserOrNull(ctx); * const session = await db.get(sessionId); * const db = wrapDatabaseReader({ user }, ctx.db, rlsRules); - * return { ctx: { db, user, session }, args: {} }; + * return { + * ctx: { db, user, session }, + * args: {}, + * finally: ({ result, error }) => { + * // Optional callback that runs after the function executes + * // Has access to resources created during input processing + * if (error) { + * console.error("Error in mutation:", error); + * } + * } + * }; * }, * }); * @@ -267,7 +293,21 @@ export function customMutation< * throw new Error("Invalid secret key"); * } * const user = await ctx.runQuery(internal.users.getUser, {}); - * return { ctx: { user }, args: {} }; + * // Create resources that can be used in the finally callback + * const logger = createLogger(); + * return { + * ctx: { user }, + * args: {}, + * finally: ({ result, error }) => { + * // Optional callback that runs after the function executes + * // Has access to resources created during input processing + * if (error) { + * logger.error("Action failed:", error); + * } else { + * logger.info("Action completed successfully"); + * } + * } + * }; * }, * }); * From b20cfd190a868a3aa76cf8c0686fe72c520a498a Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Mon, 28 Apr 2025 14:51:07 -0700 Subject: [PATCH 13/16] Update packages/convex-helpers/server/customFunctions.test.ts --- packages/convex-helpers/server/customFunctions.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 25d8bfa5..55b85192 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -29,7 +29,15 @@ import { queryGeneric, } from "convex/server"; import { v } from "convex/values"; -import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { + afterEach, + assertType, + beforeEach, + describe, + expect, + test, + vi +} from "vitest"; import { modules } from "./setup.test.js"; const schema = defineSchema({ From c7525e7add6437dc77b5d00599af46ad71e5c01d Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Mon, 28 Apr 2025 16:24:04 -0700 Subject: [PATCH 14/16] prettier --- packages/convex-helpers/README.md | 8 ++++---- .../convex-helpers/server/customFunctions.test.ts | 2 +- packages/convex-helpers/server/customFunctions.ts | 14 +++++++------- src/index.css | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/convex-helpers/README.md b/packages/convex-helpers/README.md index 9f86e72c..5b4490f3 100644 --- a/packages/convex-helpers/README.md +++ b/packages/convex-helpers/README.md @@ -37,7 +37,7 @@ define custom behavior, allowing you to: - Consume arguments from the client that are not passed to the action, such as taking in an authentication parameter like an API key or session ID. These arguments must be sent up by the client along with each request. -- Execute cleanup or logging logic after function execution using the `finally` +- Execute cleanup or logging logic after function execution using the `finally` callback, which has access to the function's result or error. See the associated [Stack Post](https://stack.convex.dev/custom-functions) @@ -52,8 +52,8 @@ const myQueryBuilder = customQuery(query, { input: async (ctx, args) => { const apiUser = await getApiUser(args.apiToken); const db = wrapDatabaseReader({ apiUser }, ctx.db, rlsRules); - return { - ctx: { db, apiUser }, + return { + ctx: { db, apiUser }, args: {}, finally: ({ result, error }) => { // Optional callback that runs after the function executes @@ -63,7 +63,7 @@ const myQueryBuilder = customQuery(query, { } else { console.log("Query completed with result:", result); } - } + }, }; }, }); diff --git a/packages/convex-helpers/server/customFunctions.test.ts b/packages/convex-helpers/server/customFunctions.test.ts index 55b85192..9f1a9206 100644 --- a/packages/convex-helpers/server/customFunctions.test.ts +++ b/packages/convex-helpers/server/customFunctions.test.ts @@ -36,7 +36,7 @@ import { describe, expect, test, - vi + vi, } from "vitest"; import { modules } from "./setup.test.js"; diff --git a/packages/convex-helpers/server/customFunctions.ts b/packages/convex-helpers/server/customFunctions.ts index 0f6df018..9b6883b8 100644 --- a/packages/convex-helpers/server/customFunctions.ts +++ b/packages/convex-helpers/server/customFunctions.ts @@ -45,7 +45,7 @@ import { omit, pick } from "../index.js"; * provided for the modified function. All returned ctx and args will show up * in the type signature for the modified function. * To remove something from `ctx`, you can return it as `undefined`. - * + * * The `input` function can also return a `finally` callback that will be called * after the function executes with either the result or error. This is useful for * cleanup operations or logging that should happen regardless of whether the @@ -121,8 +121,8 @@ export const NoOp = { * const user = await getUserOrNull(ctx); * const session = await db.get(sessionId); * const db = wrapDatabaseReader({ user }, ctx.db, rlsRules); - * return { - * ctx: { db, user, session }, + * return { + * ctx: { db, user, session }, * args: {}, * finally: ({ result, error }) => { * // Optional callback that runs after the function executes @@ -203,8 +203,8 @@ export function customQuery< * const user = await getUserOrNull(ctx); * const session = await db.get(sessionId); * const db = wrapDatabaseReader({ user }, ctx.db, rlsRules); - * return { - * ctx: { db, user, session }, + * return { + * ctx: { db, user, session }, * args: {}, * finally: ({ result, error }) => { * // Optional callback that runs after the function executes @@ -294,8 +294,8 @@ export function customMutation< * const user = await ctx.runQuery(internal.users.getUser, {}); * // Create resources that can be used in the finally callback * const logger = createLogger(); - * return { - * ctx: { user }, + * return { + * ctx: { user }, * args: {}, * finally: ({ result, error }) => { * // Optional callback that runs after the function executes diff --git a/src/index.css b/src/index.css index 70b0aa2f..c0d37da9 100644 --- a/src/index.css +++ b/src/index.css @@ -7,8 +7,8 @@ } body { - font-family: - system-ui, "Segoe UI", Roboto, "Helvetica Neue", helvetica, sans-serif; + font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", helvetica, + sans-serif; } main { From 66a347c4d74478f6870cacf102ca69fac4b0568e Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Mon, 28 Apr 2025 16:33:51 -0700 Subject: [PATCH 15/16] prettier with new version --- src/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.css b/src/index.css index c0d37da9..70b0aa2f 100644 --- a/src/index.css +++ b/src/index.css @@ -7,8 +7,8 @@ } body { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", helvetica, - sans-serif; + font-family: + system-ui, "Segoe UI", Roboto, "Helvetica Neue", helvetica, sans-serif; } main { From 162cb25de3d405328fc3b244c69175abce08464d Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Tue, 29 Apr 2025 13:33:29 -0700 Subject: [PATCH 16/16] npm 0.1.81-alpha.0 --- package-lock.json | 2 +- packages/convex-helpers/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 896241ae..4a9f115e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8167,7 +8167,7 @@ }, "packages/convex-helpers/dist": { "name": "convex-helpers", - "version": "0.1.80", + "version": "0.1.81-alpha.0", "license": "Apache-2.0", "bin": { "convex-helpers": "bin.cjs" diff --git a/packages/convex-helpers/package.json b/packages/convex-helpers/package.json index c33a1eff..53f8155a 100644 --- a/packages/convex-helpers/package.json +++ b/packages/convex-helpers/package.json @@ -1,6 +1,6 @@ { "name": "convex-helpers", - "version": "0.1.80", + "version": "0.1.81-alpha.0", "description": "A collection of useful code to complement the official convex package.", "type": "module", "bin": {