kitcn 0.16.1 → 0.17.1

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.
Files changed (38) hide show
  1. package/dist/aggregate/index.d.ts +1 -1
  2. package/dist/auth/client/index.js +1 -1
  3. package/dist/auth/index.js +19 -21
  4. package/dist/auth/nextjs/index.d.ts +1 -1
  5. package/dist/auth/nextjs/index.js +4 -4
  6. package/dist/{auth-store-ssZDPa37.js → auth-store-BnGZxmnY.js} +4 -1
  7. package/dist/{backend-core-DqPydYyx.mjs → backend-core-KwJ5FZgj.mjs} +214 -169
  8. package/dist/{builder-DBgto1yn.js → builder-Dwy6D2QA.js} +261 -166
  9. package/dist/{caller-factory-NEfgD5E0.js → caller-factory-DHywSoGZ.js} +7 -5
  10. package/dist/cli.mjs +14 -7
  11. package/dist/crpc/index.js +1 -127
  12. package/dist/{middleware-Bg-PdtrI.js → middleware-qzHEHaDy.js} +1 -1
  13. package/dist/orm/index.d.ts +1 -1
  14. package/dist/orm/index.js +335 -106
  15. package/dist/plugins/index.js +1 -1
  16. package/dist/{procedure-caller-9m6NBxQu.js → procedure-caller-JB9kjYsy.js} +1 -1
  17. package/dist/{procedure-name-Cy1AxayA.d.ts → procedure-name-exVcmr_p.d.ts} +28 -3
  18. package/dist/{query-context-B47_3n97.js → query-context-C90vNlc9.js} +105 -22
  19. package/dist/query-options-C_eBSIXG.js +247 -0
  20. package/dist/ratelimit/index.d.ts +26 -6
  21. package/dist/ratelimit/index.js +427 -100
  22. package/dist/ratelimit/react/index.d.ts +14 -0
  23. package/dist/ratelimit/react/index.js +149 -16
  24. package/dist/react/index.d.ts +3 -1
  25. package/dist/react/index.js +48 -15
  26. package/dist/rsc/index.js +22 -33
  27. package/dist/server/index.d.ts +1 -1
  28. package/dist/server/index.js +3 -3
  29. package/dist/solid/index.js +19 -5
  30. package/dist/watcher.mjs +2 -2
  31. package/dist/{where-clause-compiler-C8UKgzTO.d.ts → where-clause-compiler-BRhLW1dp.d.ts} +24 -0
  32. package/package.json +1 -1
  33. package/skills/kitcn/SKILL.md +1 -0
  34. package/skills/kitcn/references/features/create-plugins.md +1 -1
  35. package/skills/kitcn/references/features/orm.md +11 -1
  36. package/skills/kitcn/references/features/ratelimit.md +105 -0
  37. package/skills/kitcn/references/setup/server.md +1 -1
  38. package/dist/query-options-C96zLANM.js +0 -121
