tthr 0.3.21 → 0.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +176 -143
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17,23 +17,53 @@ import {
17
17
 
18
18
  // src/index.ts
19
19
  import { Command } from "commander";
20
- import fs8 from "fs-extra";
20
+ import fs9 from "fs-extra";
21
21
  import { fileURLToPath } from "url";
22
- import path8 from "path";
22
+ import path9 from "path";
23
+
24
+ // src/utils/dotenv.ts
25
+ import fs from "fs-extra";
26
+ import path from "path";
27
+ function loadDotenv(cwd = process.cwd()) {
28
+ applyEnvFile(path.resolve(cwd, ".env.local"));
29
+ }
30
+ function applyEnvFile(envPath) {
31
+ if (!fs.existsSync(envPath)) return;
32
+ let content;
33
+ try {
34
+ content = fs.readFileSync(envPath, "utf-8");
35
+ } catch {
36
+ return;
37
+ }
38
+ for (const rawLine of content.split("\n")) {
39
+ const line = rawLine.trim();
40
+ if (!line || line.startsWith("#")) continue;
41
+ const eq = line.indexOf("=");
42
+ if (eq < 0) continue;
43
+ const key = line.slice(0, eq).trim();
44
+ if (!key || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
45
+ if (Object.prototype.hasOwnProperty.call(process.env, key)) continue;
46
+ let value = line.slice(eq + 1).trim();
47
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
48
+ value = value.slice(1, -1);
49
+ }
50
+ process.env[key] = value;
51
+ }
52
+ }
23
53
 
24
54
  // src/commands/init.ts
25
55
  import chalk2 from "chalk";
26
56
  import ora2 from "ora";
27
57
  import prompts from "prompts";
28
- import fs2 from "fs-extra";
29
- import path2 from "path";
58
+ import fs3 from "fs-extra";
59
+ import path3 from "path";
30
60
  import { execSync } from "child_process";
31
61
 
32
62
  // src/commands/deploy.ts
33
63
  import chalk from "chalk";
34
64
  import ora from "ora";
35
- import fs from "fs-extra";
36
- import path from "path";
65
+ import fs2 from "fs-extra";
66
+ import path2 from "path";
37
67
  import * as esbuild from "esbuild";
38
68
  async function describeHttpError(response, action) {
39
69
  const status = response.status;
@@ -73,8 +103,8 @@ async function describeHttpError(response, action) {
73
103
  }
74
104
  async function deployCommand(options) {
75
105
  const credentials = await requireAuth();
76
- const configPath = path.resolve(process.cwd(), "tether.config.ts");
77
- if (!await fs.pathExists(configPath)) {
106
+ const configPath = path2.resolve(process.cwd(), "tether.config.ts");
107
+ if (!await fs2.pathExists(configPath)) {
78
108
  console.log(chalk.red("\nError: Not a Tether project"));
79
109
  console.log(chalk.dim("Run `tthr init` to create a new project\n"));
80
110
  process.exit(1);
@@ -82,9 +112,9 @@ async function deployCommand(options) {
82
112
  const config = await loadConfig();
83
113
  let projectId = config.projectId;
84
114
  if (!projectId) {
85
- const envPath = path.resolve(process.cwd(), ".env");
86
- if (await fs.pathExists(envPath)) {
87
- const envContent = await fs.readFile(envPath, "utf-8");
115
+ const envPath = path2.resolve(process.cwd(), ".env");
116
+ if (await fs2.pathExists(envPath)) {
117
+ const envContent = await fs2.readFile(envPath, "utf-8");
88
118
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
89
119
  projectId = match?.[1]?.trim();
90
120
  }
@@ -99,7 +129,8 @@ async function deployCommand(options) {
99
129
  console.log(chalk.bold("\n\u26A1 Deploying to Tether\n"));
100
130
  console.log(chalk.dim(` Project: ${projectId}`));
101
131
  console.log(chalk.dim(` Environment: ${environment}`));
102
- console.log(chalk.dim(` API: ${API_URL2}
132
+ console.log(chalk.dim(` API: ${API_URL2}`));
133
+ console.log(chalk.dim(` \u21B3 override with TETHER_API_URL in your shell or .env.local
103
134
  `));
104
135
  console.log(chalk.dim(" Generating types..."));
105
136
  try {
@@ -124,15 +155,15 @@ async function deploySchemaToServer(projectId, token, schemaPath, environment, d
124
155
  const API_URL2 = apiUrl || `${await resolveApiUrl()}/api/v1`;
125
156
  const spinner = ora("Reading schema...").start();
126
157
  try {
127
- if (!await fs.pathExists(schemaPath)) {
158
+ if (!await fs2.pathExists(schemaPath)) {
128
159
  spinner.warn("No schema file found");
129
- const relativePath = path.relative(process.cwd(), schemaPath);
160
+ const relativePath = path2.relative(process.cwd(), schemaPath);
130
161
  console.log(chalk.dim(` Create ${relativePath} to define your database schema
131
162
  `));
132
163
  return;
133
164
  }
134
165
  console.log(chalk.dim(` Schema path: ${schemaPath}`));
135
- const schemaSource = await fs.readFile(schemaPath, "utf-8");
166
+ const schemaSource = await fs2.readFile(schemaPath, "utf-8");
136
167
  console.log(chalk.dim(` Schema source length: ${schemaSource.length} chars`));
137
168
  console.log(chalk.dim(` Parsing schema...`));
138
169
  const tables = parseSchema(schemaSource);
@@ -220,14 +251,14 @@ async function deployFunctionsToServer(projectId, token, functionsDir, environme
220
251
  const API_URL2 = apiUrl || `${await resolveApiUrl()}/api/v1`;
221
252
  const spinner = ora("Reading functions...").start();
222
253
  try {
223
- if (!await fs.pathExists(functionsDir)) {
254
+ if (!await fs2.pathExists(functionsDir)) {
224
255
  spinner.warn("No functions directory found");
225
- const relativePath = path.relative(process.cwd(), functionsDir);
256
+ const relativePath = path2.relative(process.cwd(), functionsDir);
226
257
  console.log(chalk.dim(` Create ${relativePath}/ to define your API functions
227
258
  `));
228
259
  return;
229
260
  }
230
- const files = await fs.readdir(functionsDir);
261
+ const files = await fs2.readdir(functionsDir);
231
262
  const tsFiles = files.filter((f) => f.endsWith(".ts") && !f.startsWith("_"));
232
263
  const helperFiles = files.filter((f) => f.endsWith(".ts") && f.startsWith("_"));
233
264
  if (tsFiles.length === 0) {
@@ -236,13 +267,13 @@ async function deployFunctionsToServer(projectId, token, functionsDir, environme
236
267
  }
237
268
  const helperSources = /* @__PURE__ */ new Map();
238
269
  for (const hf of helperFiles) {
239
- const hfPath = path.join(functionsDir, hf);
240
- helperSources.set(hf, await fs.readFile(hfPath, "utf-8"));
270
+ const hfPath = path2.join(functionsDir, hf);
271
+ helperSources.set(hf, await fs2.readFile(hfPath, "utf-8"));
241
272
  }
242
273
  const functions = [];
243
274
  for (const file of tsFiles) {
244
- const filePath = path.join(functionsDir, file);
245
- const source = await fs.readFile(filePath, "utf-8");
275
+ const filePath = path2.join(functionsDir, file);
276
+ const source = await fs2.readFile(filePath, "utf-8");
246
277
  const moduleName = file.replace(".ts", "");
247
278
  const parsedFunctions = parseFunctions(moduleName, source);
248
279
  if (parsedFunctions.length === 0) continue;
@@ -676,8 +707,8 @@ ${packageManager} is not installed on your system.`));
676
707
  });
677
708
  template = response.template || "nuxt";
678
709
  }
679
- const projectPath = path2.resolve(process.cwd(), projectName);
680
- if (await fs2.pathExists(projectPath)) {
710
+ const projectPath = path3.resolve(process.cwd(), projectName);
711
+ if (await fs3.pathExists(projectPath)) {
681
712
  const { overwrite } = await prompts({
682
713
  type: "confirm",
683
714
  name: "overwrite",
@@ -688,7 +719,7 @@ ${packageManager} is not installed on your system.`));
688
719
  console.log(chalk2.yellow("Cancelled"));
689
720
  process.exit(0);
690
721
  }
691
- await fs2.remove(projectPath);
722
+ await fs3.remove(projectPath);
692
723
  }
693
724
  const spinner = ora2(`Scaffolding ${template} project...`).start();
694
725
  let projectId;
@@ -764,7 +795,7 @@ function getPackageRunnerCommand(pm) {
764
795
  }
765
796
  }
766
797
  async function scaffoldNuxtProject(projectName, projectPath, spinner, packageManager) {
767
- const parentDir = path2.dirname(projectPath);
798
+ const parentDir = path3.dirname(projectPath);
768
799
  const runner = getPackageRunnerCommand(packageManager);
769
800
  spinner.stop();
770
801
  console.log(chalk2.dim("\nRunning nuxi init...\n"));
@@ -773,7 +804,7 @@ async function scaffoldNuxtProject(projectName, projectPath, spinner, packageMan
773
804
  cwd: parentDir,
774
805
  stdio: "inherit"
775
806
  });
776
- if (!await fs2.pathExists(path2.join(projectPath, "package.json"))) {
807
+ if (!await fs3.pathExists(path3.join(projectPath, "package.json"))) {
777
808
  throw new Error("Nuxt project was not created successfully - package.json missing");
778
809
  }
779
810
  spinner.start();
@@ -786,7 +817,7 @@ async function scaffoldNuxtProject(projectName, projectPath, spinner, packageMan
786
817
  }
787
818
  }
788
819
  async function scaffoldNextProject(projectName, projectPath, spinner, packageManager) {
789
- const parentDir = path2.dirname(projectPath);
820
+ const parentDir = path3.dirname(projectPath);
790
821
  const runner = getPackageRunnerCommand(packageManager);
791
822
  const pmFlag = packageManager === "npm" ? "--use-npm" : packageManager === "pnpm" ? "--use-pnpm" : packageManager === "yarn" ? "--use-yarn" : "--use-bun";
792
823
  spinner.stop();
@@ -796,7 +827,7 @@ async function scaffoldNextProject(projectName, projectPath, spinner, packageMan
796
827
  cwd: parentDir,
797
828
  stdio: "inherit"
798
829
  });
799
- if (!await fs2.pathExists(path2.join(projectPath, "package.json"))) {
830
+ if (!await fs3.pathExists(path3.join(projectPath, "package.json"))) {
800
831
  throw new Error("Next.js project was not created successfully - package.json missing");
801
832
  }
802
833
  spinner.start();
@@ -809,7 +840,7 @@ async function scaffoldNextProject(projectName, projectPath, spinner, packageMan
809
840
  }
810
841
  }
811
842
  async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packageManager) {
812
- const parentDir = path2.dirname(projectPath);
843
+ const parentDir = path3.dirname(projectPath);
813
844
  const runner = getPackageRunnerCommand(packageManager);
814
845
  spinner.stop();
815
846
  console.log(chalk2.dim("\nRunning sv create...\n"));
@@ -818,7 +849,7 @@ async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packa
818
849
  cwd: parentDir,
819
850
  stdio: "inherit"
820
851
  });
821
- if (!await fs2.pathExists(path2.join(projectPath, "package.json"))) {
852
+ if (!await fs3.pathExists(path3.join(projectPath, "package.json"))) {
822
853
  throw new Error("SvelteKit project was not created successfully - package.json missing");
823
854
  }
824
855
  spinner.start();
@@ -837,8 +868,8 @@ async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packa
837
868
  }
838
869
  async function scaffoldVanillaProject(projectName, projectPath, spinner) {
839
870
  spinner.text = "Creating vanilla TypeScript project...";
840
- await fs2.ensureDir(projectPath);
841
- await fs2.ensureDir(path2.join(projectPath, "src"));
871
+ await fs3.ensureDir(projectPath);
872
+ await fs3.ensureDir(path3.join(projectPath, "src"));
842
873
  const packageJson = {
843
874
  name: projectName,
844
875
  version: "0.0.1",
@@ -855,9 +886,9 @@ async function scaffoldVanillaProject(projectName, projectPath, spinner) {
855
886
  "@types/node": "latest"
856
887
  }
857
888
  };
858
- await fs2.writeJSON(path2.join(projectPath, "package.json"), packageJson, { spaces: 2 });
859
- await fs2.writeJSON(
860
- path2.join(projectPath, "tsconfig.json"),
889
+ await fs3.writeJSON(path3.join(projectPath, "package.json"), packageJson, { spaces: 2 });
890
+ await fs3.writeJSON(
891
+ path3.join(projectPath, "tsconfig.json"),
861
892
  {
862
893
  compilerOptions: {
863
894
  target: "ES2022",
@@ -878,8 +909,8 @@ async function scaffoldVanillaProject(projectName, projectPath, spinner) {
878
909
  },
879
910
  { spaces: 2 }
880
911
  );
881
- await fs2.writeFile(
882
- path2.join(projectPath, "src", "index.ts"),
912
+ await fs3.writeFile(
913
+ path3.join(projectPath, "src", "index.ts"),
883
914
  `import { createClient } from '@tthr/client';
884
915
 
885
916
  const tether = createClient({
@@ -898,8 +929,8 @@ async function main() {
898
929
  main().catch(console.error);
899
930
  `
900
931
  );
901
- await fs2.writeFile(
902
- path2.join(projectPath, ".gitignore"),
932
+ await fs3.writeFile(
933
+ path3.join(projectPath, ".gitignore"),
903
934
  `node_modules/
904
935
  dist/
905
936
  .env
@@ -909,11 +940,11 @@ dist/
909
940
  );
910
941
  }
911
942
  async function addTetherFiles(projectPath, projectId, apiKey, template) {
912
- await fs2.ensureDir(path2.join(projectPath, "tether"));
913
- await fs2.ensureDir(path2.join(projectPath, "tether", "functions"));
943
+ await fs3.ensureDir(path3.join(projectPath, "tether"));
944
+ await fs3.ensureDir(path3.join(projectPath, "tether", "functions"));
914
945
  const configPackage = template === "nuxt" ? "@tthr/vue" : template === "next" ? "@tthr/react" : template === "sveltekit" ? "@tthr/svelte" : "@tthr/client";
915
- await fs2.writeFile(
916
- path2.join(projectPath, "tether.config.ts"),
946
+ await fs3.writeFile(
947
+ path3.join(projectPath, "tether.config.ts"),
917
948
  `import { defineConfig } from '${configPackage}';
918
949
 
919
950
  export default defineConfig({
@@ -925,8 +956,8 @@ export default defineConfig({
925
956
  });
926
957
  `
927
958
  );
928
- await fs2.writeFile(
929
- path2.join(projectPath, "tether", "schema.ts"),
959
+ await fs3.writeFile(
960
+ path3.join(projectPath, "tether", "schema.ts"),
930
961
  `import { defineSchema, text, timestamp } from '@tthr/schema';
931
962
 
932
963
  export default defineSchema({
@@ -948,8 +979,8 @@ export default defineSchema({
948
979
  });
949
980
  `
950
981
  );
951
- await fs2.writeFile(
952
- path2.join(projectPath, "tether", "functions", "posts.ts"),
982
+ await fs3.writeFile(
983
+ path3.join(projectPath, "tether", "functions", "posts.ts"),
953
984
  `import { query, mutation, z } from '../_generated/db';
954
985
 
955
986
  // List all posts
@@ -1030,8 +1061,8 @@ export const remove = mutation({
1030
1061
  });
1031
1062
  `
1032
1063
  );
1033
- await fs2.writeFile(
1034
- path2.join(projectPath, "tether", "functions", "index.ts"),
1064
+ await fs3.writeFile(
1065
+ path3.join(projectPath, "tether", "functions", "index.ts"),
1035
1066
  `// Re-export all function modules
1036
1067
  // The Tether SDK uses this file to discover custom functions
1037
1068
 
@@ -1039,8 +1070,8 @@ export * as posts from './posts';
1039
1070
  export * as comments from './comments';
1040
1071
  `
1041
1072
  );
1042
- await fs2.writeFile(
1043
- path2.join(projectPath, "tether", "functions", "comments.ts"),
1073
+ await fs3.writeFile(
1074
+ path3.join(projectPath, "tether", "functions", "comments.ts"),
1044
1075
  `import { query, mutation, z } from '../_generated/db';
1045
1076
 
1046
1077
  // List all comments
@@ -1108,26 +1139,26 @@ export const remove = mutation({
1108
1139
  TETHER_PROJECT_ID=${projectId}
1109
1140
  TETHER_API_KEY=${apiKey}
1110
1141
  `;
1111
- const envPath = path2.join(projectPath, ".env");
1112
- if (await fs2.pathExists(envPath)) {
1113
- const existing = await fs2.readFile(envPath, "utf-8");
1114
- await fs2.writeFile(envPath, existing + "\n" + envContent);
1142
+ const envPath = path3.join(projectPath, ".env");
1143
+ if (await fs3.pathExists(envPath)) {
1144
+ const existing = await fs3.readFile(envPath, "utf-8");
1145
+ await fs3.writeFile(envPath, existing + "\n" + envContent);
1115
1146
  } else {
1116
- await fs2.writeFile(envPath, envContent);
1147
+ await fs3.writeFile(envPath, envContent);
1117
1148
  }
1118
- const gitignorePath = path2.join(projectPath, ".gitignore");
1149
+ const gitignorePath = path3.join(projectPath, ".gitignore");
1119
1150
  const tetherGitignore = `
1120
1151
  # Tether
1121
1152
  .env
1122
1153
  .env.local
1123
1154
  `;
1124
- if (await fs2.pathExists(gitignorePath)) {
1125
- const existing = await fs2.readFile(gitignorePath, "utf-8");
1155
+ if (await fs3.pathExists(gitignorePath)) {
1156
+ const existing = await fs3.readFile(gitignorePath, "utf-8");
1126
1157
  if (!existing.includes("_generated/")) {
1127
- await fs2.writeFile(gitignorePath, existing + tetherGitignore);
1158
+ await fs3.writeFile(gitignorePath, existing + tetherGitignore);
1128
1159
  }
1129
1160
  } else {
1130
- await fs2.writeFile(gitignorePath, tetherGitignore.trim());
1161
+ await fs3.writeFile(gitignorePath, tetherGitignore.trim());
1131
1162
  }
1132
1163
  }
1133
1164
  async function configureFramework(projectPath, template) {
@@ -1140,9 +1171,9 @@ async function configureFramework(projectPath, template) {
1140
1171
  }
1141
1172
  }
1142
1173
  async function configureNuxt(projectPath) {
1143
- const configPath = path2.join(projectPath, "nuxt.config.ts");
1144
- if (!await fs2.pathExists(configPath)) {
1145
- await fs2.writeFile(
1174
+ const configPath = path3.join(projectPath, "nuxt.config.ts");
1175
+ if (!await fs3.pathExists(configPath)) {
1176
+ await fs3.writeFile(
1146
1177
  configPath,
1147
1178
  `// https://nuxt.com/docs/api/configuration/nuxt-config
1148
1179
  export default defineNuxtConfig({
@@ -1160,7 +1191,7 @@ export default defineNuxtConfig({
1160
1191
  );
1161
1192
  return;
1162
1193
  }
1163
- let config = await fs2.readFile(configPath, "utf-8");
1194
+ let config = await fs3.readFile(configPath, "utf-8");
1164
1195
  if (config.includes("modules:")) {
1165
1196
  config = config.replace(
1166
1197
  /modules:\s*\[/,
@@ -1187,11 +1218,11 @@ export default defineNuxtConfig({
1187
1218
  `
1188
1219
  );
1189
1220
  }
1190
- await fs2.writeFile(configPath, config);
1221
+ await fs3.writeFile(configPath, config);
1191
1222
  }
1192
1223
  async function configureNext(projectPath) {
1193
- const providersPath = path2.join(projectPath, "src", "app", "providers.tsx");
1194
- await fs2.writeFile(
1224
+ const providersPath = path3.join(projectPath, "src", "app", "providers.tsx");
1225
+ await fs3.writeFile(
1195
1226
  providersPath,
1196
1227
  `'use client';
1197
1228
 
@@ -1209,9 +1240,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
1209
1240
  }
1210
1241
  `
1211
1242
  );
1212
- const layoutPath = path2.join(projectPath, "src", "app", "layout.tsx");
1213
- if (await fs2.pathExists(layoutPath)) {
1214
- let layout = await fs2.readFile(layoutPath, "utf-8");
1243
+ const layoutPath = path3.join(projectPath, "src", "app", "layout.tsx");
1244
+ if (await fs3.pathExists(layoutPath)) {
1245
+ let layout = await fs3.readFile(layoutPath, "utf-8");
1215
1246
  if (!layout.includes("import { Providers }")) {
1216
1247
  layout = layout.replace(
1217
1248
  /^(import.*\n)+/m,
@@ -1224,24 +1255,24 @@ export function Providers({ children }: { children: React.ReactNode }) {
1224
1255
  "$1<Providers>$2</Providers>$3"
1225
1256
  );
1226
1257
  }
1227
- await fs2.writeFile(layoutPath, layout);
1258
+ await fs3.writeFile(layoutPath, layout);
1228
1259
  }
1229
- const envLocalPath = path2.join(projectPath, ".env.local");
1260
+ const envLocalPath = path3.join(projectPath, ".env.local");
1230
1261
  const nextEnvContent = `# Tether Configuration (client-side)
1231
1262
  NEXT_PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
1232
1263
  `;
1233
- if (await fs2.pathExists(envLocalPath)) {
1234
- const existing = await fs2.readFile(envLocalPath, "utf-8");
1235
- await fs2.writeFile(envLocalPath, existing + "\n" + nextEnvContent);
1264
+ if (await fs3.pathExists(envLocalPath)) {
1265
+ const existing = await fs3.readFile(envLocalPath, "utf-8");
1266
+ await fs3.writeFile(envLocalPath, existing + "\n" + nextEnvContent);
1236
1267
  } else {
1237
- await fs2.writeFile(envLocalPath, nextEnvContent);
1268
+ await fs3.writeFile(envLocalPath, nextEnvContent);
1238
1269
  }
1239
1270
  }
1240
1271
  async function configureSvelteKit(projectPath) {
1241
- const libPath = path2.join(projectPath, "src", "lib");
1242
- await fs2.ensureDir(libPath);
1243
- await fs2.writeFile(
1244
- path2.join(libPath, "tether.ts"),
1272
+ const libPath = path3.join(projectPath, "src", "lib");
1273
+ await fs3.ensureDir(libPath);
1274
+ await fs3.writeFile(
1275
+ path3.join(libPath, "tether.ts"),
1245
1276
  `import { createClient } from '@tthr/svelte';
1246
1277
  import { PUBLIC_TETHER_PROJECT_ID, PUBLIC_TETHER_URL } from '$env/static/public';
1247
1278
 
@@ -1251,14 +1282,14 @@ export const tether = createClient({
1251
1282
  });
1252
1283
  `
1253
1284
  );
1254
- const envPath = path2.join(projectPath, ".env");
1285
+ const envPath = path3.join(projectPath, ".env");
1255
1286
  const svelteEnvContent = `# Tether Configuration (public)
1256
1287
  PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
1257
1288
  `;
1258
- if (await fs2.pathExists(envPath)) {
1259
- const existing = await fs2.readFile(envPath, "utf-8");
1289
+ if (await fs3.pathExists(envPath)) {
1290
+ const existing = await fs3.readFile(envPath, "utf-8");
1260
1291
  if (!existing.includes("PUBLIC_TETHER_")) {
1261
- await fs2.writeFile(envPath, existing + "\n" + svelteEnvContent);
1292
+ await fs3.writeFile(envPath, existing + "\n" + svelteEnvContent);
1262
1293
  }
1263
1294
  }
1264
1295
  }
@@ -1313,10 +1344,10 @@ async function installTetherPackages(projectPath, template, packageManager) {
1313
1344
  }
1314
1345
  async function createDemoPage(projectPath, template) {
1315
1346
  if (template === "nuxt") {
1316
- const nuxt4AppVuePath = path2.join(projectPath, "app", "app.vue");
1317
- const nuxt3AppVuePath = path2.join(projectPath, "app.vue");
1318
- const appVuePath = await fs2.pathExists(path2.join(projectPath, "app")) ? nuxt4AppVuePath : nuxt3AppVuePath;
1319
- await fs2.writeFile(
1347
+ const nuxt4AppVuePath = path3.join(projectPath, "app", "app.vue");
1348
+ const nuxt3AppVuePath = path3.join(projectPath, "app.vue");
1349
+ const appVuePath = await fs3.pathExists(path3.join(projectPath, "app")) ? nuxt4AppVuePath : nuxt3AppVuePath;
1350
+ await fs3.writeFile(
1320
1351
  appVuePath,
1321
1352
  `<template>
1322
1353
  <TetherWelcome />
@@ -1324,8 +1355,8 @@ async function createDemoPage(projectPath, template) {
1324
1355
  `
1325
1356
  );
1326
1357
  } else if (template === "next") {
1327
- const pagePath = path2.join(projectPath, "src", "app", "page.tsx");
1328
- await fs2.writeFile(
1358
+ const pagePath = path3.join(projectPath, "src", "app", "page.tsx");
1359
+ await fs3.writeFile(
1329
1360
  pagePath,
1330
1361
  `'use client';
1331
1362
 
@@ -1435,8 +1466,8 @@ export default function Home() {
1435
1466
  `
1436
1467
  );
1437
1468
  } else if (template === "sveltekit") {
1438
- const pagePath = path2.join(projectPath, "src", "routes", "+page.svelte");
1439
- await fs2.writeFile(
1469
+ const pagePath = path3.join(projectPath, "src", "routes", "+page.svelte");
1470
+ await fs3.writeFile(
1440
1471
  pagePath,
1441
1472
  `<script lang="ts">
1442
1473
  import { onMount } from 'svelte';
@@ -1704,8 +1735,8 @@ export default function Home() {
1704
1735
  // src/commands/dev.ts
1705
1736
  import chalk3 from "chalk";
1706
1737
  import ora3 from "ora";
1707
- import fs3 from "fs-extra";
1708
- import path3 from "path";
1738
+ import fs4 from "fs-extra";
1739
+ import path4 from "path";
1709
1740
  import { spawn } from "child_process";
1710
1741
  import { watch } from "chokidar";
1711
1742
  var frameworkProcess = null;
@@ -1779,7 +1810,7 @@ async function runDeploy(what, projectId, token, schemaPath, functionsDir, envir
1779
1810
  }
1780
1811
  async function validateSchema(schemaPath) {
1781
1812
  try {
1782
- const content = await fs3.readFile(schemaPath, "utf-8");
1813
+ const content = await fs4.readFile(schemaPath, "utf-8");
1783
1814
  if (!content.includes("defineSchema")) {
1784
1815
  return { valid: false, error: "Missing defineSchema() call" };
1785
1816
  }
@@ -1801,17 +1832,17 @@ async function validateSchema(schemaPath) {
1801
1832
  }
1802
1833
  async function validateFunctions(functionsDir) {
1803
1834
  const errors = [];
1804
- if (!await fs3.pathExists(functionsDir)) {
1835
+ if (!await fs4.pathExists(functionsDir)) {
1805
1836
  return { valid: true, errors: [] };
1806
1837
  }
1807
- const files = await fs3.readdir(functionsDir);
1838
+ const files = await fs4.readdir(functionsDir);
1808
1839
  for (const file of files) {
1809
1840
  if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
1810
- const filePath = path3.join(functionsDir, file);
1811
- const stat = await fs3.stat(filePath);
1841
+ const filePath = path4.join(functionsDir, file);
1842
+ const stat = await fs4.stat(filePath);
1812
1843
  if (!stat.isFile()) continue;
1813
1844
  try {
1814
- const content = await fs3.readFile(filePath, "utf-8");
1845
+ const content = await fs4.readFile(filePath, "utf-8");
1815
1846
  const hasExports = /export\s+const\s+\w+\s*=\s*(query|mutation)\s*\(/.test(content);
1816
1847
  if (!hasExports && !file.includes("index")) {
1817
1848
  errors.push(`${file}: No query or mutation exports found`);
@@ -1930,17 +1961,17 @@ function cleanup() {
1930
1961
  }
1931
1962
  async function devCommand(options) {
1932
1963
  const credentials = await requireAuth();
1933
- const configPath = path3.resolve(process.cwd(), "tether.config.ts");
1934
- if (!await fs3.pathExists(configPath)) {
1964
+ const configPath = path4.resolve(process.cwd(), "tether.config.ts");
1965
+ if (!await fs4.pathExists(configPath)) {
1935
1966
  console.log(chalk3.red("\nError: Not a Tether project"));
1936
1967
  console.log(chalk3.dim("Run `tthr init` to create a new project\n"));
1937
1968
  process.exit(1);
1938
1969
  }
1939
1970
  const config = await loadConfig();
1940
1971
  if (!config.projectId) {
1941
- const envPath = path3.resolve(process.cwd(), ".env");
1942
- if (await fs3.pathExists(envPath)) {
1943
- const envContent = await fs3.readFile(envPath, "utf-8");
1972
+ const envPath = path4.resolve(process.cwd(), ".env");
1973
+ if (await fs4.pathExists(envPath)) {
1974
+ const envContent = await fs4.readFile(envPath, "utf-8");
1944
1975
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
1945
1976
  if (match) config.projectId = match[1].trim();
1946
1977
  }
@@ -1962,9 +1993,9 @@ async function devCommand(options) {
1962
1993
  console.log(chalk3.dim(` Dev command: ${devCmd}`));
1963
1994
  }
1964
1995
  console.log(chalk3.dim(` Environment: ${environment}${isLocal ? " (local)" : " (cloud)"}`));
1965
- console.log(chalk3.dim(` Schema: ${path3.relative(process.cwd(), schemaPath)}`));
1966
- console.log(chalk3.dim(` Functions: ${path3.relative(process.cwd(), functionsDir)}`));
1967
- console.log(chalk3.dim(` Output: ${path3.relative(process.cwd(), outputDir)}`));
1996
+ console.log(chalk3.dim(` Schema: ${path4.relative(process.cwd(), schemaPath)}`));
1997
+ console.log(chalk3.dim(` Functions: ${path4.relative(process.cwd(), functionsDir)}`));
1998
+ console.log(chalk3.dim(` Output: ${path4.relative(process.cwd(), outputDir)}`));
1968
1999
  if (options.extraArgs && options.extraArgs.length > 0) {
1969
2000
  console.log(chalk3.dim(` Extra args: ${options.extraArgs.join(" ")}`));
1970
2001
  }
@@ -2005,7 +2036,7 @@ async function devCommand(options) {
2005
2036
  spinner.warn("No project ID configured \u2014 skipping auto-deploy");
2006
2037
  }
2007
2038
  spinner.start("Setting up file watchers...");
2008
- if (await fs3.pathExists(schemaPath)) {
2039
+ if (await fs4.pathExists(schemaPath)) {
2009
2040
  schemaWatcher = watch(schemaPath, {
2010
2041
  ignoreInitial: true,
2011
2042
  awaitWriteFinish: {
@@ -2026,8 +2057,8 @@ async function devCommand(options) {
2026
2057
  }
2027
2058
  });
2028
2059
  }
2029
- if (await fs3.pathExists(functionsDir)) {
2030
- functionsWatcher = watch(path3.join(functionsDir, "**/*.{ts,js}"), {
2060
+ if (await fs4.pathExists(functionsDir)) {
2061
+ functionsWatcher = watch(path4.join(functionsDir, "**/*.{ts,js}"), {
2031
2062
  ignoreInitial: true,
2032
2063
  awaitWriteFinish: {
2033
2064
  stabilityThreshold: 300,
@@ -2035,7 +2066,7 @@ async function devCommand(options) {
2035
2066
  }
2036
2067
  });
2037
2068
  functionsWatcher.on("all", async (event, filePath) => {
2038
- const relativePath = path3.relative(functionsDir, filePath);
2069
+ const relativePath = path4.relative(functionsDir, filePath);
2039
2070
  console.log(chalk3.cyan(`
2040
2071
  Function ${event}: ${relativePath}`));
2041
2072
  const validation = await validateFunctions(functionsDir);
@@ -2090,7 +2121,8 @@ async function loginCommand() {
2090
2121
  console.log(chalk4.dim("\nRun `tthr logout` to sign out\n"));
2091
2122
  return;
2092
2123
  }
2093
- console.log(chalk4.dim(` URL: ${API_URL}
2124
+ console.log(chalk4.dim(` URL: ${API_URL}`));
2125
+ console.log(chalk4.dim(` \u21B3 override with TETHER_API_URL in your shell or .env.local
2094
2126
  `));
2095
2127
  const spinner = ora4("Generating authentication code...").start();
2096
2128
  try {
@@ -2265,8 +2297,8 @@ function openBrowser(url) {
2265
2297
  // src/commands/update.ts
2266
2298
  import chalk5 from "chalk";
2267
2299
  import ora5 from "ora";
2268
- import fs4 from "fs-extra";
2269
- import path4 from "path";
2300
+ import fs5 from "fs-extra";
2301
+ import path5 from "path";
2270
2302
  import { execSync as execSync2 } from "child_process";
2271
2303
  var TETHER_PACKAGES = [
2272
2304
  "@tthr/client",
@@ -2278,21 +2310,21 @@ var TETHER_PACKAGES = [
2278
2310
  ];
2279
2311
  function detectPackageManager() {
2280
2312
  let dir = process.cwd();
2281
- const root = path4.parse(dir).root;
2313
+ const root = path5.parse(dir).root;
2282
2314
  while (dir !== root) {
2283
- if (fs4.existsSync(path4.join(dir, "bun.lockb")) || fs4.existsSync(path4.join(dir, "bun.lock"))) {
2315
+ if (fs5.existsSync(path5.join(dir, "bun.lockb")) || fs5.existsSync(path5.join(dir, "bun.lock"))) {
2284
2316
  return "bun";
2285
2317
  }
2286
- if (fs4.existsSync(path4.join(dir, "pnpm-lock.yaml"))) {
2318
+ if (fs5.existsSync(path5.join(dir, "pnpm-lock.yaml"))) {
2287
2319
  return "pnpm";
2288
2320
  }
2289
- if (fs4.existsSync(path4.join(dir, "yarn.lock"))) {
2321
+ if (fs5.existsSync(path5.join(dir, "yarn.lock"))) {
2290
2322
  return "yarn";
2291
2323
  }
2292
- if (fs4.existsSync(path4.join(dir, "package-lock.json"))) {
2324
+ if (fs5.existsSync(path5.join(dir, "package-lock.json"))) {
2293
2325
  return "npm";
2294
2326
  }
2295
- dir = path4.dirname(dir);
2327
+ dir = path5.dirname(dir);
2296
2328
  }
2297
2329
  return "npm";
2298
2330
  }
@@ -2321,14 +2353,14 @@ async function getLatestVersion2(packageName) {
2321
2353
  }
2322
2354
  }
2323
2355
  async function updateCommand(options) {
2324
- const packageJsonPath = path4.resolve(process.cwd(), "package.json");
2325
- if (!await fs4.pathExists(packageJsonPath)) {
2356
+ const packageJsonPath = path5.resolve(process.cwd(), "package.json");
2357
+ if (!await fs5.pathExists(packageJsonPath)) {
2326
2358
  console.log(chalk5.red("\nError: No package.json found"));
2327
2359
  console.log(chalk5.dim("Make sure you're in the root of your project\n"));
2328
2360
  process.exit(1);
2329
2361
  }
2330
2362
  console.log(chalk5.bold("\n\u26A1 Updating Tether packages\n"));
2331
- const packageJson = await fs4.readJson(packageJsonPath);
2363
+ const packageJson = await fs5.readJson(packageJsonPath);
2332
2364
  const deps = packageJson.dependencies || {};
2333
2365
  const devDeps = packageJson.devDependencies || {};
2334
2366
  const installedDeps = [];
@@ -2414,8 +2446,8 @@ async function updateCommand(options) {
2414
2446
  // src/commands/exec.ts
2415
2447
  import chalk6 from "chalk";
2416
2448
  import ora6 from "ora";
2417
- import fs5 from "fs-extra";
2418
- import path5 from "path";
2449
+ import fs6 from "fs-extra";
2450
+ import path6 from "path";
2419
2451
  async function execCommand(sql) {
2420
2452
  if (!sql || sql.trim() === "") {
2421
2453
  console.log(chalk6.red("\nError: SQL query required"));
@@ -2423,10 +2455,10 @@ async function execCommand(sql) {
2423
2455
  process.exit(1);
2424
2456
  }
2425
2457
  const credentials = await requireAuth();
2426
- const envPath = path5.resolve(process.cwd(), ".env");
2458
+ const envPath = path6.resolve(process.cwd(), ".env");
2427
2459
  let projectId;
2428
- if (await fs5.pathExists(envPath)) {
2429
- const envContent = await fs5.readFile(envPath, "utf-8");
2460
+ if (await fs6.pathExists(envPath)) {
2461
+ const envContent = await fs6.readFile(envPath, "utf-8");
2430
2462
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
2431
2463
  projectId = match?.[1]?.trim();
2432
2464
  }
@@ -2487,17 +2519,17 @@ async function execCommand(sql) {
2487
2519
  // src/commands/env.ts
2488
2520
  import chalk7 from "chalk";
2489
2521
  import ora7 from "ora";
2490
- import fs6 from "fs-extra";
2491
- import path6 from "path";
2522
+ import fs7 from "fs-extra";
2523
+ import path7 from "path";
2492
2524
  async function resolveApiUrl2() {
2493
2525
  const config = await loadConfig();
2494
2526
  return `${getApiUrl2(config)}/api/v1`;
2495
2527
  }
2496
2528
  async function getProjectId() {
2497
- const envPath = path6.resolve(process.cwd(), ".env");
2529
+ const envPath = path7.resolve(process.cwd(), ".env");
2498
2530
  let projectId;
2499
- if (await fs6.pathExists(envPath)) {
2500
- const envContent = await fs6.readFile(envPath, "utf-8");
2531
+ if (await fs7.pathExists(envPath)) {
2532
+ const envContent = await fs7.readFile(envPath, "utf-8");
2501
2533
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
2502
2534
  projectId = match?.[1]?.trim();
2503
2535
  }
@@ -2654,15 +2686,15 @@ function maskApiKey(key) {
2654
2686
  // src/commands/migrate.ts
2655
2687
  import chalk8 from "chalk";
2656
2688
  import ora8 from "ora";
2657
- import fs7 from "fs-extra";
2658
- import path7 from "path";
2689
+ import fs8 from "fs-extra";
2690
+ import path8 from "path";
2659
2691
  import readline2 from "readline";
2660
2692
  async function migrateSystemColumnsCommand(options) {
2661
2693
  const credentials = await requireAuth();
2662
- const envPath = path7.resolve(process.cwd(), ".env");
2694
+ const envPath = path8.resolve(process.cwd(), ".env");
2663
2695
  let projectId;
2664
- if (await fs7.pathExists(envPath)) {
2665
- const envContent = await fs7.readFile(envPath, "utf-8");
2696
+ if (await fs8.pathExists(envPath)) {
2697
+ const envContent = await fs8.readFile(envPath, "utf-8");
2666
2698
  const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
2667
2699
  projectId = match?.[1]?.trim();
2668
2700
  }
@@ -2800,9 +2832,10 @@ function askConfirmation(prompt) {
2800
2832
  }
2801
2833
 
2802
2834
  // src/index.ts
2835
+ loadDotenv();
2803
2836
  var __filename = fileURLToPath(import.meta.url);
2804
- var __dirname = path8.dirname(__filename);
2805
- var pkg = fs8.readJsonSync(path8.resolve(__dirname, "../package.json"));
2837
+ var __dirname = path9.dirname(__filename);
2838
+ var pkg = fs9.readJsonSync(path9.resolve(__dirname, "../package.json"));
2806
2839
  var program = new Command();
2807
2840
  program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version(pkg.version).enablePositionalOptions();
2808
2841
  program.command("init [name]").description("Create a new Tether project").option("-t, --template <template>", "Project template (vue, svelte, react, vanilla)", "vue").action(initCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tthr",
3
- "version": "0.3.21",
3
+ "version": "0.3.22",
4
4
  "description": "Tether CLI - project scaffolding and deployment",
5
5
  "type": "module",
6
6
  "bin": {