cloesce 0.0.3-fix.1 → 0.0.3-fix.3

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.
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env node
2
+ import { WASI } from "node:wasi";
3
+ import fs from "node:fs";
4
+ import { readFile } from "fs/promises";
5
+ import path from "node:path";
6
+ import { command, run, subcommands, flag, option, optional, string, } from "cmd-ts";
7
+ import { Project } from "ts-morph";
8
+ import { CidlExtractor } from "./extract.js";
9
+ import { ExtractorError, ExtractorErrorCode, getErrorInfo } from "../common.js";
10
+ const cmds = subcommands({
11
+ name: "cloesce",
12
+ cmds: {
13
+ compile: command({
14
+ name: "compile",
15
+ description: "Run through the full compilation process.",
16
+ args: {
17
+ debug: flag({
18
+ long: "debug",
19
+ short: "d",
20
+ description: "Show debug output",
21
+ }),
22
+ },
23
+ handler: async (args) => {
24
+ const config = loadCloesceConfig(process.cwd(), args.debug);
25
+ if (!config.workersUrl || !config.clientUrl) {
26
+ console.error("Error: `workersUrl` and `clientUrl` must be defined in cloesce.config.json");
27
+ process.exit(1);
28
+ }
29
+ // Creates a `cidl.json` file. Exits the process on failure.
30
+ await extract({ debug: args.debug });
31
+ const root = process.cwd();
32
+ const outputDir = config.outputDir ?? ".generated";
33
+ const allConfig = {
34
+ name: "all",
35
+ wasmFile: "generator.wasm",
36
+ args: [
37
+ "generate",
38
+ "all",
39
+ path.join(outputDir, "cidl.json"),
40
+ path.join(root, "wrangler.toml"),
41
+ path.join(root, "migrations/migrations.sql"),
42
+ path.join(outputDir, "workers.ts"),
43
+ path.join(outputDir, "client.ts"),
44
+ config.clientUrl,
45
+ config.workersUrl,
46
+ ],
47
+ };
48
+ // Runs a generator command. Exits the process on failure.
49
+ await generate(allConfig);
50
+ },
51
+ }),
52
+ wrangler: command({
53
+ name: "wrangler",
54
+ description: "Generate wrangler.toml configuration",
55
+ args: {},
56
+ handler: async () => {
57
+ const root = process.cwd();
58
+ await generate({
59
+ name: "wrangler",
60
+ wasmFile: "generator.wasm",
61
+ args: ["generate", "wrangler", path.join(root, "wrangler.toml")],
62
+ });
63
+ },
64
+ }),
65
+ d1: command({
66
+ name: "d1",
67
+ description: "Generate database schema",
68
+ args: {},
69
+ handler: async () => {
70
+ const config = loadCloesceConfig(process.cwd());
71
+ const root = process.cwd();
72
+ const outputDir = config.outputDir ?? ".generated";
73
+ await generate({
74
+ name: "d1",
75
+ wasmFile: "generator.wasm",
76
+ args: [
77
+ "generate",
78
+ "d1",
79
+ path.join(outputDir, "cidl.json"),
80
+ path.join(root, "migrations/migrations.sql"),
81
+ ],
82
+ });
83
+ },
84
+ }),
85
+ workers: command({
86
+ name: "workers",
87
+ description: "Generate workers TypeScript",
88
+ args: {},
89
+ handler: async () => {
90
+ const config = loadCloesceConfig(process.cwd());
91
+ const root = process.cwd();
92
+ const outputDir = config.outputDir ?? ".generated";
93
+ if (!config.workersUrl) {
94
+ console.error("Error: workersUrl must be defined in cloesce-config.json");
95
+ process.exit(1);
96
+ }
97
+ await generate({
98
+ name: "workers",
99
+ wasmFile: "generator.wasm",
100
+ args: [
101
+ "generate",
102
+ "workers",
103
+ path.join(outputDir, "cidl.json"),
104
+ path.join(outputDir, "workers.ts"),
105
+ path.join(root, "wrangler.toml"),
106
+ config.workersUrl,
107
+ ],
108
+ });
109
+ },
110
+ }),
111
+ client: command({
112
+ name: "client",
113
+ description: "Generate client TypeScript",
114
+ args: {},
115
+ handler: async () => {
116
+ const config = loadCloesceConfig(process.cwd());
117
+ const outputDir = config.outputDir ?? ".generated";
118
+ if (!config.clientUrl) {
119
+ console.error("Error: clientUrl must be defined in cloesce-config.json");
120
+ process.exit(1);
121
+ }
122
+ await generate({
123
+ name: "client",
124
+ wasmFile: "generator.wasm",
125
+ args: [
126
+ "generate",
127
+ "client",
128
+ path.join(outputDir, "cidl.json"),
129
+ path.join(outputDir, "client.ts"),
130
+ config.clientUrl,
131
+ ],
132
+ });
133
+ },
134
+ }),
135
+ extract: command({
136
+ name: "extract",
137
+ description: "Extract models and write cidl.json only",
138
+ args: {
139
+ projectName: option({
140
+ long: "project-name",
141
+ type: optional(string),
142
+ description: "Project name",
143
+ }),
144
+ out: option({
145
+ long: "out",
146
+ short: "o",
147
+ type: optional(string),
148
+ description: "Output path (default: .generated/cidl.json)",
149
+ }),
150
+ inp: option({
151
+ long: "in",
152
+ short: "i",
153
+ type: optional(string),
154
+ description: "Input file or directory",
155
+ }),
156
+ location: option({
157
+ long: "location",
158
+ short: "l",
159
+ type: optional(string),
160
+ description: "Project directory (default: cwd)",
161
+ }),
162
+ truncateSourcePaths: flag({
163
+ long: "truncateSourcePaths",
164
+ description: "Sets all source paths to just their file name",
165
+ }),
166
+ debug: flag({
167
+ long: "debug",
168
+ short: "d",
169
+ description: "Show debug output",
170
+ }),
171
+ },
172
+ handler: async (args) => {
173
+ await extract({ ...args });
174
+ },
175
+ }),
176
+ },
177
+ });
178
+ async function extract(opts) {
179
+ const root = process.cwd();
180
+ const projectRoot = process.cwd();
181
+ const config = loadCloesceConfig(projectRoot, opts.debug);
182
+ const searchPaths = opts.inp ? [opts.inp] : (config.paths ?? [root]);
183
+ const outputDir = config.outputDir ?? ".generated";
184
+ const outPath = opts.out ?? path.join(outputDir, "cidl.json");
185
+ const truncate = opts.truncateSourcePaths ?? config.truncateSourcePaths ?? false;
186
+ const cloesceProjectName = opts.projectName ??
187
+ config.projectName ??
188
+ readPackageJsonProjectName(projectRoot);
189
+ const project = new Project({
190
+ compilerOptions: {
191
+ strictNullChecks: true,
192
+ },
193
+ });
194
+ findCloesceProject(root, searchPaths, project);
195
+ const fileCount = project.getSourceFiles().length;
196
+ if (fileCount === 0) {
197
+ new ExtractorError(ExtractorErrorCode.MissingFile);
198
+ }
199
+ if (opts.debug)
200
+ console.log(`Found ${fileCount} .cloesce.ts files`);
201
+ try {
202
+ const extractor = new CidlExtractor(cloesceProjectName, "v0.0.3");
203
+ const result = extractor.extract(project);
204
+ if (!result.ok) {
205
+ console.error(formatErr(result.value));
206
+ process.exit(1);
207
+ }
208
+ let ast = result.value;
209
+ if (truncate) {
210
+ ast.wrangler_env.source_path =
211
+ "./" + path.basename(ast.wrangler_env.source_path);
212
+ for (const model of Object.values(ast.models)) {
213
+ model.source_path =
214
+ "./" + path.basename(model.source_path);
215
+ }
216
+ if (ast.poos) {
217
+ for (const poo of Object.values(ast.poos)) {
218
+ poo.source_path =
219
+ "./" + path.basename(poo.source_path);
220
+ }
221
+ }
222
+ }
223
+ const json = JSON.stringify(ast, null, 4);
224
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
225
+ fs.writeFileSync(outPath, json);
226
+ console.log(`CIDL generated successfully at ${outPath}`);
227
+ return { outPath, projectName: cloesceProjectName };
228
+ }
229
+ catch (err) {
230
+ console.error("Critical uncaught error. Submit a ticket to https://github.com/bens-schreiber/cloesce: ", err?.message ?? err);
231
+ process.exit(1);
232
+ }
233
+ }
234
+ async function generate(config) {
235
+ const root = process.cwd();
236
+ // Look for wrangler.toml in the root directory
237
+ const wranglerPath = path.join(root, "wrangler.toml");
238
+ if (!fs.existsSync(wranglerPath)) {
239
+ fs.writeFileSync(wranglerPath, "");
240
+ }
241
+ const wasiArgs = config.args.map((arg) => {
242
+ // Skip URLs
243
+ if (/^[a-zA-Z]+:\/\//.test(arg)) {
244
+ return arg;
245
+ }
246
+ // Convert file path it to Unix style
247
+ if (arg.includes(path.sep) || arg.includes("/")) {
248
+ // Convert to relative path from root
249
+ const relativePath = path.isAbsolute(arg)
250
+ ? path.relative(root, arg)
251
+ : arg;
252
+ // Convert Windows separators to Unix and ensure leading slash
253
+ const unixPath = relativePath.replace(/\\/g, "/");
254
+ return unixPath.startsWith("/") ? unixPath : "/" + unixPath;
255
+ }
256
+ return arg;
257
+ });
258
+ const wasi = new WASI({
259
+ version: "preview1",
260
+ args: ["generate", ...wasiArgs],
261
+ env: { ...process.env, ...config.env },
262
+ preopens: { "/": root },
263
+ });
264
+ // Since `generator.wasm` is a binary and not a library, it must be
265
+ // manually read
266
+ const wasm = await readFile(new URL("../generator.wasm", import.meta.url));
267
+ const mod = await WebAssembly.compile(new Uint8Array(wasm));
268
+ let instance = await WebAssembly.instantiate(mod, {
269
+ wasi_snapshot_preview1: wasi.wasiImport,
270
+ });
271
+ try {
272
+ wasi.start(instance);
273
+ }
274
+ catch (err) {
275
+ console.error(`WASM execution failed for ${config.name}:`, err);
276
+ }
277
+ }
278
+ function loadCloesceConfig(root, debug = false) {
279
+ const configPath = path.join(root, "cloesce.config.json");
280
+ if (fs.existsSync(configPath)) {
281
+ try {
282
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
283
+ if (debug)
284
+ console.log(`Loaded config from ${configPath}`);
285
+ return config;
286
+ }
287
+ catch (err) {
288
+ console.warn(`Failed to parse cloesce.config.json: ${err}`);
289
+ }
290
+ }
291
+ return {};
292
+ }
293
+ function readPackageJsonProjectName(cwd) {
294
+ const pkgPath = path.join(cwd, "package.json");
295
+ let projectName = path.basename(cwd);
296
+ if (fs.existsSync(pkgPath)) {
297
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
298
+ projectName = pkg.name ?? projectName;
299
+ }
300
+ return projectName;
301
+ }
302
+ function findCloesceProject(root, searchPaths, project) {
303
+ for (const searchPath of searchPaths) {
304
+ let fullPath;
305
+ if (path.isAbsolute(searchPath) || searchPath.startsWith(root)) {
306
+ fullPath = path.normalize(searchPath);
307
+ }
308
+ else {
309
+ fullPath = path.resolve(root, searchPath);
310
+ }
311
+ if (!fs.existsSync(fullPath)) {
312
+ console.warn(`Warning: Path "${searchPath}" does not exist`);
313
+ continue;
314
+ }
315
+ const stats = fs.statSync(fullPath);
316
+ if (stats.isFile() && /\.cloesce\.ts$/i.test(fullPath)) {
317
+ project.addSourceFileAtPath(fullPath);
318
+ }
319
+ else if (stats.isDirectory()) {
320
+ walkDirectory(fullPath, project);
321
+ }
322
+ }
323
+ function walkDirectory(dir, project) {
324
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
325
+ const fullPath = path.join(dir, entry.name);
326
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
327
+ walkDirectory(fullPath, project);
328
+ }
329
+ else if (entry.isFile() && /\.cloesce\.ts$/i.test(entry.name)) {
330
+ project.addSourceFileAtPath(fullPath);
331
+ }
332
+ }
333
+ }
334
+ }
335
+ function formatErr(e) {
336
+ const { description, suggestion } = getErrorInfo(e.code);
337
+ const contextLine = e.context ? `Context: ${e.context}\n` : "";
338
+ const snippetLine = e.snippet ? `${e.snippet}\n\n` : "";
339
+ return `==== CLOESCE ERROR ====
340
+ Error [${ExtractorErrorCode[e.code]}]: ${description}
341
+ Phase: TypeScript IDL Extraction
342
+ ${contextLine}${snippetLine}Suggested fix: ${suggestion}`;
343
+ }
344
+ run(cmds, process.argv.slice(2)).catch((err) => {
345
+ console.error(err);
346
+ process.exit(1);
347
+ });
@@ -1,5 +1,5 @@
1
1
  import { SyntaxKind, } from "ts-morph";
2
- import { HttpVerb, left, right, ExtractorError, ExtractorErrorCode, } from "./common.js";
2
+ import { HttpVerb, left, right, ExtractorError, ExtractorErrorCode, } from "../common.js";
3
3
  import { TypeFormatFlags } from "typescript";
4
4
  var AttributeDecoratorKind;
5
5
  (function (AttributeDecoratorKind) {
Binary file
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { cloesce, modelsFromSql } from "./cloesce.js";
1
+ export { cloesce, modelsFromSql } from "./runtime/runtime.js";
2
2
  // Compiler hints
3
3
  export const D1 = () => { };
4
4
  export const PlainOldObject = () => { };