@supacloud/compiler 0.33.0 → 0.35.0

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 (3) hide show
  1. package/dist/cli.js +52 -17
  2. package/dist/index.js +52 -17
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2380,6 +2380,15 @@ function renderClient(graph, options) {
2380
2380
  const command = route.command === undefined ? undefined : commandByClassName.get(route.command);
2381
2381
  const procedureKind = route.method === "GET" || route.method === "HEAD" ? "query" : "mutation";
2382
2382
  const idempotencyMode = command?.idempotency ?? "none";
2383
+ const procedureMetadata = {
2384
+ key: `${controller.className}.${route.handler}`,
2385
+ method: route.method,
2386
+ path: fullPath,
2387
+ controller: controller.className,
2388
+ handler: route.handler,
2389
+ kind: procedureKind,
2390
+ idempotency: idempotencyMode
2391
+ };
2383
2392
  allProcedures.push({
2384
2393
  method: route.method,
2385
2394
  path: fullPath,
@@ -2391,7 +2400,7 @@ function renderClient(graph, options) {
2391
2400
  idempotency: idempotencyMode
2392
2401
  });
2393
2402
  routeMethods.push(`
2394
- ${route.handler}: makeRoute<${requestTypeName}, ${responseType === "never" ? "never" : responseTypeName}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}>(${JSON.stringify(route.method)}, ${JSON.stringify(fullPath)}, ${responseSchemaArgument}, ${responseKindArgument}, ${route.parse === "none"}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}),`);
2403
+ ${route.handler}: makeRoute<${requestTypeName}, ${responseType === "never" ? "never" : responseTypeName}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}>(${JSON.stringify(route.method)}, ${JSON.stringify(fullPath)}, ${responseSchemaArgument}, ${responseKindArgument}, ${route.parse === "none"}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}, ${JSON.stringify(procedureMetadata)}),`);
2395
2404
  }
2396
2405
  controllerEntries.push(`
