convex-rust 0.0.0-stage → 0.1.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 (90) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +137 -2
  3. package/dist/cli/bin.d.ts +2 -0
  4. package/dist/cli/bin.js +200 -0
  5. package/dist/cli/build.d.ts +146 -0
  6. package/dist/cli/build.js +902 -0
  7. package/dist/cli/demangle.d.ts +7 -0
  8. package/dist/cli/demangle.js +328 -0
  9. package/dist/cli/diagnostics.d.ts +34 -0
  10. package/dist/cli/diagnostics.js +296 -0
  11. package/dist/cli/generate.d.ts +29 -0
  12. package/dist/cli/generate.js +82 -0
  13. package/dist/cli/manifest.d.ts +24 -0
  14. package/dist/cli/manifest.js +75 -0
  15. package/dist/cli/rustApi.d.ts +78 -0
  16. package/dist/cli/rustApi.js +609 -0
  17. package/dist/cli/scaffold.d.ts +22 -0
  18. package/dist/cli/scaffold.js +114 -0
  19. package/dist/cli/shim.d.ts +30 -0
  20. package/dist/cli/shim.js +140 -0
  21. package/dist/cli/size.d.ts +17 -0
  22. package/dist/cli/size.js +73 -0
  23. package/dist/cli/split.d.ts +20 -0
  24. package/dist/cli/split.js +140 -0
  25. package/dist/cli/symbols.d.ts +30 -0
  26. package/dist/cli/symbols.js +133 -0
  27. package/dist/cli/toolchain.d.ts +53 -0
  28. package/dist/cli/toolchain.js +167 -0
  29. package/dist/cli/validators.d.ts +6 -0
  30. package/dist/cli/validators.js +66 -0
  31. package/dist/cli/wasisdk.d.ts +82 -0
  32. package/dist/cli/wasisdk.js +220 -0
  33. package/dist/cli/wasmHints.d.ts +29 -0
  34. package/dist/cli/wasmHints.js +172 -0
  35. package/dist/runtime/base64.d.ts +4 -0
  36. package/dist/runtime/base64.js +77 -0
  37. package/dist/runtime/crypto.d.ts +7 -0
  38. package/dist/runtime/crypto.js +421 -0
  39. package/dist/runtime/fastjson.d.ts +3 -0
  40. package/dist/runtime/fastjson.js +170 -0
  41. package/dist/runtime/host.d.ts +73 -0
  42. package/dist/runtime/host.js +1230 -0
  43. package/dist/runtime/http.d.ts +40 -0
  44. package/dist/runtime/http.js +157 -0
  45. package/dist/runtime/index.d.ts +2 -0
  46. package/dist/runtime/index.js +1 -0
  47. package/dist/runtime/intl.d.ts +5 -0
  48. package/dist/runtime/intl.js +69 -0
  49. package/dist/runtime/tmpfs.d.ts +129 -0
  50. package/dist/runtime/tmpfs.js +624 -0
  51. package/dist/runtime/wasi.d.ts +33 -0
  52. package/dist/runtime/wasi.js +216 -0
  53. package/package.json +57 -4
  54. package/rust/convex_fn/Cargo.toml +32 -0
  55. package/rust/convex_fn/LICENSE +201 -0
  56. package/rust/convex_fn/build.rs +3 -0
  57. package/rust/convex_fn/src/args.rs +416 -0
  58. package/rust/convex_fn/src/crypto.rs +237 -0
  59. package/rust/convex_fn/src/ctx.rs +1044 -0
  60. package/rust/convex_fn/src/dns.rs +356 -0
  61. package/rust/convex_fn/src/error.rs +179 -0
  62. package/rust/convex_fn/src/executor.rs +577 -0
  63. package/rust/convex_fn/src/fanout.rs +106 -0
  64. package/rust/convex_fn/src/fs.rs +19 -0
  65. package/rust/convex_fn/src/function_ref.rs +219 -0
  66. package/rust/convex_fn/src/http.rs +441 -0
  67. package/rust/convex_fn/src/intl.rs +75 -0
  68. package/rust/convex_fn/src/lib.rs +242 -0
  69. package/rust/convex_fn/src/logging.rs +222 -0
  70. package/rust/convex_fn/src/native.rs +360 -0
  71. package/rust/convex_fn/src/net.rs +122 -0
  72. package/rust/convex_fn/src/sys.rs +178 -0
  73. package/rust/convex_fn/src/values.rs +121 -0
  74. package/rust/convex_fn/src/wire.rs +579 -0
  75. package/rust/convex_fn_macros/Cargo.toml +13 -0
  76. package/rust/convex_fn_macros/LICENSE +201 -0
  77. package/rust/convex_fn_macros/src/derive.rs +632 -0
  78. package/rust/convex_fn_macros/src/lib.rs +338 -0
  79. package/rust/convex_reqwest/Cargo.toml +69 -0
  80. package/rust/convex_reqwest/LICENSE +201 -0
  81. package/rust/convex_reqwest/src/blocking.rs +287 -0
  82. package/rust/convex_reqwest/src/body.rs +152 -0
  83. package/rust/convex_reqwest/src/client.rs +359 -0
  84. package/rust/convex_reqwest/src/config_types.rs +150 -0
  85. package/rust/convex_reqwest/src/error.rs +241 -0
  86. package/rust/convex_reqwest/src/into_url.rs +57 -0
  87. package/rust/convex_reqwest/src/lib.rs +42 -0
  88. package/rust/convex_reqwest/src/multipart.rs +140 -0
  89. package/rust/convex_reqwest/src/request.rs +358 -0
  90. package/rust/convex_reqwest/src/response.rs +310 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Function-name map for the deployed wasm. The shipped wasm has its name section stripped (wasm-opt without -g),