@@ -745,6 +745,20 @@ var CRPCError = class extends ConvexError {
745
745
  function isOrmNotFoundErrorLike(cause) {
746
746
  return cause instanceof Error && cause.name === "OrmNotFoundError";
747
747
  }
748
+ function isConvexErrorLike(cause) {
749
+ return cause instanceof Error && "data" in cause;
750
+ }
751
+ /**
752
+ * `ctx.runQuery`/`ctx.runMutation` never rethrow the original error object:
753
+ * Convex builds a fresh `ConvexError` from the message and re-attaches `.data`.
754
+ * A cRPC error therefore arrives as a plain `ConvexError` whose `data` still
755
+ * carries the original `code`/`message`.
756
+ */
757
+ function isCRPCErrorData(data) {
758
+ if (!data || typeof data !== "object" || Array.isArray(data)) return false;
759
+ const { code, message } = data;
760
+ return typeof code === "string" && code in CRPC_ERROR_CODES_BY_KEY && (message === void 0 || typeof message === "string");
761
+ }
748
762
  function isApiErrorLike(cause) {
749
763
  return cause instanceof Error && cause.name === "APIError" && typeof cause.statusCode === "number";
750
764
  }
@@ -781,35 +795,51 @@ function getApiErrorMessage(cause) {
781
795
  * Convert known framework/library errors into CRPCError.
782
796
  *
783
797
  * Intended for cRPC internals so callers don't need per-endpoint try/catch.
798
+ *
799
+ * Never assign `.stack` on a converted error. The Convex backend only forwards
800
+ * `data` for a thrown error after it reads `__frameData` off it, and
801
+ * `__frameData` is written as a side effect of the runtime's
802
+ * `Error.prepareStackTrace` hook, which V8 runs lazily on the *first* read of
803
+ * `.stack`. Assigning `.stack` satisfies later reads without ever running the
804
+ * hook, so the backend bails out of source mapping and the client receives a
805
+ * bare `Error` with the message redacted to `Server Error` and no `.data` —
806
+ * losing the cRPC `code`, `message`, and custom payload. The original stack
807
+ * stays reachable at `err.cause.stack`. See `error.vitest.ts`.
784
808
  */
785
809
  function toCRPCError(cause) {
786
810
  if (cause instanceof CRPCError) return cause;
787
811
  if (cause instanceof Error && cause.name === "CRPCError") return cause;
788
- if (isOrmNotFoundErrorLike(cause)) {
789
- const err = new CRPCError({
790
- code: "NOT_FOUND",
791
- message: cause.message,
792
- cause
812
+ if (isConvexErrorLike(cause) && isCRPCErrorData(cause.data)) {
813
+ const { code, message, ...data } = cause.data;
814
+ return new CRPCError({
815
+ code,
816
+ message,
817
+ cause,
818
+ data
793
819
  });
794
- if (cause.stack) err.stack = cause.stack;
795
- return err;
796
820
  }
821
+ if (isOrmNotFoundErrorLike(cause)) return new CRPCError({
822
+ code: "NOT_FOUND",
823
+ message: cause.message,
824
+ cause
825
+ });
797
826
  if (isApiErrorLike(cause)) {
798
827
  const status = cause.status;
799
828
  const statusCode = cause.statusCode;
800
- const err = new CRPCError({
829
+ return new CRPCError({
801
830
  code: typeof status === "string" && status in CRPC_ERROR_CODES_BY_KEY ? status : typeof statusCode === "number" ? mapHttpStatusCodeToCRPCCode(statusCode) : "INTERNAL_SERVER_ERROR",
802
831
  message: getApiErrorMessage(cause),
803
832
  cause
804
833
  });
805
- if (cause.stack) err.stack = cause.stack;
806
- return err;
807
834
  }
808
835
  return null;
809
836
  }
810
837
  /**
811
838
  * Wrap unknown error in CRPCError (from tRPC)
812
839
  *
840
+ * The original stack stays reachable at `err.cause.stack`. See the note on
841
+ * `toCRPCError` for why it must not be copied onto the returned error.
842
+ *
813
843
  * @example
814
844
  * ```typescript
815
845
  * try {
@@ -822,12 +852,10 @@ function toCRPCError(cause) {
822
852
  function getCRPCErrorFromUnknown(cause) {
823
853
  const handled = toCRPCError(cause);
824
854
  if (handled) return handled;
825
- const error = new CRPCError({
855
+ return new CRPCError({
826
856
  code: "INTERNAL_SERVER_ERROR",
827
857
  cause
828
858
  });
829
- if (cause instanceof Error && cause.stack) error.stack = cause.stack;
830
- return error;
831
859
  }
832
860
  /**
833
861
  * Get HTTP status code from CRPCError
@@ -845,6 +873,44 @@ function isCRPCError(error) {
845
873
  return error instanceof CRPCError;
846
874
  }
847
875
 
876
+ //#endregion
877
+ //#region src/server/middleware-runner.ts
878
+ /** Run middleware around a terminal procedure resolver. */
879
+ const executeMiddlewares = async (middlewares, ctx, meta, procedure, input, getRawInput, resolve, index = 0) => {
880
+ if (index >= middlewares.length) return {
881
+ marker: void 0,
882
+ ctx,
883
+ input,
884
+ output: await resolve({
885
+ ctx,
886
+ input
887
+ })
888
+ };
889
+ const middleware = middlewares[index];
890
+ let currentInput = input;
891
+ let innerResult;
892
+ const next = async (opts) => {
893
+ const nextCtx = opts?.ctx ?? ctx;
894
+ if (opts?.input !== void 0) currentInput = opts.input;
895
+ innerResult = await executeMiddlewares(middlewares, nextCtx, meta, procedure, currentInput, getRawInput, resolve, index + 1);
896
+ return innerResult;
897
+ };
898
+ const result = await middleware({
899
+ ctx,
900
+ meta,
901
+ procedure,
902
+ input,
903
+ getRawInput,
904
+ next
905
+ });
906
+ return {
907
+ marker: void 0,
908
+ ctx: result.ctx ?? ctx,
909
+ input: result.input ?? innerResult?.input ?? currentInput,
910
+ output: result.output ?? innerResult?.output
911
+ };
912
+ };
913
+
848
914
  //#endregion
849
915
  //#region src/server/http-builder.ts
850
916
  function extractPathParams(path) {
@@ -865,29 +931,41 @@ function matchPathParams(template, pathname) {
865
931
  return params;
866
932
  }
867
933
  function handleHttpError(error) {
868
- if (error instanceof CRPCError) {
869
- const status = {
870
- BAD_REQUEST: 400,
871
- UNAUTHORIZED: 401,
872
- FORBIDDEN: 403,
873
- NOT_FOUND: 404,
874
- METHOD_NOT_SUPPORTED: 405,
875
- CONFLICT: 409,
876
- UNPROCESSABLE_CONTENT: 422,
877
- TOO_MANY_REQUESTS: 429,
878
- INTERNAL_SERVER_ERROR: 500
879
- }[error.code] ?? 500;
880
- return Response.json({ error: {
881
- code: error.code,
882
- message: error.message
883
- } }, { status });
884
- }
934
+ const crpcError = toCRPCError(error);
935
+ if (crpcError) return Response.json({ error: {
936
+ code: crpcError.code,
937
+ message: crpcError.message
938
+ } }, { status: getHTTPStatusCodeFromError(crpcError) });
885
939
  console.error("Unhandled HTTP error:", error);
886
940
  return Response.json({ error: {
887
941
  code: "INTERNAL_SERVER_ERROR",
888
942
  message: "An unexpected error occurred"
889
943
  } }, { status: 500 });
890
944
  }
945
+ /** Read a JSON body, reporting parse failures as client errors. */
946
+ async function readJsonBody(request) {
947
+ try {
948
+ return await request.json();
949
+ } catch (cause) {
950
+ throw new CRPCError({
951
+ code: "BAD_REQUEST",
952
+ message: "Invalid JSON body",
953
+ cause
954
+ });
955
+ }
956
+ }
957
+ /** Read a form body, reporting parse failures as client errors. */
958
+ async function readFormDataBody(request) {
959
+ try {
960
+ return await request.formData();
961
+ } catch (cause) {
962
+ throw new CRPCError({
963
+ code: "BAD_REQUEST",
964
+ message: "Invalid form data",
965
+ cause
966
+ });
967
+ }
968
+ }
891
969
  function getBaseSchema(schema) {
892
970
  if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) return getBaseSchema(schema.unwrap());
893
971
  if (schema instanceof z.ZodDefault) return getBaseSchema(schema._def.innerType);
@@ -949,109 +1027,86 @@ function createProcedure(def, handler, _type) {
949
1027
  try {
950
1028
  const url = new URL(request.url);
951
1029
  const pathParams = c.req.param() ?? matchPathParams(def.route.path, url.pathname) ?? {};
952
- let ctx = def.functionConfig.createContext(convexCtx);
1030
+ const initialCtx = def.functionConfig.createContext(convexCtx);
953
1031
  const getRawInput = async () => {
954
- if ((request.headers.get("content-type") ?? "").includes("application/json")) return request.clone().json();
1032
+ if ((request.headers.get("content-type") ?? "").includes("application/json")) return readJsonBody(request.clone());
955
1033
  return null;
956
1034
  };
957
- let currentInput;
958
- for (const middleware of def.middlewares) {
959
- const result = await middleware({
960
- ctx,
961
- procedure: middlewareProcedure,
962
- input: currentInput,
963
- getRawInput,
964
- next: async (opts) => {
965
- if (opts?.ctx) ctx = {
966
- ...ctx,
967
- ...opts.ctx
968
- };
969
- if (opts?.input !== void 0) currentInput = opts.input;
970
- return {
971
- ctx,
972
- marker: void 0
973
- };
974
- },
975
- meta: def.meta
976
- });
977
- if (result?.ctx) ctx = {
978
- ...ctx,
979
- ...result.ctx
980
- };
981
- }
982
- let parsedParams;
983
- if (def.paramsSchema) try {
984
- parsedParams = def.paramsSchema.parse(pathParams);
985
- } catch (error) {
986
- if (error instanceof z.ZodError) throw new CRPCError({
987
- code: "BAD_REQUEST",
988
- message: "Invalid path params",
989
- cause: error
990
- });
991
- throw error;
992
- }
993
- let parsedQuery;
994
- if (def.querySchema) {
995
- const queryParams = parseQueryParams(url, def.querySchema);
996
- try {
997
- parsedQuery = def.querySchema.parse(queryParams);
1035
+ return (await executeMiddlewares(def.middlewares, initialCtx, def.meta, middlewareProcedure, void 0, getRawInput, async ({ ctx }) => {
1036
+ let parsedParams;
1037
+ if (def.paramsSchema) try {
1038
+ parsedParams = def.paramsSchema.parse(pathParams);
998
1039
  } catch (error) {
999
1040
  if (error instanceof z.ZodError) throw new CRPCError({
1000
1041
  code: "BAD_REQUEST",
1001
- message: "Invalid query params",
1042
+ message: "Invalid path params",
1002
1043
  cause: error
1003
1044
  });
1004
1045
  throw error;
1005
1046
  }
1006
- }
1007
- let parsedInput;
1008
- if (def.inputSchema && request.method !== "GET") {
1009
- const contentType = request.headers.get("content-type") ?? "";
1010
- let body;
1011
- if (contentType.includes("application/json")) body = await request.json();
1012
- else if (contentType.includes("application/x-www-form-urlencoded")) {
1013
- const formData = await request.formData();
1014
- body = Object.fromEntries(formData.entries());
1015
- } else body = await request.json().catch(() => ({}));
1016
- try {
1017
- parsedInput = def.inputSchema.parse(def.functionConfig.transformer.input.deserialize(body));
1018
- } catch (error) {
1019
- if (error instanceof z.ZodError) throw new CRPCError({
1020
- code: "BAD_REQUEST",
1021
- message: "Invalid input",
1022
- cause: error
1023
- });
1024
- throw error;
1047
+ let parsedQuery;
1048
+ if (def.querySchema) {
1049
+ const queryParams = parseQueryParams(url, def.querySchema);
1050
+ try {
1051
+ parsedQuery = def.querySchema.parse(queryParams);
1052
+ } catch (error) {
1053
+ if (error instanceof z.ZodError) throw new CRPCError({
1054
+ code: "BAD_REQUEST",
1055
+ message: "Invalid query params",
1056
+ cause: error
1057
+ });
1058
+ throw error;
1059
+ }
1025
1060
  }
1026
- }
1027
- let parsedForm;
1028
- if (def.formSchema && request.method !== "GET") {
1029
- const formData = await request.formData();
1030
- const formObj = {};
1031
- for (const [key, value] of formData.entries()) formObj[key] = value;
1032
- try {
1033
- parsedForm = def.formSchema.parse(formObj);
1034
- } catch (error) {
1035
- if (error instanceof z.ZodError) throw new CRPCError({
1036
- code: "BAD_REQUEST",
1037
- message: "Invalid form data",
1038
- cause: error
1039
- });
1040
- throw error;
1061
+ let parsedInput;
1062
+ if (def.inputSchema && request.method !== "GET") {
1063
+ const contentType = request.headers.get("content-type") ?? "";
1064
+ let body;
1065
+ if (contentType.includes("application/json")) body = await readJsonBody(request);
1066
+ else if (contentType.includes("application/x-www-form-urlencoded")) {
1067
+ const formData = await readFormDataBody(request);
1068
+ body = Object.fromEntries(formData.entries());
1069
+ } else body = await request.json().catch(() => ({}));
1070
+ try {
1071
+ parsedInput = def.inputSchema.parse(def.functionConfig.transformer.input.deserialize(body));
1072
+ } catch (error) {
1073
+ if (error instanceof z.ZodError) throw new CRPCError({
1074
+ code: "BAD_REQUEST",
1075
+ message: "Invalid input",
1076
+ cause: error
1077
+ });
1078
+ throw error;
1079
+ }
1041
1080
  }
1042
- }
1043
- const handlerOpts = {
1044
- ctx,
1045
- c
1046
- };
1047
- if (parsedInput !== void 0) handlerOpts.input = parsedInput;
1048
- if (parsedParams !== void 0) handlerOpts.params = parsedParams;
1049
- if (parsedQuery !== void 0) handlerOpts.searchParams = parsedQuery;
1050
- if (parsedForm !== void 0) handlerOpts.form = parsedForm;
1051
- const result = await handler(handlerOpts);
1052
- if (result instanceof Response) return result;
1053
- const output = def.outputSchema ? def.outputSchema.parse(result) : result;
1054
- return c.json(def.functionConfig.transformer.output.serialize(output));
1081
+ let parsedForm;
1082
+ if (def.formSchema && request.method !== "GET") {
1083
+ const formData = await readFormDataBody(request);
1084
+ const formObj = {};
1085
+ for (const [key, value] of formData.entries()) formObj[key] = value;
1086
+ try {
1087
+ parsedForm = def.formSchema.parse(formObj);
1088
+ } catch (error) {
1089
+ if (error instanceof z.ZodError) throw new CRPCError({
1090
+ code: "BAD_REQUEST",
1091
+ message: "Invalid form data",
1092
+ cause: error
1093
+ });
1094
+ throw error;
1095
+ }
1096
+ }
1097
+ const handlerOpts = {
1098
+ ctx,
1099
+ c
1100
+ };
1101
+ if (parsedInput !== void 0) handlerOpts.input = parsedInput;
1102
+ if (parsedParams !== void 0) handlerOpts.params = parsedParams;
1103
+ if (parsedQuery !== void 0) handlerOpts.searchParams = parsedQuery;
1104
+ if (parsedForm !== void 0) handlerOpts.form = parsedForm;
1105
+ const result = await handler(handlerOpts);
1106
+ if (result instanceof Response) return result;
1107
+ const output = def.outputSchema ? def.outputSchema.parse(result) : result;
1108
+ return c.json(def.functionConfig.transformer.output.serialize(output));
1109
+ })).output;
1055
1110
  } catch (error) {
1056
1111
  return handleHttpError(error);
1057
1112
  }
@@ -1541,34 +1596,6 @@ const resolveProcedureInfo = (type, procedureName, procedureFn) => {
1541
1596
  name: (procedureFn && typeof procedureFn === "object" && typeof procedureFn[FUNCTION_NAME_SYMBOL] === "string" ? procedureFn[FUNCTION_NAME_SYMBOL] : void 0) ?? procedureName
1542
1597
  };
1543
1598
  };
1544
- /** Execute middleware chain recursively with input access */
1545
- async function executeMiddlewares(middlewares, ctx, meta, procedure, input, getRawInput, index = 0) {
1546
- if (index >= middlewares.length) return {
1547
- marker: void 0,
1548
- ctx,
1549
- input
1550
- };
1551
- const middleware = middlewares[index];
1552
- let currentInput = input;
1553
- const next = async (opts) => {
1554
- const nextCtx = opts?.ctx ?? ctx;
1555
- const nextInput = opts?.input ?? currentInput;
1556
- if (opts?.input !== void 0) currentInput = opts.input;
1557
- return await executeMiddlewares(middlewares, nextCtx, meta, procedure, nextInput, getRawInput, index + 1);
1558
- };
1559
- return {
1560
- marker: void 0,
1561
- ctx: (await middleware({
1562
- ctx,
1563
- meta,
1564
- procedure,
1565
- input,
1566
- getRawInput,
1567
- next
1568
- })).ctx ?? ctx,
1569
- input: currentInput
1570
- };
1571
- }
1572
1599
  const isPlainObject = (value) => !!value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
1573
1600
  const toConvexSafeValue = (value) => {
1574
1601
  if (value instanceof Date) return value.getTime();
@@ -1686,22 +1713,92 @@ const replaceUnencodableInputTypes = (schema) => {
1686
1713
  if (schema instanceof z.ZodDefault) return replaceUnencodableInputTypes(schema.removeDefault());
1687
1714
  return schema;
1688
1715
  };
1716
+ /**
1717
+ * Rebuild a shape from the Convex validator it produces.
1718
+ *
1719
+ * The result is structurally identical (so the generated Convex validator is
1720
+ * unchanged) but carries no checks, transforms, or defaults. cRPC's own
1721
+ * `.input()` parse stays the single authoritative one, so refinements and
1722
+ * transforms never run twice.
1723
+ */
1724
+ const toWireShape = (shape) => {
1725
+ try {
1726
+ return convexToZodFields(zodToConvexFields(shape));
1727
+ } catch {
1728
+ return shape;
1729
+ }
1730
+ };
1689
1731
  const resolveConvexArgsShape = (inputShape) => {
1690
1732
  if (!inputShape) return;
1691
1733
  const rawSchema = z.object(inputShape);
1692
1734
  try {
1693
1735
  zodToConvex(rawSchema);
1694
- return inputShape;
1736
+ return toWireShape(inputShape);
1695
1737
  } catch {
1696
1738
  const compatibleSchema = replaceUnencodableInputTypes(rawSchema);
1697
1739
  try {
1698
1740
  zodToConvex(compatibleSchema);
1699
- return compatibleSchema.shape;
1741
+ return toWireShape(compatibleSchema.shape);
1700
1742
  } catch {
1701
1743
  return Object.fromEntries(Object.keys(inputShape).map((key) => [key, z.any()]));
1702
1744
  }
1703
1745
  }
1704
1746
  };
1747
+ /**
1748
+ * Strip the field validators for keys a later `.input()` redeclares.
1749
+ *
1750
+ * The keys stay on the schema so its object-level checks still see them - and
1751
+ * read the value their owner parsed - while only the last declaration validates
1752
+ * them. `safeExtend` is the one zod object operation that rewrites keys on a
1753
+ * schema carrying object-level checks; `omit` and `extend` both refuse.
1754
+ */
1755
+ const relaxSupersededKeys = (schema, superseded) => {
1756
+ if (superseded.length === 0) return schema;
1757
+ try {
1758
+ return schema.safeExtend(Object.fromEntries(superseded.map((key) => [key, z.any()])));
1759
+ } catch {
1760
+ return schema;
1761
+ }
1762
+ };
1763
+ /**
1764
+ * Parse the wire payload through every `.input()` schema.
1765
+ *
1766
+ * Each schema parses only the keys it owns - the last `.input()` to declare a
1767
+ * key wins, matching the merged shape the Convex arg validator is built from -
1768
+ * so object-level checks run against the shape they were written for without an
1769
+ * earlier, superseded declaration vetoing the payload.
1770
+ */
1771
+ const parseInput = (inputSchemas, value) => {
1772
+ try {
1773
+ if (inputSchemas.length === 1) return inputSchemas[0].parse(value);
1774
+ const ownerOf = /* @__PURE__ */ new Map();
1775
+ inputSchemas.forEach((schema, index) => {
1776
+ for (const key of Object.keys(schema.shape)) ownerOf.set(key, index);
1777
+ });
1778
+ const source = isPlainObject(value) ? value : {};
1779
+ const parsed = {};
1780
+ for (let index = inputSchemas.length - 1; index >= 0; index -= 1) {
1781
+ const schema = inputSchemas[index];
1782
+ const keys = Object.keys(schema.shape);
1783
+ const superseded = new Set(keys.filter((key) => ownerOf.get(key) !== index));
1784
+ const scoped = relaxSupersededKeys(schema, [...superseded]);
1785
+ const narrowed = {};
1786
+ for (const key of keys) {
1787
+ if (superseded.has(key)) {
1788
+ if (key in parsed) narrowed[key] = parsed[key];
1789
+ continue;
1790
+ }
1791
+ if (Object.hasOwn(source, key)) narrowed[key] = source[key];
1792
+ }
1793
+ const result = scoped.parse(narrowed);
1794
+ for (const key of keys) if (ownerOf.get(key) === index && key in result) parsed[key] = result[key];
1795
+ }
1796
+ return parsed;
1797
+ } catch (cause) {
1798
+ if (cause instanceof z.ZodError) throw new ConvexError({ ZodError: JSON.parse(JSON.stringify(cause.issues, null, 2)) });
1799
+ throw cause;
1800
+ }
1801
+ };
1705
1802
  const resolveConvexReturnsSchema = (schema) => {
1706
1803
  if (!schema) return;
1707
1804
  try {
@@ -1744,7 +1841,7 @@ var ProcedureBuilder = class {
1744
1841
  _input(schema) {
1745
1842
  return {
1746
1843
  ...this._def,
1747
- inputSchemas: [...this._def.inputSchemas, schema.shape]
1844
+ inputSchemas: [...this._def.inputSchemas, schema]
1748
1845
  };
1749
1846
  }
1750
1847
  /** Define output schema - to be overridden by subclasses */
@@ -1771,17 +1868,15 @@ var ProcedureBuilder = class {
1771
1868
  procedureName: value
1772
1869
  };
1773
1870
  }
1774
- /** Merge all input schemas into one */
1871
+ /** Merge every `.input()` shape - used to build the Convex arg validator */
1775
1872
  _getMergedInput() {
1776
1873
  const { inputSchemas } = this._def;
1777
1874
  if (inputSchemas.length === 0) return;
1778
- return Object.assign({}, ...inputSchemas);
1875
+ return Object.assign({}, ...inputSchemas.map((schema) => schema.shape));
1779
1876
  }
1780
1877
  _createFunction(handler, baseFunction, customFn, fnType) {
1781
- const { middlewares, outputSchema, meta, procedureName, functionConfig, isInternal } = this._def;
1782
- const mergedInput = this._getMergedInput();
1783
- const inputSchema = mergedInput ? z.object(mergedInput) : void 0;
1784
- const convexArgs = resolveConvexArgsShape(mergedInput);
1878
+ const { middlewares, inputSchemas, outputSchema, meta, procedureName, functionConfig, isInternal } = this._def;
1879
+ const convexArgs = resolveConvexArgsShape(this._getMergedInput());
1785
1880
  const customFunction = customFn(baseFunction, customCtx(async (_ctx) => withConvexSafeRunners(await functionConfig.createContext(_ctx))));
1786
1881
  const returnsSchema = resolveConvexReturnsSchema(outputSchema);
1787
1882
  const typedReturnsSchema = returnsSchema;
@@ -1794,16 +1889,16 @@ var ProcedureBuilder = class {
1794
1889
  ...typedReturnsSchema ? { returns: typedReturnsSchema } : {},
1795
1890
  handler: async (ctx, rawInput) => {
1796
1891
  const decodedInput = functionConfig.transformer.input.deserialize(rawInput);
1797
- const parsedInput = inputSchema ? inputSchema.parse(decodedInput) : decodedInput;
1892
+ const parsedInput = inputSchemas.length > 0 ? parseInput(inputSchemas, decodedInput) : decodedInput;
1798
1893
  const getRawInput = async () => parsedInput;
1799
1894
  try {
1800
- const result = await executeMiddlewares(middlewares, ctx, meta, resolveProcedureInfo(fnType, resolvedProcedureName, fn), parsedInput, getRawInput);
1801
- const handlerInput = result.input === parsedInput ? parsedInput : functionConfig.transformer.input.deserialize(result.input ?? parsedInput);
1802
- const output = await handler({
1803
- ctx: result.ctx,
1804
- input: handlerInput
1895
+ const result = await executeMiddlewares(middlewares, ctx, meta, resolveProcedureInfo(fnType, resolvedProcedureName, fn), parsedInput, getRawInput, async ({ ctx: resolvedCtx, input: resolvedInput }) => {
1896
+ return await handler({
1897
+ ctx: resolvedCtx,
1898
+ input: resolvedInput === parsedInput ? parsedInput : functionConfig.transformer.input.deserialize(resolvedInput ?? parsedInput)
1899
+ });
1805
1900
  });
1806
- const validatedOutput = shouldValidateOutputWithZod ? outputSchema.parse(output) : output;
1901
+ const validatedOutput = shouldValidateOutputWithZod ? outputSchema.parse(result.output) : result.output;
1807
1902
  return functionConfig.transformer.output.serialize(validatedOutput);
1808
1903
  } catch (cause) {
1809
1904
  const err = toCRPCError(cause);
@@ -1868,7 +1963,7 @@ var QueryProcedureBuilder = class QueryProcedureBuilder extends ProcedureBuilder
1868
1963
  });
1869
1964
  return new QueryProcedureBuilder({
1870
1965
  ...this._def,
1871
- inputSchemas: [...this._def.inputSchemas, paginationSchemaWithDefault.shape],
1966
+ inputSchemas: [...this._def.inputSchemas, paginationSchemaWithDefault],
1872
1967
  outputSchema,
1873
1968
  meta: {
1874
1969
  ...this._def.meta,
@@ -1,3 +1,4 @@
1
+ import { n as defaultIsUnauthorized } from "./error-Bvo7YEhk.js";
1
2
  import { i as getFunctionType, n as getFuncRef, t as buildMetaIndex } from "./meta-utils-D9K4fICl.js";
2
3
  import { s as getTransformer } from "./transformer-C6pGVHqx.js";
3
4
  import { fetchAction, fetchMutation, fetchQuery } from "convex/nextjs";
@@ -147,15 +148,16 @@ function createCallerFactory(opts) {
147
148
  const siteUrl = parseConvexSiteUrl(opts.convexSiteUrl);
148
149
  const convexUrl = getConvexUrl(siteUrl, opts.convexUrl);
149
150
  const getToken = opts.auth?.getToken ?? noAuthGetToken;
150
- const isUnauthorized = opts.auth?.isUnauthorized;
151
+ const isRetryableAuthError = opts.auth ? opts.auth.isUnauthorized ?? defaultIsUnauthorized : void 0;
152
+ const shouldReturnNullOnUnauthorized = opts.auth?.isUnauthorized;
151
153
  const crpcMeta = buildMetaIndex(opts.api);
152
154
  const callWithTokenAndRetry = async (fn, tokenResult, headers) => {
153
- const shouldRetryWithFreshToken = !!opts.auth && !tokenResult.isFresh;
155
+ const canRefreshToken = !!opts.auth && !tokenResult.isFresh;
154
156
  try {
155
157
  return await fn(tokenResult.token);
156
158
  } catch (error) {
157
- if (!shouldRetryWithFreshToken) {
158
- if (isUnauthorized?.(error)) return null;
159
+ if (!(canRefreshToken && isRetryableAuthError?.(error))) {
160
+ if (shouldReturnNullOnUnauthorized?.(error)) return null;
159
161
  throw error;
160
162
  }
161
163
  const newToken = await getToken(siteUrl, headers, {
@@ -165,7 +167,7 @@ function createCallerFactory(opts) {
165
167
  try {
166
168
  return await fn(newToken.token);
167
169
  } catch (retryError) {
168
- if (isUnauthorized?.(retryError)) return null;
170
+ if (shouldReturnNullOnUnauthorized?.(retryError)) return null;
169
171
  throw retryError;
170
172
  }
171
173
  }
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as promptForScaffoldTemplateSelection, A as resolveCodegenTrimSegments, At as highlighter, B as runConfiguredCodegen, C as isEntryPoint, Ct as formatDependencyInstallCommand, D as parseInitCommandArgs, E as parseBackendRunJson, Et as stripConvexCommandNoise, F as resolveRunDeps, G as runMigrationFlow, H as runDevSchemaBackfillIfNeeded, I as runAfterScaffoldScript, J as withWorkingDirectory, K as trackProcess, L as runAggregateBackfillFlow, M as resolveDocTopic, N as resolveInitProjectDir, O as readPackageVersions, P as resolveMigrationConfig, Q as promptForPluginSelection, R as runAggregatePruneFlow, S as isConvexDevPreRunConflictFlag, St as detectPackageManager, T as parseArgs, Tt as serializeEnvValue, U as runInitCommandFlow, V as runConvexInitIfNeeded, W as runMigrationCreate, X as collectPluginScaffoldTemplates, Y as createSpinner, Z as filterScaffoldTemplatePathMap, _ as formatInfoOutput, _t as applyPlanningDependencyInstall, a as cleanup, at as getPluginCatalogEntry, b as getDevAggregateBackfillStatePath, bt as resolveSupportedDependencyWarnings, c as createCommandEnv, ct as buildPluginInstallPlan, d as extractBackfillCliOptions, dt as collectInstalledPluginKeys, et as resolveAddTemplateDefaults, f as extractConcaveRunTargetArgs, ft as getPluginLockfilePath, g as formatDocsOutput, gt as applyDependencyHintsInstall, h as extractResetCliOptions, ht as resolveSchemaInstalledPlugins, i as buildInitializationPlan, it as resolveTemplatesByIdOrThrow, j as resolveConfiguredBackend, k as resolveBackfillConfig, kt as logger, l as ensureConvexGitignoreEntry, lt as resolvePluginScaffoldRoots, m as extractMigrationDownOptions, mt as readPluginLockfile, n as applyPluginInstallPlanFiles, nt as resolvePresetScaffoldTemplates, o as createBackendAdapter, ot as getSupportedPluginKeys, p as extractMigrationCliOptions, pt as getSchemaFilePath, q as withLocalCodegenEnv, r as assertNoRemovedDevPreRunFlag, rt as resolveTemplateSelectionSource, s as createBackendCommandEnv, st as isSupportedPluginKey, t as applyDependencyInstallPlan, tt as resolvePluginPreset, u as extractBackendRunTargetArgs, ut as assertSchemaFileExists, v as getAggregateBackfillDeploymentKey, vt as applyPluginDependencyInstall, w as isInitialized, wt as resolveAuthEnvState, x as hasRemoteConvexDeploymentEnv, xt as resolveProjectScaffoldContext, y as getConvexDeploymentCommandEnv, yt as inspectPluginDependencyInstall, z as runBackendFunction } from "./backend-core-DqPydYyx.mjs";
2
+ import { $ as promptForScaffoldTemplateSelection, A as resolveCodegenTrimSegments, B as runConfiguredCodegen, C as isEntryPoint, Ct as formatDependencyInstallCommand, D as parseInitCommandArgs, E as parseBackendRunJson, Et as stripConvexCommandNoise, F as resolveRunDeps, G as runMigrationFlow, H as runDevSchemaBackfillIfNeeded, I as runAfterScaffoldScript, J as withWorkingDirectory, K as trackProcess, L as runAggregateBackfillFlow, M as resolveDocTopic, N as resolveInitProjectDir, O as readPackageVersions, P as resolveMigrationConfig, Q as promptForPluginSelection, R as runAggregatePruneFlow, S as isConvexDevPreRunConflictFlag, St as detectPackageManager, T as parseArgs, Tt as serializeEnvValue, U as runInitCommandFlow, V as runConvexInitIfNeeded, W as runMigrationCreate, X as collectPluginScaffoldTemplates, Y as createSpinner, Z as filterScaffoldTemplatePathMap, _ as formatInfoOutput, _t as applyPlanningDependencyInstall, a as cleanup, at as getPluginCatalogEntry, b as getDevAggregateBackfillStatePath, bt as resolveSupportedDependencyWarnings, c as createCommandEnv, ct as buildPluginInstallPlan, d as extractBackfillCliOptions, dt as collectInstalledPluginKeys, et as resolveAddTemplateDefaults, f as extractConcaveRunTargetArgs, ft as getPluginLockfilePath, g as formatDocsOutput, gt as applyDependencyHintsInstall, h as extractResetCliOptions, ht as resolveSchemaInstalledPlugins, i as buildInitializationPlan, it as resolveTemplatesByIdOrThrow, j as resolveConfiguredBackend, jt as highlighter, k as resolveBackfillConfig, kt as logger, l as ensureConvexGitignoreEntry, lt as resolvePluginScaffoldRoots, m as extractMigrationDownOptions, mt as readPluginLockfile, n as applyPluginInstallPlanFiles, nt as resolvePresetScaffoldTemplates, o as createBackendAdapter, ot as getSupportedPluginKeys, p as extractMigrationCliOptions, pt as getSchemaFilePath, q as withLocalCodegenEnv, r as assertNoRemovedDevPreRunFlag, rt as resolveTemplateSelectionSource, s as createBackendCommandEnv, st as isSupportedPluginKey, t as applyDependencyInstallPlan, tt as resolvePluginPreset, u as extractBackendRunTargetArgs, ut as assertSchemaFileExists, v as getAggregateBackfillDeploymentKey, vt as applyPluginDependencyInstall, w as isInitialized, wt as resolveAuthEnvState, x as hasRemoteConvexDeploymentEnv, xt as resolveProjectScaffoldContext, y as getConvexDeploymentCommandEnv, yt as inspectPluginDependencyInstall, z as runBackendFunction } from "./backend-core-KwJ5FZgj.mjs";
3
3
  import fs, { existsSync, readFileSync } from "node:fs";
4
4
  import path, { delimiter, dirname, join, relative, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -1471,16 +1471,22 @@ const handleAddCommand = async (argv, deps = {}) => {
1471
1471
  created: applyResult.created,
1472
1472
  manualActions: applyResult.manualActions,
1473
1473
  updated: applyResult.updated,
1474
- skipped: applyResult.skipped
1474
+ skipped: applyResult.skipped,
1475
+ refused: applyResult.refused,
1476
+ complete: applyResult.refused.length === 0
1475
1477
  };
1478
+ const resultLine = `${selectedPlugin} scaffold results: ${applyResult.created.length} created, ${applyResult.updated.length} updated, ${applyResult.skipped.length} skipped, ${applyResult.refused.length} refused.`;
1476
1479
  if (addArgs.json) console.info(JSON.stringify(payload));
1477
1480
  else {
1478
- logger.success(`✔ ${selectedPlugin} scaffold results: ${applyResult.created.length} created, ${applyResult.updated.length} updated, ${applyResult.skipped.length} skipped.`);
1481
+ if (applyResult.refused.length > 0) logger.error(`✖ ${resultLine}`);
1482
+ else logger.success(`✔ ${resultLine}`);
1479
1483
  if (applyResult.created.length > 0) logger.write(`Created files:\n${applyResult.created.map((file) => ` - ${file}`).join("\n")}`);
1480
1484
  if (applyResult.updated.length > 0) logger.write(`Updated files:\n${applyResult.updated.map((file) => ` - ${file}`).join("\n")}`);
1481
- if (applyResult.skipped.length > 0) {
1482
- logger.write(`Skipped files:\n${applyResult.skipped.map((file) => ` - ${file}`).join("\n")}`);
1483
- if (!addArgs.schema && !addArgs.overwrite) logger.info("Re-run with --overwrite to replace changed files.");
1485
+ if (applyResult.skipped.length > 0) logger.write(`Skipped files (already up to date):\n${applyResult.skipped.map((file) => ` - ${file}`).join("\n")}`);
1486
+ if (applyResult.refused.length > 0) {
1487
+ logger.write(`Refused files (your changes were kept):\n${applyResult.refused.map((file) => ` - ${file}`).join("\n")}`);
1488
+ logger.info("Re-run with --overwrite to replace these files, or merge the changes yourself.");
1489
+ logger.error(`${selectedPlugin} is only partially installed.`);
1484
1490
  }
1485
1491
  if (applyResult.manualActions.length > 0) {
1486
1492
  logger.info("Manual actions:");
@@ -1551,7 +1557,7 @@ const handleAddCommand = async (argv, deps = {}) => {
1551
1557
  });
1552
1558
  if (hookExitCode !== 0) return hookExitCode;
1553
1559
  }
1554
- return 0;
1560
+ return applyResult.refused.length > 0 ? 1 : 0;
1555
1561
  };
1556
1562
 
1557
1563
  //#endregion
@@ -2240,6 +2246,7 @@ const handleInitCommand = async (argv, deps = {}) => {
2240
2246
  created: result.created,
2241
2247
  updated: result.updated,
2242
2248
  skipped: result.skipped,
2249
+ refused: result.refused,
2243
2250
  usedShadcn: result.usedShadcn,
2244
2251
  template: result.template,
2245
2252
  codegen: result.codegen,