2397
2406
  ${controllerKey}: {${routeMethods.join("")}
@@ -2425,6 +2434,19 @@ function renderClient(graph, options) {
2425
2434
  'export type ProcedureKind = "query" | "mutation";',
2426
2435
  'export type IdempotencyMode = "required" | "none";',
2427
2436
  "",
2437
+ "export interface ProcedureMetadata<",
2438
+ " Kind extends ProcedureKind = ProcedureKind,",
2439
+ " Idempotency extends IdempotencyMode = IdempotencyMode,",
2440
+ "> {",
2441
+ " key: string;",
2442
+ " method: string;",
2443
+ " path: string;",
2444
+ " controller: string;",
2445
+ " handler: string;",
2446
+ " kind: Kind;",
2447
+ " idempotency: Idempotency;",
2448
+ "}",
2449
+ "",
2428
2450
  "export interface ProcedureExecutionOptions {",
2429
2451
  " /** Reuse the same key when retrying an uncertain mutation. */",
2430
2452
  " idempotencyKey?: string;",
@@ -2450,14 +2472,24 @@ function renderClient(graph, options) {
2450
2472
  ' Idempotency extends IdempotencyMode = "none",',
2451
2473
  "> = (...args: ProcedureArgs<Options, Idempotency>) => Promise<ProcedureResult<Result>>;",
2452
2474
  "",
2475
+ "export type QueryableProcedure<",
2476
+ " Options extends ClientRequestOptions,",
2477
+ " Result,",
2478
+ " Idempotency extends IdempotencyMode,",
2479
+ " Kind extends ProcedureKind,",
2480
+ "> = ProcedureCall<Options, Result, Idempotency> & {",
2481
+ " readonly __supacloudProcedure: ProcedureMetadata<Kind, Idempotency>;",
2482
+ " readonly __supacloudInput?: Options;",
2483
+ "};",
2484
+ "",
2453
2485
  "export type ProcedureMember<",
2454
2486
  " Options extends ClientRequestOptions,",
2455
2487
  " Result,",
2456
2488
  " Kind extends ProcedureKind,",
2457
2489
  " Idempotency extends IdempotencyMode,",
2458
2490
  '> = Kind extends "query"',
2459
- " ? { query: ProcedureCall<Options, Result, Idempotency> }",
2460
- " : { mutate: ProcedureCall<Options, Result, Idempotency> };",
2491
+ ' ? { query: QueryableProcedure<Options, Result, Idempotency, "query"> }',
2492
+ ' : { mutate: QueryableProcedure<Options, Result, Idempotency, "mutation"> };',
2461
2493
  "",
2462
2494
  'export type ApiClientErrorCode = "API_HTTP_ERROR" | "API_RESPONSE_INVALID" | "API_RESPONSE_UNDECLARED";',
2463
2495
  "",
@@ -2477,7 +2509,7 @@ function renderClient(graph, options) {
2477
2509
  " readonly path: string;",
2478
2510
  " readonly status: number;",
2479
2511
  " readonly requestId: string | undefined;",
2480
- " /** Undeclared HTTP response for explicit inspection; excluded from JSON serialization. */",
2512
+ " /** HTTP response for inspection; successful response bodies may already be consumed. */",
2481
2513
  " readonly response: Response | undefined;",
2482
2514
  " constructor(message: string, details: ApiClientErrorDetails) {",
2483
2515
  " super(message);",
@@ -2824,25 +2856,27 @@ function renderClient(graph, options) {
2824
2856
  " const declaredSchema = responseSchemas ? selectResponseSchema(response.status, responseSchemas) : undefined;",
2825
2857
  " const failure = (code: ApiClientErrorCode, message: string) => new ApiClientError(message, {",
2826
2858
  ' code, method, path, status: response.status, requestId: response.headers.get("x-request-id"),',
2827
- ' ...(code === "API_HTTP_ERROR" ? { response } : {}),',
2859
+ " response,",
2828
2860
  " });",
2829
2861
  " if (!response.ok && declaredSchema === undefined) {",
2830
2862
  ' throw failure("API_HTTP_ERROR", `API request failed: ${method} ${path} -> ${response.status}`);',
2831
2863
  " }",
2864
+ " // Preserve the original HTTP error response if a declared error body fails validation.",
2865
+ " const bodyResponse = response.ok ? response : response.clone();",
2832
2866
  ' const contentType = response.headers?.get("content-type") ?? "";',
2833
2867
  " let value: unknown;",
2834
2868
  " if ([204, 205, 304].includes(response.status)) {",
2835
2869
  " value = undefined;",
2836
2870
  ' } else if (contentType.includes("application/json") || contentType.includes("+json")) {',
2837
- " const responseText = await response.text();",
2871
+ " const responseText = await bodyResponse.text();",
2838
2872
  " try { value = JSON.parse(responseText); }",
2839
2873
  ' catch { throw failure("API_RESPONSE_INVALID", "Response is not valid JSON"); }',
2840
2874
  ' } else if (responseKind === "binary" || contentType.includes("application/octet-stream")) {',
2841
- " value = await response.arrayBuffer();",
2875
+ " value = await bodyResponse.arrayBuffer();",
2842
2876
  ' } else if (responseKind === "stream") {',
2843
- " value = response.body;",
2877
+ " value = bodyResponse.body;",
2844
2878
  " } else {",
2845
- " value = await response.text();",
2879
+ " value = await bodyResponse.text();",
2846
2880
  " }",
2847
2881
  " if (!responseSchemas) return decode ? decode(value) : value;",
2848
2882
  " if (declaredSchema === undefined) {",
@@ -2869,9 +2903,10 @@ function renderClient(graph, options) {
2869
2903
  " ): ClientRequestOptions {",
2870
2904
  " const headers = { ...(options.headers as Record<string, string> | undefined) };",
2871
2905
  " if (execution?.idempotencyKey !== undefined) {",
2872
- " if (!/^[A-Za-z0-9._:-]{1,512}$/.test(execution.idempotencyKey)) {",
2906
+ ' if (typeof execution.idempotencyKey !== "string" || !/^[A-Za-z0-9._:-]{1,512}$/.test(execution.idempotencyKey)) {',
2873
2907
  ' throw new TypeError("idempotencyKey must contain only safe header characters and be at most 512 characters");',
2874
2908
  " }",
2909
+ ' for (const key of Object.keys(headers)) if (key.toLowerCase() === "idempotency-key") delete headers[key];',
2875
2910
  ' headers["idempotency-key"] = execution.idempotencyKey;',
2876
2911
  " }",
2877
2912
  ' if (idempotency === "required" && !hasHeader(headers, "idempotency-key")) {',
@@ -2885,17 +2920,17 @@ function renderClient(graph, options) {
2885
2920
  ' return Object.hasOwn(value, "idempotencyKey") || Object.hasOwn(value, "signal");',
2886
2921
  " }",
2887
2922
  "",
2888
- ` function makeProcedure<Options extends ClientRequestOptions, Result = never, Idempotency extends IdempotencyMode = "none">(method: string, path: string, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false, idempotency: Idempotency = "none" as Idempotency): ProcedureCall<Options, Result, Idempotency> {`,
2923
+ ` function makeProcedure<Options extends ClientRequestOptions, Result = never, Idempotency extends IdempotencyMode = "none", Kind extends ProcedureKind = ProcedureKind>(metadata: ProcedureMetadata<Kind, Idempotency>, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false): QueryableProcedure<Options, Result, Idempotency, Kind> {`,
2889
2924
  " const procedure = async (first?: Options | ProcedureExecutionOptions, second?: ProcedureExecutionOptions): Promise<unknown> => {",
2890
2925
  " const execution = second ?? (isProcedureExecution(first) ? first : undefined);",
2891
2926
  " const input = (execution === first ? undefined : first) ?? {};",
2892
- " const requestOptions = withProcedureExecution(input as ClientRequestOptions, execution, idempotency);",
2893
- " return request(method, path, requestOptions, undefined, responseSchemas, responseKind, raw, execution);",
2927
+ " const requestOptions = withProcedureExecution(input as ClientRequestOptions, execution, metadata.idempotency);",
2928
+ " return request(metadata.method, metadata.path, requestOptions, undefined, responseSchemas, responseKind, raw, execution);",
2894
2929
  " };",
2895
- " return procedure as ProcedureCall<Options, Result, Idempotency>;",
2930
+ " return Object.assign(procedure, { __supacloudProcedure: Object.freeze(metadata) }) as QueryableProcedure<Options, Result, Idempotency, Kind>;",
2896
2931
  " }",
2897
2932
  "",
2898
- ` function makeRoute<Options extends ClientRequestOptions, Result = never, Kind extends ProcedureKind = never, Idempotency extends IdempotencyMode = "none">(method: string, path: string, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false, kind?: Kind, idempotency: Idempotency = "none" as Idempotency): RouteMethod<Options, Result> & ProcedureMember<Options, Result, Kind, Idempotency> {`,
2933
+ ` function makeRoute<Options extends ClientRequestOptions, Result = never, Kind extends ProcedureKind = never, Idempotency extends IdempotencyMode = "none">(method: string, path: string, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false, kind?: Kind, idempotency: Idempotency = "none" as Idempotency, metadata?: ProcedureMetadata<Kind, Idempotency>): RouteMethod<Options, Result> & ProcedureMember<Options, Result, Kind, Idempotency> {`,
2899
2934
  " function route<T>(options: Options, decode: ResponseDecoder<T>): Promise<T>;",
2900
2935
  " function route(options: Options): Promise<Result>;",
2901
2936
  " function route(options?: Options): Promise<unknown>;",
@@ -2903,8 +2938,8 @@ function renderClient(graph, options) {
2903
2938
  " const requestOptions = options ?? {};",
2904
2939
  " return request(method, path, requestOptions, decode, responseSchemas, responseKind, raw);",
2905
2940
  " }",
2906
- ' if (kind === "query") Object.assign(route, { query: makeProcedure<Options, Result, Idempotency>(method, path, responseSchemas, responseKind, raw, idempotency) });',
2907
- ' if (kind === "mutation") Object.assign(route, { mutate: makeProcedure<Options, Result, Idempotency>(method, path, responseSchemas, responseKind, raw, idempotency) });',
2941
+ ' if (kind === "query" && metadata) Object.assign(route, { query: makeProcedure<Options, Result, Idempotency, "query">(metadata as ProcedureMetadata<"query", Idempotency>, responseSchemas, responseKind, raw) });',
2942
+ ' if (kind === "mutation" && metadata) Object.assign(route, { mutate: makeProcedure<Options, Result, Idempotency, "mutation">(metadata as ProcedureMetadata<"mutation", Idempotency>, responseSchemas, responseKind, raw) });',
2908
2943
  " return route as RouteMethod<Options, Result> & ProcedureMember<Options, Result, Kind, Idempotency>;",
2909
2944
  " }",
2910
2945
  "",
package/dist/index.js CHANGED
@@ -2379,6 +2379,15 @@ function renderClient(graph, options) {
2379
2379
  const command = route.command === undefined ? undefined : commandByClassName.get(route.command);
2380
2380
  const procedureKind = route.method === "GET" || route.method === "HEAD" ? "query" : "mutation";
2381
2381
  const idempotencyMode = command?.idempotency ?? "none";
2382
+ const procedureMetadata = {
2383
+ key: `${controller.className}.${route.handler}`,
2384
+ method: route.method,
2385
+ path: fullPath,
2386
+ controller: controller.className,
2387
+ handler: route.handler,
2388
+ kind: procedureKind,
2389
+ idempotency: idempotencyMode
2390
+ };
2382
2391
  allProcedures.push({
2383
2392
  method: route.method,
2384
2393
  path: fullPath,
@@ -2390,7 +2399,7 @@ function renderClient(graph, options) {
2390
2399
  idempotency: idempotencyMode
2391
2400
  });
2392
2401
  routeMethods.push(`
2393
- ${route.handler}: makeRoute<${requestTypeName}, ${responseType === "never" ? "never" : responseTypeName}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}>(${JSON.stringify(route.method)}, ${JSON.stringify(fullPath)}, ${responseSchemaArgument}, ${responseKindArgument}, ${route.parse === "none"}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}),`);
2402
+ ${route.handler}: makeRoute<${requestTypeName}, ${responseType === "never" ? "never" : responseTypeName}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}>(${JSON.stringify(route.method)}, ${JSON.stringify(fullPath)}, ${responseSchemaArgument}, ${responseKindArgument}, ${route.parse === "none"}, ${JSON.stringify(procedureKind)}, ${JSON.stringify(idempotencyMode)}, ${JSON.stringify(procedureMetadata)}),`);
2394
2403
  }
2395
2404
  controllerEntries.push(`
2396
2405
  ${controllerKey}: {${routeMethods.join("")}
@@ -2424,6 +2433,19 @@ function renderClient(graph, options) {
2424
2433
  'export type ProcedureKind = "query" | "mutation";',
2425
2434
  'export type IdempotencyMode = "required" | "none";',
2426
2435
  "",
2436
+ "export interface ProcedureMetadata<",
2437
+ " Kind extends ProcedureKind = ProcedureKind,",
2438
+ " Idempotency extends IdempotencyMode = IdempotencyMode,",
2439
+ "> {",
2440
+ " key: string;",
2441
+ " method: string;",
2442
+ " path: string;",
2443
+ " controller: string;",
2444
+ " handler: string;",
2445
+ " kind: Kind;",
2446
+ " idempotency: Idempotency;",
2447
+ "}",
2448
+ "",
2427
2449
  "export interface ProcedureExecutionOptions {",
2428
2450
  " /** Reuse the same key when retrying an uncertain mutation. */",
2429
2451
  " idempotencyKey?: string;",
@@ -2449,14 +2471,24 @@ function renderClient(graph, options) {
2449
2471
  ' Idempotency extends IdempotencyMode = "none",',
2450
2472
  "> = (...args: ProcedureArgs<Options, Idempotency>) => Promise<ProcedureResult<Result>>;",
2451
2473
  "",
2474
+ "export type QueryableProcedure<",
2475
+ " Options extends ClientRequestOptions,",
2476
+ " Result,",
2477
+ " Idempotency extends IdempotencyMode,",
2478
+ " Kind extends ProcedureKind,",
2479
+ "> = ProcedureCall<Options, Result, Idempotency> & {",
2480
+ " readonly __supacloudProcedure: ProcedureMetadata<Kind, Idempotency>;",
2481
+ " readonly __supacloudInput?: Options;",
2482
+ "};",
2483
+ "",
2452
2484
  "export type ProcedureMember<",
2453
2485
  " Options extends ClientRequestOptions,",
2454
2486
  " Result,",
2455
2487
  " Kind extends ProcedureKind,",
2456
2488
  " Idempotency extends IdempotencyMode,",
2457
2489
  '> = Kind extends "query"',
2458
- " ? { query: ProcedureCall<Options, Result, Idempotency> }",
2459
- " : { mutate: ProcedureCall<Options, Result, Idempotency> };",
2490
+ ' ? { query: QueryableProcedure<Options, Result, Idempotency, "query"> }',
2491
+ ' : { mutate: QueryableProcedure<Options, Result, Idempotency, "mutation"> };',
2460
2492
  "",
2461
2493
  'export type ApiClientErrorCode = "API_HTTP_ERROR" | "API_RESPONSE_INVALID" | "API_RESPONSE_UNDECLARED";',
2462
2494
  "",
@@ -2476,7 +2508,7 @@ function renderClient(graph, options) {
2476
2508
  " readonly path: string;",
2477
2509
  " readonly status: number;",
2478
2510
  " readonly requestId: string | undefined;",
2479
- " /** Undeclared HTTP response for explicit inspection; excluded from JSON serialization. */",
2511
+ " /** HTTP response for inspection; successful response bodies may already be consumed. */",
2480
2512
  " readonly response: Response | undefined;",
2481
2513
  " constructor(message: string, details: ApiClientErrorDetails) {",
2482
2514
  " super(message);",
@@ -2823,25 +2855,27 @@ function renderClient(graph, options) {
2823
2855
  " const declaredSchema = responseSchemas ? selectResponseSchema(response.status, responseSchemas) : undefined;",
2824
2856
  " const failure = (code: ApiClientErrorCode, message: string) => new ApiClientError(message, {",
2825
2857
  ' code, method, path, status: response.status, requestId: response.headers.get("x-request-id"),',
2826
- ' ...(code === "API_HTTP_ERROR" ? { response } : {}),',
2858
+ " response,",
2827
2859
  " });",
2828
2860
  " if (!response.ok && declaredSchema === undefined) {",
2829
2861
  ' throw failure("API_HTTP_ERROR", `API request failed: ${method} ${path} -> ${response.status}`);',
2830
2862
  " }",
2863
+ " // Preserve the original HTTP error response if a declared error body fails validation.",
2864
+ " const bodyResponse = response.ok ? response : response.clone();",
2831
2865
  ' const contentType = response.headers?.get("content-type") ?? "";',
2832
2866
  " let value: unknown;",
2833
2867
  " if ([204, 205, 304].includes(response.status)) {",
2834
2868
  " value = undefined;",
2835
2869
  ' } else if (contentType.includes("application/json") || contentType.includes("+json")) {',
2836
- " const responseText = await response.text();",
2870
+ " const responseText = await bodyResponse.text();",
2837
2871
  " try { value = JSON.parse(responseText); }",
2838
2872
  ' catch { throw failure("API_RESPONSE_INVALID", "Response is not valid JSON"); }',
2839
2873
  ' } else if (responseKind === "binary" || contentType.includes("application/octet-stream")) {',
2840
- " value = await response.arrayBuffer();",
2874
+ " value = await bodyResponse.arrayBuffer();",
2841
2875
  ' } else if (responseKind === "stream") {',
2842
- " value = response.body;",
2876
+ " value = bodyResponse.body;",
2843
2877
  " } else {",
2844
- " value = await response.text();",
2878
+ " value = await bodyResponse.text();",
2845
2879
  " }",
2846
2880
  " if (!responseSchemas) return decode ? decode(value) : value;",
2847
2881
  " if (declaredSchema === undefined) {",
@@ -2868,9 +2902,10 @@ function renderClient(graph, options) {
2868
2902
  " ): ClientRequestOptions {",
2869
2903
  " const headers = { ...(options.headers as Record<string, string> | undefined) };",
2870
2904
  " if (execution?.idempotencyKey !== undefined) {",
2871
- " if (!/^[A-Za-z0-9._:-]{1,512}$/.test(execution.idempotencyKey)) {",
2905
+ ' if (typeof execution.idempotencyKey !== "string" || !/^[A-Za-z0-9._:-]{1,512}$/.test(execution.idempotencyKey)) {',
2872
2906
  ' throw new TypeError("idempotencyKey must contain only safe header characters and be at most 512 characters");',
2873
2907
  " }",
2908
+ ' for (const key of Object.keys(headers)) if (key.toLowerCase() === "idempotency-key") delete headers[key];',
2874
2909
  ' headers["idempotency-key"] = execution.idempotencyKey;',
2875
2910
  " }",
2876
2911
  ' if (idempotency === "required" && !hasHeader(headers, "idempotency-key")) {',
@@ -2884,17 +2919,17 @@ function renderClient(graph, options) {
2884
2919
  ' return Object.hasOwn(value, "idempotencyKey") || Object.hasOwn(value, "signal");',
2885
2920
  " }",
2886
2921
  "",
2887
- ` function makeProcedure<Options extends ClientRequestOptions, Result = never, Idempotency extends IdempotencyMode = "none">(method: string, path: string, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false, idempotency: Idempotency = "none" as Idempotency): ProcedureCall<Options, Result, Idempotency> {`,
2922
+ ` function makeProcedure<Options extends ClientRequestOptions, Result = never, Idempotency extends IdempotencyMode = "none", Kind extends ProcedureKind = ProcedureKind>(metadata: ProcedureMetadata<Kind, Idempotency>, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false): QueryableProcedure<Options, Result, Idempotency, Kind> {`,
2888
2923
  " const procedure = async (first?: Options | ProcedureExecutionOptions, second?: ProcedureExecutionOptions): Promise<unknown> => {",
2889
2924
  " const execution = second ?? (isProcedureExecution(first) ? first : undefined);",
2890
2925
  " const input = (execution === first ? undefined : first) ?? {};",
2891
- " const requestOptions = withProcedureExecution(input as ClientRequestOptions, execution, idempotency);",
2892
- " return request(method, path, requestOptions, undefined, responseSchemas, responseKind, raw, execution);",
2926
+ " const requestOptions = withProcedureExecution(input as ClientRequestOptions, execution, metadata.idempotency);",
2927
+ " return request(metadata.method, metadata.path, requestOptions, undefined, responseSchemas, responseKind, raw, execution);",
2893
2928
  " };",
2894
- " return procedure as ProcedureCall<Options, Result, Idempotency>;",
2929
+ " return Object.assign(procedure, { __supacloudProcedure: Object.freeze(metadata) }) as QueryableProcedure<Options, Result, Idempotency, Kind>;",
2895
2930
  " }",
2896
2931
  "",
2897
- ` function makeRoute<Options extends ClientRequestOptions, Result = never, Kind extends ProcedureKind = never, Idempotency extends IdempotencyMode = "none">(method: string, path: string, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false, kind?: Kind, idempotency: Idempotency = "none" as Idempotency): RouteMethod<Options, Result> & ProcedureMember<Options, Result, Kind, Idempotency> {`,
2932
+ ` function makeRoute<Options extends ClientRequestOptions, Result = never, Kind extends ProcedureKind = never, Idempotency extends IdempotencyMode = "none">(method: string, path: string, responseSchemas?: Readonly<Record<string, ${responseSchemaType}>>, responseKind?: string, raw = false, kind?: Kind, idempotency: Idempotency = "none" as Idempotency, metadata?: ProcedureMetadata<Kind, Idempotency>): RouteMethod<Options, Result> & ProcedureMember<Options, Result, Kind, Idempotency> {`,
2898
2933
  " function route<T>(options: Options, decode: ResponseDecoder<T>): Promise<T>;",
2899
2934
  " function route(options: Options): Promise<Result>;",
2900
2935
  " function route(options?: Options): Promise<unknown>;",
@@ -2902,8 +2937,8 @@ function renderClient(graph, options) {
2902
2937
  " const requestOptions = options ?? {};",
2903
2938
  " return request(method, path, requestOptions, decode, responseSchemas, responseKind, raw);",
2904
2939
  " }",
2905
- ' if (kind === "query") Object.assign(route, { query: makeProcedure<Options, Result, Idempotency>(method, path, responseSchemas, responseKind, raw, idempotency) });',
2906
- ' if (kind === "mutation") Object.assign(route, { mutate: makeProcedure<Options, Result, Idempotency>(method, path, responseSchemas, responseKind, raw, idempotency) });',
2940
+ ' if (kind === "query" && metadata) Object.assign(route, { query: makeProcedure<Options, Result, Idempotency, "query">(metadata as ProcedureMetadata<"query", Idempotency>, responseSchemas, responseKind, raw) });',
2941
+ ' if (kind === "mutation" && metadata) Object.assign(route, { mutate: makeProcedure<Options, Result, Idempotency, "mutation">(metadata as ProcedureMetadata<"mutation", Idempotency>, responseSchemas, responseKind, raw) });',
2907
2942
  " return route as RouteMethod<Options, Result> & ProcedureMember<Options, Result, Kind, Idempotency>;",
2908
2943
  " }",
2909
2944
  "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/compiler",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",