3
+ * so the names are captured at build time and kept next to cargo's target dir, never in the deployed bundle.
4
+ * `convex-rust symbolize` uses them to rewrite `wasm-function[123]:0x...` frames from Convex logs.
5
+ */
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { readLeb } from "./size.js";
9
+ import { demangle } from "./demangle.js";
10
+ const dec = new TextDecoder();
11
+ /** Function names (subsection 1) of the "name" custom section, by function index. */
12
+ export function parseNameSection(wasm) {
13
+ const out = new Map();
14
+ let pos = 8;
15
+ while (pos < wasm.length) {
16
+ const id = wasm[pos++];
17
+ let size;
18
+ [size, pos] = readLeb(wasm, pos);
19
+ const end = pos + size;
20
+ if (id === 0) {
21
+ let len;
22
+ let p;
23
+ [len, p] = readLeb(wasm, pos);
24
+ if (dec.decode(wasm.subarray(p, p + len)) === "name") {
25
+ p += len;
26
+ while (p < end) {
27
+ const sub = wasm[p++];
28
+ let subSize;
29
+ [subSize, p] = readLeb(wasm, p);
30
+ const subEnd = p + subSize;
31
+ if (sub === 1) {
32
+ let n;
33
+ [n, p] = readLeb(wasm, p);
34
+ for (let i = 0; i < n; i++) {
35
+ let idx;
36
+ let l;
37
+ [idx, p] = readLeb(wasm, p);
38
+ [l, p] = readLeb(wasm, p);
39
+ out.set(idx, dec.decode(wasm.subarray(p, p + l)));
40
+ p += l;
41
+ }
42
+ }
43
+ p = subEnd;
44
+ }
45
+ }
46
+ }
47
+ pos = end;
48
+ }
49
+ return out;
50
+ }
51
+ /** Parses `wasm-opt --print-function-map` output (`index:name` per line). */
52
+ export function parseFunctionMap(text) {
53
+ const out = new Map();
54
+ for (const line of text.split("\n")) {
55
+ const m = line.match(/^(\d+):(.+)$/);
56
+ if (m)
57
+ out.set(Number(m[1]), m[2].trim());
58
+ }
59
+ return out;
60
+ }
61
+ export function buildSymbolMap(names, info) {
62
+ const out = {};
63
+ for (const [idx, n] of [...names].sort((a, b) => a[0] - b[0]))
64
+ out[String(idx)] = demangle(n);
65
+ return { version: 1, wasm: info.wasm, modules: info.modules, names: out };
66
+ }
67
+ export function symbolsDir(targetDir) {
68
+ return path.join(targetDir, "convex-rust-symbols");
69
+ }
70
+ /** Replaces the maps in `dir` with `maps` (stale ones removed). */
71
+ export function writeSymbolMaps(dir, maps) {
72
+ fs.mkdirSync(dir, { recursive: true });
73
+ const keep = new Set(maps.map((m) => `${m.wasm}.json`));
74
+ for (const f of fs.readdirSync(dir)) {
75
+ if (f.endsWith(".wasm.json") && !keep.has(f))
76
+ fs.rmSync(path.join(dir, f), { force: true });
77
+ }
78
+ return maps.map((m) => {
79
+ const file = path.join(dir, `${m.wasm}.json`);
80
+ fs.writeFileSync(file, JSON.stringify(m));
81
+ return file;
82
+ });
83
+ }
84
+ export function readSymbolMaps(dir) {
85
+ let files = [];
86
+ try {
87
+ files = fs.readdirSync(dir).filter((f) => f.endsWith(".wasm.json")).sort();
88
+ }
89
+ catch {
90
+ return [];
91
+ }
92
+ return files.map((f) => JSON.parse(fs.readFileSync(path.join(dir, f), "utf8")));
93
+ }
94
+ const FRAME = /wasm-function\[(\d+)\](:0x[0-9a-fA-F]+)?/g;
95
+ /**
96
+ * Rewrites `wasm-function[N]:0xOFF` into `name (wasm-function[N]:0xOFF)`. With several maps, `module` picks one;
97
+ * otherwise a module named like `rust/<module>` in the text does, if exactly one does.
98
+ */
99
+ export function symbolize(text, maps, module) {
100
+ if (maps.length === 0)
101
+ return { text, replaced: 0, note: "no symbol maps found (run `convex-rust build` first)" };
102
+ let map;
103
+ let note;
104
+ if (maps.length === 1)
105
+ map = maps[0];
106
+ else if (module) {
107
+ map = maps.find((m) => m.modules.includes(module) || m.wasm.replace(/\.wasm$/, "").endsWith(`.${module.replace(/\//g, "__")}`));
108
+ if (!map)
109
+ note = `no symbol map for module "${module}" (have: ${maps.flatMap((m) => m.modules).join(", ")})`;
110
+ }
111
+ else {
112
+ const seen = new Set();
113
+ for (const m of maps)
114
+ for (const mod of m.modules)
115
+ if (text.includes(`rust/${mod}`))
116
+ seen.add(m);
117
+ if (seen.size === 1)
118
+ map = [...seen][0];
119
+ else
120
+ note = `several wasm modules were built; pass --module <name> (one of: ${maps.flatMap((m) => m.modules).join(", ")})`;
121
+ }
122
+ if (!map)
123
+ return { text, replaced: 0, ...(note ? { note } : {}) };
124
+ let replaced = 0;
125
+ const out = text.replace(FRAME, (all, idx) => {
126
+ const n = map.names[idx];
127
+ if (!n)
128
+ return all;
129
+ replaced++;
130
+ return `${n} (${all})`;
131
+ });
132
+ return { text: out, replaced };
133
+ }
@@ -0,0 +1,53 @@
1
+ export declare const RUST_TARGET = "wasm32-wasip1";
2
+ export interface ExecResult {
3
+ status: number | null;
4
+ stdout: string;
5
+ stderr: string;
6
+ error?: {
7
+ code?: string;
8
+ } | Error | undefined;
9
+ }
10
+ export type Exec = (cmd: string, args: string[], opts: {
11
+ cwd: string;
12
+ inherit?: boolean;
13
+ }) => ExecResult;
14
+ export declare const defaultExec: Exec;
15
+ export type ToolchainStatus = {
16
+ ok: true;
17
+ } | {
18
+ ok: false;
19
+ kind: "no-cargo" | "no-rustup" | "no-toolchain" | "no-target";
20
+ message: string;
21
+ /** Command that fixes it (for no-target / no-toolchain). */
22
+ fix?: string[];
23
+ };
24
+ /** `channel` from `<rustDir>/rust-toolchain.toml`, if any. */
25
+ export declare function readToolchainChannel(rustDir: string): string | null;
26
+ export declare function checkRustToolchain(rustDir: string, exec?: Exec, target?: string): ToolchainStatus;
27
+ /** Install hint for binaryen's `wasm-opt` for the current platform. */
28
+ export declare function wasmOptInstallHint(platform?: string): string;
29
+ /**
30
+ * When the toolchain check failed with a fixable cause (`status.fix`) and a person is at the terminal,
31
+ * offers to run the fix (`Run ... now? (Y/n)`), then re-checks. Never installs anything without an
32
+ * answer: without a TTY (CI) or when declined the original failing status is returned.
33
+ */
34
+ export declare function offerToolchainFix(rustDir: string, status: ToolchainStatus, o?: {
35
+ interactive?: boolean;
36
+ ask?: (question: string) => Promise<string>;
37
+ exec?: Exec;
38
+ log?: (m: string) => void;
39
+ }): Promise<ToolchainStatus>;
40
+ export interface DoctorLine {
41
+ ok: boolean;
42
+ /** Missing but not fatal (wasm-opt). */
43
+ optional?: boolean;
44
+ text: string;
45
+ }
46
+ /** Environment report for `convex-rust doctor`. `ok` is false when a required piece is missing. */
47
+ export declare function doctorReport(rustDir: string | null, exec?: Exec, extra?: {
48
+ convexFnPath?: string | undefined;
49
+ functionsDirExists?: boolean;
50
+ }): {
51
+ ok: boolean;
52
+ lines: DoctorLine[];
53
+ };
@@ -0,0 +1,167 @@
1
+ import path from "path";
2
+ import nodeFs from "fs";
3
+ import { spawnSync } from "child_process";
4
+ export const RUST_TARGET = "wasm32-wasip1";
5
+ export const defaultExec = (cmd, args, opts) => {
6
+ const r = spawnSync(cmd, args, {
7
+ cwd: opts.cwd,
8
+ encoding: "utf8",
9
+ stdio: opts.inherit ? "inherit" : ["ignore", "pipe", "pipe"],
10
+ });
11
+ return {
12
+ status: r.status,
13
+ stdout: r.stdout ?? "",
14
+ stderr: r.stderr ?? "",
15
+ error: r.error,
16
+ };
17
+ };
18
+ /** `channel` from `<rustDir>/rust-toolchain.toml`, if any. */
19
+ export function readToolchainChannel(rustDir) {
20
+ for (const f of ["rust-toolchain.toml", "rust-toolchain"]) {
21
+ try {
22
+ const text = nodeFs.readFileSync(path.join(rustDir, f), "utf8");
23
+ const m = text.match(/^\s*channel\s*=\s*"([^"]+)"/m);
24
+ if (m)
25
+ return m[1];
26
+ const bare = text.trim();
27
+ if (f === "rust-toolchain" && bare && !bare.includes("\n"))
28
+ return bare;
29
+ }
30
+ catch {
31
+ // not present
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ const RUSTUP_URL = "https://rustup.rs";
37
+ export function checkRustToolchain(rustDir, exec = defaultExec, target = RUST_TARGET) {
38
+ const notFound = (r) => r.error?.code === "ENOENT";
39
+ const cargo = exec("cargo", ["--version"], { cwd: rustDir });
40
+ const rustup = exec("rustup", ["--version"], { cwd: rustDir });
41
+ if (notFound(rustup)) {
42
+ if (notFound(cargo)) {
43
+ return {
44
+ ok: false,
45
+ kind: "no-cargo",
46
+ message: `Neither \`cargo\` nor \`rustup\` was found. Install Rust with rustup: ${RUSTUP_URL}\nThen re-run this command (the ${target} target will be set up for you).`,
47
+ };
48
+ }
49
+ return {
50
+ ok: false,
51
+ kind: "no-rustup",
52
+ message: `\`rustup\` was not found, so the ${target} target can't be verified. Install rustup: ${RUSTUP_URL}\n(If you manage Rust another way, make sure the ${target} target is installed.)`,
53
+ };
54
+ }
55
+ if (notFound(cargo)) {
56
+ return {
57
+ ok: false,
58
+ kind: "no-cargo",
59
+ message: `\`cargo\` was not found. Install Rust with rustup: ${RUSTUP_URL}`,
60
+ };
61
+ }
62
+ const channel = readToolchainChannel(rustDir);
63
+ const toolchainArgs = channel ? ["--toolchain", channel] : [];
64
+ const list = exec("rustup", ["target", "list", "--installed", ...toolchainArgs], { cwd: rustDir });
65
+ if (list.status !== 0) {
66
+ const fix = [
67
+ "rustup",
68
+ "toolchain",
69
+ "install",
70
+ channel ?? "stable",
71
+ "--profile",
72
+ "minimal",
73
+ "--target",
74
+ target,
75
+ ];
76
+ if (/not installed|no such toolchain|is not installed/i.test(list.stderr)) {
77
+ return {
78
+ ok: false,
79
+ kind: "no-toolchain",
80
+ message: `The Rust toolchain \`${channel ?? "stable"}\` is not installed. Run:\n ${fix.join(" ")}`,
81
+ fix,
82
+ };
83
+ }
84
+ // Unknown rustup failure: don't block the build, cargo will report it.
85
+ return { ok: true };
86
+ }
87
+ const installed = list.stdout.split("\n").map((l) => l.trim());
88
+ if (installed.includes(target))
89
+ return { ok: true };
90
+ const fix = ["rustup", "target", "add", target, ...toolchainArgs];
91
+ return {
92
+ ok: false,
93
+ kind: "no-target",
94
+ message: `The ${target} Rust target is not installed${channel ? ` for toolchain ${channel}` : ""}. Run:\n ${fix.join(" ")}`,
95
+ fix,
96
+ };
97
+ }
98
+ /** Install hint for binaryen's `wasm-opt` for the current platform. */
99
+ export function wasmOptInstallHint(platform = process.platform) {
100
+ if (platform === "darwin")
101
+ return "brew install binaryen";
102
+ if (platform === "win32")
103
+ return "npm install --save-dev binaryen (or scoop install binaryen)";
104
+ return "apt install binaryen / dnf install binaryen / npm install --save-dev binaryen";
105
+ }
106
+ /**
107
+ * When the toolchain check failed with a fixable cause (`status.fix`) and a person is at the terminal,
108
+ * offers to run the fix (`Run ... now? (Y/n)`), then re-checks. Never installs anything without an
109
+ * answer: without a TTY (CI) or when declined the original failing status is returned.
110
+ */
111
+ export async function offerToolchainFix(rustDir, status, o = {}) {
112
+ if (status.ok || !status.fix)
113
+ return status;
114
+ const exec = o.exec ?? defaultExec;
115
+ const interactive = o.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
116
+ if (!interactive)
117
+ return status;
118
+ const ask = o.ask ??
119
+ (async (q) => {
120
+ const rl = (await import("node:readline/promises")).createInterface({ input: process.stdin, output: process.stderr });
121
+ try {
122
+ return await rl.question(q);
123
+ }
124
+ finally {
125
+ rl.close();
126
+ }
127
+ });
128
+ const cmd = status.fix.join(" ");
129
+ const answer = (await ask(`${status.message.split("\n")[0]}\nRun \`${cmd}\` now? (Y/n) `)).trim().toLowerCase();
130
+ if (answer !== "" && answer !== "y" && answer !== "yes")
131
+ return status;
132
+ const r = exec(status.fix[0], status.fix.slice(1), { cwd: rustDir, inherit: true });
133
+ if (r.status !== 0)
134
+ return status;
135
+ return checkRustToolchain(rustDir, exec);
136
+ }
137
+ /** Environment report for `convex-rust doctor`. `ok` is false when a required piece is missing. */
138
+ export function doctorReport(rustDir, exec = defaultExec, extra = {}) {
139
+ const lines = [];
140
+ const ver = (cmd, args = ["--version"]) => {
141
+ const r = exec(cmd, args, { cwd: rustDir ?? process.cwd() });
142
+ return r.status === 0 ? r.stdout.trim().split("\n")[0] : null;
143
+ };
144
+ const cargo = ver("cargo");
145
+ lines.push({ ok: cargo !== null, text: cargo ? `cargo: ${cargo}` : "cargo not found. Install Rust with rustup: https://rustup.rs" });
146
+ const rustup = ver("rustup");
147
+ lines.push({ ok: rustup !== null, text: rustup ? `rustup: ${rustup.split(" (")[0]}` : "rustup not found. Install it from https://rustup.rs" });
148
+ if (rustDir && cargo && rustup) {
149
+ const st = checkRustToolchain(rustDir, exec);
150
+ const channel = readToolchainChannel(rustDir);
151
+ lines.push({
152
+ ok: st.ok,
153
+ text: st.ok ? `toolchain ${channel ?? "default"} with ${RUST_TARGET}` : st.message,
154
+ });
155
+ }
156
+ if (extra.convexFnPath !== undefined) {
157
+ const has = nodeFs.existsSync(path.join(extra.convexFnPath, "Cargo.toml"));
158
+ lines.push({ ok: has, text: has ? `convex_fn: ${extra.convexFnPath}` : `convex_fn not found at ${extra.convexFnPath}. Reinstall convex-rust (npm install convex-rust).` });
159
+ }
160
+ const wo = ver("wasm-opt");
161
+ lines.push({
162
+ ok: wo !== null,
163
+ optional: true,
164
+ text: wo ? `wasm-opt: ${wo}` : `wasm-opt not found (optional: smaller wasm). Install: ${wasmOptInstallHint()}`,
165
+ });
166
+ return { ok: lines.every((l) => l.ok || l.optional), lines };
167
+ }
@@ -0,0 +1,6 @@
1
+ export declare function validatorToTs(j: any): string;
2
+ /** `{ a: v.string(), b: v.optional(v.number()) }` */
3
+ export declare function objectFields(fields: Record<string, {
4
+ fieldType: any;
5
+ optional: boolean;
6
+ }>): string;
@@ -0,0 +1,66 @@
1
+ // ValidatorJson (the shape `v.*().json` produces) -> TypeScript source using `v`.
2
+ const IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3
+ function key(k) {
4
+ return IDENT.test(k) ? k : JSON.stringify(k);
5
+ }
6
+ function int64FromJson(b64) {
7
+ const bytes = Buffer.from(b64, "base64");
8
+ if (bytes.length !== 8)
9
+ throw new Error(`bad $integer literal ${b64}`);
10
+ return bytes.readBigInt64LE(0);
11
+ }
12
+ function literal(value) {
13
+ if (value === null)
14
+ return "v.null()";
15
+ if (value && typeof value === "object" && typeof value.$integer === "string") {
16
+ return `v.literal(${int64FromJson(value.$integer)}n)`;
17
+ }
18
+ if (typeof value === "string" || typeof value === "boolean") {
19
+ return `v.literal(${JSON.stringify(value)})`;
20
+ }
21
+ if (typeof value === "number")
22
+ return `v.literal(${JSON.stringify(value)})`;
23
+ throw new Error(`unsupported literal validator ${JSON.stringify(value)}`);
24
+ }
25
+ export function validatorToTs(j) {
26
+ switch (j?.type) {
27
+ case "null":
28
+ return "v.null()";
29
+ case "number":
30
+ return "v.number()";
31
+ case "bigint":
32
+ return "v.int64()";
33
+ case "boolean":
34
+ return "v.boolean()";
35
+ case "string":
36
+ return "v.string()";
37
+ case "bytes":
38
+ return "v.bytes()";
39
+ case "any":
40
+ return "v.any()";
41
+ case "literal":
42
+ return literal(j.value);
43
+ case "id":
44
+ return `v.id(${JSON.stringify(j.tableName)})`;
45
+ case "array":
46
+ return `v.array(${validatorToTs(j.value)})`;
47
+ case "record":
48
+ return `v.record(${validatorToTs(j.keys)}, ${validatorToTs(j.values.fieldType)})`;
49
+ case "union":
50
+ if (j.value.length === 1)
51
+ return validatorToTs(j.value[0]);
52
+ return `v.union(${j.value.map(validatorToTs).join(", ")})`;
53
+ case "object":
54
+ return `v.object(${objectFields(j.value)})`;
55
+ default:
56
+ throw new Error(`unsupported validator type ${JSON.stringify(j?.type)}`);
57
+ }
58
+ }
59
+ /** `{ a: v.string(), b: v.optional(v.number()) }` */
60
+ export function objectFields(fields) {
61
+ const parts = Object.entries(fields).map(([name, f]) => {
62
+ const t = validatorToTs(f.fieldType);
63
+ return `${key(name)}: ${f.optional ? `v.optional(${t})` : t}`;
64
+ });
65
+ return parts.length === 0 ? "{}" : `{ ${parts.join(", ")} }`;
66
+ }
@@ -0,0 +1,82 @@
1
+ export declare const WASI_SDK_VERSION = "34.0";
2
+ /** Pinned release archives (sha256 as published by GitHub for the release assets). */
3
+ export declare const WASI_SDK_ASSETS: Record<string, {
4
+ file: string;
5
+ sha256: string;
6
+ size: number;
7
+ }>;
8
+ type Env = Record<string, string | undefined>;
9
+ /** `~/.cache/convex-rust` (override: `CONVEX_RUST_CACHE_DIR`; honours `XDG_CACHE_HOME`). */
10
+ export declare function cacheRoot(env?: Env): string;
11
+ export declare function cachedSdkDir(env?: Env): string;
12
+ /** True when `dir` looks like a wasi-sdk (clang + sysroot). */
13
+ export declare function isWasiSdk(dir: string): boolean;
14
+ export type SdkSource = "env" | "cache";
15
+ /** `WASI_SDK_PATH` first, then the cache dir. Null when neither holds a usable SDK. */
16
+ export declare function findWasiSdk(env?: Env): {
17
+ dir: string;
18
+ source: SdkSource;
19
+ } | null;
20
+ /**
21
+ * Environment that points cc-rs at the SDK. Variables the user already set are left alone.
22
+ * `clang` is used as `ar` fallback only when `llvm-ar` is missing.
23
+ */
24
+ export declare function wasiSdkEnv(sdkDir: string, env?: Env): Record<string, string>;
25
+ /** Subset of `cargo metadata` used to spot C build scripts. */
26
+ export interface MetadataLite {
27
+ packages: {
28
+ id: string;
29
+ name: string;
30
+ }[];
31
+ resolve?: {
32
+ nodes: {
33
+ id: string;
34
+ deps: {
35
+ name: string;
36
+ pkg: string;
37
+ dep_kinds?: {
38
+ kind: string | null;
39
+ }[];
40
+ }[];
41
+ }[];
42
+ } | null;
43
+ }
44
+ /**
45
+ * Crates in the resolved (target-filtered) graph whose build script uses a C compiler driver
46
+ * (`cc`, `cmake`, `bindgen` is not counted). Their names, sorted.
47
+ */
48
+ export declare function crateNamesWithCBuild(meta: MetadataLite): string[];
49
+ export declare function assetForPlatform(platform?: string, arch?: string): {
50
+ file: string;
51
+ sha256: string;
52
+ size: number;
53
+ } | null;
54
+ export interface InstallOptions {
55
+ env?: Env;
56
+ log?: (m: string) => void;
57
+ /** Test hook: replaces `fetch`. */
58
+ fetchImpl?: typeof fetch;
59
+ }
60
+ /**
61
+ * Downloads the pinned wasi-sdk into the cache dir (sha256 checked) and returns its directory.
62
+ * An archive already present in `<cache>/dl` with the right hash is reused.
63
+ */
64
+ export declare function installWasiSdk(opts?: InstallOptions): Promise<string>;
65
+ export type WasiSdkMode = "auto" | "always" | "never";
66
+ /** `--wasi-sdk` / `--no-wasi-sdk` / `CONVEX_RUST_WASI_SDK=1|0`. Default: auto (download only if a C build is detected). */
67
+ export declare function wasiSdkMode(flags: {
68
+ wasiSdk?: boolean;
69
+ noWasiSdk?: boolean;
70
+ }, env?: Env): WasiSdkMode;
71
+ /**
72
+ * Decides the cargo environment for C compilation. Uses an SDK that is already there (always, it is free);
73
+ * downloads one only when `mode` is "always", or "auto" and `cNames` (crates with a C build script) is non-empty.
74
+ */
75
+ export declare function prepareWasiSdk(args: {
76
+ mode: WasiSdkMode;
77
+ cNames: () => Promise<string[]> | string[];
78
+ env?: Env;
79
+ log: (m: string) => void;
80
+ install?: typeof installWasiSdk;
81
+ }): Promise<Record<string, string>>;
82
+ export {};