···11+import { createParamDecorator, type ExecutionContext } from "@nestjs/common";
22+import { GqlExecutionContext } from "@nestjs/graphql";
33+44+type SafeParseResult<T> =
55+ | { success: true; data: T }
66+ | { success: false; error: unknown };
77+88+interface SafeParseable<T> {
99+ safeParse(data: unknown): SafeParseResult<T>;
1010+}
1111+1212+interface GqlParamDecoratorOptions<TSchema, TSuccess, TFallback> {
1313+ schema: SafeParseable<TSchema>;
1414+ source?: "request" | "context";
1515+ resolve?: (context: ExecutionContext) => unknown;
1616+ lens: (data: TSchema) => TSuccess;
1717+ fallback: TFallback | (() => never);
1818+}
1919+2020+/**
2121+ * Creates a NestJS GQL param decorator that validates the execution context
2222+ * with a schema (e.g. Zod) and extracts a value using a lens function.
2323+ *
2424+ * @param options.schema - A schema with a `safeParse` method (e.g. a Zod schema)
2525+ * @param options.source - What to parse: "request" (req object) or "context" (full GQL context). Defaults to "request".
2626+ * @param options.resolve - Custom function to extract raw data from the ExecutionContext. Overrides `source`.
2727+ * @param options.lens - Extracts the desired value from the parsed data
2828+ * @param options.fallback - Value to return on parse failure, or a function that throws
2929+ *
3030+ * @example
3131+ * ```typescript
3232+ * const CurrentRefreshTokenId = createGqlParamDecorator({
3333+ * schema: z.object({ jwtPayload: z.object({ refreshTokenId: z.string() }) }),
3434+ * lens: (data) => data.jwtPayload.refreshTokenId,
3535+ * fallback: undefined,
3636+ * });
3737+ *
3838+ * const CurrentUser = createGqlParamDecorator({
3939+ * schema: z.object({ user: z.object({ id: z.string(), email: z.string() }) }),
4040+ * source: "context",
4141+ * lens: (data) => data.user,
4242+ * fallback: () => raise("User not found in request context"),
4343+ * });
4444+ * ```
4545+ */
4646+export const createGqlParamDecorator = <TSchema, TSuccess, TFallback>({
4747+ schema,
4848+ source,
4949+ resolve = (ctx) => resolveDefault(ctx, source),
5050+ lens,
5151+ fallback,
5252+}: GqlParamDecoratorOptions<TSchema, TSuccess, TFallback>) =>
5353+ createParamDecorator(
5454+ (_data: unknown, context: ExecutionContext): TSuccess | TFallback => {
5555+ const result = schema.safeParse(resolve(context));
5656+5757+ return result.success
5858+ ? lens(result.data)
5959+ : typeof fallback === "function"
6060+ ? (fallback as () => never)()
6161+ : fallback;
6262+ },
6363+ );
6464+6565+const resolveDefault = (
6666+ context: ExecutionContext,
6767+ source?: "request" | "context",
6868+) => {
6969+ const ctx = GqlExecutionContext.create(context).getContext();
7070+7171+ return source === "context" ? ctx : ctx.req;
7272+};
+1
packages/system/src/base/index.ts
···11export * from "./base.entity";
22+export * from "./create-gql-param-decorator";
23export * from "./base.module";
34export * from "./clock.service";
45export * from "./connection.types";