tthr 0.0.32 → 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,510 +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("");
1753
- }
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};`);
1758
- }
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");
1779
- }
1780
- async function generateCommand() {
1781
- 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"));
1786
- process.exit(1);
1787
- }
1788
- console.log(chalk5.bold("\n\u26A1 Generating types from schema\n"));
1789
- const spinner = ora4("Reading schema...").start();
1790
- 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);
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...");
1800
1574
  }
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;
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));
1807
1580
  }
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)`));
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}`));
1829
1589
  }
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
1835
- `));
1836
- } catch (error) {
1837
- spinner.fail("Failed to generate types");
1838
- console.error(chalk5.red(error instanceof Error ? error.message : "Unknown error"));
1839
- process.exit(1);
1840
- }
1590
+ });
1591
+ return child;
1841
1592
  }
1842
-
1843
- // src/commands/migrate.ts
1844
- import chalk6 from "chalk";
1845
- import ora5 from "ora";
1846
- import fs7 from "fs-extra";
1847
- import path7 from "path";
1848
- import prompts2 from "prompts";
1849
- async function migrateCommand(action, options) {
1850
- await requireAuth();
1851
- const configPath = path7.resolve(process.cwd(), "tether.config.ts");
1852
- if (!await fs7.pathExists(configPath)) {
1853
- console.log(chalk6.red("\nError: Not a Tether project"));
1854
- console.log(chalk6.dim("Run `tthr init` to create a new project\n"));
1855
- process.exit(1);
1593
+ function cleanup() {
1594
+ if (frameworkProcess) {
1595
+ frameworkProcess.kill("SIGTERM");
1596
+ frameworkProcess = null;
1856
1597
  }
1857
- const migrationsDir = path7.resolve(process.cwd(), "tether", "migrations");
1858
- switch (action) {
1859
- case "create":
1860
- await createMigration(migrationsDir, options.name);
1861
- break;
1862
- case "up":
1863
- await runMigrations(migrationsDir, "up");
1864
- break;
1865
- case "down":
1866
- await runMigrations(migrationsDir, "down");
1867
- break;
1868
- case "status":
1869
- default:
1870
- await showStatus(migrationsDir);
1871
- break;
1598
+ if (schemaWatcher) {
1599
+ schemaWatcher.close();
1600
+ schemaWatcher = null;
1872
1601
  }
1873
- }
1874
- async function createMigration(migrationsDir, name) {
1875
- console.log(chalk6.bold("\n\u26A1 Create migration\n"));
1876
- let migrationName = name;
1877
- if (!migrationName) {
1878
- const response = await prompts2({
1879
- type: "text",
1880
- name: "name",
1881
- message: "Migration name:",
1882
- initial: "update_schema"
1883
- });
1884
- migrationName = response.name;
1885
- if (!migrationName) {
1886
- console.log(chalk6.red("Migration name is required"));
1887
- process.exit(1);
1888
- }
1889
- }
1890
- const spinner = ora5("Creating migration...").start();
1891
- try {
1892
- await fs7.ensureDir(migrationsDir);
1893
- const timestamp = Date.now();
1894
- const filename = `${timestamp}_${migrationName}.sql`;
1895
- const filepath = path7.join(migrationsDir, filename);
1896
- await fs7.writeFile(
1897
- filepath,
1898
- `-- Migration: ${migrationName}
1899
- -- Created at: ${(/* @__PURE__ */ new Date()).toISOString()}
1900
-
1901
- -- Up migration
1902
- -- Add your schema changes here
1903
-
1904
- -- Example:
1905
- -- CREATE TABLE IF NOT EXISTS users (
1906
- -- id TEXT PRIMARY KEY,
1907
- -- email TEXT NOT NULL UNIQUE,
1908
- -- name TEXT,
1909
- -- created_at TEXT NOT NULL DEFAULT (datetime('now'))
1910
- -- );
1911
-
1912
- -- Down migration (for rollback)
1913
- -- DROP TABLE IF EXISTS users;
1914
- `
1915
- );
1916
- spinner.succeed("Migration created");
1917
- console.log(chalk6.dim(`
1918
- ${filepath}
1919
- `));
1920
- } catch (error) {
1921
- spinner.fail("Failed to create migration");
1922
- console.error(chalk6.red(error instanceof Error ? error.message : "Unknown error"));
1923
- process.exit(1);
1602
+ if (functionsWatcher) {
1603
+ functionsWatcher.close();
1604
+ functionsWatcher = null;
1924
1605
  }
1925
1606
  }
1926
- async function runMigrations(migrationsDir, direction) {
1927
- console.log(chalk6.bold(`
1928
- \u26A1 Running migrations (${direction})
1929
- `));
1930
- const spinner = ora5("Checking migrations...").start();
1931
- try {
1932
- if (!await fs7.pathExists(migrationsDir)) {
1933
- spinner.info("No migrations directory found");
1934
- console.log(chalk6.dim("\nRun `tthr migrate create` to create your first migration\n"));
1935
- return;
1936
- }
1937
- const files = await fs7.readdir(migrationsDir);
1938
- const migrations = files.filter((f) => f.endsWith(".sql")).sort((a, b) => direction === "up" ? a.localeCompare(b) : b.localeCompare(a));
1939
- if (migrations.length === 0) {
1940
- spinner.info("No migrations found");
1941
- console.log(chalk6.dim("\nRun `tthr migrate create` to create your first migration\n"));
1942
- return;
1943
- }
1944
- spinner.text = `Found ${migrations.length} migration(s)`;
1945
- spinner.succeed(`Would run ${migrations.length} migration(s)`);
1946
- console.log("\nMigrations to run:");
1947
- for (const migration of migrations) {
1948
- console.log(chalk6.dim(` ${migration}`));
1949
- }
1950
- console.log(chalk6.yellow("\nNote: Migration execution not yet implemented\n"));
1951
- } catch (error) {
1952
- spinner.fail("Failed to run migrations");
1953
- console.error(chalk6.red(error instanceof Error ? error.message : "Unknown error"));
1607
+ async function devCommand(options) {
1608
+ await requireAuth();
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"));
1954
1613
  process.exit(1);
1955
1614
  }
1956
- }
1957
- async function showStatus(migrationsDir) {
1958
- console.log(chalk6.bold("\n\u26A1 Migration status\n"));
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
+ });
1959
1644
  try {
1960
- if (!await fs7.pathExists(migrationsDir)) {
1961
- console.log(chalk6.dim("No migrations directory found"));
1962
- console.log(chalk6.dim("\nRun `tthr migrate create` to create your first migration\n"));
1963
- return;
1645
+ spinner.text = "Validating schema...";
1646
+ const schemaValidation = await validateSchema(schemaPath);
1647
+ if (!schemaValidation.valid) {
1648
+ spinner.warn(`Schema validation warning: ${schemaValidation.error}`);
1649
+ }
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
+ }
1964
1657
  }
1965
- const files = await fs7.readdir(migrationsDir);
1966
- const migrations = files.filter((f) => f.endsWith(".sql")).sort();
1967
- if (migrations.length === 0) {
1968
- console.log(chalk6.dim("No migrations found"));
1969
- console.log(chalk6.dim("\nRun `tthr migrate create` to create your first migration\n"));
1970
- return;
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
+ });
1971
1701
  }
1972
- console.log(`Found ${migrations.length} migration(s):
1973
- `);
1974
- for (const migration of migrations) {
1975
- console.log(chalk6.yellow(" \u25CB") + ` ${migration} ${chalk6.dim("(pending)")}`);
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
1711
+ `));
1976
1712
  }
1977
- console.log();
1713
+ await new Promise(() => {
1714
+ });
1978
1715
  } catch (error) {
1979
- console.error(chalk6.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();
1980
1719
  process.exit(1);
1981
1720
  }
1982
1721
  }
1983
1722
 
1984
1723
  // src/commands/login.ts
1985
- import chalk7 from "chalk";
1986
- import ora6 from "ora";
1724
+ import chalk4 from "chalk";
1725
+ import ora4 from "ora";
1987
1726
  import os from "os";
1988
1727
  var isDev3 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
1989
1728
  var API_URL3 = isDev3 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
1990
1729
  var AUTH_URL = isDev3 ? "http://localhost:3000/cli" : "https://tthr.io/cli";
1991
1730
  async function loginCommand() {
1992
- console.log(chalk7.bold("\u26A1 Login to Tether\n"));
1731
+ console.log(chalk4.bold("\u26A1 Login to Tether\n"));
1993
1732
  const existing = await getCredentials();
1994
1733
  if (existing) {
1995
- console.log(chalk7.green("\u2713") + ` Already logged in as ${chalk7.cyan(existing.email)}`);
1996
- console.log(chalk7.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"));
1997
1736
  return;
1998
1737
  }
1999
- const spinner = ora6("Generating authentication code...").start();
1738
+ const spinner = ora4("Generating authentication code...").start();
2000
1739
  try {
2001
1740
  const deviceCode = await requestDeviceCode();
2002
1741
  spinner.stop();
2003
1742
  const authUrl = `${AUTH_URL}/${deviceCode.userCode}`;
2004
- console.log(chalk7.dim("Open this URL in your browser to authenticate:\n"));
2005
- console.log(chalk7.cyan.bold(` ${authUrl}
1743
+ console.log(chalk4.dim("Open this URL in your browser to authenticate:\n"));
1744
+ console.log(chalk4.cyan.bold(` ${authUrl}
2006
1745
  `));
2007
- console.log(chalk7.dim(`Your code: ${chalk7.white.bold(deviceCode.userCode)}
1746
+ console.log(chalk4.dim(`Your code: ${chalk4.white.bold(deviceCode.userCode)}
2008
1747
  `));
2009
1748
  const credentials = await pollForApproval(deviceCode.deviceCode, deviceCode.interval, deviceCode.expiresIn);
2010
1749
  await saveCredentials(credentials);
2011
- console.log(chalk7.green("\u2713") + ` Logged in as ${chalk7.cyan(credentials.email)}`);
1750
+ console.log(chalk4.green("\u2713") + ` Logged in as ${chalk4.cyan(credentials.email)}`);
2012
1751
  console.log();
2013
1752
  } catch (error) {
2014
1753
  spinner.fail("Authentication failed");
2015
- console.error(chalk7.red(error instanceof Error ? error.message : "Unknown error"));
2016
- console.log(chalk7.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"));
2017
1756
  process.exit(1);
2018
1757
  }
2019
1758
  }
2020
1759
  async function logoutCommand() {
2021
- console.log(chalk7.bold("\n\u26A1 Logout from Tether\n"));
1760
+ console.log(chalk4.bold("\n\u26A1 Logout from Tether\n"));
2022
1761
  const credentials = await getCredentials();
2023
1762
  if (!credentials) {
2024
- console.log(chalk7.dim("Not logged in\n"));
1763
+ console.log(chalk4.dim("Not logged in\n"));
2025
1764
  return;
2026
1765
  }
2027
- const spinner = ora6("Logging out...").start();
1766
+ const spinner = ora4("Logging out...").start();
2028
1767
  try {
2029
1768
  await clearCredentials();
2030
- spinner.succeed(`Logged out from ${chalk7.cyan(credentials.email)}`);
1769
+ spinner.succeed(`Logged out from ${chalk4.cyan(credentials.email)}`);
2031
1770
  console.log();
2032
1771
  } catch (error) {
2033
1772
  spinner.fail("Logout failed");
2034
- console.error(chalk7.red(error instanceof Error ? error.message : "Unknown error"));
1773
+ console.error(chalk4.red(error instanceof Error ? error.message : "Unknown error"));
2035
1774
  process.exit(1);
2036
1775
  }
2037
1776
  }
2038
1777
  async function whoamiCommand() {
2039
1778
  const credentials = await getCredentials();
2040
1779
  if (!credentials) {
2041
- console.log(chalk7.dim("\nNot logged in"));
2042
- console.log(chalk7.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"));
2043
1782
  return;
2044
1783
  }
2045
- console.log(chalk7.bold("\n\u26A1 Current user\n"));
2046
- console.log(` Email: ${chalk7.cyan(credentials.email)}`);
2047
- console.log(` User ID: ${chalk7.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)}`);
2048
1787
  if (credentials.expiresAt) {
2049
1788
  const expiresAt = new Date(credentials.expiresAt);
2050
1789
  const now = /* @__PURE__ */ new Date();
2051
1790
  if (expiresAt > now) {
2052
- console.log(` Session: ${chalk7.green("Active")}`);
1791
+ console.log(` Session: ${chalk4.green("Active")}`);
2053
1792
  } else {
2054
- console.log(` Session: ${chalk7.yellow("Expired")}`);
1793
+ console.log(` Session: ${chalk4.yellow("Expired")}`);
2055
1794
  }
2056
1795
  }
2057
1796
  console.log();
@@ -2093,20 +1832,20 @@ async function requestDeviceCode() {
2093
1832
  async function pollForApproval(deviceCode, interval, expiresIn) {
2094
1833
  const startTime = Date.now();
2095
1834
  const expiresAt = startTime + expiresIn * 1e3;
2096
- const spinner = ora6("").start();
1835
+ const spinner = ora4("").start();
2097
1836
  const updateCountdown = () => {
2098
1837
  const remaining = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1e3));
2099
1838
  const mins = Math.floor(remaining / 60);
2100
1839
  const secs = remaining % 60;
2101
1840
  const timeStr = mins > 0 ? `${mins}:${secs.toString().padStart(2, "0")}` : `${secs}s`;
2102
- spinner.text = `Waiting for approval... ${chalk7.dim(`(${timeStr} remaining)`)}`;
1841
+ spinner.text = `Waiting for approval... ${chalk4.dim(`(${timeStr} remaining)`)}`;
2103
1842
  };
2104
1843
  updateCountdown();
2105
1844
  const countdownInterval = setInterval(updateCountdown, 1e3);
2106
1845
  try {
2107
1846
  while (Date.now() < expiresAt) {
2108
1847
  await sleep(interval * 1e3);
2109
- spinner.text = `Checking... ${chalk7.dim(`(${Math.ceil((expiresAt - Date.now()) / 1e3)}s remaining)`)}`;
1848
+ spinner.text = `Checking... ${chalk4.dim(`(${Math.ceil((expiresAt - Date.now()) / 1e3)}s remaining)`)}`;
2110
1849
  const response = await fetch(`${API_URL3}/auth/device/${deviceCode}`, {
2111
1850
  method: "GET"
2112
1851
  }).catch(() => null);
@@ -2139,10 +1878,10 @@ function sleep(ms) {
2139
1878
  }
2140
1879
 
2141
1880
  // src/commands/update.ts
2142
- import chalk8 from "chalk";
2143
- import ora7 from "ora";
2144
- import fs8 from "fs-extra";
2145
- import path8 from "path";
1881
+ import chalk5 from "chalk";
1882
+ import ora5 from "ora";
1883
+ import fs4 from "fs-extra";
1884
+ import path4 from "path";
2146
1885
  import { execSync as execSync2 } from "child_process";
2147
1886
  var TETHER_PACKAGES = [
2148
1887
  "@tthr/client",
@@ -2154,13 +1893,13 @@ var TETHER_PACKAGES = [
2154
1893
  ];
2155
1894
  function detectPackageManager() {
2156
1895
  const cwd = process.cwd();
2157
- if (fs8.existsSync(path8.join(cwd, "bun.lockb")) || fs8.existsSync(path8.join(cwd, "bun.lock"))) {
1896
+ if (fs4.existsSync(path4.join(cwd, "bun.lockb")) || fs4.existsSync(path4.join(cwd, "bun.lock"))) {
2158
1897
  return "bun";
2159
1898
  }
2160
- if (fs8.existsSync(path8.join(cwd, "pnpm-lock.yaml"))) {
1899
+ if (fs4.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) {
2161
1900
  return "pnpm";
2162
1901
  }
2163
- if (fs8.existsSync(path8.join(cwd, "yarn.lock"))) {
1902
+ if (fs4.existsSync(path4.join(cwd, "yarn.lock"))) {
2164
1903
  return "yarn";
2165
1904
  }
2166
1905
  return "npm";
@@ -2190,14 +1929,14 @@ async function getLatestVersion2(packageName) {
2190
1929
  }
2191
1930
  }
2192
1931
  async function updateCommand(options) {
2193
- const packageJsonPath = path8.resolve(process.cwd(), "package.json");
2194
- if (!await fs8.pathExists(packageJsonPath)) {
2195
- console.log(chalk8.red("\nError: No package.json found"));
2196
- console.log(chalk8.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"));
2197
1936
  process.exit(1);
2198
1937
  }
2199
- console.log(chalk8.bold("\n\u26A1 Updating Tether packages\n"));
2200
- const packageJson = await fs8.readJson(packageJsonPath);
1938
+ console.log(chalk5.bold("\n\u26A1 Updating Tether packages\n"));
1939
+ const packageJson = await fs4.readJson(packageJsonPath);
2201
1940
  const deps = packageJson.dependencies || {};
2202
1941
  const devDeps = packageJson.devDependencies || {};
2203
1942
  const installedDeps = [];
@@ -2210,13 +1949,13 @@ async function updateCommand(options) {
2210
1949
  }
2211
1950
  }
2212
1951
  if (installedDeps.length === 0) {
2213
- console.log(chalk8.yellow(" No updatable Tether packages found in package.json"));
2214
- console.log(chalk8.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"));
2215
1954
  return;
2216
1955
  }
2217
- console.log(chalk8.dim(` Found ${installedDeps.length} Tether package(s):
1956
+ console.log(chalk5.dim(` Found ${installedDeps.length} Tether package(s):
2218
1957
  `));
2219
- const spinner = ora7("Checking for updates...").start();
1958
+ const spinner = ora5("Checking for updates...").start();
2220
1959
  const packagesToUpdate = [];
2221
1960
  for (const pkg of installedDeps) {
2222
1961
  const latestVersion = await getLatestVersion2(pkg.name);
@@ -2230,32 +1969,32 @@ async function updateCommand(options) {
2230
1969
  isDev: pkg.isDev
2231
1970
  });
2232
1971
  } else {
2233
- console.log(chalk8.dim(` ${pkg.name}@${currentClean} (up to date)`));
1972
+ console.log(chalk5.dim(` ${pkg.name}@${currentClean} (up to date)`));
2234
1973
  }
2235
1974
  }
2236
1975
  }
2237
1976
  spinner.stop();
2238
1977
  if (packagesToUpdate.length === 0) {
2239
- console.log(chalk8.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"));
2240
1979
  return;
2241
1980
  }
2242
- console.log(chalk8.cyan("\n Updates available:\n"));
1981
+ console.log(chalk5.cyan("\n Updates available:\n"));
2243
1982
  for (const pkg of packagesToUpdate) {
2244
1983
  console.log(
2245
- chalk8.white(` ${pkg.name}`) + chalk8.red(` ${pkg.current}`) + chalk8.dim(" \u2192 ") + chalk8.green(`${pkg.latest}`) + (pkg.isDev ? chalk8.dim(" (dev)") : "")
1984
+ chalk5.white(` ${pkg.name}`) + chalk5.red(` ${pkg.current}`) + chalk5.dim(" \u2192 ") + chalk5.green(`${pkg.latest}`) + (pkg.isDev ? chalk5.dim(" (dev)") : "")
2246
1985
  );
2247
1986
  }
2248
1987
  if (options.dryRun) {
2249
- console.log(chalk8.yellow("\n Dry run - no packages were updated\n"));
1988
+ console.log(chalk5.yellow("\n Dry run - no packages were updated\n"));
2250
1989
  return;
2251
1990
  }
2252
1991
  const pm = detectPackageManager();
2253
- console.log(chalk8.dim(`
1992
+ console.log(chalk5.dim(`
2254
1993
  Using ${pm} to update packages...
2255
1994
  `));
2256
1995
  const depsToUpdate = packagesToUpdate.filter((p) => !p.isDev).map((p) => `${p.name}@latest`);
2257
1996
  const devDepsToUpdate = packagesToUpdate.filter((p) => p.isDev).map((p) => `${p.name}@latest`);
2258
- const updateSpinner = ora7("Installing updates...").start();
1997
+ const updateSpinner = ora5("Installing updates...").start();
2259
1998
  try {
2260
1999
  if (depsToUpdate.length > 0) {
2261
2000
  const cmd = getInstallCommand2(pm, depsToUpdate, false);
@@ -2266,43 +2005,43 @@ async function updateCommand(options) {
2266
2005
  execSync2(cmd, { stdio: "pipe", cwd: process.cwd() });
2267
2006
  }
2268
2007
  updateSpinner.succeed("Packages updated successfully");
2269
- console.log(chalk8.green(`
2008
+ console.log(chalk5.green(`
2270
2009
  \u2713 Updated ${packagesToUpdate.length} package(s)
2271
2010
  `));
2272
2011
  } catch (error) {
2273
2012
  updateSpinner.fail("Failed to update packages");
2274
- console.error(chalk8.red(error instanceof Error ? error.message : "Unknown error"));
2013
+ console.error(chalk5.red(error instanceof Error ? error.message : "Unknown error"));
2275
2014
  process.exit(1);
2276
2015
  }
2277
2016
  }
2278
2017
 
2279
2018
  // src/commands/exec.ts
2280
- import chalk9 from "chalk";
2281
- import ora8 from "ora";
2282
- import fs9 from "fs-extra";
2283
- import path9 from "path";
2019
+ import chalk6 from "chalk";
2020
+ import ora6 from "ora";
2021
+ import fs5 from "fs-extra";
2022
+ import path5 from "path";
2284
2023
  var isDev4 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
2285
2024
  var API_URL4 = isDev4 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
2286
2025
  async function execCommand(sql) {
2287
2026
  if (!sql || sql.trim() === "") {
2288
- console.log(chalk9.red("\nError: SQL query required"));
2289
- console.log(chalk9.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'));
2290
2029
  process.exit(1);
2291
2030
  }
2292
2031
  const credentials = await requireAuth();
2293
- const envPath = path9.resolve(process.cwd(), ".env");
2032
+ const envPath = path5.resolve(process.cwd(), ".env");
2294
2033
  let projectId;
2295
- if (await fs9.pathExists(envPath)) {
2296
- const envContent = await fs9.readFile(envPath, "utf-8");
2034
+ if (await fs5.pathExists(envPath)) {
2035
+ const envContent = await fs5.readFile(envPath, "utf-8");
2297
2036
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
2298
2037
  projectId = match?.[1]?.trim();
2299
2038
  }
2300
2039
  if (!projectId) {
2301
- console.log(chalk9.red("\nError: Project ID not found"));
2302
- console.log(chalk9.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"));
2303
2042
  process.exit(1);
2304
2043
  }
2305
- const spinner = ora8("Executing query...").start();
2044
+ const spinner = ora6("Executing query...").start();
2306
2045
  try {
2307
2046
  const response = await fetch(`${API_URL4}/projects/${projectId}/exec`, {
2308
2047
  method: "POST",
@@ -2322,24 +2061,24 @@ async function execCommand(sql) {
2322
2061
  if (result.columns && result.rows) {
2323
2062
  console.log();
2324
2063
  if (result.rows.length === 0) {
2325
- console.log(chalk9.dim(" No rows returned"));
2064
+ console.log(chalk6.dim(" No rows returned"));
2326
2065
  } else {
2327
- console.log(chalk9.bold(" " + result.columns.join(" ")));
2328
- console.log(chalk9.dim(" " + result.columns.map(() => "--------").join(" ")));
2066
+ console.log(chalk6.bold(" " + result.columns.join(" ")));
2067
+ console.log(chalk6.dim(" " + result.columns.map(() => "--------").join(" ")));
2329
2068
  for (const row of result.rows) {
2330
2069
  const values = result.columns.map((col) => {
2331
2070
  const val = row[col];
2332
- if (val === null) return chalk9.dim("NULL");
2071
+ if (val === null) return chalk6.dim("NULL");
2333
2072
  if (typeof val === "object") return JSON.stringify(val);
2334
2073
  return String(val);
2335
2074
  });
2336
2075
  console.log(" " + values.join(" "));
2337
2076
  }
2338
- console.log(chalk9.dim(`
2077
+ console.log(chalk6.dim(`
2339
2078
  ${result.rows.length} row(s)`));
2340
2079
  }
2341
2080
  } else if (result.rowsAffected !== void 0) {
2342
- console.log(chalk9.dim(`
2081
+ console.log(chalk6.dim(`
2343
2082
  ${result.rowsAffected} row(s) affected`));
2344
2083
  }
2345
2084
  console.log();
@@ -2353,9 +2092,8 @@ async function execCommand(sql) {
2353
2092
  var program = new Command();
2354
2093
  program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version("0.0.1");
2355
2094
  program.command("init [name]").description("Create a new Tether project").option("-t, --template <template>", "Project template (vue, svelte, react, vanilla)", "vue").action(initCommand);
2356
- 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);
2357
2096
  program.command("generate").alias("gen").description("Generate types from schema").action(generateCommand);
2358
- program.command("migrate").description("Database migrations").argument("[action]", "Migration action: create, up, down, status", "status").option("-n, --name <name>", "Migration name (for create)").action(migrateCommand);
2359
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);
2360
2098
  program.command("login").description("Authenticate with Tether").action(loginCommand);
2361
2099
  program.command("logout").description("Sign out of Tether").action(logoutCommand);