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.
- package/dist/aggregate/index.d.ts +1 -1
- package/dist/auth/client/index.js +1 -1
- package/dist/auth/index.js +19 -21
- package/dist/auth/nextjs/index.d.ts +1 -1
- package/dist/auth/nextjs/index.js +4 -4
- package/dist/{auth-store-ssZDPa37.js → auth-store-BnGZxmnY.js} +4 -1
- package/dist/{backend-core-DqPydYyx.mjs → backend-core-KwJ5FZgj.mjs} +214 -169
- package/dist/{builder-DBgto1yn.js → builder-Dwy6D2QA.js} +261 -166
- package/dist/{caller-factory-NEfgD5E0.js → caller-factory-DHywSoGZ.js} +7 -5
- package/dist/cli.mjs +14 -7
- package/dist/crpc/index.js +1 -127
- package/dist/{middleware-Bg-PdtrI.js → middleware-qzHEHaDy.js} +1 -1
- package/dist/orm/index.d.ts +1 -1
- package/dist/orm/index.js +335 -106
- package/dist/plugins/index.js +1 -1
- package/dist/{procedure-caller-9m6NBxQu.js → procedure-caller-JB9kjYsy.js} +1 -1
- package/dist/{procedure-name-Cy1AxayA.d.ts → procedure-name-exVcmr_p.d.ts} +28 -3
- package/dist/{query-context-B47_3n97.js → query-context-C90vNlc9.js} +105 -22
- package/dist/query-options-C_eBSIXG.js +247 -0
- package/dist/ratelimit/index.d.ts +26 -6
- package/dist/ratelimit/index.js +427 -100
- package/dist/ratelimit/react/index.d.ts +14 -0
- package/dist/ratelimit/react/index.js +149 -16
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +48 -15
- package/dist/rsc/index.js +22 -33
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +3 -3
- package/dist/solid/index.js +19 -5
- package/dist/watcher.mjs +2 -2
- package/dist/{where-clause-compiler-C8UKgzTO.d.ts → where-clause-compiler-BRhLW1dp.d.ts} +24 -0
- package/package.json +1 -1
- package/skills/kitcn/SKILL.md +1 -0
- package/skills/kitcn/references/features/create-plugins.md +1 -1
- package/skills/kitcn/references/features/orm.md +11 -1
- package/skills/kitcn/references/features/ratelimit.md +105 -0
- package/skills/kitcn/references/setup/server.md +1 -1
- 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 (
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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
|
-
|
|
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()
|
|
1032
|
+
if ((request.headers.get("content-type") ?? "").includes("application/json")) return readJsonBody(request.clone());
|
|
955
1033
|
return null;
|
|
956
1034
|
};
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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
|
|
1042
|
+
message: "Invalid path params",
|
|
1002
1043
|
cause: error
|
|
1003
1044
|
});
|
|
1004
1045
|
throw error;
|
|
1005
1046
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
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
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
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
|
|
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
|
|
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
|
|
155
|
+
const canRefreshToken = !!opts.auth && !tokenResult.isFresh;
|
|
154
156
|
try {
|
|
155
157
|
return await fn(tokenResult.token);
|
|
156
158
|
} catch (error) {
|
|
157
|
-
if (!
|
|
158
|
-
if (
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
1483
|
-
|
|
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,
|