@onyx.dev/onyx-database 0.1.4

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,796 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var process = require('process');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var process__default = /*#__PURE__*/_interopDefault(process);
9
+
10
+ // src/config/defaults.ts
11
+ var DEFAULT_BASE_URL = "https://api.onyx.dev";
12
+ var sanitizeBaseUrl = (u) => u.replace(/\/+$/, "");
13
+
14
+ // src/errors/config-error.ts
15
+ var OnyxConfigError = class extends Error {
16
+ name = "OnyxConfigError";
17
+ constructor(message) {
18
+ super(message);
19
+ }
20
+ };
21
+
22
+ // src/config/chain.ts
23
+ var gProcess = globalThis.process;
24
+ var isNode = !!gProcess?.versions?.node;
25
+ var dbg = (...args) => {
26
+ if (gProcess?.env?.ONYX_DEBUG == "true") {
27
+ const fmt = (v) => {
28
+ if (typeof v === "string") return v;
29
+ try {
30
+ return JSON.stringify(v);
31
+ } catch {
32
+ return String(v);
33
+ }
34
+ };
35
+ gProcess.stderr?.write?.(`[onyx-config] ${args.map(fmt).join(" ")}
36
+ `);
37
+ }
38
+ };
39
+ function dropUndefined(obj) {
40
+ if (!obj) return {};
41
+ const out = {};
42
+ for (const [k, v] of Object.entries(obj)) {
43
+ if (v !== void 0) out[k] = v;
44
+ }
45
+ return out;
46
+ }
47
+ function readEnv(targetId) {
48
+ if (!gProcess?.env) return {};
49
+ const env = gProcess.env;
50
+ const pick = (...keys) => {
51
+ for (const k of keys) {
52
+ const v = env[k];
53
+ if (typeof v === "string") {
54
+ const cleaned = v.replace(/[\r\n]+/g, "").trim();
55
+ if (cleaned !== "") return cleaned;
56
+ }
57
+ }
58
+ return void 0;
59
+ };
60
+ const envId = pick("ONYX_DATABASE_ID");
61
+ if (targetId && envId !== targetId) return {};
62
+ const res = dropUndefined({
63
+ baseUrl: pick("ONYX_DATABASE_BASE_URL"),
64
+ databaseId: envId,
65
+ apiKey: pick("ONYX_DATABASE_API_KEY"),
66
+ apiSecret: pick("ONYX_DATABASE_API_SECRET")
67
+ });
68
+ if (Object.keys(res).length === 0) return {};
69
+ dbg("env:", mask(res));
70
+ return res;
71
+ }
72
+ async function readProjectFile(databaseId) {
73
+ if (!isNode) return {};
74
+ const fs = await import('fs/promises');
75
+ const path = await import('path');
76
+ const cwd = gProcess?.cwd?.() ?? ".";
77
+ const tryRead = async (p) => {
78
+ const txt = await fs.readFile(p, "utf8");
79
+ const sanitized = txt.replace(/[\r\n]+/g, "");
80
+ const json = dropUndefined(JSON.parse(sanitized));
81
+ dbg("project file:", p, "\u2192", mask(json));
82
+ return json;
83
+ };
84
+ if (databaseId) {
85
+ const specific = path.resolve(cwd, `onyx-database-${databaseId}.json`);
86
+ try {
87
+ return await tryRead(specific);
88
+ } catch {
89
+ dbg("project file not found:", specific);
90
+ }
91
+ }
92
+ const fallback = path.resolve(cwd, "onyx-database.json");
93
+ try {
94
+ return await tryRead(fallback);
95
+ } catch {
96
+ dbg("project file not found:", fallback);
97
+ return {};
98
+ }
99
+ }
100
+ async function readHomeProfile(databaseId) {
101
+ if (!isNode) return {};
102
+ const fs = await import('fs/promises');
103
+ const os = await import('os');
104
+ const path = await import('path');
105
+ const home = os.homedir();
106
+ const dir = path.join(home, ".onyx");
107
+ const fileExists = async (p) => {
108
+ try {
109
+ await fs.access(p);
110
+ return true;
111
+ } catch {
112
+ return false;
113
+ }
114
+ };
115
+ const readProfile = async (p) => {
116
+ try {
117
+ const txt = await fs.readFile(p, "utf8");
118
+ const sanitized = txt.replace(/[\r\n]+/g, "");
119
+ const json = dropUndefined(JSON.parse(sanitized));
120
+ dbg("home profile used:", p, "\u2192", mask(json));
121
+ return json;
122
+ } catch (e) {
123
+ const msg = e instanceof Error ? e.message : String(e);
124
+ throw new OnyxConfigError(`Failed to read ${p}: ${msg}`);
125
+ }
126
+ };
127
+ if (databaseId) {
128
+ const specific = `${dir}/onyx-database-${databaseId}.json`;
129
+ if (await fileExists(specific)) return readProfile(specific);
130
+ dbg("no specific profile:", specific);
131
+ }
132
+ const defaultInDir = `${dir}/onyx-database.json`;
133
+ if (await fileExists(defaultInDir)) return readProfile(defaultInDir);
134
+ dbg("no default profile in dir:", defaultInDir);
135
+ const defaultInHome = `${home}/onyx-database.json`;
136
+ if (await fileExists(defaultInHome)) return readProfile(defaultInHome);
137
+ dbg("no home-root fallback:", defaultInHome);
138
+ if (!await fileExists(dir)) {
139
+ dbg("~/.onyx does not exist:", dir);
140
+ return {};
141
+ }
142
+ const files = await fs.readdir(dir).catch(() => []);
143
+ const matches = files.filter((f) => f.startsWith("onyx-database-") && f.endsWith(".json"));
144
+ if (matches.length === 1) {
145
+ const only = `${dir}/${matches[0]}`;
146
+ return readProfile(only);
147
+ }
148
+ if (matches.length > 1) {
149
+ throw new OnyxConfigError(
150
+ "Multiple ~/.onyx/onyx-database-*.json profiles found. Specify databaseId via env or provide ./onyx-database.json."
151
+ );
152
+ }
153
+ dbg("no usable home profiles found in", dir);
154
+ return {};
155
+ }
156
+ async function readConfigPath(p) {
157
+ if (!isNode) return {};
158
+ const fs = await import('fs/promises');
159
+ const path = await import('path');
160
+ const cwd = gProcess?.cwd?.() ?? ".";
161
+ const resolved = path.isAbsolute(p) ? p : path.resolve(cwd, p);
162
+ try {
163
+ const txt = await fs.readFile(resolved, "utf8");
164
+ const sanitized = txt.replace(/[\r\n]+/g, "");
165
+ const json = dropUndefined(JSON.parse(sanitized));
166
+ dbg("config path:", resolved, "\u2192", mask(json));
167
+ return json;
168
+ } catch (e) {
169
+ const msg = e instanceof Error ? e.message : String(e);
170
+ throw new OnyxConfigError(`Failed to read ${resolved}: ${msg}`);
171
+ }
172
+ }
173
+ async function resolveConfig(input) {
174
+ const configPath = gProcess?.env?.ONYX_CONFIG_PATH;
175
+ const env = readEnv(input?.databaseId);
176
+ let cfgPath = {};
177
+ if (configPath) {
178
+ cfgPath = await readConfigPath(configPath);
179
+ }
180
+ const targetId = input?.databaseId ?? env.databaseId ?? cfgPath.databaseId;
181
+ let haveDbId = !!(input?.databaseId ?? env.databaseId ?? cfgPath.databaseId);
182
+ let haveApiKey = !!(input?.apiKey ?? env.apiKey ?? cfgPath.apiKey);
183
+ let haveApiSecret = !!(input?.apiSecret ?? env.apiSecret ?? cfgPath.apiSecret);
184
+ let project = {};
185
+ if (!(haveDbId && haveApiKey && haveApiSecret)) {
186
+ project = await readProjectFile(targetId);
187
+ if (project.databaseId) haveDbId = true;
188
+ if (project.apiKey) haveApiKey = true;
189
+ if (project.apiSecret) haveApiSecret = true;
190
+ }
191
+ let home = {};
192
+ if (!(haveDbId && haveApiKey && haveApiSecret)) {
193
+ home = await readHomeProfile(targetId);
194
+ }
195
+ const merged = {
196
+ baseUrl: DEFAULT_BASE_URL,
197
+ ...dropUndefined(home),
198
+ ...dropUndefined(project),
199
+ ...dropUndefined(cfgPath),
200
+ ...dropUndefined(env),
201
+ ...dropUndefined(input)
202
+ };
203
+ dbg("merged (pre-validate):", mask(merged));
204
+ const baseUrl = sanitizeBaseUrl(merged.baseUrl ?? DEFAULT_BASE_URL);
205
+ const databaseId = merged.databaseId ?? "";
206
+ const apiKey = merged.apiKey ?? "";
207
+ const apiSecret = merged.apiSecret ?? "";
208
+ const gfetch = globalThis.fetch;
209
+ const fetchImpl = merged.fetch ?? (typeof gfetch === "function" ? (u, i) => gfetch(u, i) : async () => {
210
+ throw new OnyxConfigError("No fetch available; provide OnyxConfig.fetch");
211
+ });
212
+ const missing = [];
213
+ if (!databaseId) missing.push("databaseId");
214
+ if (!apiKey) missing.push("apiKey");
215
+ if (!apiSecret) missing.push("apiSecret");
216
+ if (missing.length) {
217
+ dbg("validation failed. merged:", mask(merged));
218
+ const sources = [
219
+ "env",
220
+ configPath ?? "env ONYX_CONFIG_PATH",
221
+ ...isNode ? [
222
+ "./onyx-database-<databaseId>.json",
223
+ "./onyx-database.json",
224
+ "~/.onyx/onyx-database-<databaseId>.json",
225
+ "~/.onyx/onyx-database.json",
226
+ "~/onyx-database.json"
227
+ ] : [],
228
+ "explicit config"
229
+ ];
230
+ throw new OnyxConfigError(
231
+ `Missing required config: ${missing.join(", ")}. Sources: ${sources.join(", ")}`
232
+ );
233
+ }
234
+ const resolved = { baseUrl, databaseId, apiKey, apiSecret, fetch: fetchImpl };
235
+ const source = {
236
+ databaseId: input?.databaseId ? "explicit config" : env.databaseId ? "env" : cfgPath.databaseId ? "env ONYX_CONFIG_PATH" : project.databaseId ? "project file" : home.databaseId ? "home profile" : "unknown",
237
+ apiKey: input?.apiKey ? "explicit config" : env.apiKey ? "env" : cfgPath.apiKey ? "env ONYX_CONFIG_PATH" : project.apiKey ? "project file" : home.apiKey ? "home profile" : "unknown",
238
+ apiSecret: input?.apiSecret ? "explicit config" : env.apiSecret ? "env" : cfgPath.apiSecret ? "env ONYX_CONFIG_PATH" : project.apiSecret ? "project file" : home.apiSecret ? "home profile" : "unknown"
239
+ };
240
+ dbg("credential source:", JSON.stringify(source));
241
+ dbg("resolved:", mask(resolved));
242
+ return resolved;
243
+ }
244
+ function mask(obj) {
245
+ if (!obj) return obj;
246
+ const clone = { ...obj };
247
+ if (typeof clone.apiKey === "string") clone.apiKey = "***";
248
+ if (typeof clone.apiSecret === "string") clone.apiSecret = "***";
249
+ return clone;
250
+ }
251
+
252
+ // src/errors/http-error.ts
253
+ var OnyxHttpError = class extends Error {
254
+ name = "OnyxHttpError";
255
+ status;
256
+ statusText;
257
+ body;
258
+ rawBody;
259
+ constructor(message, status, statusText, body, rawBody) {
260
+ super(message);
261
+ this.status = status;
262
+ this.statusText = statusText;
263
+ this.body = body;
264
+ this.rawBody = rawBody;
265
+ }
266
+ toJSON() {
267
+ return {
268
+ name: this.name,
269
+ message: this.message,
270
+ status: this.status,
271
+ statusText: this.statusText,
272
+ body: this.body,
273
+ rawBody: this.rawBody,
274
+ stack: this.stack
275
+ };
276
+ }
277
+ [Symbol.for("nodejs.util.inspect.custom")]() {
278
+ return this.toJSON();
279
+ }
280
+ };
281
+
282
+ // src/core/http.ts
283
+ function parseJsonAllowNaN(txt) {
284
+ try {
285
+ return JSON.parse(txt);
286
+ } catch {
287
+ const fixed = txt.replace(/(:\s*)(NaN|Infinity|-Infinity)(\s*[,}])/g, "$1null$3");
288
+ return JSON.parse(fixed);
289
+ }
290
+ }
291
+ var HttpClient = class {
292
+ baseUrl;
293
+ apiKey;
294
+ apiSecret;
295
+ fetchImpl;
296
+ defaults;
297
+ requestLoggingEnabled;
298
+ responseLoggingEnabled;
299
+ constructor(opts) {
300
+ if (!opts.baseUrl || opts.baseUrl.trim() === "") {
301
+ throw new OnyxConfigError("baseUrl is required");
302
+ }
303
+ try {
304
+ new URL(opts.baseUrl);
305
+ } catch {
306
+ throw new OnyxConfigError("baseUrl must include protocol, e.g. https://");
307
+ }
308
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
309
+ this.apiKey = opts.apiKey;
310
+ this.apiSecret = opts.apiSecret;
311
+ const gfetch = globalThis.fetch;
312
+ if (opts.fetchImpl) {
313
+ this.fetchImpl = opts.fetchImpl;
314
+ } else if (typeof gfetch === "function") {
315
+ this.fetchImpl = (url, init) => gfetch(url, init);
316
+ } else {
317
+ throw new Error("global fetch is not available; provide OnyxConfig.fetch");
318
+ }
319
+ this.defaults = Object.assign({}, opts.defaultHeaders);
320
+ const envDebug = globalThis.process?.env?.ONYX_DEBUG === "true";
321
+ this.requestLoggingEnabled = !!opts.requestLoggingEnabled || envDebug;
322
+ this.responseLoggingEnabled = !!opts.responseLoggingEnabled || envDebug;
323
+ }
324
+ headers(extra) {
325
+ const extras = { ...extra ?? {} };
326
+ delete extras["x-onyx-key"];
327
+ delete extras["x-onyx-secret"];
328
+ return {
329
+ "x-onyx-key": this.apiKey,
330
+ "x-onyx-secret": this.apiSecret,
331
+ "Accept": "application/json",
332
+ "Content-Type": "application/json",
333
+ ...this.defaults,
334
+ ...extras
335
+ };
336
+ }
337
+ async request(method, path, body, extraHeaders) {
338
+ if (!path.startsWith("/")) {
339
+ throw new OnyxConfigError("path must start with /");
340
+ }
341
+ const url = `${this.baseUrl}${path}`;
342
+ const headers = this.headers({
343
+ ...method === "DELETE" ? { Prefer: "return=representation" } : {},
344
+ ...extraHeaders ?? {}
345
+ });
346
+ if (body == null) delete headers["Content-Type"];
347
+ if (this.requestLoggingEnabled) {
348
+ console.log(`${method} ${url}`);
349
+ if (body != null) {
350
+ const logBody = typeof body === "string" ? body : JSON.stringify(body);
351
+ console.log(logBody);
352
+ }
353
+ const headerLog = { ...headers, "x-onyx-secret": "[REDACTED]" };
354
+ console.log("Headers:", headerLog);
355
+ }
356
+ const payload = body == null ? void 0 : typeof body === "string" ? body : JSON.stringify(body);
357
+ const init = {
358
+ method,
359
+ headers,
360
+ body: payload
361
+ };
362
+ const isQuery = path.includes("/query/") && !/\/query\/(?:update|delete)\//.test(path);
363
+ const canRetry = method === "GET" || isQuery;
364
+ const maxAttempts = canRetry ? 3 : 1;
365
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
366
+ try {
367
+ const res = await this.fetchImpl(url, init);
368
+ const contentType = res.headers.get("Content-Type") || "";
369
+ const raw = await res.text();
370
+ if (this.responseLoggingEnabled) {
371
+ const statusLine = `${res.status} ${res.statusText}`.trim();
372
+ console.log(statusLine);
373
+ if (raw.trim().length > 0) {
374
+ console.log(raw);
375
+ }
376
+ }
377
+ const isJson = raw.trim().length > 0 && (contentType.includes("application/json") || /^[\[{]/.test(raw.trim()));
378
+ const data = isJson ? parseJsonAllowNaN(raw) : raw;
379
+ if (!res.ok) {
380
+ const msg = typeof data === "object" && data !== null && "error" in data && typeof data.error?.message === "string" ? String(data.error.message) : `${res.status} ${res.statusText}`;
381
+ if (canRetry && res.status >= 500 && attempt + 1 < maxAttempts) {
382
+ await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
383
+ continue;
384
+ }
385
+ throw new OnyxHttpError(msg, res.status, res.statusText, data, raw);
386
+ }
387
+ return data;
388
+ } catch (err) {
389
+ const retryable = canRetry && (!(err instanceof OnyxHttpError) || err.status >= 500);
390
+ if (attempt + 1 < maxAttempts && retryable) {
391
+ await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
392
+ continue;
393
+ }
394
+ throw err;
395
+ }
396
+ }
397
+ throw new Error("Request failed after retries");
398
+ }
399
+ };
400
+
401
+ // gen/emit.ts
402
+ var DEFAULTS = {
403
+ schemaTypeName: "OnyxSchema",
404
+ timestampMode: "date",
405
+ modelNamePrefix: "",
406
+ optionalStrategy: "non-null"
407
+ };
408
+ function isValidIdentifier(name) {
409
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
410
+ }
411
+ function toPascalCase(raw) {
412
+ const cleaned = String(raw).replace(/[^A-Za-z0-9]+/g, " ");
413
+ const pc = cleaned.trim().split(/\s+/).filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
414
+ return pc.length === 0 ? "Table" : /^[0-9]/.test(pc) ? `T${pc}` : pc;
415
+ }
416
+ function tsTypeFor(attrType, timestampMode) {
417
+ switch (attrType) {
418
+ case "String":
419
+ return "string";
420
+ case "Int":
421
+ return "number";
422
+ case "Boolean":
423
+ return "boolean";
424
+ case "Timestamp":
425
+ return timestampMode === "date" ? "Date" : timestampMode === "number" ? "number" : "string";
426
+ case "EmbeddedList":
427
+ return "any[]";
428
+ case "EmbeddedObject":
429
+ return "any";
430
+ default:
431
+ return "any";
432
+ }
433
+ }
434
+ function propertyLine(attr, timestampMode, optionalStrategy) {
435
+ const key = isValidIdentifier(attr.name) ? attr.name : JSON.stringify(attr.name);
436
+ const makeOptional = optionalStrategy === "non-null" ? !attr.isNullable : optionalStrategy === "nullable" ? attr.isNullable : false;
437
+ const t = tsTypeFor(attr.type, timestampMode);
438
+ const nullableUnion = attr.isNullable ? " | null" : "";
439
+ return ` ${key}${makeOptional ? "?" : ""}: ${t}${nullableUnion};`;
440
+ }
441
+ function emitTypes(schema, options) {
442
+ const opts = { ...DEFAULTS, ...options ?? {} };
443
+ const usedEnumKeys = /* @__PURE__ */ new Set();
444
+ const makeEnumKey = (raw) => {
445
+ const base = toPascalCase(raw);
446
+ let key = base;
447
+ let i = 2;
448
+ while (usedEnumKeys.has(key)) {
449
+ key = `${base}_${i++}`;
450
+ }
451
+ usedEnumKeys.add(key);
452
+ return key;
453
+ };
454
+ const lines = [];
455
+ lines.push(`// AUTO-GENERATED BY onyx-gen. DO NOT EDIT.`);
456
+ lines.push(`// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
457
+ lines.push("");
458
+ for (const t of schema.tables) {
459
+ const typeName = `${opts.modelNamePrefix}${toPascalCase(t.name)}`;
460
+ lines.push(`export interface ${typeName} {`);
461
+ for (const a of t.attributes) {
462
+ lines.push(propertyLine(a, opts.timestampMode, opts.optionalStrategy));
463
+ }
464
+ lines.push(" [key: string]: any;");
465
+ lines.push("}");
466
+ lines.push("");
467
+ }
468
+ lines.push(`export type ${opts.schemaTypeName} = {`);
469
+ for (const t of schema.tables) {
470
+ const key = isValidIdentifier(t.name) ? t.name : JSON.stringify(t.name);
471
+ const modelName = `${opts.modelNamePrefix}${toPascalCase(t.name)}`;
472
+ lines.push(` ${key}: ${modelName};`);
473
+ }
474
+ lines.push("};");
475
+ lines.push("");
476
+ if (opts.schemaTypeName !== "Schema") {
477
+ lines.push(`export type Schema = ${opts.schemaTypeName};`);
478
+ }
479
+ lines.push(`export const Schema = {} as ${opts.schemaTypeName};`);
480
+ lines.push("");
481
+ lines.push("export enum tables {");
482
+ for (const t of schema.tables) {
483
+ const enumKey = makeEnumKey(t.name);
484
+ const enumVal = JSON.stringify(t.name);
485
+ lines.push(` ${enumKey} = ${enumVal},`);
486
+ }
487
+ lines.push("}");
488
+ lines.push("");
489
+ return lines.join("\n");
490
+ }
491
+
492
+ // gen/generate.ts
493
+ var DEFAULTS2 = {
494
+ source: "auto",
495
+ outBaseName: "onyx.schema",
496
+ emitJson: false,
497
+ overwrite: true,
498
+ timestampMode: "date",
499
+ schemaTypeName: "OnyxSchema",
500
+ optional: "non-null",
501
+ quiet: false
502
+ };
503
+ async function readFileJson(path) {
504
+ const fs = await import('fs/promises');
505
+ const txt = await fs.readFile(path, "utf8");
506
+ return JSON.parse(txt);
507
+ }
508
+ async function ensureDir(dir) {
509
+ const fs = await import('fs/promises');
510
+ await fs.mkdir(dir, { recursive: true });
511
+ }
512
+ async function writeFile(path, data, overwrite) {
513
+ const fs = await import('fs/promises');
514
+ if (!overwrite) {
515
+ try {
516
+ await fs.access(path);
517
+ throw new Error(`Refusing to overwrite existing file: ${path}`);
518
+ } catch {
519
+ }
520
+ }
521
+ await fs.writeFile(path, data, "utf8");
522
+ }
523
+ function isIntrospection(x) {
524
+ return !!x && typeof x === "object" && Array.isArray(x.tables);
525
+ }
526
+ async function fetchSchemaFromApi(http, databaseId, candidates) {
527
+ const defaultCandidates = [
528
+ `/schema/${encodeURIComponent(databaseId)}`,
529
+ `/data/${encodeURIComponent(databaseId)}/schema`,
530
+ `/meta/schema/${encodeURIComponent(databaseId)}`,
531
+ `/schema?databaseId=${encodeURIComponent(databaseId)}`,
532
+ `/meta/schema?databaseId=${encodeURIComponent(databaseId)}`
533
+ ];
534
+ const paths = candidates && candidates.length ? candidates : defaultCandidates;
535
+ let lastErr;
536
+ for (const p of paths) {
537
+ try {
538
+ const res = await http.request("GET", p);
539
+ if (isIntrospection(res)) return res;
540
+ } catch (e) {
541
+ lastErr = e;
542
+ }
543
+ }
544
+ const err = lastErr instanceof Error ? lastErr.message : String(lastErr ?? "Unknown error");
545
+ throw new Error(
546
+ `Unable to fetch schema from API. Tried: ${paths.join(", ")}. Last error: ${err}`
547
+ );
548
+ }
549
+ async function generateTypes(options) {
550
+ const path = await import('path');
551
+ const opts = { ...DEFAULTS2, ...options ?? {} };
552
+ const typesDir = opts.typesOutDir ?? opts.outDir ?? "generated";
553
+ let schema = null;
554
+ if (opts.source === "file" || opts.source === "auto" && opts.schemaPath) {
555
+ if (!opts.schemaPath) throw new Error('schemaPath is required when source="file"');
556
+ if (!opts.quiet)
557
+ process__default.default.stderr.write(`[onyx-gen] reading schema from file: ${opts.schemaPath}
558
+ `);
559
+ schema = await readFileJson(opts.schemaPath);
560
+ }
561
+ if (!schema) {
562
+ if (opts.source === "file") throw new Error("Failed to read schema from file");
563
+ const cfg = await resolveConfig({});
564
+ const http = new HttpClient({
565
+ baseUrl: cfg.baseUrl,
566
+ apiKey: cfg.apiKey,
567
+ apiSecret: cfg.apiSecret,
568
+ fetchImpl: cfg.fetch
569
+ });
570
+ if (!opts.quiet)
571
+ process__default.default.stderr.write(`[onyx-gen] fetching schema from API for db ${cfg.databaseId}
572
+ `);
573
+ schema = await fetchSchemaFromApi(http, cfg.databaseId, options?.apiPaths);
574
+ }
575
+ if (!isIntrospection(schema)) {
576
+ throw new Error('Invalid schema: missing "tables" array.');
577
+ }
578
+ const outIsFile = typeof opts.typesOutFile === "string" && (opts.typesOutFile.endsWith(".ts") || opts.typesOutFile.endsWith(".mts") || opts.typesOutFile.endsWith(".cts") || opts.typesOutFile.endsWith(".d.ts") || opts.typesOutFile.endsWith(".d.mts") || opts.typesOutFile.endsWith(".d.cts"));
579
+ let typesPath;
580
+ let jsonBaseName;
581
+ let typesDirAbs;
582
+ if (outIsFile) {
583
+ const typesOutFile = opts.typesOutFile;
584
+ typesPath = path.resolve(process__default.default.cwd(), typesOutFile);
585
+ typesDirAbs = path.dirname(typesPath);
586
+ await ensureDir(typesDirAbs);
587
+ jsonBaseName = path.basename(typesPath, path.extname(typesPath));
588
+ } else {
589
+ typesDirAbs = path.resolve(process__default.default.cwd(), typesDir);
590
+ await ensureDir(typesDirAbs);
591
+ typesPath = path.join(typesDirAbs, `${opts.outBaseName}.ts`);
592
+ jsonBaseName = opts.outBaseName;
593
+ }
594
+ const types = emitTypes(schema, {
595
+ schemaTypeName: opts.schemaTypeName,
596
+ timestampMode: opts.timestampMode,
597
+ modelNamePrefix: opts.prefix ?? "",
598
+ optionalStrategy: opts.optional
599
+ });
600
+ await writeFile(typesPath, `${types}
601
+ `, opts.overwrite);
602
+ let jsonPath;
603
+ if (opts.emitJson) {
604
+ const jsonOutDirAbs = path.resolve(
605
+ process__default.default.cwd(),
606
+ opts.jsonOutDir ?? (outIsFile ? typesDirAbs : typesDirAbs)
607
+ );
608
+ await ensureDir(jsonOutDirAbs);
609
+ jsonPath = path.join(jsonOutDirAbs, `${jsonBaseName}.json`);
610
+ const jsonPretty = JSON.stringify(schema, null, 2);
611
+ await writeFile(jsonPath, `${jsonPretty}
612
+ `, opts.overwrite);
613
+ }
614
+ if (!opts.quiet) {
615
+ process__default.default.stderr.write(`[onyx-gen] wrote ${typesPath}
616
+ `);
617
+ if (jsonPath) process__default.default.stderr.write(`[onyx-gen] wrote ${jsonPath}
618
+ `);
619
+ }
620
+ return { typesPath, jsonPath };
621
+ }
622
+
623
+ // gen/cli/generate.ts
624
+ function isTypesFilePath(p) {
625
+ if (!p) return false;
626
+ return p.endsWith(".ts") || p.endsWith(".mts") || p.endsWith(".cts") || p.endsWith(".d.ts") || p.endsWith(".d.mts") || p.endsWith(".d.cts");
627
+ }
628
+ function parseArgs(argv) {
629
+ const opts = {};
630
+ const next = (i) => argv[i + 1];
631
+ for (let i = 2; i < argv.length; i++) {
632
+ const a = argv[i];
633
+ switch (a) {
634
+ case "--out":
635
+ case "--outDir": {
636
+ const val = next(i);
637
+ if (!val) throw new Error(`Missing value for ${a}`);
638
+ if (isTypesFilePath(val)) opts.typesOutFile = val;
639
+ else opts.typesOutDir = val;
640
+ i++;
641
+ break;
642
+ }
643
+ case "--types-out":
644
+ case "--typesOut": {
645
+ const val = next(i);
646
+ if (!val) throw new Error(`Missing value for ${a}`);
647
+ if (isTypesFilePath(val)) opts.typesOutFile = val;
648
+ else opts.typesOutDir = val;
649
+ i++;
650
+ break;
651
+ }
652
+ case "--types-file":
653
+ case "--typesFile": {
654
+ const val = next(i);
655
+ if (!val) throw new Error(`Missing value for ${a}`);
656
+ opts.typesOutFile = val;
657
+ i++;
658
+ break;
659
+ }
660
+ case "--json-out":
661
+ case "--jsonOut":
662
+ opts.jsonOutDir = next(i);
663
+ i++;
664
+ break;
665
+ case "--base":
666
+ case "--baseName":
667
+ opts.outBaseName = next(i);
668
+ i++;
669
+ break;
670
+ case "--schema":
671
+ opts.schemaPath = next(i);
672
+ i++;
673
+ break;
674
+ case "--source": {
675
+ const v = (next(i) ?? "").toLowerCase();
676
+ if (v === "api" || v === "file" || v === "auto") opts.source = v;
677
+ else throw new Error(`Invalid --source: ${v}`);
678
+ i++;
679
+ break;
680
+ }
681
+ case "--timestamps": {
682
+ const v = (next(i) ?? "").toLowerCase();
683
+ if (v === "string" || v === "date" || v === "number") opts.timestampMode = v;
684
+ else throw new Error(`Invalid --timestamps: ${v}`);
685
+ i++;
686
+ break;
687
+ }
688
+ case "--name":
689
+ case "--schemaTypeName":
690
+ opts.schemaTypeName = next(i);
691
+ i++;
692
+ break;
693
+ case "--prefix":
694
+ opts.prefix = next(i);
695
+ i++;
696
+ break;
697
+ case "--optional": {
698
+ const v = (next(i) ?? "").toLowerCase();
699
+ if (v === "non-null" || v === "nonnull" || v === "nonull") {
700
+ opts.optional = "non-null";
701
+ } else if (v === "nullable") {
702
+ opts.optional = "nullable";
703
+ } else if (v === "none") {
704
+ opts.optional = "none";
705
+ } else {
706
+ throw new Error(`Invalid --optional: ${v} (use non-null|nullable|none)`);
707
+ }
708
+ i++;
709
+ break;
710
+ }
711
+ case "--emit-json":
712
+ opts.emitJson = true;
713
+ break;
714
+ case "--no-emit-json":
715
+ opts.emitJson = false;
716
+ break;
717
+ case "--api-path": {
718
+ const v = next(i);
719
+ if (!v || !v.startsWith("/")) throw new Error(`--api-path must start with "/": ${v}`);
720
+ if (!opts.apiPaths) opts.apiPaths = [];
721
+ opts.apiPaths.push(v);
722
+ i++;
723
+ break;
724
+ }
725
+ case "--overwrite":
726
+ opts.overwrite = true;
727
+ break;
728
+ case "--no-overwrite":
729
+ opts.overwrite = false;
730
+ break;
731
+ case "-q":
732
+ case "--quiet":
733
+ opts.quiet = true;
734
+ break;
735
+ case "-h":
736
+ case "--help":
737
+ printHelp();
738
+ process__default.default.exit(0);
739
+ break;
740
+ default:
741
+ if (a.startsWith("-")) throw new Error(`Unknown option: ${a}`);
742
+ break;
743
+ }
744
+ }
745
+ return opts;
746
+ }
747
+ function printHelp() {
748
+ process__default.default.stdout.write(`onyx-gen \u2014 Generate Onyx schema TypeScript types
749
+
750
+ Usage:
751
+ onyx-gen [options]
752
+
753
+ Output selection:
754
+ --out <path> If <path> ends with ".ts", writes exactly to that file.
755
+ Otherwise treats <path> as a directory.
756
+ --types-out <dir|file> Same as --out (file-or-dir).
757
+ --types-file <file.ts> Explicit file output.
758
+ --base, --baseName <name> Base filename (without ext) when writing to a directory (default: onyx.schema)
759
+ --json-out <dir> JSON output directory (used only with --emit-json)
760
+
761
+ Source selection:
762
+ --source <auto|api|file> Where to get schema (default: auto)
763
+ --schema <path> Path to schema JSON when --source=file (or to force local)
764
+
765
+ Type emission:
766
+ --timestamps <string|date|number> Timestamp representation in types (default: date)
767
+ --name, --schemaTypeName <T> Exported schema type name (default: OnyxSchema)
768
+ --prefix <Prefix> Prefix to prepend to generated model names (default: none)
769
+ --optional <non-null|nullable|none>
770
+ Where to add '?' optional props (default: non-null)
771
+
772
+ Other:
773
+ --emit-json / --no-emit-json Emit schema JSON copy (default: no-emit-json)
774
+ --api-path <path> Candidate API path to fetch schema; can be repeated
775
+ --overwrite / --no-overwrite Overwrite existing files (default: overwrite)
776
+ -q, --quiet Suppress logs
777
+ -h, --help Show this help
778
+
779
+ Notes:
780
+ \u2022 Env/config for --source=api uses the same resolver as onyx.init()
781
+ (env vars, ./onyx-database.json, ~/.onyx/onyx-database-<id>.json, etc.).
782
+ `);
783
+ }
784
+ (async () => {
785
+ try {
786
+ const opts = parseArgs(process__default.default.argv);
787
+ await generateTypes(opts);
788
+ } catch (e) {
789
+ const msg = e instanceof Error ? e.message : String(e);
790
+ process__default.default.stderr.write(`onyx-gen: ${msg}
791
+ `);
792
+ process__default.default.exit(1);
793
+ }
794
+ })();
795
+ //# sourceMappingURL=generate.cjs.map
796
+ //# sourceMappingURL=generate.cjs.map