tthr 0.0.33 → 0.0.34

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.
package/dist/index.js CHANGED
@@ -1,145 +1,57 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ clearCredentials,
4
+ detectFramework,
5
+ generateCommand,
6
+ getCredentials,
7
+ getFrameworkDevCommand,
8
+ loadConfig,
9
+ requireAuth,
10
+ resolvePath,
11
+ saveCredentials
12
+ } from "./chunk-ZWLVHKNL.js";
2
13
 
3
14
  // src/index.ts
4
15
  import { Command } from "commander";
5
16
 
6
17
  // src/commands/init.ts
7
- import chalk3 from "chalk";
18
+ import chalk2 from "chalk";
8
19
  import ora2 from "ora";
9
20
  import prompts from "prompts";
10
- import fs4 from "fs-extra";
11
- import path4 from "path";
21
+ import fs2 from "fs-extra";
22
+ import path2 from "path";
12
23
  import { execSync } from "child_process";
13
24
 
14
- // src/utils/auth.ts
25
+ // src/commands/deploy.ts
15
26
  import chalk from "chalk";
27
+ import ora from "ora";
16
28
  import fs from "fs-extra";
17
29
  import path from "path";
18
- var CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || "", ".tether");
19
- var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
20
- async function getCredentials() {
21
- try {
22
- if (await fs.pathExists(CREDENTIALS_FILE)) {
23
- return await fs.readJSON(CREDENTIALS_FILE);
24
- }
25
- } catch {
26
- }
27
- return null;
28
- }
29
- async function requireAuth() {
30
- const credentials = await getCredentials();
31
- if (!credentials) {
32
- console.error(chalk.red("\n\u2717 Not logged in\n"));
33
- console.log(chalk.dim("Run `tthr login` to authenticate\n"));
34
- process.exit(1);
35
- }
36
- if (credentials.expiresAt) {
37
- const expiresAt = new Date(credentials.expiresAt);
38
- if (/* @__PURE__ */ new Date() > expiresAt) {
39
- console.error(chalk.red("\n\u2717 Session expired\n"));
40
- console.log(chalk.dim("Run `tthr login` to authenticate\n"));
41
- process.exit(1);
42
- }
43
- }
44
- return credentials;
45
- }
46
- async function saveCredentials(credentials) {
47
- await fs.ensureDir(CONFIG_DIR);
48
- await fs.writeJSON(CREDENTIALS_FILE, credentials, { spaces: 2, mode: 384 });
49
- }
50
- async function clearCredentials() {
51
- try {
52
- await fs.remove(CREDENTIALS_FILE);
53
- } catch {
54
- }
55
- }
56
-
57
- // src/commands/deploy.ts
58
- import chalk2 from "chalk";
59
- import ora from "ora";
60
- import fs3 from "fs-extra";
61
- import path3 from "path";
62
-
63
- // src/utils/config.ts
64
- import fs2 from "fs-extra";
65
- import path2 from "path";
66
- var DEFAULT_CONFIG = {
67
- schema: "./tether/schema.ts",
68
- functions: "./tether/functions",
69
- output: "./tether/_generated",
70
- dev: {
71
- port: 3001,
72
- host: "localhost"
73
- },
74
- database: {
75
- walMode: true
76
- }
77
- };
78
- async function loadConfig(cwd = process.cwd()) {
79
- const configPath = path2.resolve(cwd, "tether.config.ts");
80
- if (!await fs2.pathExists(configPath)) {
81
- return DEFAULT_CONFIG;
82
- }
83
- const configSource = await fs2.readFile(configPath, "utf-8");
84
- const config = {};
85
- const schemaMatch = configSource.match(/schema\s*:\s*['"]([^'"]+)['"]/);
86
- if (schemaMatch) {
87
- config.schema = schemaMatch[1];
88
- }
89
- const functionsMatch = configSource.match(/functions\s*:\s*['"]([^'"]+)['"]/);
90
- if (functionsMatch) {
91
- config.functions = functionsMatch[1];
92
- }
93
- const outputMatch = configSource.match(/output\s*:\s*['"]([^'"]+)['"]/);
94
- if (outputMatch) {
95
- config.output = outputMatch[1];
96
- }
97
- const portMatch = configSource.match(/port\s*:\s*(\d+)/);
98
- if (portMatch) {
99
- config.dev = { ...config.dev, port: parseInt(portMatch[1], 10) };
100
- }
101
- const hostMatch = configSource.match(/host\s*:\s*['"]([^'"]+)['"]/);
102
- if (hostMatch) {
103
- config.dev = { ...config.dev, host: hostMatch[1] };
104
- }
105
- return {
106
- ...DEFAULT_CONFIG,
107
- ...config,
108
- dev: { ...DEFAULT_CONFIG.dev, ...config.dev },
109
- database: { ...DEFAULT_CONFIG.database, ...config.database }
110
- };
111
- }
112
- function resolvePath(configPath, cwd = process.cwd()) {
113
- const normalised = configPath.replace(/^\.\//, "");
114
- return path2.resolve(cwd, normalised);
115
- }
116
-
117
- // src/commands/deploy.ts
118
30
  var isDev = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
119
31
  var API_URL = isDev ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
120
32
  async function deployCommand(options) {
121
33
  const credentials = await requireAuth();
122
- const configPath = path3.resolve(process.cwd(), "tether.config.ts");
123
- if (!await fs3.pathExists(configPath)) {
124
- console.log(chalk2.red("\nError: Not a Tether project"));
125
- console.log(chalk2.dim("Run `tthr init` to create a new project\n"));
34
+ const configPath = path.resolve(process.cwd(), "tether.config.ts");
35
+ if (!await fs.pathExists(configPath)) {
36
+ console.log(chalk.red("\nError: Not a Tether project"));
37
+ console.log(chalk.dim("Run `tthr init` to create a new project\n"));
126
38
  process.exit(1);
127
39
  }
128
- const envPath = path3.resolve(process.cwd(), ".env");
40
+ const envPath = path.resolve(process.cwd(), ".env");
129
41
  let projectId;
130
- if (await fs3.pathExists(envPath)) {
131
- const envContent = await fs3.readFile(envPath, "utf-8");
42
+ if (await fs.pathExists(envPath)) {
43
+ const envContent = await fs.readFile(envPath, "utf-8");
132
44
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
133
45
  projectId = match?.[1]?.trim();
134
46
  }
135
47
  if (!projectId) {
136
- console.log(chalk2.red("\nError: Project ID not found"));
137
- console.log(chalk2.dim("Make sure TETHER_PROJECT_ID is set in your .env file\n"));
48
+ console.log(chalk.red("\nError: Project ID not found"));
49
+ console.log(chalk.dim("Make sure TETHER_PROJECT_ID is set in your .env file\n"));
138
50
  process.exit(1);
139
51
  }
140
- console.log(chalk2.bold("\n\u26A1 Deploying to Tether\n"));
141
- console.log(chalk2.dim(` Project: ${projectId}`));
142
- console.log(chalk2.dim(` API: ${API_URL}
52
+ console.log(chalk.bold("\n\u26A1 Deploying to Tether\n"));
53
+ console.log(chalk.dim(` Project: ${projectId}`));
54
+ console.log(chalk.dim(` API: ${API_URL}
143
55
  `));
144
56
  const config = await loadConfig();
145
57
  const deploySchema = options.schema || !options.schema && !options.functions;
@@ -152,29 +64,29 @@ async function deployCommand(options) {
152
64
  const functionsDir = resolvePath(config.functions);
153
65
  await deployFunctionsToServer(projectId, credentials.accessToken, functionsDir, options.dryRun);
154
66
  }
155
- console.log(chalk2.green("\n\u2713 Deployment complete\n"));
67
+ console.log(chalk.green("\n\u2713 Deployment complete\n"));
156
68
  }
157
69
  async function deploySchemaToServer(projectId, token, schemaPath, dryRun) {
158
70
  const spinner = ora("Reading schema...").start();
159
71
  try {
160
- if (!await fs3.pathExists(schemaPath)) {
72
+ if (!await fs.pathExists(schemaPath)) {
161
73
  spinner.warn("No schema file found");
162
- const relativePath = path3.relative(process.cwd(), schemaPath);
163
- console.log(chalk2.dim(` Create ${relativePath} to define your database schema
74
+ const relativePath = path.relative(process.cwd(), schemaPath);
75
+ console.log(chalk.dim(` Create ${relativePath} to define your database schema
164
76
  `));
165
77
  return;
166
78
  }
167
- const schemaSource = await fs3.readFile(schemaPath, "utf-8");
79
+ const schemaSource = await fs.readFile(schemaPath, "utf-8");
168
80
  const tables = parseSchema(schemaSource);
169
81
  spinner.text = `Found ${tables.length} table(s)`;
170
82
  const sql = generateSchemaSQL(tables);
171
83
  if (dryRun) {
172
84
  spinner.info("Dry run - would deploy schema:");
173
85
  for (const table of tables) {
174
- console.log(chalk2.dim(` - ${table.name} (${Object.keys(table.columns).length} columns)`));
86
+ console.log(chalk.dim(` - ${table.name} (${Object.keys(table.columns).length} columns)`));
175
87
  }
176
- console.log(chalk2.bold("\nGenerated SQL:\n"));
177
- console.log(chalk2.cyan(sql));
88
+ console.log(chalk.bold("\nGenerated SQL:\n"));
89
+ console.log(chalk.cyan(sql));
178
90
  return;
179
91
  }
180
92
  spinner.text = "Deploying schema...";
@@ -200,20 +112,20 @@ async function deploySchemaToServer(projectId, token, schemaPath, dryRun) {
200
112
  spinner.succeed(`Schema deployed (${tables.length} table(s))`);
201
113
  } catch (error) {
202
114
  spinner.fail("Failed to deploy schema");
203
- console.error(chalk2.red(error instanceof Error ? error.message : "Unknown error"));
115
+ console.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
204
116
  }
205
117
  }
206
118
  async function deployFunctionsToServer(projectId, token, functionsDir, dryRun) {
207
119
  const spinner = ora("Reading functions...").start();
208
120
  try {
209
- if (!await fs3.pathExists(functionsDir)) {
121
+ if (!await fs.pathExists(functionsDir)) {
210
122
  spinner.warn("No functions directory found");
211
- const relativePath = path3.relative(process.cwd(), functionsDir);
212
- console.log(chalk2.dim(` Create ${relativePath}/ to define your API functions
123
+ const relativePath = path.relative(process.cwd(), functionsDir);
124
+ console.log(chalk.dim(` Create ${relativePath}/ to define your API functions
213
125
  `));
214
126
  return;
215
127
  }
216
- const files = await fs3.readdir(functionsDir);
128
+ const files = await fs.readdir(functionsDir);
217
129
  const tsFiles = files.filter((f) => f.endsWith(".ts"));
218
130
  if (tsFiles.length === 0) {
219
131
  spinner.info("No function files found");
@@ -221,8 +133,8 @@ async function deployFunctionsToServer(projectId, token, functionsDir, dryRun) {
221
133
  }
222
134
  const functions = [];
223
135
  for (const file of tsFiles) {
224
- const filePath = path3.join(functionsDir, file);
225
- const source = await fs3.readFile(filePath, "utf-8");
136
+ const filePath = path.join(functionsDir, file);
137
+ const source = await fs.readFile(filePath, "utf-8");
226
138
  const moduleName = file.replace(".ts", "");
227
139
  const parsedFunctions = parseFunctions(moduleName, source);
228
140
  functions.push(...parsedFunctions);
@@ -232,7 +144,7 @@ async function deployFunctionsToServer(projectId, token, functionsDir, dryRun) {
232
144
  spinner.info("Dry run - would deploy functions:");
233
145
  for (const fn of functions) {
234
146
  const icon = fn.type === "query" ? "\u{1F50D}" : fn.type === "mutation" ? "\u270F\uFE0F" : "\u26A1";
235
- console.log(chalk2.dim(` ${icon} ${fn.name} (${fn.type})`));
147
+ console.log(chalk.dim(` ${icon} ${fn.name} (${fn.type})`));
236
148
  }
237
149
  return;
238
150
  }
@@ -260,29 +172,29 @@ async function deployFunctionsToServer(projectId, token, functionsDir, dryRun) {
260
172
  const mutations = functions.filter((f) => f.type === "mutation");
261
173
  const actions = functions.filter((f) => f.type === "action");
262
174
  if (queries.length > 0) {
263
- console.log(chalk2.dim(`
175
+ console.log(chalk.dim(`
264
176
  Queries:`));
265
177
  for (const fn of queries) {
266
- console.log(chalk2.dim(` - ${fn.name}`));
178
+ console.log(chalk.dim(` - ${fn.name}`));
267
179
  }
268
180
  }
269
181
  if (mutations.length > 0) {
270
- console.log(chalk2.dim(`
182
+ console.log(chalk.dim(`
271
183
  Mutations:`));
272
184
  for (const fn of mutations) {
273
- console.log(chalk2.dim(` - ${fn.name}`));
185
+ console.log(chalk.dim(` - ${fn.name}`));
274
186
  }
275
187
  }
276
188
  if (actions.length > 0) {
277
- console.log(chalk2.dim(`
189
+ console.log(chalk.dim(`
278
190
  Actions:`));
279
191
  for (const fn of actions) {
280
- console.log(chalk2.dim(` - ${fn.name}`));
192
+ console.log(chalk.dim(` - ${fn.name}`));
281
193
  }
282
194
  }
283
195
  } catch (error) {
284
196
  spinner.fail("Failed to deploy functions");
285
- console.error(chalk2.red(error instanceof Error ? error.message : "Unknown error"));
197
+ console.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
286
198
  }
287
199
  }
288
200
  function parseSchema(source) {
@@ -451,7 +363,7 @@ function isPackageManagerInstalled(pm) {
451
363
  }
452
364
  async function initCommand(name, options) {
453
365
  const credentials = await requireAuth();
454
- console.log(chalk3.bold("\n\u26A1 Create a new Tether project\n"));
366
+ console.log(chalk2.bold("\n\u26A1 Create a new Tether project\n"));
455
367
  let projectName = name;
456
368
  if (!projectName) {
457
369
  const response = await prompts({
@@ -462,7 +374,7 @@ async function initCommand(name, options) {
462
374
  });
463
375
  projectName = response.name;
464
376
  if (!projectName) {
465
- console.log(chalk3.red("Project name is required"));
377
+ console.log(chalk2.red("Project name is required"));
466
378
  process.exit(1);
467
379
  }
468
380
  }
@@ -480,9 +392,9 @@ async function initCommand(name, options) {
480
392
  });
481
393
  const packageManager = pmResponse.packageManager || "npm";
482
394
  if (!isPackageManagerInstalled(packageManager)) {
483
- console.log(chalk3.red(`
395
+ console.log(chalk2.red(`
484
396
  ${packageManager} is not installed on your system.`));
485
- console.log(chalk3.dim(`Please install ${packageManager} and try again.
397
+ console.log(chalk2.dim(`Please install ${packageManager} and try again.
486
398
  `));
487
399
  process.exit(1);
488
400
  }
@@ -502,8 +414,8 @@ ${packageManager} is not installed on your system.`));
502
414
  });
503
415
  template = response.template || "nuxt";
504
416
  }
505
- const projectPath = path4.resolve(process.cwd(), projectName);
506
- if (await fs4.pathExists(projectPath)) {
417
+ const projectPath = path2.resolve(process.cwd(), projectName);
418
+ if (await fs2.pathExists(projectPath)) {
507
419
  const { overwrite } = await prompts({
508
420
  type: "confirm",
509
421
  name: "overwrite",
@@ -511,10 +423,10 @@ ${packageManager} is not installed on your system.`));
511
423
  initial: false
512
424
  });
513
425
  if (!overwrite) {
514
- console.log(chalk3.yellow("Cancelled"));
426
+ console.log(chalk2.yellow("Cancelled"));
515
427
  process.exit(0);
516
428
  }
517
- await fs4.remove(projectPath);
429
+ await fs2.remove(projectPath);
518
430
  }
519
431
  const spinner = ora2(`Scaffolding ${template} project...`).start();
520
432
  let projectId;
@@ -562,17 +474,17 @@ ${packageManager} is not installed on your system.`));
562
474
  } finally {
563
475
  process.chdir(originalCwd);
564
476
  }
565
- console.log("\n" + chalk3.green("\u2713") + " Project created successfully!\n");
477
+ console.log("\n" + chalk2.green("\u2713") + " Project created successfully!\n");
566
478
  console.log("Next steps:\n");
567
- console.log(chalk3.cyan(` cd ${projectName}`));
479
+ console.log(chalk2.cyan(` cd ${projectName}`));
568
480
  if (template === "vanilla") {
569
- console.log(chalk3.cyan(` ${packageManager} install`));
481
+ console.log(chalk2.cyan(` ${packageManager} install`));
570
482
  }
571
- console.log(chalk3.cyan(` ${packageManager} run dev`));
572
- console.log("\n" + chalk3.dim("For more information, visit https://tthr.io/docs\n"));
483
+ console.log(chalk2.cyan(` ${packageManager} run dev`));
484
+ console.log("\n" + chalk2.dim("For more information, visit https://tthr.io/docs\n"));
573
485
  } catch (error) {
574
486
  spinner.fail("Failed to create project");
575
- console.error(chalk3.red(error instanceof Error ? error.message : "Unknown error"));
487
+ console.error(chalk2.red(error instanceof Error ? error.message : "Unknown error"));
576
488
  process.exit(1);
577
489
  }
578
490
  }
@@ -589,16 +501,16 @@ function getPackageRunnerCommand(pm) {
589
501
  }
590
502
  }
591
503
  async function scaffoldNuxtProject(projectName, projectPath, spinner, packageManager) {
592
- const parentDir = path4.dirname(projectPath);
504
+ const parentDir = path2.dirname(projectPath);
593
505
  const runner = getPackageRunnerCommand(packageManager);
594
506
  spinner.stop();
595
- console.log(chalk3.dim("\nRunning nuxi init...\n"));
507
+ console.log(chalk2.dim("\nRunning nuxi init...\n"));
596
508
  try {
597
509
  execSync(`${runner} nuxi@latest init ${projectName} -t minimal --packageManager ${packageManager} --gitInit false`, {
598
510
  cwd: parentDir,
599
511
  stdio: "inherit"
600
512
  });
601
- if (!await fs4.pathExists(path4.join(projectPath, "package.json"))) {
513
+ if (!await fs2.pathExists(path2.join(projectPath, "package.json"))) {
602
514
  throw new Error("Nuxt project was not created successfully - package.json missing");
603
515
  }
604
516
  spinner.start();
@@ -611,17 +523,17 @@ async function scaffoldNuxtProject(projectName, projectPath, spinner, packageMan
611
523
  }
612
524
  }
613
525
  async function scaffoldNextProject(projectName, projectPath, spinner, packageManager) {
614
- const parentDir = path4.dirname(projectPath);
526
+ const parentDir = path2.dirname(projectPath);
615
527
  const runner = getPackageRunnerCommand(packageManager);
616
528
  const pmFlag = packageManager === "npm" ? "--use-npm" : packageManager === "pnpm" ? "--use-pnpm" : packageManager === "yarn" ? "--use-yarn" : "--use-bun";
617
529
  spinner.stop();
618
- console.log(chalk3.dim("\nRunning create-next-app...\n"));
530
+ console.log(chalk2.dim("\nRunning create-next-app...\n"));
619
531
  try {
620
532
  execSync(`${runner} create-next-app@latest ${projectName} --typescript --eslint --tailwind --src-dir --app --import-alias "@/*" ${pmFlag}`, {
621
533
  cwd: parentDir,
622
534
  stdio: "inherit"
623
535
  });
624
- if (!await fs4.pathExists(path4.join(projectPath, "package.json"))) {
536
+ if (!await fs2.pathExists(path2.join(projectPath, "package.json"))) {
625
537
  throw new Error("Next.js project was not created successfully - package.json missing");
626
538
  }
627
539
  spinner.start();
@@ -634,16 +546,16 @@ async function scaffoldNextProject(projectName, projectPath, spinner, packageMan
634
546
  }
635
547
  }
636
548
  async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packageManager) {
637
- const parentDir = path4.dirname(projectPath);
549
+ const parentDir = path2.dirname(projectPath);
638
550
  const runner = getPackageRunnerCommand(packageManager);
639
551
  spinner.stop();
640
- console.log(chalk3.dim("\nRunning sv create...\n"));
552
+ console.log(chalk2.dim("\nRunning sv create...\n"));
641
553
  try {
642
554
  execSync(`${runner} sv create ${projectName} --template minimal --types ts --no-add-ons --no-install`, {
643
555
  cwd: parentDir,
644
556
  stdio: "inherit"
645
557
  });
646
- if (!await fs4.pathExists(path4.join(projectPath, "package.json"))) {
558
+ if (!await fs2.pathExists(path2.join(projectPath, "package.json"))) {
647
559
  throw new Error("SvelteKit project was not created successfully - package.json missing");
648
560
  }
649
561
  spinner.start();
@@ -662,8 +574,8 @@ async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packa
662
574
  }
663
575
  async function scaffoldVanillaProject(projectName, projectPath, spinner) {
664
576
  spinner.text = "Creating vanilla TypeScript project...";
665
- await fs4.ensureDir(projectPath);
666
- await fs4.ensureDir(path4.join(projectPath, "src"));
577
+ await fs2.ensureDir(projectPath);
578
+ await fs2.ensureDir(path2.join(projectPath, "src"));
667
579
  const packageJson = {
668
580
  name: projectName,
669
581
  version: "0.0.1",
@@ -680,9 +592,9 @@ async function scaffoldVanillaProject(projectName, projectPath, spinner) {
680
592
  "@types/node": "latest"
681
593
  }
682
594
  };
683
- await fs4.writeJSON(path4.join(projectPath, "package.json"), packageJson, { spaces: 2 });
684
- await fs4.writeJSON(
685
- path4.join(projectPath, "tsconfig.json"),
595
+ await fs2.writeJSON(path2.join(projectPath, "package.json"), packageJson, { spaces: 2 });
596
+ await fs2.writeJSON(
597
+ path2.join(projectPath, "tsconfig.json"),
686
598
  {
687
599
  compilerOptions: {
688
600
  target: "ES2022",
@@ -703,8 +615,8 @@ async function scaffoldVanillaProject(projectName, projectPath, spinner) {
703
615
  },
704
616
  { spaces: 2 }
705
617
  );
706
- await fs4.writeFile(
707
- path4.join(projectPath, "src", "index.ts"),
618
+ await fs2.writeFile(
619
+ path2.join(projectPath, "src", "index.ts"),
708
620
  `import { createClient } from '@tthr/client';
709
621
 
710
622
  const tether = createClient({
@@ -723,8 +635,8 @@ async function main() {
723
635
  main().catch(console.error);
724
636
  `
725
637
  );
726
- await fs4.writeFile(
727
- path4.join(projectPath, ".gitignore"),
638
+ await fs2.writeFile(
639
+ path2.join(projectPath, ".gitignore"),
728
640
  `node_modules/
729
641
  dist/
730
642
  .env
@@ -734,11 +646,11 @@ dist/
734
646
  );
735
647
  }
736
648
  async function addTetherFiles(projectPath, projectId, apiKey, template) {
737
- await fs4.ensureDir(path4.join(projectPath, "tether"));
738
- await fs4.ensureDir(path4.join(projectPath, "tether", "functions"));
649
+ await fs2.ensureDir(path2.join(projectPath, "tether"));
650
+ await fs2.ensureDir(path2.join(projectPath, "tether", "functions"));
739
651
  const configPackage = template === "nuxt" ? "@tthr/vue" : template === "next" ? "@tthr/react" : template === "sveltekit" ? "@tthr/svelte" : "@tthr/client";
740
- await fs4.writeFile(
741
- path4.join(projectPath, "tether.config.ts"),
652
+ await fs2.writeFile(
653
+ path2.join(projectPath, "tether.config.ts"),
742
654
  `import { defineConfig } from '${configPackage}';
743
655
 
744
656
  export default defineConfig({
@@ -759,8 +671,8 @@ export default defineConfig({
759
671
  });
760
672
  `
761
673
  );
762
- await fs4.writeFile(
763
- path4.join(projectPath, "tether", "schema.ts"),
674
+ await fs2.writeFile(
675
+ path2.join(projectPath, "tether", "schema.ts"),
764
676
  `import { defineSchema, text, integer, timestamp } from '@tthr/schema';
765
677
 
766
678
  export default defineSchema({
@@ -785,8 +697,8 @@ export default defineSchema({
785
697
  });
786
698
  `
787
699
  );
788
- await fs4.writeFile(
789
- path4.join(projectPath, "tether", "functions", "posts.ts"),
700
+ await fs2.writeFile(
701
+ path2.join(projectPath, "tether", "functions", "posts.ts"),
790
702
  `import { query, mutation, z } from '@tthr/server';
791
703
 
792
704
  // List all posts
@@ -871,8 +783,8 @@ export const remove = mutation({
871
783
  });
872
784
  `
873
785
  );
874
- await fs4.writeFile(
875
- path4.join(projectPath, "tether", "functions", "index.ts"),
786
+ await fs2.writeFile(
787
+ path2.join(projectPath, "tether", "functions", "index.ts"),
876
788
  `// Re-export all function modules
877
789
  // The Tether SDK uses this file to discover custom functions
878
790
 
@@ -880,8 +792,8 @@ export * as posts from './posts';
880
792
  export * as comments from './comments';
881
793
  `
882
794
  );
883
- await fs4.writeFile(
884
- path4.join(projectPath, "tether", "functions", "comments.ts"),
795
+ await fs2.writeFile(
796
+ path2.join(projectPath, "tether", "functions", "comments.ts"),
885
797
  `import { query, mutation, z } from '@tthr/server';
886
798
 
887
799
  // List all comments
@@ -953,27 +865,27 @@ export const remove = mutation({
953
865
  TETHER_PROJECT_ID=${projectId}
954
866
  TETHER_API_KEY=${apiKey}
955
867
  `;
956
- const envPath = path4.join(projectPath, ".env");
957
- if (await fs4.pathExists(envPath)) {
958
- const existing = await fs4.readFile(envPath, "utf-8");
959
- await fs4.writeFile(envPath, existing + "\n" + envContent);
868
+ const envPath = path2.join(projectPath, ".env");
869
+ if (await fs2.pathExists(envPath)) {
870
+ const existing = await fs2.readFile(envPath, "utf-8");
871
+ await fs2.writeFile(envPath, existing + "\n" + envContent);
960
872
  } else {
961
- await fs4.writeFile(envPath, envContent);
873
+ await fs2.writeFile(envPath, envContent);
962
874
  }
963
- const gitignorePath = path4.join(projectPath, ".gitignore");
875
+ const gitignorePath = path2.join(projectPath, ".gitignore");
964
876
  const tetherGitignore = `
965
877
  # Tether
966
878
  _generated/
967
879
  .env
968
880
  .env.local
969
881
  `;
970
- if (await fs4.pathExists(gitignorePath)) {
971
- const existing = await fs4.readFile(gitignorePath, "utf-8");
882
+ if (await fs2.pathExists(gitignorePath)) {
883
+ const existing = await fs2.readFile(gitignorePath, "utf-8");
972
884
  if (!existing.includes("_generated/")) {
973
- await fs4.writeFile(gitignorePath, existing + tetherGitignore);
885
+ await fs2.writeFile(gitignorePath, existing + tetherGitignore);
974
886
  }
975
887
  } else {
976
- await fs4.writeFile(gitignorePath, tetherGitignore.trim());
888
+ await fs2.writeFile(gitignorePath, tetherGitignore.trim());
977
889
  }
978
890
  }
979
891
  async function configureFramework(projectPath, template) {
@@ -986,9 +898,9 @@ async function configureFramework(projectPath, template) {
986
898
  }
987
899
  }
988
900
  async function configureNuxt(projectPath) {
989
- const configPath = path4.join(projectPath, "nuxt.config.ts");
990
- if (!await fs4.pathExists(configPath)) {
991
- await fs4.writeFile(
901
+ const configPath = path2.join(projectPath, "nuxt.config.ts");
902
+ if (!await fs2.pathExists(configPath)) {
903
+ await fs2.writeFile(
992
904
  configPath,
993
905
  `// https://nuxt.com/docs/api/configuration/nuxt-config
994
906
  export default defineNuxtConfig({
@@ -1006,7 +918,7 @@ export default defineNuxtConfig({
1006
918
  );
1007
919
  return;
1008
920
  }
1009
- let config = await fs4.readFile(configPath, "utf-8");
921
+ let config = await fs2.readFile(configPath, "utf-8");
1010
922
  if (config.includes("modules:")) {
1011
923
  config = config.replace(
1012
924
  /modules:\s*\[/,
@@ -1033,11 +945,11 @@ export default defineNuxtConfig({
1033
945
  `
1034
946
  );
1035
947
  }
1036
- await fs4.writeFile(configPath, config);
948
+ await fs2.writeFile(configPath, config);
1037
949
  }
1038
950
  async function configureNext(projectPath) {
1039
- const providersPath = path4.join(projectPath, "src", "app", "providers.tsx");
1040
- await fs4.writeFile(
951
+ const providersPath = path2.join(projectPath, "src", "app", "providers.tsx");
952
+ await fs2.writeFile(
1041
953
  providersPath,
1042
954
  `'use client';
1043
955
 
@@ -1055,9 +967,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
1055
967
  }
1056
968
  `
1057
969
  );
1058
- const layoutPath = path4.join(projectPath, "src", "app", "layout.tsx");
1059
- if (await fs4.pathExists(layoutPath)) {
1060
- let layout = await fs4.readFile(layoutPath, "utf-8");
970
+ const layoutPath = path2.join(projectPath, "src", "app", "layout.tsx");
971
+ if (await fs2.pathExists(layoutPath)) {
972
+ let layout = await fs2.readFile(layoutPath, "utf-8");
1061
973
  if (!layout.includes("import { Providers }")) {
1062
974
  layout = layout.replace(
1063
975
  /^(import.*\n)+/m,
@@ -1070,24 +982,24 @@ export function Providers({ children }: { children: React.ReactNode }) {
1070
982
  "$1<Providers>$2</Providers>$3"
1071
983
  );
1072
984
  }
1073
- await fs4.writeFile(layoutPath, layout);
985
+ await fs2.writeFile(layoutPath, layout);
1074
986
  }
1075
- const envLocalPath = path4.join(projectPath, ".env.local");
987
+ const envLocalPath = path2.join(projectPath, ".env.local");
1076
988
  const nextEnvContent = `# Tether Configuration (client-side)
1077
989
  NEXT_PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
1078
990
  `;
1079
- if (await fs4.pathExists(envLocalPath)) {
1080
- const existing = await fs4.readFile(envLocalPath, "utf-8");
1081
- await fs4.writeFile(envLocalPath, existing + "\n" + nextEnvContent);
991
+ if (await fs2.pathExists(envLocalPath)) {
992
+ const existing = await fs2.readFile(envLocalPath, "utf-8");
993
+ await fs2.writeFile(envLocalPath, existing + "\n" + nextEnvContent);
1082
994
  } else {
1083
- await fs4.writeFile(envLocalPath, nextEnvContent);
995
+ await fs2.writeFile(envLocalPath, nextEnvContent);
1084
996
  }
1085
997
  }
1086
998
  async function configureSvelteKit(projectPath) {
1087
- const libPath = path4.join(projectPath, "src", "lib");
1088
- await fs4.ensureDir(libPath);
1089
- await fs4.writeFile(
1090
- path4.join(libPath, "tether.ts"),
999
+ const libPath = path2.join(projectPath, "src", "lib");
1000
+ await fs2.ensureDir(libPath);
1001
+ await fs2.writeFile(
1002
+ path2.join(libPath, "tether.ts"),
1091
1003
  `import { createClient } from '@tthr/svelte';
1092
1004
  import { PUBLIC_TETHER_PROJECT_ID, PUBLIC_TETHER_URL } from '$env/static/public';
1093
1005
 
@@ -1097,14 +1009,14 @@ export const tether = createClient({
1097
1009
  });
1098
1010
  `
1099
1011
  );
1100
- const envPath = path4.join(projectPath, ".env");
1012
+ const envPath = path2.join(projectPath, ".env");
1101
1013
  const svelteEnvContent = `# Tether Configuration (public)
1102
1014
  PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
1103
1015
  `;
1104
- if (await fs4.pathExists(envPath)) {
1105
- const existing = await fs4.readFile(envPath, "utf-8");
1016
+ if (await fs2.pathExists(envPath)) {
1017
+ const existing = await fs2.readFile(envPath, "utf-8");
1106
1018
  if (!existing.includes("PUBLIC_TETHER_")) {
1107
- await fs4.writeFile(envPath, existing + "\n" + svelteEnvContent);
1019
+ await fs2.writeFile(envPath, existing + "\n" + svelteEnvContent);
1108
1020
  }
1109
1021
  }
1110
1022
  }
@@ -1154,15 +1066,15 @@ async function installTetherPackages(projectPath, template, packageManager) {
1154
1066
  stdio: "pipe"
1155
1067
  });
1156
1068
  } catch (error) {
1157
- console.warn(chalk3.yellow("\nNote: Some Tether packages may not be published yet."));
1069
+ console.warn(chalk2.yellow("\nNote: Some Tether packages may not be published yet."));
1158
1070
  }
1159
1071
  }
1160
1072
  async function createDemoPage(projectPath, template) {
1161
1073
  if (template === "nuxt") {
1162
- const nuxt4AppVuePath = path4.join(projectPath, "app", "app.vue");
1163
- const nuxt3AppVuePath = path4.join(projectPath, "app.vue");
1164
- const appVuePath = await fs4.pathExists(path4.join(projectPath, "app")) ? nuxt4AppVuePath : nuxt3AppVuePath;
1165
- await fs4.writeFile(
1074
+ const nuxt4AppVuePath = path2.join(projectPath, "app", "app.vue");
1075
+ const nuxt3AppVuePath = path2.join(projectPath, "app.vue");
1076
+ const appVuePath = await fs2.pathExists(path2.join(projectPath, "app")) ? nuxt4AppVuePath : nuxt3AppVuePath;
1077
+ await fs2.writeFile(
1166
1078
  appVuePath,
1167
1079
  `<template>
1168
1080
  <TetherWelcome />
@@ -1170,8 +1082,8 @@ async function createDemoPage(projectPath, template) {
1170
1082
  `
1171
1083
  );
1172
1084
  } else if (template === "next") {
1173
- const pagePath = path4.join(projectPath, "src", "app", "page.tsx");
1174
- await fs4.writeFile(
1085
+ const pagePath = path2.join(projectPath, "src", "app", "page.tsx");
1086
+ await fs2.writeFile(
1175
1087
  pagePath,
1176
1088
  `'use client';
1177
1089
 
@@ -1281,8 +1193,8 @@ export default function Home() {
1281
1193
  `
1282
1194
  );
1283
1195
  } else if (template === "sveltekit") {
1284
- const pagePath = path4.join(projectPath, "src", "routes", "+page.svelte");
1285
- await fs4.writeFile(
1196
+ const pagePath = path2.join(projectPath, "src", "routes", "+page.svelte");
1197
+ await fs2.writeFile(
1286
1198
  pagePath,
1287
1199
  `<script lang="ts">
1288
1200
  import { onMount } from 'svelte';
@@ -1548,369 +1460,337 @@ export default function Home() {
1548
1460
  }
1549
1461
 
1550
1462
  // src/commands/dev.ts
1551
- import chalk4 from "chalk";
1463
+ import chalk3 from "chalk";
1552
1464
  import ora3 from "ora";
1553
- import fs5 from "fs-extra";
1554
- import path5 from "path";
1555
- async function devCommand(options) {
1556
- await requireAuth();
1557
- const configPath = path5.resolve(process.cwd(), "tether.config.ts");
1558
- if (!await fs5.pathExists(configPath)) {
1559
- console.log(chalk4.red("\nError: Not a Tether project"));
1560
- console.log(chalk4.dim("Run `tthr init` to create a new project\n"));
1561
- process.exit(1);
1465
+ import fs3 from "fs-extra";
1466
+ import path3 from "path";
1467
+ import { spawn } from "child_process";
1468
+ import { watch } from "chokidar";
1469
+ var frameworkProcess = null;
1470
+ var schemaWatcher = null;
1471
+ var functionsWatcher = null;
1472
+ var isGenerating = false;
1473
+ var pendingGenerate = false;
1474
+ async function runGenerate(spinner) {
1475
+ if (isGenerating) {
1476
+ pendingGenerate = true;
1477
+ return true;
1562
1478
  }
1563
- const config = await loadConfig();
1564
- const port = options.port || String(config.dev?.port || 3001);
1565
- console.log(chalk4.bold("\n\u26A1 Starting Tether development server\n"));
1566
- const spinner = ora3("Starting server...").start();
1479
+ isGenerating = true;
1567
1480
  try {
1568
- spinner.succeed(`Development server running on port ${port}`);
1569
- console.log("\n" + chalk4.cyan(` Local: http://localhost:${port}`));
1570
- console.log(chalk4.cyan(` WebSocket: ws://localhost:${port}/ws
1571
- `));
1572
- console.log(chalk4.dim(" Press Ctrl+C to stop\n"));
1573
- process.on("SIGINT", () => {
1574
- console.log(chalk4.yellow("\n\nShutting down...\n"));
1575
- process.exit(0);
1576
- });
1577
- await new Promise(() => {
1578
- });
1481
+ const { generateTypes } = await import("./generate-26HERX3T.js");
1482
+ spinner.text = "Regenerating types...";
1483
+ await generateTypes({ silent: true });
1484
+ spinner.succeed("Types regenerated");
1485
+ spinner.start("Watching for changes...");
1486
+ return true;
1579
1487
  } catch (error) {
1580
- spinner.fail("Failed to start server");
1581
- console.error(chalk4.red(error instanceof Error ? error.message : "Unknown error"));
1582
- process.exit(1);
1488
+ spinner.fail("Type generation failed");
1489
+ console.error(chalk3.red(error instanceof Error ? error.message : "Unknown error"));
1490
+ spinner.start("Watching for changes...");
1491
+ return false;
1492
+ } finally {
1493
+ isGenerating = false;
1494
+ if (pendingGenerate) {
1495
+ pendingGenerate = false;
1496
+ setTimeout(() => runGenerate(spinner), 100);
1497
+ }
1583
1498
  }
1584
1499
  }
1585
-
1586
- // src/commands/generate.ts
1587
- import chalk5 from "chalk";
1588
- import ora4 from "ora";
1589
- import fs6 from "fs-extra";
1590
- import path6 from "path";
1591
- function parseSchemaFile(source) {
1592
- const tables = [];
1593
- const schemaMatch = source.match(/defineSchema\s*\(\s*\{([\s\S]*)\}\s*\)/);
1594
- if (!schemaMatch) return tables;
1595
- const schemaContent = schemaMatch[1];
1596
- const tableStartRegex = /(\w+)\s*:\s*\{/g;
1597
- let match;
1598
- while ((match = tableStartRegex.exec(schemaContent)) !== null) {
1599
- const tableName = match[1];
1600
- const startOffset = match.index + match[0].length;
1601
- let braceCount = 1;
1602
- let endOffset = startOffset;
1603
- for (let i = startOffset; i < schemaContent.length && braceCount > 0; i++) {
1604
- const char = schemaContent[i];
1500
+ async function validateSchema(schemaPath) {
1501
+ try {
1502
+ const content = await fs3.readFile(schemaPath, "utf-8");
1503
+ if (!content.includes("defineSchema")) {
1504
+ return { valid: false, error: "Missing defineSchema() call" };
1505
+ }
1506
+ let braceCount = 0;
1507
+ for (const char of content) {
1605
1508
  if (char === "{") braceCount++;
1606
1509
  else if (char === "}") braceCount--;
1607
- endOffset = i;
1510
+ if (braceCount < 0) {
1511
+ return { valid: false, error: "Unmatched closing brace" };
1512
+ }
1608
1513
  }
1609
- const columnsContent = schemaContent.slice(startOffset, endOffset);
1610
- const columns = parseColumns(columnsContent);
1611
- tables.push({ name: tableName, columns });
1612
- }
1613
- return tables;
1614
- }
1615
- function parseColumns(content) {
1616
- const columns = [];
1617
- const columnRegex = /(\w+)\s*:\s*(\w+)\s*\(\s*\)([^,\n}]*)/g;
1618
- let match;
1619
- while ((match = columnRegex.exec(content)) !== null) {
1620
- const name = match[1];
1621
- const schemaType = match[2];
1622
- const modifiers = match[3] || "";
1623
- const nullable = !modifiers.includes(".notNull()") && !modifiers.includes(".primaryKey()");
1624
- const primaryKey = modifiers.includes(".primaryKey()");
1625
- columns.push({
1626
- name,
1627
- type: schemaTypeToTS(schemaType),
1628
- nullable,
1629
- primaryKey
1630
- });
1631
- }
1632
- return columns;
1633
- }
1634
- function schemaTypeToTS(schemaType) {
1635
- switch (schemaType) {
1636
- case "text":
1637
- return "string";
1638
- case "integer":
1639
- return "number";
1640
- case "real":
1641
- return "number";
1642
- case "boolean":
1643
- return "boolean";
1644
- case "timestamp":
1645
- return "string";
1646
- case "json":
1647
- return "unknown";
1648
- case "blob":
1649
- return "Uint8Array";
1650
- default:
1651
- return "unknown";
1652
- }
1653
- }
1654
- function tableNameToInterface(tableName) {
1655
- let name = tableName;
1656
- if (name.endsWith("ies")) {
1657
- name = name.slice(0, -3) + "y";
1658
- } else if (name.endsWith("s") && !name.endsWith("ss")) {
1659
- name = name.slice(0, -1);
1660
- }
1661
- return name.charAt(0).toUpperCase() + name.slice(1);
1662
- }
1663
- function generateDbFile(tables) {
1664
- const lines = [
1665
- "// Auto-generated by Tether CLI - do not edit manually",
1666
- `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1667
- "",
1668
- "import { createDatabaseProxy, type TetherDatabase } from '@tthr/client';",
1669
- ""
1670
- ];
1671
- for (const table of tables) {
1672
- const interfaceName = tableNameToInterface(table.name);
1673
- lines.push(`export interface ${interfaceName} {`);
1674
- for (const col of table.columns) {
1675
- const typeStr = col.nullable ? `${col.type} | null` : col.type;
1676
- lines.push(` ${col.name}: ${typeStr};`);
1514
+ if (braceCount !== 0) {
1515
+ return { valid: false, error: "Unmatched opening brace" };
1677
1516
  }
1678
- lines.push("}");
1679
- lines.push("");
1517
+ return { valid: true };
1518
+ } catch (error) {
1519
+ return { valid: false, error: error instanceof Error ? error.message : "Unknown error" };
1680
1520
  }
1681
- lines.push("export interface Schema {");
1682
- for (const table of tables) {
1683
- const interfaceName = tableNameToInterface(table.name);
1684
- lines.push(` ${table.name}: ${interfaceName};`);
1685
- }
1686
- lines.push("}");
1687
- lines.push("");
1688
- lines.push("// Database client with typed tables");
1689
- lines.push("// This is a proxy that will be populated by the Tether runtime");
1690
- lines.push("export const db: TetherDatabase<Schema> = createDatabaseProxy<Schema>();");
1691
- lines.push("");
1692
- return lines.join("\n");
1693
1521
  }
1694
- async function parseFunctionsDir(functionsDir) {
1695
- const functions = [];
1696
- if (!await fs6.pathExists(functionsDir)) {
1697
- return functions;
1522
+ async function validateFunctions(functionsDir) {
1523
+ const errors = [];
1524
+ if (!await fs3.pathExists(functionsDir)) {
1525
+ return { valid: true, errors: [] };
1698
1526
  }
1699
- const files = await fs6.readdir(functionsDir);
1527
+ const files = await fs3.readdir(functionsDir);
1700
1528
  for (const file of files) {
1701
1529
  if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
1702
- const filePath = path6.join(functionsDir, file);
1703
- const stat = await fs6.stat(filePath);
1530
+ const filePath = path3.join(functionsDir, file);
1531
+ const stat = await fs3.stat(filePath);
1704
1532
  if (!stat.isFile()) continue;
1705
- const moduleName = file.replace(/\.(ts|js)$/, "");
1706
- const source = await fs6.readFile(filePath, "utf-8");
1707
- const exportRegex = /export\s+const\s+(\w+)\s*=\s*(query|mutation|action)\s*\(/g;
1708
- let match;
1709
- while ((match = exportRegex.exec(source)) !== null) {
1710
- functions.push({
1711
- name: match[1],
1712
- moduleName
1713
- });
1533
+ try {
1534
+ const content = await fs3.readFile(filePath, "utf-8");
1535
+ const hasExports = /export\s+const\s+\w+\s*=\s*(query|mutation|action)\s*\(/.test(content);
1536
+ if (!hasExports && !file.includes("index")) {
1537
+ errors.push(`${file}: No query, mutation, or action exports found`);
1538
+ }
1539
+ let braceCount = 0;
1540
+ for (const char of content) {
1541
+ if (char === "{") braceCount++;
1542
+ else if (char === "}") braceCount--;
1543
+ }
1544
+ if (braceCount !== 0) {
1545
+ errors.push(`${file}: Mismatched braces`);
1546
+ }
1547
+ } catch (error) {
1548
+ errors.push(`${file}: ${error instanceof Error ? error.message : "Read error"}`);
1714
1549
  }
1715
1550
  }
1716
- return functions;
1551
+ return { valid: errors.length === 0, errors };
1717
1552
  }
1718
- async function generateApiFile(functionsDir) {
1719
- const functions = await parseFunctionsDir(functionsDir);
1720
- const moduleMap = /* @__PURE__ */ new Map();
1721
- for (const fn of functions) {
1722
- if (!moduleMap.has(fn.moduleName)) {
1723
- moduleMap.set(fn.moduleName, []);
1553
+ function startFrameworkDev(command, port, spinner) {
1554
+ const [cmd, ...args] = command.split(" ");
1555
+ const portArgs = ["--port", port];
1556
+ spinner.text = `Starting ${command}...`;
1557
+ const child = spawn(cmd, [...args, ...portArgs], {
1558
+ cwd: process.cwd(),
1559
+ stdio: ["inherit", "pipe", "pipe"],
1560
+ shell: true,
1561
+ env: {
1562
+ ...process.env,
1563
+ FORCE_COLOR: "1"
1724
1564
  }
1725
- moduleMap.get(fn.moduleName).push(fn.name);
1726
- }
1727
- const lines = [
1728
- "// Auto-generated by Tether CLI - do not edit manually",
1729
- `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1730
- "",
1731
- "import { createApiProxy } from '@tthr/client';",
1732
- "",
1733
- "/**",
1734
- " * API function reference type for useQuery/useMutation.",
1735
- ' * The _name property contains the function path (e.g., "users.list").',
1736
- " */",
1737
- "export interface ApiFunction<TArgs = unknown, TResult = unknown> {",
1738
- " _name: string;",
1739
- " _args?: TArgs;",
1740
- " _result?: TResult;",
1741
- "}",
1742
- ""
1743
- ];
1744
- if (moduleMap.size > 0) {
1745
- for (const [moduleName, fnNames] of moduleMap) {
1746
- const interfaceName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1) + "Api";
1747
- lines.push(`export interface ${interfaceName} {`);
1748
- for (const fnName of fnNames) {
1749
- lines.push(` ${fnName}: ApiFunction;`);
1750
- }
1751
- lines.push("}");
1752
- lines.push("");
1565
+ });
1566
+ let started = false;
1567
+ child.stdout?.on("data", (data) => {
1568
+ const output = data.toString();
1569
+ process.stdout.write(output);
1570
+ if (!started && (output.includes("Local:") || output.includes("ready") || output.includes("listening"))) {
1571
+ started = true;
1572
+ spinner.succeed(`Framework dev server running`);
1573
+ spinner.start("Watching for changes...");
1753
1574
  }
1754
- lines.push("export interface Api {");
1755
- for (const moduleName of moduleMap.keys()) {
1756
- const interfaceName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1) + "Api";
1757
- lines.push(` ${moduleName}: ${interfaceName};`);
1575
+ });
1576
+ child.stderr?.on("data", (data) => {
1577
+ const output = data.toString();
1578
+ if (!output.includes("ExperimentalWarning") && !output.includes("--trace-warnings")) {
1579
+ process.stderr.write(chalk3.dim(output));
1758
1580
  }
1759
- lines.push("}");
1760
- } else {
1761
- lines.push("/**");
1762
- lines.push(" * Flexible API type that allows access to any function path.");
1763
- lines.push(" * Functions are accessed as api.moduleName.functionName");
1764
- lines.push(" * The actual types depend on your function definitions.");
1765
- lines.push(" */");
1766
- lines.push("export type Api = {");
1767
- lines.push(" [module: string]: {");
1768
- lines.push(" [fn: string]: ApiFunction;");
1769
- lines.push(" };");
1770
- lines.push("};");
1771
- }
1772
- lines.push("");
1773
- lines.push("// API client proxy - provides typed access to your functions");
1774
- lines.push("// On the client: returns { _name } references for useQuery/useMutation");
1775
- lines.push("// In Tether functions: calls the actual function implementation");
1776
- lines.push("export const api = createApiProxy<Api>();");
1777
- lines.push("");
1778
- return lines.join("\n");
1581
+ });
1582
+ child.on("error", (error) => {
1583
+ spinner.fail(`Failed to start framework: ${error.message}`);
1584
+ });
1585
+ child.on("exit", (code) => {
1586
+ if (code !== 0 && code !== null) {
1587
+ console.log(chalk3.yellow(`
1588
+ Framework process exited with code ${code}`));
1589
+ }
1590
+ });
1591
+ return child;
1592
+ }
1593
+ function cleanup() {
1594
+ if (frameworkProcess) {
1595
+ frameworkProcess.kill("SIGTERM");
1596
+ frameworkProcess = null;
1597
+ }
1598
+ if (schemaWatcher) {
1599
+ schemaWatcher.close();
1600
+ schemaWatcher = null;
1601
+ }
1602
+ if (functionsWatcher) {
1603
+ functionsWatcher.close();
1604
+ functionsWatcher = null;
1605
+ }
1779
1606
  }
1780
- async function generateCommand() {
1607
+ async function devCommand(options) {
1781
1608
  await requireAuth();
1782
- const configPath = path6.resolve(process.cwd(), "tether.config.ts");
1783
- if (!await fs6.pathExists(configPath)) {
1784
- console.log(chalk5.red("\nError: Not a Tether project"));
1785
- console.log(chalk5.dim("Run `tthr init` to create a new project\n"));
1609
+ const configPath = path3.resolve(process.cwd(), "tether.config.ts");
1610
+ if (!await fs3.pathExists(configPath)) {
1611
+ console.log(chalk3.red("\nError: Not a Tether project"));
1612
+ console.log(chalk3.dim("Run `tthr init` to create a new project\n"));
1786
1613
  process.exit(1);
1787
1614
  }
1788
- console.log(chalk5.bold("\n\u26A1 Generating types from schema\n"));
1789
- const spinner = ora4("Reading schema...").start();
1615
+ const config = await loadConfig();
1616
+ const port = options.port || String(config.dev?.port || 3001);
1617
+ const schemaPath = resolvePath(config.schema);
1618
+ const functionsDir = resolvePath(config.functions);
1619
+ const outputDir = resolvePath(config.output);
1620
+ const framework = config.framework || await detectFramework();
1621
+ const devCommand2 = config.dev?.command || getFrameworkDevCommand(framework);
1622
+ console.log(chalk3.bold("\n\u26A1 Starting Tether development server\n"));
1623
+ if (framework !== "unknown") {
1624
+ console.log(chalk3.dim(` Framework: ${framework}`));
1625
+ }
1626
+ if (devCommand2) {
1627
+ console.log(chalk3.dim(` Dev command: ${devCommand2}`));
1628
+ }
1629
+ console.log(chalk3.dim(` Schema: ${path3.relative(process.cwd(), schemaPath)}`));
1630
+ console.log(chalk3.dim(` Functions: ${path3.relative(process.cwd(), functionsDir)}`));
1631
+ console.log(chalk3.dim(` Output: ${path3.relative(process.cwd(), outputDir)}`));
1632
+ console.log("");
1633
+ const spinner = ora3("Initialising...").start();
1634
+ process.on("SIGINT", () => {
1635
+ spinner.stop();
1636
+ console.log(chalk3.yellow("\n\nShutting down...\n"));
1637
+ cleanup();
1638
+ process.exit(0);
1639
+ });
1640
+ process.on("SIGTERM", () => {
1641
+ cleanup();
1642
+ process.exit(0);
1643
+ });
1790
1644
  try {
1791
- const config = await loadConfig();
1792
- const schemaPath = resolvePath(config.schema);
1793
- const outputDir = resolvePath(config.output);
1794
- const functionsDir = resolvePath(config.functions);
1795
- if (!await fs6.pathExists(schemaPath)) {
1796
- spinner.fail("Schema file not found");
1797
- console.log(chalk5.dim(`Expected: ${schemaPath}
1798
- `));
1799
- process.exit(1);
1645
+ spinner.text = "Validating schema...";
1646
+ const schemaValidation = await validateSchema(schemaPath);
1647
+ if (!schemaValidation.valid) {
1648
+ spinner.warn(`Schema validation warning: ${schemaValidation.error}`);
1800
1649
  }
1801
- const schemaSource = await fs6.readFile(schemaPath, "utf-8");
1802
- const tables = parseSchemaFile(schemaSource);
1803
- if (tables.length === 0) {
1804
- spinner.warn("No tables found in schema");
1805
- console.log(chalk5.dim(" Make sure your schema uses defineSchema({ ... })\n"));
1806
- return;
1650
+ spinner.text = "Validating functions...";
1651
+ const functionsValidation = await validateFunctions(functionsDir);
1652
+ if (!functionsValidation.valid) {
1653
+ spinner.warn("Function validation warnings:");
1654
+ for (const error of functionsValidation.errors) {
1655
+ console.log(chalk3.yellow(` - ${error}`));
1656
+ }
1807
1657
  }
1808
- spinner.text = `Generating types for ${tables.length} table(s)...`;
1809
- await fs6.ensureDir(outputDir);
1810
- await fs6.writeFile(
1811
- path6.join(outputDir, "db.ts"),
1812
- generateDbFile(tables)
1813
- );
1814
- await fs6.writeFile(
1815
- path6.join(outputDir, "api.ts"),
1816
- await generateApiFile(functionsDir)
1817
- );
1818
- await fs6.writeFile(
1819
- path6.join(outputDir, "index.ts"),
1820
- `// Auto-generated by Tether CLI - do not edit manually
1821
- export * from './db';
1822
- export * from './api';
1823
- `
1824
- );
1825
- spinner.succeed(`Types generated for ${tables.length} table(s)`);
1826
- console.log("\n" + chalk5.green("\u2713") + " Tables:");
1827
- for (const table of tables) {
1828
- console.log(chalk5.dim(` - ${table.name} (${table.columns.length} columns)`));
1658
+ spinner.text = "Generating types...";
1659
+ const { generateTypes } = await import("./generate-26HERX3T.js");
1660
+ await generateTypes({ silent: true });
1661
+ spinner.succeed("Types generated");
1662
+ spinner.start("Setting up file watchers...");
1663
+ if (await fs3.pathExists(schemaPath)) {
1664
+ schemaWatcher = watch(schemaPath, {
1665
+ ignoreInitial: true,
1666
+ awaitWriteFinish: {
1667
+ stabilityThreshold: 300,
1668
+ pollInterval: 100
1669
+ }
1670
+ });
1671
+ schemaWatcher.on("change", async () => {
1672
+ console.log(chalk3.cyan("\n Schema changed, validating..."));
1673
+ const validation = await validateSchema(schemaPath);
1674
+ if (!validation.valid) {
1675
+ console.log(chalk3.yellow(` Schema error: ${validation.error}`));
1676
+ return;
1677
+ }
1678
+ await runGenerate(spinner);
1679
+ });
1680
+ }
1681
+ if (await fs3.pathExists(functionsDir)) {
1682
+ functionsWatcher = watch(path3.join(functionsDir, "**/*.{ts,js}"), {
1683
+ ignoreInitial: true,
1684
+ awaitWriteFinish: {
1685
+ stabilityThreshold: 300,
1686
+ pollInterval: 100
1687
+ }
1688
+ });
1689
+ functionsWatcher.on("all", async (event, filePath) => {
1690
+ const relativePath = path3.relative(functionsDir, filePath);
1691
+ console.log(chalk3.cyan(`
1692
+ Function ${event}: ${relativePath}`));
1693
+ const validation = await validateFunctions(functionsDir);
1694
+ if (!validation.valid) {
1695
+ for (const error of validation.errors) {
1696
+ console.log(chalk3.yellow(` Warning: ${error}`));
1697
+ }
1698
+ }
1699
+ await runGenerate(spinner);
1700
+ });
1829
1701
  }
1830
- const relativeOutput = path6.relative(process.cwd(), outputDir);
1831
- console.log("\n" + chalk5.green("\u2713") + " Generated files:");
1832
- console.log(chalk5.dim(` ${relativeOutput}/db.ts`));
1833
- console.log(chalk5.dim(` ${relativeOutput}/api.ts`));
1834
- console.log(chalk5.dim(` ${relativeOutput}/index.ts
1702
+ spinner.succeed("File watchers ready");
1703
+ if (devCommand2 && !options.skipFramework) {
1704
+ spinner.start(`Starting ${framework} dev server...`);
1705
+ frameworkProcess = startFrameworkDev(devCommand2, port, spinner);
1706
+ } else {
1707
+ spinner.start("Watching for changes...");
1708
+ console.log("\n" + chalk3.cyan(` Tether dev ready`));
1709
+ console.log(chalk3.dim(` Watching schema and functions for changes`));
1710
+ console.log(chalk3.dim(` Press Ctrl+C to stop
1835
1711
  `));
1712
+ }
1713
+ await new Promise(() => {
1714
+ });
1836
1715
  } catch (error) {
1837
- spinner.fail("Failed to generate types");
1838
- console.error(chalk5.red(error instanceof Error ? error.message : "Unknown error"));
1716
+ spinner.fail("Failed to start dev server");
1717
+ console.error(chalk3.red(error instanceof Error ? error.message : "Unknown error"));
1718
+ cleanup();
1839
1719
  process.exit(1);
1840
1720
  }
1841
1721
  }
1842
1722
 
1843
1723
  // src/commands/login.ts
1844
- import chalk6 from "chalk";
1845
- import ora5 from "ora";
1724
+ import chalk4 from "chalk";
1725
+ import ora4 from "ora";
1846
1726
  import os from "os";
1847
1727
  var isDev3 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
1848
1728
  var API_URL3 = isDev3 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
1849
1729
  var AUTH_URL = isDev3 ? "http://localhost:3000/cli" : "https://tthr.io/cli";
1850
1730
  async function loginCommand() {
1851
- console.log(chalk6.bold("\u26A1 Login to Tether\n"));
1731
+ console.log(chalk4.bold("\u26A1 Login to Tether\n"));
1852
1732
  const existing = await getCredentials();
1853
1733
  if (existing) {
1854
- console.log(chalk6.green("\u2713") + ` Already logged in as ${chalk6.cyan(existing.email)}`);
1855
- console.log(chalk6.dim("\nRun `tthr logout` to sign out\n"));
1734
+ console.log(chalk4.green("\u2713") + ` Already logged in as ${chalk4.cyan(existing.email)}`);
1735
+ console.log(chalk4.dim("\nRun `tthr logout` to sign out\n"));
1856
1736
  return;
1857
1737
  }
1858
- const spinner = ora5("Generating authentication code...").start();
1738
+ const spinner = ora4("Generating authentication code...").start();
1859
1739
  try {
1860
1740
  const deviceCode = await requestDeviceCode();
1861
1741
  spinner.stop();
1862
1742
  const authUrl = `${AUTH_URL}/${deviceCode.userCode}`;
1863
- console.log(chalk6.dim("Open this URL in your browser to authenticate:\n"));
1864
- console.log(chalk6.cyan.bold(` ${authUrl}
1743
+ console.log(chalk4.dim("Open this URL in your browser to authenticate:\n"));
1744
+ console.log(chalk4.cyan.bold(` ${authUrl}
1865
1745
  `));
1866
- console.log(chalk6.dim(`Your code: ${chalk6.white.bold(deviceCode.userCode)}
1746
+ console.log(chalk4.dim(`Your code: ${chalk4.white.bold(deviceCode.userCode)}
1867
1747
  `));
1868
1748
  const credentials = await pollForApproval(deviceCode.deviceCode, deviceCode.interval, deviceCode.expiresIn);
1869
1749
  await saveCredentials(credentials);
1870
- console.log(chalk6.green("\u2713") + ` Logged in as ${chalk6.cyan(credentials.email)}`);
1750
+ console.log(chalk4.green("\u2713") + ` Logged in as ${chalk4.cyan(credentials.email)}`);
1871
1751
  console.log();
1872
1752
  } catch (error) {
1873
1753
  spinner.fail("Authentication failed");
1874
- console.error(chalk6.red(error instanceof Error ? error.message : "Unknown error"));
1875
- console.log(chalk6.dim("\nTry again or visit https://tthr.io/docs/cli for help\n"));
1754
+ console.error(chalk4.red(error instanceof Error ? error.message : "Unknown error"));
1755
+ console.log(chalk4.dim("\nTry again or visit https://tthr.io/docs/cli for help\n"));
1876
1756
  process.exit(1);
1877
1757
  }
1878
1758
  }
1879
1759
  async function logoutCommand() {
1880
- console.log(chalk6.bold("\n\u26A1 Logout from Tether\n"));
1760
+ console.log(chalk4.bold("\n\u26A1 Logout from Tether\n"));
1881
1761
  const credentials = await getCredentials();
1882
1762
  if (!credentials) {
1883
- console.log(chalk6.dim("Not logged in\n"));
1763
+ console.log(chalk4.dim("Not logged in\n"));
1884
1764
  return;
1885
1765
  }
1886
- const spinner = ora5("Logging out...").start();
1766
+ const spinner = ora4("Logging out...").start();
1887
1767
  try {
1888
1768
  await clearCredentials();
1889
- spinner.succeed(`Logged out from ${chalk6.cyan(credentials.email)}`);
1769
+ spinner.succeed(`Logged out from ${chalk4.cyan(credentials.email)}`);
1890
1770
  console.log();
1891
1771
  } catch (error) {
1892
1772
  spinner.fail("Logout failed");
1893
- console.error(chalk6.red(error instanceof Error ? error.message : "Unknown error"));
1773
+ console.error(chalk4.red(error instanceof Error ? error.message : "Unknown error"));
1894
1774
  process.exit(1);
1895
1775
  }
1896
1776
  }
1897
1777
  async function whoamiCommand() {
1898
1778
  const credentials = await getCredentials();
1899
1779
  if (!credentials) {
1900
- console.log(chalk6.dim("\nNot logged in"));
1901
- console.log(chalk6.dim("Run `tthr login` to authenticate\n"));
1780
+ console.log(chalk4.dim("\nNot logged in"));
1781
+ console.log(chalk4.dim("Run `tthr login` to authenticate\n"));
1902
1782
  return;
1903
1783
  }
1904
- console.log(chalk6.bold("\n\u26A1 Current user\n"));
1905
- console.log(` Email: ${chalk6.cyan(credentials.email)}`);
1906
- console.log(` User ID: ${chalk6.dim(credentials.userId)}`);
1784
+ console.log(chalk4.bold("\n\u26A1 Current user\n"));
1785
+ console.log(` Email: ${chalk4.cyan(credentials.email)}`);
1786
+ console.log(` User ID: ${chalk4.dim(credentials.userId)}`);
1907
1787
  if (credentials.expiresAt) {
1908
1788
  const expiresAt = new Date(credentials.expiresAt);
1909
1789
  const now = /* @__PURE__ */ new Date();
1910
1790
  if (expiresAt > now) {
1911
- console.log(` Session: ${chalk6.green("Active")}`);
1791
+ console.log(` Session: ${chalk4.green("Active")}`);
1912
1792
  } else {
1913
- console.log(` Session: ${chalk6.yellow("Expired")}`);
1793
+ console.log(` Session: ${chalk4.yellow("Expired")}`);
1914
1794
  }
1915
1795
  }
1916
1796
  console.log();
@@ -1952,20 +1832,20 @@ async function requestDeviceCode() {
1952
1832
  async function pollForApproval(deviceCode, interval, expiresIn) {
1953
1833
  const startTime = Date.now();
1954
1834
  const expiresAt = startTime + expiresIn * 1e3;
1955
- const spinner = ora5("").start();
1835
+ const spinner = ora4("").start();
1956
1836
  const updateCountdown = () => {
1957
1837
  const remaining = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1e3));
1958
1838
  const mins = Math.floor(remaining / 60);
1959
1839
  const secs = remaining % 60;
1960
1840
  const timeStr = mins > 0 ? `${mins}:${secs.toString().padStart(2, "0")}` : `${secs}s`;
1961
- spinner.text = `Waiting for approval... ${chalk6.dim(`(${timeStr} remaining)`)}`;
1841
+ spinner.text = `Waiting for approval... ${chalk4.dim(`(${timeStr} remaining)`)}`;
1962
1842
  };
1963
1843
  updateCountdown();
1964
1844
  const countdownInterval = setInterval(updateCountdown, 1e3);
1965
1845
  try {
1966
1846
  while (Date.now() < expiresAt) {
1967
1847
  await sleep(interval * 1e3);
1968
- spinner.text = `Checking... ${chalk6.dim(`(${Math.ceil((expiresAt - Date.now()) / 1e3)}s remaining)`)}`;
1848
+ spinner.text = `Checking... ${chalk4.dim(`(${Math.ceil((expiresAt - Date.now()) / 1e3)}s remaining)`)}`;
1969
1849
  const response = await fetch(`${API_URL3}/auth/device/${deviceCode}`, {
1970
1850
  method: "GET"
1971
1851
  }).catch(() => null);
@@ -1998,10 +1878,10 @@ function sleep(ms) {
1998
1878
  }
1999
1879
 
2000
1880
  // src/commands/update.ts
2001
- import chalk7 from "chalk";
2002
- import ora6 from "ora";
2003
- import fs7 from "fs-extra";
2004
- import path7 from "path";
1881
+ import chalk5 from "chalk";
1882
+ import ora5 from "ora";
1883
+ import fs4 from "fs-extra";
1884
+ import path4 from "path";
2005
1885
  import { execSync as execSync2 } from "child_process";
2006
1886
  var TETHER_PACKAGES = [
2007
1887
  "@tthr/client",
@@ -2013,13 +1893,13 @@ var TETHER_PACKAGES = [
2013
1893
  ];
2014
1894
  function detectPackageManager() {
2015
1895
  const cwd = process.cwd();
2016
- if (fs7.existsSync(path7.join(cwd, "bun.lockb")) || fs7.existsSync(path7.join(cwd, "bun.lock"))) {
1896
+ if (fs4.existsSync(path4.join(cwd, "bun.lockb")) || fs4.existsSync(path4.join(cwd, "bun.lock"))) {
2017
1897
  return "bun";
2018
1898
  }
2019
- if (fs7.existsSync(path7.join(cwd, "pnpm-lock.yaml"))) {
1899
+ if (fs4.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) {
2020
1900
  return "pnpm";
2021
1901
  }
2022
- if (fs7.existsSync(path7.join(cwd, "yarn.lock"))) {
1902
+ if (fs4.existsSync(path4.join(cwd, "yarn.lock"))) {
2023
1903
  return "yarn";
2024
1904
  }
2025
1905
  return "npm";
@@ -2049,14 +1929,14 @@ async function getLatestVersion2(packageName) {
2049
1929
  }
2050
1930
  }
2051
1931
  async function updateCommand(options) {
2052
- const packageJsonPath = path7.resolve(process.cwd(), "package.json");
2053
- if (!await fs7.pathExists(packageJsonPath)) {
2054
- console.log(chalk7.red("\nError: No package.json found"));
2055
- console.log(chalk7.dim("Make sure you're in the root of your project\n"));
1932
+ const packageJsonPath = path4.resolve(process.cwd(), "package.json");
1933
+ if (!await fs4.pathExists(packageJsonPath)) {
1934
+ console.log(chalk5.red("\nError: No package.json found"));
1935
+ console.log(chalk5.dim("Make sure you're in the root of your project\n"));
2056
1936
  process.exit(1);
2057
1937
  }
2058
- console.log(chalk7.bold("\n\u26A1 Updating Tether packages\n"));
2059
- const packageJson = await fs7.readJson(packageJsonPath);
1938
+ console.log(chalk5.bold("\n\u26A1 Updating Tether packages\n"));
1939
+ const packageJson = await fs4.readJson(packageJsonPath);
2060
1940
  const deps = packageJson.dependencies || {};
2061
1941
  const devDeps = packageJson.devDependencies || {};
2062
1942
  const installedDeps = [];
@@ -2069,13 +1949,13 @@ async function updateCommand(options) {
2069
1949
  }
2070
1950
  }
2071
1951
  if (installedDeps.length === 0) {
2072
- console.log(chalk7.yellow(" No updatable Tether packages found in package.json"));
2073
- console.log(chalk7.dim(" Tether packages start with @tthr/ (workspace: dependencies are skipped)\n"));
1952
+ console.log(chalk5.yellow(" No updatable Tether packages found in package.json"));
1953
+ console.log(chalk5.dim(" Tether packages start with @tthr/ (workspace: dependencies are skipped)\n"));
2074
1954
  return;
2075
1955
  }
2076
- console.log(chalk7.dim(` Found ${installedDeps.length} Tether package(s):
1956
+ console.log(chalk5.dim(` Found ${installedDeps.length} Tether package(s):
2077
1957
  `));
2078
- const spinner = ora6("Checking for updates...").start();
1958
+ const spinner = ora5("Checking for updates...").start();
2079
1959
  const packagesToUpdate = [];
2080
1960
  for (const pkg of installedDeps) {
2081
1961
  const latestVersion = await getLatestVersion2(pkg.name);
@@ -2089,32 +1969,32 @@ async function updateCommand(options) {
2089
1969
  isDev: pkg.isDev
2090
1970
  });
2091
1971
  } else {
2092
- console.log(chalk7.dim(` ${pkg.name}@${currentClean} (up to date)`));
1972
+ console.log(chalk5.dim(` ${pkg.name}@${currentClean} (up to date)`));
2093
1973
  }
2094
1974
  }
2095
1975
  }
2096
1976
  spinner.stop();
2097
1977
  if (packagesToUpdate.length === 0) {
2098
- console.log(chalk7.green("\n\u2713 All Tether packages are up to date\n"));
1978
+ console.log(chalk5.green("\n\u2713 All Tether packages are up to date\n"));
2099
1979
  return;
2100
1980
  }
2101
- console.log(chalk7.cyan("\n Updates available:\n"));
1981
+ console.log(chalk5.cyan("\n Updates available:\n"));
2102
1982
  for (const pkg of packagesToUpdate) {
2103
1983
  console.log(
2104
- chalk7.white(` ${pkg.name}`) + chalk7.red(` ${pkg.current}`) + chalk7.dim(" \u2192 ") + chalk7.green(`${pkg.latest}`) + (pkg.isDev ? chalk7.dim(" (dev)") : "")
1984
+ chalk5.white(` ${pkg.name}`) + chalk5.red(` ${pkg.current}`) + chalk5.dim(" \u2192 ") + chalk5.green(`${pkg.latest}`) + (pkg.isDev ? chalk5.dim(" (dev)") : "")
2105
1985
  );
2106
1986
  }
2107
1987
  if (options.dryRun) {
2108
- console.log(chalk7.yellow("\n Dry run - no packages were updated\n"));
1988
+ console.log(chalk5.yellow("\n Dry run - no packages were updated\n"));
2109
1989
  return;
2110
1990
  }
2111
1991
  const pm = detectPackageManager();
2112
- console.log(chalk7.dim(`
1992
+ console.log(chalk5.dim(`
2113
1993
  Using ${pm} to update packages...
2114
1994
  `));
2115
1995
  const depsToUpdate = packagesToUpdate.filter((p) => !p.isDev).map((p) => `${p.name}@latest`);
2116
1996
  const devDepsToUpdate = packagesToUpdate.filter((p) => p.isDev).map((p) => `${p.name}@latest`);
2117
- const updateSpinner = ora6("Installing updates...").start();
1997
+ const updateSpinner = ora5("Installing updates...").start();
2118
1998
  try {
2119
1999
  if (depsToUpdate.length > 0) {
2120
2000
  const cmd = getInstallCommand2(pm, depsToUpdate, false);
@@ -2125,43 +2005,43 @@ async function updateCommand(options) {
2125
2005
  execSync2(cmd, { stdio: "pipe", cwd: process.cwd() });
2126
2006
  }
2127
2007
  updateSpinner.succeed("Packages updated successfully");
2128
- console.log(chalk7.green(`
2008
+ console.log(chalk5.green(`
2129
2009
  \u2713 Updated ${packagesToUpdate.length} package(s)
2130
2010
  `));
2131
2011
  } catch (error) {
2132
2012
  updateSpinner.fail("Failed to update packages");
2133
- console.error(chalk7.red(error instanceof Error ? error.message : "Unknown error"));
2013
+ console.error(chalk5.red(error instanceof Error ? error.message : "Unknown error"));
2134
2014
  process.exit(1);
2135
2015
  }
2136
2016
  }
2137
2017
 
2138
2018
  // src/commands/exec.ts
2139
- import chalk8 from "chalk";
2140
- import ora7 from "ora";
2141
- import fs8 from "fs-extra";
2142
- import path8 from "path";
2019
+ import chalk6 from "chalk";
2020
+ import ora6 from "ora";
2021
+ import fs5 from "fs-extra";
2022
+ import path5 from "path";
2143
2023
  var isDev4 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
2144
2024
  var API_URL4 = isDev4 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
2145
2025
  async function execCommand(sql) {
2146
2026
  if (!sql || sql.trim() === "") {
2147
- console.log(chalk8.red("\nError: SQL query required"));
2148
- console.log(chalk8.dim('Usage: tthr exec "SELECT * FROM table_name"\n'));
2027
+ console.log(chalk6.red("\nError: SQL query required"));
2028
+ console.log(chalk6.dim('Usage: tthr exec "SELECT * FROM table_name"\n'));
2149
2029
  process.exit(1);
2150
2030
  }
2151
2031
  const credentials = await requireAuth();
2152
- const envPath = path8.resolve(process.cwd(), ".env");
2032
+ const envPath = path5.resolve(process.cwd(), ".env");
2153
2033
  let projectId;
2154
- if (await fs8.pathExists(envPath)) {
2155
- const envContent = await fs8.readFile(envPath, "utf-8");
2034
+ if (await fs5.pathExists(envPath)) {
2035
+ const envContent = await fs5.readFile(envPath, "utf-8");
2156
2036
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
2157
2037
  projectId = match?.[1]?.trim();
2158
2038
  }
2159
2039
  if (!projectId) {
2160
- console.log(chalk8.red("\nError: Project ID not found"));
2161
- console.log(chalk8.dim("Make sure TETHER_PROJECT_ID is set in your .env file\n"));
2040
+ console.log(chalk6.red("\nError: Project ID not found"));
2041
+ console.log(chalk6.dim("Make sure TETHER_PROJECT_ID is set in your .env file\n"));
2162
2042
  process.exit(1);
2163
2043
  }
2164
- const spinner = ora7("Executing query...").start();
2044
+ const spinner = ora6("Executing query...").start();
2165
2045
  try {
2166
2046
  const response = await fetch(`${API_URL4}/projects/${projectId}/exec`, {
2167
2047
  method: "POST",
@@ -2181,24 +2061,24 @@ async function execCommand(sql) {
2181
2061
  if (result.columns && result.rows) {
2182
2062
  console.log();
2183
2063
  if (result.rows.length === 0) {
2184
- console.log(chalk8.dim(" No rows returned"));
2064
+ console.log(chalk6.dim(" No rows returned"));
2185
2065
  } else {
2186
- console.log(chalk8.bold(" " + result.columns.join(" ")));
2187
- console.log(chalk8.dim(" " + result.columns.map(() => "--------").join(" ")));
2066
+ console.log(chalk6.bold(" " + result.columns.join(" ")));
2067
+ console.log(chalk6.dim(" " + result.columns.map(() => "--------").join(" ")));
2188
2068
  for (const row of result.rows) {
2189
2069
  const values = result.columns.map((col) => {
2190
2070
  const val = row[col];
2191
- if (val === null) return chalk8.dim("NULL");
2071
+ if (val === null) return chalk6.dim("NULL");
2192
2072
  if (typeof val === "object") return JSON.stringify(val);
2193
2073
  return String(val);
2194
2074
  });
2195
2075
  console.log(" " + values.join(" "));
2196
2076
  }
2197
- console.log(chalk8.dim(`
2077
+ console.log(chalk6.dim(`
2198
2078
  ${result.rows.length} row(s)`));
2199
2079
  }
2200
2080
  } else if (result.rowsAffected !== void 0) {
2201
- console.log(chalk8.dim(`
2081
+ console.log(chalk6.dim(`
2202
2082
  ${result.rowsAffected} row(s) affected`));
2203
2083
  }
2204
2084
  console.log();
@@ -2212,7 +2092,7 @@ async function execCommand(sql) {
2212
2092
  var program = new Command();
2213
2093
  program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version("0.0.1");
2214
2094
  program.command("init [name]").description("Create a new Tether project").option("-t, --template <template>", "Project template (vue, svelte, react, vanilla)", "vue").action(initCommand);
2215
- program.command("dev").description("Start local development server with hot reload").option("-p, --port <port>", "Port to run on", "3001").action(devCommand);
2095
+ program.command("dev").description("Start local development server with hot reload").option("-p, --port <port>", "Port to run on").option("--skip-framework", "Only run Tether watchers, skip framework dev server").action(devCommand);
2216
2096
  program.command("generate").alias("gen").description("Generate types from schema").action(generateCommand);
2217
2097
  program.command("deploy").description("Deploy schema and functions to Tether").option("-s, --schema", "Deploy schema only").option("-f, --functions", "Deploy functions only").option("--dry-run", "Show what would be deployed without deploying").action(deployCommand);
2218
2098
  program.command("login").description("Authenticate with Tether").action(loginCommand);