gencow 0.1.135 → 0.1.136

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 (40) hide show
  1. package/bin/gencow.mjs +1 -3
  2. package/core/index.js +17 -0
  3. package/lib/__tests__/ai-template-smoke.test.ts +18 -2
  4. package/lib/__tests__/cli-project-runtime.test.mjs +15 -1
  5. package/lib/__tests__/codegen-command-output-dir.test.mjs +46 -0
  6. package/lib/__tests__/codegen-command.test.mjs +45 -21
  7. package/lib/__tests__/dev-local-command.test.mjs +3 -1
  8. package/lib/__tests__/init-command.test.mjs +5 -0
  9. package/lib/__tests__/name-casing.test.mjs +26 -0
  10. package/lib/__tests__/test-script-regression.test.ts +92 -0
  11. package/lib/api-codegen.mjs +31 -4
  12. package/lib/cli-dev-runtime.mjs +103 -127
  13. package/lib/cli-project-runtime.mjs +20 -2
  14. package/lib/codegen/index.mjs +1505 -0
  15. package/lib/codegen-command.mjs +63 -100
  16. package/lib/dev-cloud-command.mjs +1 -1
  17. package/lib/dev-local-command.mjs +9 -4
  18. package/lib/init-command.mjs +7 -1
  19. package/lib/name-casing.d.ts +2 -0
  20. package/lib/name-casing.mjs +17 -0
  21. package/package.json +5 -6
  22. package/scripts/bundle-codegen.mjs +39 -0
  23. package/scripts/pre-publish-check.mjs +15 -1
  24. package/server/index.js +4 -0
  25. package/server/index.js.map +2 -2
  26. package/dashboard/apple-touch-icon.png +0 -0
  27. package/dashboard/assets/index-C1IxULeS.css +0 -1
  28. package/dashboard/assets/index-D0ZYKx4B.js +0 -378
  29. package/dashboard/favicon-16.png +0 -0
  30. package/dashboard/favicon-192.png +0 -0
  31. package/dashboard/favicon-32.png +0 -0
  32. package/dashboard/favicon-512.png +0 -0
  33. package/dashboard/favicon.ico +0 -0
  34. package/dashboard/favicon.svg +0 -1
  35. package/dashboard/file.svg +0 -1
  36. package/dashboard/globe.svg +0 -1
  37. package/dashboard/index.html +0 -29
  38. package/dashboard/next.svg +0 -1
  39. package/dashboard/vercel.svg +0 -1
  40. package/dashboard/window.svg +0 -1
package/bin/gencow.mjs CHANGED
@@ -75,9 +75,7 @@ import { platformFetch, requireCreds, rpcMutation, rpcQuery } from "../lib/platf
75
75
 
76
76
  const __dirname = dirname(fileURLToPath(import.meta.url));
77
77
  const _drizzleKitCmd = (subcmd) => buildDrizzleKitCommand(subcmd);
78
- const generateApiTs = createApiCodegenRuntime({
79
- findServerRootImpl: findServerRoot,
80
- });
78
+ const generateApiTs = createApiCodegenRuntime({});
81
79
  const runAddCommand = createAddCommand({
82
80
  loadConfig,
83
81
  updateComponentReadmeImpl: updateComponentReadme,
package/core/index.js CHANGED
@@ -2683,6 +2683,13 @@ var _ownerRlsTables = [];
2683
2683
  function getOwnerRlsTables() {
2684
2684
  return _ownerRlsTables;
2685
2685
  }
2686
+ if (!globalThis.__gencow_crudCodegenRegistry) {
2687
+ globalThis.__gencow_crudCodegenRegistry = /* @__PURE__ */ new Map();
2688
+ }
2689
+ var crudCodegenRegistry = globalThis.__gencow_crudCodegenRegistry;
2690
+ function getRegisteredCrudCodegenMeta() {
2691
+ return Array.from(crudCodegenRegistry.values());
2692
+ }
2686
2693
  function detectIdType(column) {
2687
2694
  const colType = column.dataType;
2688
2695
  if (colType === "string") return v.string();
@@ -2872,6 +2879,15 @@ function crud(table, options) {
2872
2879
  });
2873
2880
  }
2874
2881
  const enabledMethods = new Set(options?.methods ?? ["list", "get", "create", "update", "remove"]);
2882
+ const enabledMethodsList = Array.from(enabledMethods);
2883
+ crudCodegenRegistry.set(prefix, {
2884
+ tableName,
2885
+ prefix,
2886
+ methods: enabledMethodsList,
2887
+ allowedFilters: (options?.allowedFilters ?? []).map((field) => String(field)),
2888
+ searchFields: (options?.searchFields ?? []).map((field) => String(field)),
2889
+ isPublic
2890
+ });
2875
2891
  const listDef = !enabledMethods.has("list") ? void 0 : query(`${prefix}.list`, {
2876
2892
  public: isPublic,
2877
2893
  args: {
@@ -3052,6 +3068,7 @@ export {
3052
3068
  getOwnerRlsTables,
3053
3069
  getQueryDef,
3054
3070
  getQueryHandler,
3071
+ getRegisteredCrudCodegenMeta,
3055
3072
  getRegisteredHttpActions,
3056
3073
  getRegisteredMutations,
3057
3074
  getRegisteredQueries,
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "bun:test";
2
2
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "fs";
3
+ import { createRequire } from "module";
3
4
  import { join, resolve } from "path";
4
5
  import { tmpdir } from "os";
5
6
 
@@ -9,17 +10,32 @@ const syncedTemplatePaths = [
9
10
  join(repoRoot, "packages/cli/templates/fullstack/ai.ts"),
10
11
  join(repoRoot, "packages/cli/templates/ai-chat/ai.ts"),
11
12
  ];
13
+ const fixtureWorkspacePath = join(repoRoot, "apps/test-ai-template-fixture");
12
14
  const fixtureNodeModules = join(repoRoot, "apps/test-ai-template-fixture/node_modules");
15
+ const fixtureRequire = createRequire(join(fixtureWorkspacePath, "package.json"));
13
16
 
14
17
  function readTemplate(path: string): string {
15
18
  return readFileSync(path, "utf8");
16
19
  }
17
20
 
18
21
  function requireFixtureNodeModules(): string {
22
+ const requiredPackages = ["typescript/bin/tsc", "ai/package.json", "@ai-sdk/openai/package.json", "zod/package.json"];
23
+ for (const packagePath of requiredPackages) {
24
+ try {
25
+ fixtureRequire.resolve(packagePath);
26
+ } catch {
27
+ throw new Error(
28
+ "AI template smoke test requires the fixture workspace dependencies from a normal repo install. " +
29
+ "Run `pnpm install` at the repo root first.",
30
+ );
31
+ }
32
+ }
33
+
19
34
  if (existsSync(fixtureNodeModules)) return fixtureNodeModules;
35
+
20
36
  throw new Error(
21
- "AI template smoke test requires repo-local fixture dependencies. " +
22
- "Run `pnpm install --filter ./apps/test-ai-template-fixture` first.",
37
+ "AI template smoke test requires the fixture workspace links from a normal repo install. " +
38
+ "Run `pnpm install` at the repo root first.",
23
39
  );
24
40
  }
25
41
 
@@ -22,6 +22,7 @@ describe("loadConfig()", () => {
22
22
  ).toEqual({
23
23
  functionsDir: "./gencow",
24
24
  schema: "./gencow/schema.ts",
25
+ codegenOutDir: "./src/gencow",
25
26
  storage: "./.gencow/uploads",
26
27
  db: { url: "./.gencow/data" },
27
28
  port: 5456,
@@ -33,12 +34,13 @@ describe("loadConfig()", () => {
33
34
  const root = mkdtempSync(resolve(tmpdir(), "gencow-cli-config-"));
34
35
  writeFileSync(
35
36
  resolve(root, "gencow.config.ts"),
36
- `export default { functionsDir: "./api", schema: "./api/schema.ts", storage: "./uploads", db: { url: "./data" }, port: 6001 };`,
37
+ `export default { functionsDir: "./api", schema: "./api/schema.ts", codegenOutDir: "./src/gencow", storage: "./uploads", db: { url: "./data" }, port: 6001 };`,
37
38
  );
38
39
 
39
40
  expect(loadConfig({ cwd: root })).toEqual({
40
41
  functionsDir: "./api",
41
42
  schema: "./api/schema.ts",
43
+ codegenOutDir: "./src/gencow",
42
44
  storage: "./uploads",
43
45
  db: { url: "./data" },
44
46
  port: 6001,
@@ -46,6 +48,18 @@ describe("loadConfig()", () => {
46
48
 
47
49
  rmSync(root, { recursive: true, force: true });
48
50
  });
51
+
52
+ it("parses schema arrays from gencow.config.ts", () => {
53
+ const root = mkdtempSync(resolve(tmpdir(), "gencow-cli-config-schema-array-"));
54
+ writeFileSync(
55
+ resolve(root, "gencow.config.ts"),
56
+ `export default { schema: ["./src/custom-db.ts", "./src/auth-model.ts"] };`,
57
+ );
58
+
59
+ expect(loadConfig({ cwd: root }).schema).toEqual(["./src/custom-db.ts", "./src/auth-model.ts"]);
60
+
61
+ rmSync(root, { recursive: true, force: true });
62
+ });
49
63
  });
50
64
 
51
65
  describe("findServerRoot()", () => {
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { mkdtempSync, mkdirSync, rmSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { resolve } from "path";
5
+ import { resolveCodegenPublishDir } from "../codegen-command.mjs";
6
+
7
+ describe("resolveCodegenPublishDir()", () => {
8
+ it("prefers codegenOutDir from config when provided", () => {
9
+ const root = mkdtempSync(resolve(tmpdir(), "gencow-codegen-outdir-"));
10
+ try {
11
+ mkdirSync(resolve(root, "src"), { recursive: true });
12
+ const out = resolveCodegenPublishDir(root, {
13
+ functionsDir: "./gencow",
14
+ codegenOutDir: "./custom/generated",
15
+ });
16
+ expect(out).toBe(resolve(root, "custom/generated"));
17
+ } finally {
18
+ rmSync(root, { recursive: true, force: true });
19
+ }
20
+ });
21
+
22
+ it("falls back to src/gencow when src exists", () => {
23
+ const root = mkdtempSync(resolve(tmpdir(), "gencow-codegen-outdir-"));
24
+ try {
25
+ mkdirSync(resolve(root, "src"), { recursive: true });
26
+ const out = resolveCodegenPublishDir(root, {
27
+ functionsDir: "./gencow",
28
+ });
29
+ expect(out).toBe(resolve(root, "src/gencow"));
30
+ } finally {
31
+ rmSync(root, { recursive: true, force: true });
32
+ }
33
+ });
34
+
35
+ it("falls back to functionsDir when src does not exist", () => {
36
+ const root = mkdtempSync(resolve(tmpdir(), "gencow-codegen-outdir-"));
37
+ try {
38
+ const out = resolveCodegenPublishDir(root, {
39
+ functionsDir: "./gencow",
40
+ });
41
+ expect(out).toBe(resolve(root, "gencow"));
42
+ } finally {
43
+ rmSync(root, { recursive: true, force: true });
44
+ }
45
+ });
46
+ });
@@ -1,28 +1,52 @@
1
1
  import { describe, expect, it } from "bun:test";
2
+ import { createCodegenCommand } from "../codegen-command.mjs";
2
3
 
3
- import { buildFrontendApiTs, resolveCodegenOutdir } from "../codegen-command.mjs";
4
-
5
- describe("resolveCodegenOutdir()", () => {
6
- it("defaults to src/gencow under cwd", () => {
7
- expect(resolveCodegenOutdir("/repo/demo", [])).toBe("/repo/demo/src/gencow");
8
- });
4
+ describe("createCodegenCommand()", () => {
5
+ it("passes multi-schema config to runCodegen from CLI boundary", async () => {
6
+ let capturedInput = null;
7
+ const logs = [];
8
+ const command = createCodegenCommand({
9
+ BOLD: "",
10
+ CYAN: "",
11
+ DIM: "",
12
+ RED: "",
13
+ RESET: "",
14
+ YELLOW: "",
15
+ loadConfig: () => ({
16
+ functionsDir: "./src/gencow",
17
+ schema: ["./src/custom-db.ts", "./src/auth-model.ts"],
18
+ codegenOutDir: "./src/gencow/gen",
19
+ }),
20
+ cwdImpl: () => "/tmp/project",
21
+ resolvePathImpl: (cwd, p) => `${cwd}/${p.replace(/^\.\//, "")}`,
22
+ logImpl: (line) => logs.push(String(line)),
23
+ infoImpl: (line) => logs.push(String(line)),
24
+ successImpl: () => {},
25
+ errorImpl: (line) => logs.push(String(line)),
26
+ exitImpl: (code) => {
27
+ throw new Error(`Unexpected exit: ${code}`);
28
+ },
29
+ loadCodegenBundleImpl: async () => ({
30
+ runCodegen: async (input) => {
31
+ capturedInput = input;
32
+ return {
33
+ ok: true,
34
+ diagnostics: [],
35
+ artifacts: [{ path: "manifest.json" }, { path: "api.ts" }],
36
+ };
37
+ },
38
+ }),
39
+ });
9
40
 
10
- it("uses --outdir and -o overrides", () => {
11
- expect(resolveCodegenOutdir("/repo/demo", ["--outdir", "web/gen"])).toBe("/repo/demo/web/gen");
12
- expect(resolveCodegenOutdir("/repo/demo", ["-o", "app/api"])).toBe("/repo/demo/app/api");
13
- });
14
- });
41
+ await command();
15
42
 
16
- describe("buildFrontendApiTs()", () => {
17
- it("renders query and mutation definitions for frontend use", () => {
18
- const out = buildFrontendApiTs({
19
- tasks: { queries: ["list"], mutations: ["create"] },
20
- users: { queries: ["me"], mutations: [] },
43
+ expect(capturedInput).toEqual({
44
+ projectRoot: "/tmp/project",
45
+ functionsDir: "./src/gencow",
46
+ schema: ["./src/custom-db.ts", "./src/auth-model.ts"],
47
+ outDir: "/tmp/project/.gencow/codegen",
48
+ finalOutDir: "/tmp/project/src/gencow/gen",
21
49
  });
22
-
23
- expect(out).toContain('import { defineQuery, defineMutation } from "@gencow/react";');
24
- expect(out).toContain('list: defineQuery("tasks.list")');
25
- expect(out).toContain('create: defineMutation("tasks.create")');
26
- expect(out).toContain('me: defineQuery("users.me")');
50
+ expect(logs.join("\n")).toContain("Output:");
27
51
  });
28
52
  });
@@ -25,9 +25,11 @@ describe("buildKillPortsCommand()", () => {
25
25
  });
26
26
 
27
27
  describe("isGeneratedCodegenFile()", () => {
28
- it("filters generated api.ts and README.md files only", () => {
28
+ it("filters generated client artifact basenames", () => {
29
29
  expect(isGeneratedCodegenFile("gencow/api.ts")).toBe(true);
30
30
  expect(isGeneratedCodegenFile("nested/README.md")).toBe(true);
31
+ expect(isGeneratedCodegenFile("gencow/db.d.ts")).toBe(true);
32
+ expect(isGeneratedCodegenFile("gencow/operations.d.ts")).toBe(true);
31
33
  expect(isGeneratedCodegenFile("schema.ts")).toBe(false);
32
34
  });
33
35
  });
@@ -58,6 +58,11 @@ describe("mergeGitignoreContent()", () => {
58
58
  const merged = mergeGitignoreContent("dist/\nnode_modules/\n");
59
59
  expect(merged).toContain("# Gencow");
60
60
  expect(merged).toContain(".gencow/");
61
+ expect(merged).toContain("src/gencow/api.ts");
62
+ expect(merged).toContain("src/gencow/db.d.ts");
63
+ expect(merged).toContain("src/gencow/operations.d.ts");
64
+ expect(merged).toContain("src/gencow/zod.ts");
65
+ expect(merged).toContain("gencow/zod.ts");
61
66
  expect(merged.match(/node_modules\//g)?.length).toBe(1);
62
67
  });
63
68
  });
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { toCamelCase, toPascalCase } from "../name-casing.mjs";
3
+
4
+ describe("toPascalCase", () => {
5
+ it("splits on non-alphanumeric runs and capitalizes segments", () => {
6
+ expect(toPascalCase("tasks")).toBe("Tasks");
7
+ expect(toPascalCase("my_tasks")).toBe("MyTasks");
8
+ expect(toPascalCase("my-tasks.list")).toBe("MyTasksList");
9
+ });
10
+
11
+ it("coerces non-strings like api-codegen callers", () => {
12
+ expect(toPascalCase(12)).toBe("12");
13
+ });
14
+ });
15
+
16
+ describe("toCamelCase", () => {
17
+ it("lowercases the first character of PascalCase", () => {
18
+ expect(toCamelCase("tasks")).toBe("tasks");
19
+ expect(toCamelCase("my_tasks")).toBe("myTasks");
20
+ });
21
+
22
+ it("returns empty string for empty Pascal base", () => {
23
+ expect(toCamelCase("")).toBe("");
24
+ expect(toCamelCase("___")).toBe("");
25
+ });
26
+ });
@@ -0,0 +1,92 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { readFileSync } from "fs";
3
+ import { join, resolve } from "path";
4
+
5
+ const repoRoot = resolve(import.meta.dir, "../../../..");
6
+ const helperPath = join(repoRoot, "scripts/lib/test-helpers.sh");
7
+ const aiTemplateSmokePath = join(repoRoot, "packages/cli/lib/__tests__/ai-template-smoke.test.ts");
8
+
9
+ const coreShellScripts = [
10
+ "scripts/test-server.sh",
11
+ "scripts/test-auth-flow.sh",
12
+ "scripts/test-security.sh",
13
+ "scripts/test-realtime.sh",
14
+ "scripts/test-cron-scheduler.sh",
15
+ "scripts/test-env.sh",
16
+ ] as const;
17
+
18
+ function readRepoFile(path: string): string {
19
+ return readFileSync(path, "utf8");
20
+ }
21
+
22
+ describe("shell test helper regression", () => {
23
+ it("keeps core shell suites on the shared Bun-based helper path", () => {
24
+ const helperSource = readRepoFile(helperPath);
25
+ expect(helperSource).toContain("json_get()");
26
+ expect(helperSource).toContain("run_with_timeout()");
27
+ expect(helperSource).toContain("make_test_email()");
28
+
29
+ for (const relativePath of coreShellScripts) {
30
+ const source = readRepoFile(join(repoRoot, relativePath));
31
+ expect(source).toContain('source "${SCRIPT_DIR}/lib/test-helpers.sh"');
32
+ expect(source).not.toContain("python3 -c");
33
+ }
34
+ });
35
+
36
+ it("uses per-run identities for remote auth fixtures", () => {
37
+ const serverSource = readRepoFile(join(repoRoot, "scripts/test-server.sh"));
38
+ const securitySource = readRepoFile(join(repoRoot, "scripts/test-security.sh"));
39
+ const realtimeSource = readRepoFile(join(repoRoot, "scripts/test-realtime.sh"));
40
+
41
+ expect(serverSource).toContain('make_test_email "server-e2e"');
42
+ expect(serverSource).not.toContain("test@example.com");
43
+
44
+ expect(securitySource).toContain('make_test_email "security-e2e"');
45
+ expect(securitySource).not.toContain("sectest@example.com");
46
+
47
+ expect(realtimeSource).toContain('make_test_email "realtime-e2e"');
48
+ expect(realtimeSource).not.toContain("realtime@example.com");
49
+ });
50
+
51
+ it("keeps realtime smoke portable across timeout implementations", () => {
52
+ const realtimeSource = readRepoFile(join(repoRoot, "scripts/test-realtime.sh"));
53
+
54
+ expect(realtimeSource).toContain("run_with_timeout 3 websocat");
55
+ expect(realtimeSource).not.toMatch(/\n\s*timeout 3 websocat/);
56
+ expect(realtimeSource).toContain("portable JSON parsing + timeout fallback");
57
+ });
58
+
59
+ it("treats admin access checks as policy-aware instead of hard-coded public 200s", () => {
60
+ const securitySource = readRepoFile(join(repoRoot, "scripts/test-security.sh"));
61
+
62
+ expect(securitySource).toContain('EXPECT_ADMIN_ACCESS="${EXPECT_ADMIN_ACCESS:-auto}"');
63
+ expect(securitySource).toContain("assert_admin_access_policy");
64
+ expect(securitySource).not.toContain('assert_status "Admin status → 200" "200" "$ADMIN_STATUS"');
65
+ expect(securitySource).not.toContain('assert_status "Admin tables → 200" "200" "$ADMIN_TABLES"');
66
+ });
67
+ });
68
+
69
+ describe("AI template smoke fixture regression", () => {
70
+ it("anchors fixture dependency messaging to a normal repo install", () => {
71
+ const source = readRepoFile(aiTemplateSmokePath);
72
+
73
+ expect(source).toContain('createRequire(join(fixtureWorkspacePath, "package.json"))');
74
+ expect(source).toContain("Run `pnpm install` at the repo root first.");
75
+ expect(source).not.toContain("pnpm install --filter ./apps/test-ai-template-fixture");
76
+ });
77
+ });
78
+
79
+ describe("cowbox DEV test path regression", () => {
80
+ it("exposes a dedicated DEV Linux test entrypoint for cowbox", () => {
81
+ const packageJson = readRepoFile(join(repoRoot, "package.json"));
82
+ const scriptSource = readRepoFile(join(repoRoot, "scripts/test-cowbox-dev.sh"));
83
+
84
+ expect(packageJson).toContain('"test:cowbox:dev": "./scripts/test-cowbox-dev.sh"');
85
+ expect(scriptSource).toContain('DEV_HOST="${GENCOW_DEV_HOST:-root@100.114.201.127}"');
86
+ expect(scriptSource).toContain("cargo test -- --nocapture");
87
+ expect(scriptSource).toContain("./target/release/cowbox check");
88
+ expect(scriptSource).toContain("./target/release/cowbox run");
89
+ expect(scriptSource).toContain("allowed write smoke");
90
+ expect(scriptSource).toContain("denied write smoke");
91
+ });
92
+ });
@@ -1,3 +1,5 @@
1
+ import { toPascalCase } from "./name-casing.mjs";
2
+
1
3
  export const CORE_GENERATED_NAMESPACES = new Set(["workflows"]);
2
4
 
3
5
  export function buildApiObject({ queries = [], mutations = [] }) {
@@ -39,13 +41,18 @@ export function buildGeneratedApiTs({
39
41
  namespaceImportPaths = {},
40
42
  singleFileImportPath = "./index.ts",
41
43
  coreGeneratedNamespaces = CORE_GENERATED_NAMESPACES,
44
+ useCrudTypes = false,
45
+ crudTypeImportPath = "./crud.d.ts",
46
+ crudTypeNamespaces = [],
42
47
  }) {
43
- const hasNamespaceFiles = Object.keys(namespaceImportPaths).length > 0;
48
+ const hasNamespaceFiles = !useCrudTypes && Object.keys(namespaceImportPaths).length > 0;
44
49
 
45
50
  let out = `/**\n * Generated by Gencow\n * Do not edit this file manually.\n */\n`;
46
51
  out += `import { defineQuery, defineMutation } from "@gencow/react";\n\n`;
47
52
 
48
- if (hasNamespaceFiles) {
53
+ if (useCrudTypes) {
54
+ out += `import type * as Crud from "${crudTypeImportPath}";\n`;
55
+ } else if (hasNamespaceFiles) {
49
56
  for (const [namespace, importPath] of Object.entries(namespaceImportPaths)) {
50
57
  out += `import type * as ${namespace} from "${importPath}";\n`;
51
58
  }
@@ -55,6 +62,8 @@ export function buildGeneratedApiTs({
55
62
 
56
63
  out += `\nexport const api = {\n`;
57
64
  for (const [namespace, fns] of Object.entries(apiObj)) {
65
+ const namespaceBase = toPascalCase(namespace);
66
+ const hasCrudTypesForNamespace = useCrudTypes && crudTypeNamespaces.includes(namespace);
58
67
  const typedReference = canUseTypedReference({
59
68
  namespace,
60
69
  hasNamespaceFiles,
@@ -65,7 +74,15 @@ export function buildGeneratedApiTs({
65
74
  out += ` ${namespace}: {\n`;
66
75
  for (const queryName of fns.queries) {
67
76
  const tsExport = toTsExportName(queryName);
68
- if (typedReference && hasNamespaceFiles) {
77
+ if (hasCrudTypesForNamespace) {
78
+ if (queryName === "list") {
79
+ out += ` ${queryName}: defineQuery<{ _args?: Crud.${namespaceBase}ListInput; _return?: Crud.${namespaceBase}ListOutput }>("${namespace}.${queryName}"),\n`;
80
+ } else if (queryName === "get") {
81
+ out += ` ${queryName}: defineQuery<{ _args?: Crud.${namespaceBase}GetInput; _return?: Crud.${namespaceBase}Select }>("${namespace}.${queryName}"),\n`;
82
+ } else {
83
+ out += ` ${queryName}: defineQuery("${namespace}.${queryName}"),\n`;
84
+ }
85
+ } else if (typedReference && hasNamespaceFiles) {
69
86
  out += ` ${queryName}: defineQuery<typeof ${namespace}.${tsExport}>("${namespace}.${queryName}"),\n`;
70
87
  } else if (typedReference) {
71
88
  out += ` ${queryName}: defineQuery<typeof _all.${tsExport}>("${namespace}.${queryName}"),\n`;
@@ -75,7 +92,17 @@ export function buildGeneratedApiTs({
75
92
  }
76
93
  for (const mutationName of fns.mutations) {
77
94
  const tsExport = toTsExportName(mutationName);
78
- if (typedReference && hasNamespaceFiles) {
95
+ if (hasCrudTypesForNamespace) {
96
+ if (mutationName === "create") {
97
+ out += ` ${mutationName}: defineMutation<{ _args?: Crud.${namespaceBase}CreateInput; _return?: Crud.${namespaceBase}Select }>("${namespace}.${mutationName}"),\n`;
98
+ } else if (mutationName === "update") {
99
+ out += ` ${mutationName}: defineMutation<{ _args?: Crud.${namespaceBase}UpdateInput; _return?: Crud.${namespaceBase}Select }>("${namespace}.${mutationName}"),\n`;
100
+ } else if (mutationName === "remove") {
101
+ out += ` ${mutationName}: defineMutation<{ _args?: Crud.${namespaceBase}RemoveInput; _return?: unknown }>("${namespace}.${mutationName}"),\n`;
102
+ } else {
103
+ out += ` ${mutationName}: defineMutation("${namespace}.${mutationName}"),\n`;
104
+ }
105
+ } else if (typedReference && hasNamespaceFiles) {
79
106
  out += ` ${mutationName}: defineMutation<typeof ${namespace}.${tsExport}>("${namespace}.${mutationName}"),\n`;
80
107
  } else if (typedReference) {
81
108
  out += ` ${mutationName}: defineMutation<typeof _all.${tsExport}>("${namespace}.${mutationName}"),\n`;