tthr 0.3.20 → 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.
- package/dist/index.js +214 -163
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,28 +17,94 @@ import {
|
|
|
17
17
|
|
|
18
18
|
// src/index.ts
|
|
19
19
|
import { Command } from "commander";
|
|
20
|
-
import
|
|
20
|
+
import fs9 from "fs-extra";
|
|
21
21
|
import { fileURLToPath } from "url";
|
|
22
|
-
import
|
|
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
|
|
29
|
-
import
|
|
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
|
|
36
|
-
import
|
|
65
|
+
import fs2 from "fs-extra";
|
|
66
|
+
import path2 from "path";
|
|
37
67
|
import * as esbuild from "esbuild";
|
|
68
|
+
async function describeHttpError(response, action) {
|
|
69
|
+
const status = response.status;
|
|
70
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
71
|
+
const text = await response.text().catch(() => "");
|
|
72
|
+
if (contentType.includes("application/json")) {
|
|
73
|
+
try {
|
|
74
|
+
const json = JSON.parse(text);
|
|
75
|
+
if (typeof json?.error === "string") return json.error;
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const looksLikeHtml = contentType.includes("text/html") || /^\s*<(!doctype|html)/i.test(text);
|
|
80
|
+
if (looksLikeHtml) {
|
|
81
|
+
const hint = (() => {
|
|
82
|
+
switch (status) {
|
|
83
|
+
case 401:
|
|
84
|
+
case 403:
|
|
85
|
+
return "Authentication failed or insufficient permissions. Try `tthr login` first.";
|
|
86
|
+
case 404:
|
|
87
|
+
return `Route or resource not found. Common causes: wrong project ID, environment doesn't exist on this server, or TETHER_API_URL points at the wrong host.`;
|
|
88
|
+
case 429:
|
|
89
|
+
return "Rate-limited by the server or upstream proxy. Wait a moment and retry.";
|
|
90
|
+
case 502:
|
|
91
|
+
case 503:
|
|
92
|
+
case 504:
|
|
93
|
+
return "Server is unreachable, restarting, or under load. Check status and retry.";
|
|
94
|
+
default:
|
|
95
|
+
return `Upstream proxy returned a generic ${status} page (likely a Cloudflare / nginx fallback).`;
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
return `Failed to ${action}: ${hint} (HTTP ${status})`;
|
|
99
|
+
}
|
|
100
|
+
const trimmed = text.trim();
|
|
101
|
+
if (trimmed) return `${trimmed.slice(0, 300)}${trimmed.length > 300 ? "\u2026" : ""} (HTTP ${status})`;
|
|
102
|
+
return `HTTP ${status}: ${response.statusText || "request failed"}`;
|
|
103
|
+
}
|
|
38
104
|
async function deployCommand(options) {
|
|
39
105
|
const credentials = await requireAuth();
|
|
40
|
-
const configPath =
|
|
41
|
-
if (!await
|
|
106
|
+
const configPath = path2.resolve(process.cwd(), "tether.config.ts");
|
|
107
|
+
if (!await fs2.pathExists(configPath)) {
|
|
42
108
|
console.log(chalk.red("\nError: Not a Tether project"));
|
|
43
109
|
console.log(chalk.dim("Run `tthr init` to create a new project\n"));
|
|
44
110
|
process.exit(1);
|
|
@@ -46,9 +112,9 @@ async function deployCommand(options) {
|
|
|
46
112
|
const config = await loadConfig();
|
|
47
113
|
let projectId = config.projectId;
|
|
48
114
|
if (!projectId) {
|
|
49
|
-
const envPath =
|
|
50
|
-
if (await
|
|
51
|
-
const envContent = await
|
|
115
|
+
const envPath = path2.resolve(process.cwd(), ".env");
|
|
116
|
+
if (await fs2.pathExists(envPath)) {
|
|
117
|
+
const envContent = await fs2.readFile(envPath, "utf-8");
|
|
52
118
|
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
53
119
|
projectId = match?.[1]?.trim();
|
|
54
120
|
}
|
|
@@ -63,7 +129,8 @@ async function deployCommand(options) {
|
|
|
63
129
|
console.log(chalk.bold("\n\u26A1 Deploying to Tether\n"));
|
|
64
130
|
console.log(chalk.dim(` Project: ${projectId}`));
|
|
65
131
|
console.log(chalk.dim(` Environment: ${environment}`));
|
|
66
|
-
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
|
|
67
134
|
`));
|
|
68
135
|
console.log(chalk.dim(" Generating types..."));
|
|
69
136
|
try {
|
|
@@ -88,15 +155,15 @@ async function deploySchemaToServer(projectId, token, schemaPath, environment, d
|
|
|
88
155
|
const API_URL2 = apiUrl || `${await resolveApiUrl()}/api/v1`;
|
|
89
156
|
const spinner = ora("Reading schema...").start();
|
|
90
157
|
try {
|
|
91
|
-
if (!await
|
|
158
|
+
if (!await fs2.pathExists(schemaPath)) {
|
|
92
159
|
spinner.warn("No schema file found");
|
|
93
|
-
const relativePath =
|
|
160
|
+
const relativePath = path2.relative(process.cwd(), schemaPath);
|
|
94
161
|
console.log(chalk.dim(` Create ${relativePath} to define your database schema
|
|
95
162
|
`));
|
|
96
163
|
return;
|
|
97
164
|
}
|
|
98
165
|
console.log(chalk.dim(` Schema path: ${schemaPath}`));
|
|
99
|
-
const schemaSource = await
|
|
166
|
+
const schemaSource = await fs2.readFile(schemaPath, "utf-8");
|
|
100
167
|
console.log(chalk.dim(` Schema source length: ${schemaSource.length} chars`));
|
|
101
168
|
console.log(chalk.dim(` Parsing schema...`));
|
|
102
169
|
const tables = parseSchema(schemaSource);
|
|
@@ -146,16 +213,7 @@ async function deploySchemaToServer(projectId, token, schemaPath, environment, d
|
|
|
146
213
|
console.log(chalk.dim(` Response: ${response.status} ${response.statusText}`));
|
|
147
214
|
console.log(chalk.dim(` Content-Type: ${response.headers.get("content-type")}`));
|
|
148
215
|
if (!response.ok) {
|
|
149
|
-
|
|
150
|
-
console.log(chalk.dim(` Body: ${text.slice(0, 500)}`));
|
|
151
|
-
let errorMessage;
|
|
152
|
-
try {
|
|
153
|
-
const error = JSON.parse(text);
|
|
154
|
-
errorMessage = error.error || `HTTP ${response.status}`;
|
|
155
|
-
} catch {
|
|
156
|
-
errorMessage = text || `HTTP ${response.status}: ${response.statusText}`;
|
|
157
|
-
}
|
|
158
|
-
throw new Error(errorMessage);
|
|
216
|
+
throw new Error(await describeHttpError(response, "deploy schema"));
|
|
159
217
|
}
|
|
160
218
|
spinner.succeed(`Schema deployed (${tables.length} table(s))`);
|
|
161
219
|
} catch (error) {
|
|
@@ -193,14 +251,14 @@ async function deployFunctionsToServer(projectId, token, functionsDir, environme
|
|
|
193
251
|
const API_URL2 = apiUrl || `${await resolveApiUrl()}/api/v1`;
|
|
194
252
|
const spinner = ora("Reading functions...").start();
|
|
195
253
|
try {
|
|
196
|
-
if (!await
|
|
254
|
+
if (!await fs2.pathExists(functionsDir)) {
|
|
197
255
|
spinner.warn("No functions directory found");
|
|
198
|
-
const relativePath =
|
|
256
|
+
const relativePath = path2.relative(process.cwd(), functionsDir);
|
|
199
257
|
console.log(chalk.dim(` Create ${relativePath}/ to define your API functions
|
|
200
258
|
`));
|
|
201
259
|
return;
|
|
202
260
|
}
|
|
203
|
-
const files = await
|
|
261
|
+
const files = await fs2.readdir(functionsDir);
|
|
204
262
|
const tsFiles = files.filter((f) => f.endsWith(".ts") && !f.startsWith("_"));
|
|
205
263
|
const helperFiles = files.filter((f) => f.endsWith(".ts") && f.startsWith("_"));
|
|
206
264
|
if (tsFiles.length === 0) {
|
|
@@ -209,13 +267,13 @@ async function deployFunctionsToServer(projectId, token, functionsDir, environme
|
|
|
209
267
|
}
|
|
210
268
|
const helperSources = /* @__PURE__ */ new Map();
|
|
211
269
|
for (const hf of helperFiles) {
|
|
212
|
-
const hfPath =
|
|
213
|
-
helperSources.set(hf, await
|
|
270
|
+
const hfPath = path2.join(functionsDir, hf);
|
|
271
|
+
helperSources.set(hf, await fs2.readFile(hfPath, "utf-8"));
|
|
214
272
|
}
|
|
215
273
|
const functions = [];
|
|
216
274
|
for (const file of tsFiles) {
|
|
217
|
-
const filePath =
|
|
218
|
-
const source = await
|
|
275
|
+
const filePath = path2.join(functionsDir, file);
|
|
276
|
+
const source = await fs2.readFile(filePath, "utf-8");
|
|
219
277
|
const moduleName = file.replace(".ts", "");
|
|
220
278
|
const parsedFunctions = parseFunctions(moduleName, source);
|
|
221
279
|
if (parsedFunctions.length === 0) continue;
|
|
@@ -269,16 +327,7 @@ ${helperSource}`;
|
|
|
269
327
|
console.log(chalk.dim(` Response: ${response.status} ${response.statusText}`));
|
|
270
328
|
console.log(chalk.dim(` Content-Type: ${response.headers.get("content-type")}`));
|
|
271
329
|
if (!response.ok) {
|
|
272
|
-
|
|
273
|
-
console.log(chalk.dim(` Body: ${text.slice(0, 500)}`));
|
|
274
|
-
let errorMessage;
|
|
275
|
-
try {
|
|
276
|
-
const error = JSON.parse(text);
|
|
277
|
-
errorMessage = error.error || `HTTP ${response.status}`;
|
|
278
|
-
} catch {
|
|
279
|
-
errorMessage = text || `HTTP ${response.status}: ${response.statusText}`;
|
|
280
|
-
}
|
|
281
|
-
throw new Error(errorMessage);
|
|
330
|
+
throw new Error(await describeHttpError(response, "deploy functions"));
|
|
282
331
|
}
|
|
283
332
|
spinner.succeed(`Functions deployed (${functions.length} function(s))`);
|
|
284
333
|
const queries = functions.filter((f) => f.type === "query");
|
|
@@ -658,8 +707,8 @@ ${packageManager} is not installed on your system.`));
|
|
|
658
707
|
});
|
|
659
708
|
template = response.template || "nuxt";
|
|
660
709
|
}
|
|
661
|
-
const projectPath =
|
|
662
|
-
if (await
|
|
710
|
+
const projectPath = path3.resolve(process.cwd(), projectName);
|
|
711
|
+
if (await fs3.pathExists(projectPath)) {
|
|
663
712
|
const { overwrite } = await prompts({
|
|
664
713
|
type: "confirm",
|
|
665
714
|
name: "overwrite",
|
|
@@ -670,7 +719,7 @@ ${packageManager} is not installed on your system.`));
|
|
|
670
719
|
console.log(chalk2.yellow("Cancelled"));
|
|
671
720
|
process.exit(0);
|
|
672
721
|
}
|
|
673
|
-
await
|
|
722
|
+
await fs3.remove(projectPath);
|
|
674
723
|
}
|
|
675
724
|
const spinner = ora2(`Scaffolding ${template} project...`).start();
|
|
676
725
|
let projectId;
|
|
@@ -746,7 +795,7 @@ function getPackageRunnerCommand(pm) {
|
|
|
746
795
|
}
|
|
747
796
|
}
|
|
748
797
|
async function scaffoldNuxtProject(projectName, projectPath, spinner, packageManager) {
|
|
749
|
-
const parentDir =
|
|
798
|
+
const parentDir = path3.dirname(projectPath);
|
|
750
799
|
const runner = getPackageRunnerCommand(packageManager);
|
|
751
800
|
spinner.stop();
|
|
752
801
|
console.log(chalk2.dim("\nRunning nuxi init...\n"));
|
|
@@ -755,7 +804,7 @@ async function scaffoldNuxtProject(projectName, projectPath, spinner, packageMan
|
|
|
755
804
|
cwd: parentDir,
|
|
756
805
|
stdio: "inherit"
|
|
757
806
|
});
|
|
758
|
-
if (!await
|
|
807
|
+
if (!await fs3.pathExists(path3.join(projectPath, "package.json"))) {
|
|
759
808
|
throw new Error("Nuxt project was not created successfully - package.json missing");
|
|
760
809
|
}
|
|
761
810
|
spinner.start();
|
|
@@ -768,7 +817,7 @@ async function scaffoldNuxtProject(projectName, projectPath, spinner, packageMan
|
|
|
768
817
|
}
|
|
769
818
|
}
|
|
770
819
|
async function scaffoldNextProject(projectName, projectPath, spinner, packageManager) {
|
|
771
|
-
const parentDir =
|
|
820
|
+
const parentDir = path3.dirname(projectPath);
|
|
772
821
|
const runner = getPackageRunnerCommand(packageManager);
|
|
773
822
|
const pmFlag = packageManager === "npm" ? "--use-npm" : packageManager === "pnpm" ? "--use-pnpm" : packageManager === "yarn" ? "--use-yarn" : "--use-bun";
|
|
774
823
|
spinner.stop();
|
|
@@ -778,7 +827,7 @@ async function scaffoldNextProject(projectName, projectPath, spinner, packageMan
|
|
|
778
827
|
cwd: parentDir,
|
|
779
828
|
stdio: "inherit"
|
|
780
829
|
});
|
|
781
|
-
if (!await
|
|
830
|
+
if (!await fs3.pathExists(path3.join(projectPath, "package.json"))) {
|
|
782
831
|
throw new Error("Next.js project was not created successfully - package.json missing");
|
|
783
832
|
}
|
|
784
833
|
spinner.start();
|
|
@@ -791,7 +840,7 @@ async function scaffoldNextProject(projectName, projectPath, spinner, packageMan
|
|
|
791
840
|
}
|
|
792
841
|
}
|
|
793
842
|
async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packageManager) {
|
|
794
|
-
const parentDir =
|
|
843
|
+
const parentDir = path3.dirname(projectPath);
|
|
795
844
|
const runner = getPackageRunnerCommand(packageManager);
|
|
796
845
|
spinner.stop();
|
|
797
846
|
console.log(chalk2.dim("\nRunning sv create...\n"));
|
|
@@ -800,7 +849,7 @@ async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packa
|
|
|
800
849
|
cwd: parentDir,
|
|
801
850
|
stdio: "inherit"
|
|
802
851
|
});
|
|
803
|
-
if (!await
|
|
852
|
+
if (!await fs3.pathExists(path3.join(projectPath, "package.json"))) {
|
|
804
853
|
throw new Error("SvelteKit project was not created successfully - package.json missing");
|
|
805
854
|
}
|
|
806
855
|
spinner.start();
|
|
@@ -819,8 +868,8 @@ async function scaffoldSvelteKitProject(projectName, projectPath, spinner, packa
|
|
|
819
868
|
}
|
|
820
869
|
async function scaffoldVanillaProject(projectName, projectPath, spinner) {
|
|
821
870
|
spinner.text = "Creating vanilla TypeScript project...";
|
|
822
|
-
await
|
|
823
|
-
await
|
|
871
|
+
await fs3.ensureDir(projectPath);
|
|
872
|
+
await fs3.ensureDir(path3.join(projectPath, "src"));
|
|
824
873
|
const packageJson = {
|
|
825
874
|
name: projectName,
|
|
826
875
|
version: "0.0.1",
|
|
@@ -837,9 +886,9 @@ async function scaffoldVanillaProject(projectName, projectPath, spinner) {
|
|
|
837
886
|
"@types/node": "latest"
|
|
838
887
|
}
|
|
839
888
|
};
|
|
840
|
-
await
|
|
841
|
-
await
|
|
842
|
-
|
|
889
|
+
await fs3.writeJSON(path3.join(projectPath, "package.json"), packageJson, { spaces: 2 });
|
|
890
|
+
await fs3.writeJSON(
|
|
891
|
+
path3.join(projectPath, "tsconfig.json"),
|
|
843
892
|
{
|
|
844
893
|
compilerOptions: {
|
|
845
894
|
target: "ES2022",
|
|
@@ -860,8 +909,8 @@ async function scaffoldVanillaProject(projectName, projectPath, spinner) {
|
|
|
860
909
|
},
|
|
861
910
|
{ spaces: 2 }
|
|
862
911
|
);
|
|
863
|
-
await
|
|
864
|
-
|
|
912
|
+
await fs3.writeFile(
|
|
913
|
+
path3.join(projectPath, "src", "index.ts"),
|
|
865
914
|
`import { createClient } from '@tthr/client';
|
|
866
915
|
|
|
867
916
|
const tether = createClient({
|
|
@@ -880,8 +929,8 @@ async function main() {
|
|
|
880
929
|
main().catch(console.error);
|
|
881
930
|
`
|
|
882
931
|
);
|
|
883
|
-
await
|
|
884
|
-
|
|
932
|
+
await fs3.writeFile(
|
|
933
|
+
path3.join(projectPath, ".gitignore"),
|
|
885
934
|
`node_modules/
|
|
886
935
|
dist/
|
|
887
936
|
.env
|
|
@@ -891,11 +940,11 @@ dist/
|
|
|
891
940
|
);
|
|
892
941
|
}
|
|
893
942
|
async function addTetherFiles(projectPath, projectId, apiKey, template) {
|
|
894
|
-
await
|
|
895
|
-
await
|
|
943
|
+
await fs3.ensureDir(path3.join(projectPath, "tether"));
|
|
944
|
+
await fs3.ensureDir(path3.join(projectPath, "tether", "functions"));
|
|
896
945
|
const configPackage = template === "nuxt" ? "@tthr/vue" : template === "next" ? "@tthr/react" : template === "sveltekit" ? "@tthr/svelte" : "@tthr/client";
|
|
897
|
-
await
|
|
898
|
-
|
|
946
|
+
await fs3.writeFile(
|
|
947
|
+
path3.join(projectPath, "tether.config.ts"),
|
|
899
948
|
`import { defineConfig } from '${configPackage}';
|
|
900
949
|
|
|
901
950
|
export default defineConfig({
|
|
@@ -907,8 +956,8 @@ export default defineConfig({
|
|
|
907
956
|
});
|
|
908
957
|
`
|
|
909
958
|
);
|
|
910
|
-
await
|
|
911
|
-
|
|
959
|
+
await fs3.writeFile(
|
|
960
|
+
path3.join(projectPath, "tether", "schema.ts"),
|
|
912
961
|
`import { defineSchema, text, timestamp } from '@tthr/schema';
|
|
913
962
|
|
|
914
963
|
export default defineSchema({
|
|
@@ -930,8 +979,8 @@ export default defineSchema({
|
|
|
930
979
|
});
|
|
931
980
|
`
|
|
932
981
|
);
|
|
933
|
-
await
|
|
934
|
-
|
|
982
|
+
await fs3.writeFile(
|
|
983
|
+
path3.join(projectPath, "tether", "functions", "posts.ts"),
|
|
935
984
|
`import { query, mutation, z } from '../_generated/db';
|
|
936
985
|
|
|
937
986
|
// List all posts
|
|
@@ -1012,8 +1061,8 @@ export const remove = mutation({
|
|
|
1012
1061
|
});
|
|
1013
1062
|
`
|
|
1014
1063
|
);
|
|
1015
|
-
await
|
|
1016
|
-
|
|
1064
|
+
await fs3.writeFile(
|
|
1065
|
+
path3.join(projectPath, "tether", "functions", "index.ts"),
|
|
1017
1066
|
`// Re-export all function modules
|
|
1018
1067
|
// The Tether SDK uses this file to discover custom functions
|
|
1019
1068
|
|
|
@@ -1021,8 +1070,8 @@ export * as posts from './posts';
|
|
|
1021
1070
|
export * as comments from './comments';
|
|
1022
1071
|
`
|
|
1023
1072
|
);
|
|
1024
|
-
await
|
|
1025
|
-
|
|
1073
|
+
await fs3.writeFile(
|
|
1074
|
+
path3.join(projectPath, "tether", "functions", "comments.ts"),
|
|
1026
1075
|
`import { query, mutation, z } from '../_generated/db';
|
|
1027
1076
|
|
|
1028
1077
|
// List all comments
|
|
@@ -1090,26 +1139,26 @@ export const remove = mutation({
|
|
|
1090
1139
|
TETHER_PROJECT_ID=${projectId}
|
|
1091
1140
|
TETHER_API_KEY=${apiKey}
|
|
1092
1141
|
`;
|
|
1093
|
-
const envPath =
|
|
1094
|
-
if (await
|
|
1095
|
-
const existing = await
|
|
1096
|
-
await
|
|
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);
|
|
1097
1146
|
} else {
|
|
1098
|
-
await
|
|
1147
|
+
await fs3.writeFile(envPath, envContent);
|
|
1099
1148
|
}
|
|
1100
|
-
const gitignorePath =
|
|
1149
|
+
const gitignorePath = path3.join(projectPath, ".gitignore");
|
|
1101
1150
|
const tetherGitignore = `
|
|
1102
1151
|
# Tether
|
|
1103
1152
|
.env
|
|
1104
1153
|
.env.local
|
|
1105
1154
|
`;
|
|
1106
|
-
if (await
|
|
1107
|
-
const existing = await
|
|
1155
|
+
if (await fs3.pathExists(gitignorePath)) {
|
|
1156
|
+
const existing = await fs3.readFile(gitignorePath, "utf-8");
|
|
1108
1157
|
if (!existing.includes("_generated/")) {
|
|
1109
|
-
await
|
|
1158
|
+
await fs3.writeFile(gitignorePath, existing + tetherGitignore);
|
|
1110
1159
|
}
|
|
1111
1160
|
} else {
|
|
1112
|
-
await
|
|
1161
|
+
await fs3.writeFile(gitignorePath, tetherGitignore.trim());
|
|
1113
1162
|
}
|
|
1114
1163
|
}
|
|
1115
1164
|
async function configureFramework(projectPath, template) {
|
|
@@ -1122,9 +1171,9 @@ async function configureFramework(projectPath, template) {
|
|
|
1122
1171
|
}
|
|
1123
1172
|
}
|
|
1124
1173
|
async function configureNuxt(projectPath) {
|
|
1125
|
-
const configPath =
|
|
1126
|
-
if (!await
|
|
1127
|
-
await
|
|
1174
|
+
const configPath = path3.join(projectPath, "nuxt.config.ts");
|
|
1175
|
+
if (!await fs3.pathExists(configPath)) {
|
|
1176
|
+
await fs3.writeFile(
|
|
1128
1177
|
configPath,
|
|
1129
1178
|
`// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
1130
1179
|
export default defineNuxtConfig({
|
|
@@ -1142,7 +1191,7 @@ export default defineNuxtConfig({
|
|
|
1142
1191
|
);
|
|
1143
1192
|
return;
|
|
1144
1193
|
}
|
|
1145
|
-
let config = await
|
|
1194
|
+
let config = await fs3.readFile(configPath, "utf-8");
|
|
1146
1195
|
if (config.includes("modules:")) {
|
|
1147
1196
|
config = config.replace(
|
|
1148
1197
|
/modules:\s*\[/,
|
|
@@ -1169,11 +1218,11 @@ export default defineNuxtConfig({
|
|
|
1169
1218
|
`
|
|
1170
1219
|
);
|
|
1171
1220
|
}
|
|
1172
|
-
await
|
|
1221
|
+
await fs3.writeFile(configPath, config);
|
|
1173
1222
|
}
|
|
1174
1223
|
async function configureNext(projectPath) {
|
|
1175
|
-
const providersPath =
|
|
1176
|
-
await
|
|
1224
|
+
const providersPath = path3.join(projectPath, "src", "app", "providers.tsx");
|
|
1225
|
+
await fs3.writeFile(
|
|
1177
1226
|
providersPath,
|
|
1178
1227
|
`'use client';
|
|
1179
1228
|
|
|
@@ -1191,9 +1240,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
1191
1240
|
}
|
|
1192
1241
|
`
|
|
1193
1242
|
);
|
|
1194
|
-
const layoutPath =
|
|
1195
|
-
if (await
|
|
1196
|
-
let layout = await
|
|
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");
|
|
1197
1246
|
if (!layout.includes("import { Providers }")) {
|
|
1198
1247
|
layout = layout.replace(
|
|
1199
1248
|
/^(import.*\n)+/m,
|
|
@@ -1206,24 +1255,24 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
1206
1255
|
"$1<Providers>$2</Providers>$3"
|
|
1207
1256
|
);
|
|
1208
1257
|
}
|
|
1209
|
-
await
|
|
1258
|
+
await fs3.writeFile(layoutPath, layout);
|
|
1210
1259
|
}
|
|
1211
|
-
const envLocalPath =
|
|
1260
|
+
const envLocalPath = path3.join(projectPath, ".env.local");
|
|
1212
1261
|
const nextEnvContent = `# Tether Configuration (client-side)
|
|
1213
1262
|
NEXT_PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
|
|
1214
1263
|
`;
|
|
1215
|
-
if (await
|
|
1216
|
-
const existing = await
|
|
1217
|
-
await
|
|
1264
|
+
if (await fs3.pathExists(envLocalPath)) {
|
|
1265
|
+
const existing = await fs3.readFile(envLocalPath, "utf-8");
|
|
1266
|
+
await fs3.writeFile(envLocalPath, existing + "\n" + nextEnvContent);
|
|
1218
1267
|
} else {
|
|
1219
|
-
await
|
|
1268
|
+
await fs3.writeFile(envLocalPath, nextEnvContent);
|
|
1220
1269
|
}
|
|
1221
1270
|
}
|
|
1222
1271
|
async function configureSvelteKit(projectPath) {
|
|
1223
|
-
const libPath =
|
|
1224
|
-
await
|
|
1225
|
-
await
|
|
1226
|
-
|
|
1272
|
+
const libPath = path3.join(projectPath, "src", "lib");
|
|
1273
|
+
await fs3.ensureDir(libPath);
|
|
1274
|
+
await fs3.writeFile(
|
|
1275
|
+
path3.join(libPath, "tether.ts"),
|
|
1227
1276
|
`import { createClient } from '@tthr/svelte';
|
|
1228
1277
|
import { PUBLIC_TETHER_PROJECT_ID, PUBLIC_TETHER_URL } from '$env/static/public';
|
|
1229
1278
|
|
|
@@ -1233,14 +1282,14 @@ export const tether = createClient({
|
|
|
1233
1282
|
});
|
|
1234
1283
|
`
|
|
1235
1284
|
);
|
|
1236
|
-
const envPath =
|
|
1285
|
+
const envPath = path3.join(projectPath, ".env");
|
|
1237
1286
|
const svelteEnvContent = `# Tether Configuration (public)
|
|
1238
1287
|
PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
|
|
1239
1288
|
`;
|
|
1240
|
-
if (await
|
|
1241
|
-
const existing = await
|
|
1289
|
+
if (await fs3.pathExists(envPath)) {
|
|
1290
|
+
const existing = await fs3.readFile(envPath, "utf-8");
|
|
1242
1291
|
if (!existing.includes("PUBLIC_TETHER_")) {
|
|
1243
|
-
await
|
|
1292
|
+
await fs3.writeFile(envPath, existing + "\n" + svelteEnvContent);
|
|
1244
1293
|
}
|
|
1245
1294
|
}
|
|
1246
1295
|
}
|
|
@@ -1295,10 +1344,10 @@ async function installTetherPackages(projectPath, template, packageManager) {
|
|
|
1295
1344
|
}
|
|
1296
1345
|
async function createDemoPage(projectPath, template) {
|
|
1297
1346
|
if (template === "nuxt") {
|
|
1298
|
-
const nuxt4AppVuePath =
|
|
1299
|
-
const nuxt3AppVuePath =
|
|
1300
|
-
const appVuePath = await
|
|
1301
|
-
await
|
|
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(
|
|
1302
1351
|
appVuePath,
|
|
1303
1352
|
`<template>
|
|
1304
1353
|
<TetherWelcome />
|
|
@@ -1306,8 +1355,8 @@ async function createDemoPage(projectPath, template) {
|
|
|
1306
1355
|
`
|
|
1307
1356
|
);
|
|
1308
1357
|
} else if (template === "next") {
|
|
1309
|
-
const pagePath =
|
|
1310
|
-
await
|
|
1358
|
+
const pagePath = path3.join(projectPath, "src", "app", "page.tsx");
|
|
1359
|
+
await fs3.writeFile(
|
|
1311
1360
|
pagePath,
|
|
1312
1361
|
`'use client';
|
|
1313
1362
|
|
|
@@ -1417,8 +1466,8 @@ export default function Home() {
|
|
|
1417
1466
|
`
|
|
1418
1467
|
);
|
|
1419
1468
|
} else if (template === "sveltekit") {
|
|
1420
|
-
const pagePath =
|
|
1421
|
-
await
|
|
1469
|
+
const pagePath = path3.join(projectPath, "src", "routes", "+page.svelte");
|
|
1470
|
+
await fs3.writeFile(
|
|
1422
1471
|
pagePath,
|
|
1423
1472
|
`<script lang="ts">
|
|
1424
1473
|
import { onMount } from 'svelte';
|
|
@@ -1686,8 +1735,8 @@ export default function Home() {
|
|
|
1686
1735
|
// src/commands/dev.ts
|
|
1687
1736
|
import chalk3 from "chalk";
|
|
1688
1737
|
import ora3 from "ora";
|
|
1689
|
-
import
|
|
1690
|
-
import
|
|
1738
|
+
import fs4 from "fs-extra";
|
|
1739
|
+
import path4 from "path";
|
|
1691
1740
|
import { spawn } from "child_process";
|
|
1692
1741
|
import { watch } from "chokidar";
|
|
1693
1742
|
var frameworkProcess = null;
|
|
@@ -1761,7 +1810,7 @@ async function runDeploy(what, projectId, token, schemaPath, functionsDir, envir
|
|
|
1761
1810
|
}
|
|
1762
1811
|
async function validateSchema(schemaPath) {
|
|
1763
1812
|
try {
|
|
1764
|
-
const content = await
|
|
1813
|
+
const content = await fs4.readFile(schemaPath, "utf-8");
|
|
1765
1814
|
if (!content.includes("defineSchema")) {
|
|
1766
1815
|
return { valid: false, error: "Missing defineSchema() call" };
|
|
1767
1816
|
}
|
|
@@ -1783,17 +1832,17 @@ async function validateSchema(schemaPath) {
|
|
|
1783
1832
|
}
|
|
1784
1833
|
async function validateFunctions(functionsDir) {
|
|
1785
1834
|
const errors = [];
|
|
1786
|
-
if (!await
|
|
1835
|
+
if (!await fs4.pathExists(functionsDir)) {
|
|
1787
1836
|
return { valid: true, errors: [] };
|
|
1788
1837
|
}
|
|
1789
|
-
const files = await
|
|
1838
|
+
const files = await fs4.readdir(functionsDir);
|
|
1790
1839
|
for (const file of files) {
|
|
1791
1840
|
if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
|
|
1792
|
-
const filePath =
|
|
1793
|
-
const stat = await
|
|
1841
|
+
const filePath = path4.join(functionsDir, file);
|
|
1842
|
+
const stat = await fs4.stat(filePath);
|
|
1794
1843
|
if (!stat.isFile()) continue;
|
|
1795
1844
|
try {
|
|
1796
|
-
const content = await
|
|
1845
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
1797
1846
|
const hasExports = /export\s+const\s+\w+\s*=\s*(query|mutation)\s*\(/.test(content);
|
|
1798
1847
|
if (!hasExports && !file.includes("index")) {
|
|
1799
1848
|
errors.push(`${file}: No query or mutation exports found`);
|
|
@@ -1912,17 +1961,17 @@ function cleanup() {
|
|
|
1912
1961
|
}
|
|
1913
1962
|
async function devCommand(options) {
|
|
1914
1963
|
const credentials = await requireAuth();
|
|
1915
|
-
const configPath =
|
|
1916
|
-
if (!await
|
|
1964
|
+
const configPath = path4.resolve(process.cwd(), "tether.config.ts");
|
|
1965
|
+
if (!await fs4.pathExists(configPath)) {
|
|
1917
1966
|
console.log(chalk3.red("\nError: Not a Tether project"));
|
|
1918
1967
|
console.log(chalk3.dim("Run `tthr init` to create a new project\n"));
|
|
1919
1968
|
process.exit(1);
|
|
1920
1969
|
}
|
|
1921
1970
|
const config = await loadConfig();
|
|
1922
1971
|
if (!config.projectId) {
|
|
1923
|
-
const envPath =
|
|
1924
|
-
if (await
|
|
1925
|
-
const envContent = await
|
|
1972
|
+
const envPath = path4.resolve(process.cwd(), ".env");
|
|
1973
|
+
if (await fs4.pathExists(envPath)) {
|
|
1974
|
+
const envContent = await fs4.readFile(envPath, "utf-8");
|
|
1926
1975
|
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
1927
1976
|
if (match) config.projectId = match[1].trim();
|
|
1928
1977
|
}
|
|
@@ -1944,9 +1993,9 @@ async function devCommand(options) {
|
|
|
1944
1993
|
console.log(chalk3.dim(` Dev command: ${devCmd}`));
|
|
1945
1994
|
}
|
|
1946
1995
|
console.log(chalk3.dim(` Environment: ${environment}${isLocal ? " (local)" : " (cloud)"}`));
|
|
1947
|
-
console.log(chalk3.dim(` Schema: ${
|
|
1948
|
-
console.log(chalk3.dim(` Functions: ${
|
|
1949
|
-
console.log(chalk3.dim(` Output: ${
|
|
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)}`));
|
|
1950
1999
|
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
1951
2000
|
console.log(chalk3.dim(` Extra args: ${options.extraArgs.join(" ")}`));
|
|
1952
2001
|
}
|
|
@@ -1987,7 +2036,7 @@ async function devCommand(options) {
|
|
|
1987
2036
|
spinner.warn("No project ID configured \u2014 skipping auto-deploy");
|
|
1988
2037
|
}
|
|
1989
2038
|
spinner.start("Setting up file watchers...");
|
|
1990
|
-
if (await
|
|
2039
|
+
if (await fs4.pathExists(schemaPath)) {
|
|
1991
2040
|
schemaWatcher = watch(schemaPath, {
|
|
1992
2041
|
ignoreInitial: true,
|
|
1993
2042
|
awaitWriteFinish: {
|
|
@@ -2008,8 +2057,8 @@ async function devCommand(options) {
|
|
|
2008
2057
|
}
|
|
2009
2058
|
});
|
|
2010
2059
|
}
|
|
2011
|
-
if (await
|
|
2012
|
-
functionsWatcher = watch(
|
|
2060
|
+
if (await fs4.pathExists(functionsDir)) {
|
|
2061
|
+
functionsWatcher = watch(path4.join(functionsDir, "**/*.{ts,js}"), {
|
|
2013
2062
|
ignoreInitial: true,
|
|
2014
2063
|
awaitWriteFinish: {
|
|
2015
2064
|
stabilityThreshold: 300,
|
|
@@ -2017,7 +2066,7 @@ async function devCommand(options) {
|
|
|
2017
2066
|
}
|
|
2018
2067
|
});
|
|
2019
2068
|
functionsWatcher.on("all", async (event, filePath) => {
|
|
2020
|
-
const relativePath =
|
|
2069
|
+
const relativePath = path4.relative(functionsDir, filePath);
|
|
2021
2070
|
console.log(chalk3.cyan(`
|
|
2022
2071
|
Function ${event}: ${relativePath}`));
|
|
2023
2072
|
const validation = await validateFunctions(functionsDir);
|
|
@@ -2072,7 +2121,8 @@ async function loginCommand() {
|
|
|
2072
2121
|
console.log(chalk4.dim("\nRun `tthr logout` to sign out\n"));
|
|
2073
2122
|
return;
|
|
2074
2123
|
}
|
|
2075
|
-
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
|
|
2076
2126
|
`));
|
|
2077
2127
|
const spinner = ora4("Generating authentication code...").start();
|
|
2078
2128
|
try {
|
|
@@ -2247,8 +2297,8 @@ function openBrowser(url) {
|
|
|
2247
2297
|
// src/commands/update.ts
|
|
2248
2298
|
import chalk5 from "chalk";
|
|
2249
2299
|
import ora5 from "ora";
|
|
2250
|
-
import
|
|
2251
|
-
import
|
|
2300
|
+
import fs5 from "fs-extra";
|
|
2301
|
+
import path5 from "path";
|
|
2252
2302
|
import { execSync as execSync2 } from "child_process";
|
|
2253
2303
|
var TETHER_PACKAGES = [
|
|
2254
2304
|
"@tthr/client",
|
|
@@ -2260,21 +2310,21 @@ var TETHER_PACKAGES = [
|
|
|
2260
2310
|
];
|
|
2261
2311
|
function detectPackageManager() {
|
|
2262
2312
|
let dir = process.cwd();
|
|
2263
|
-
const root =
|
|
2313
|
+
const root = path5.parse(dir).root;
|
|
2264
2314
|
while (dir !== root) {
|
|
2265
|
-
if (
|
|
2315
|
+
if (fs5.existsSync(path5.join(dir, "bun.lockb")) || fs5.existsSync(path5.join(dir, "bun.lock"))) {
|
|
2266
2316
|
return "bun";
|
|
2267
2317
|
}
|
|
2268
|
-
if (
|
|
2318
|
+
if (fs5.existsSync(path5.join(dir, "pnpm-lock.yaml"))) {
|
|
2269
2319
|
return "pnpm";
|
|
2270
2320
|
}
|
|
2271
|
-
if (
|
|
2321
|
+
if (fs5.existsSync(path5.join(dir, "yarn.lock"))) {
|
|
2272
2322
|
return "yarn";
|
|
2273
2323
|
}
|
|
2274
|
-
if (
|
|
2324
|
+
if (fs5.existsSync(path5.join(dir, "package-lock.json"))) {
|
|
2275
2325
|
return "npm";
|
|
2276
2326
|
}
|
|
2277
|
-
dir =
|
|
2327
|
+
dir = path5.dirname(dir);
|
|
2278
2328
|
}
|
|
2279
2329
|
return "npm";
|
|
2280
2330
|
}
|
|
@@ -2303,14 +2353,14 @@ async function getLatestVersion2(packageName) {
|
|
|
2303
2353
|
}
|
|
2304
2354
|
}
|
|
2305
2355
|
async function updateCommand(options) {
|
|
2306
|
-
const packageJsonPath =
|
|
2307
|
-
if (!await
|
|
2356
|
+
const packageJsonPath = path5.resolve(process.cwd(), "package.json");
|
|
2357
|
+
if (!await fs5.pathExists(packageJsonPath)) {
|
|
2308
2358
|
console.log(chalk5.red("\nError: No package.json found"));
|
|
2309
2359
|
console.log(chalk5.dim("Make sure you're in the root of your project\n"));
|
|
2310
2360
|
process.exit(1);
|
|
2311
2361
|
}
|
|
2312
2362
|
console.log(chalk5.bold("\n\u26A1 Updating Tether packages\n"));
|
|
2313
|
-
const packageJson = await
|
|
2363
|
+
const packageJson = await fs5.readJson(packageJsonPath);
|
|
2314
2364
|
const deps = packageJson.dependencies || {};
|
|
2315
2365
|
const devDeps = packageJson.devDependencies || {};
|
|
2316
2366
|
const installedDeps = [];
|
|
@@ -2396,8 +2446,8 @@ async function updateCommand(options) {
|
|
|
2396
2446
|
// src/commands/exec.ts
|
|
2397
2447
|
import chalk6 from "chalk";
|
|
2398
2448
|
import ora6 from "ora";
|
|
2399
|
-
import
|
|
2400
|
-
import
|
|
2449
|
+
import fs6 from "fs-extra";
|
|
2450
|
+
import path6 from "path";
|
|
2401
2451
|
async function execCommand(sql) {
|
|
2402
2452
|
if (!sql || sql.trim() === "") {
|
|
2403
2453
|
console.log(chalk6.red("\nError: SQL query required"));
|
|
@@ -2405,10 +2455,10 @@ async function execCommand(sql) {
|
|
|
2405
2455
|
process.exit(1);
|
|
2406
2456
|
}
|
|
2407
2457
|
const credentials = await requireAuth();
|
|
2408
|
-
const envPath =
|
|
2458
|
+
const envPath = path6.resolve(process.cwd(), ".env");
|
|
2409
2459
|
let projectId;
|
|
2410
|
-
if (await
|
|
2411
|
-
const envContent = await
|
|
2460
|
+
if (await fs6.pathExists(envPath)) {
|
|
2461
|
+
const envContent = await fs6.readFile(envPath, "utf-8");
|
|
2412
2462
|
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
2413
2463
|
projectId = match?.[1]?.trim();
|
|
2414
2464
|
}
|
|
@@ -2469,17 +2519,17 @@ async function execCommand(sql) {
|
|
|
2469
2519
|
// src/commands/env.ts
|
|
2470
2520
|
import chalk7 from "chalk";
|
|
2471
2521
|
import ora7 from "ora";
|
|
2472
|
-
import
|
|
2473
|
-
import
|
|
2522
|
+
import fs7 from "fs-extra";
|
|
2523
|
+
import path7 from "path";
|
|
2474
2524
|
async function resolveApiUrl2() {
|
|
2475
2525
|
const config = await loadConfig();
|
|
2476
2526
|
return `${getApiUrl2(config)}/api/v1`;
|
|
2477
2527
|
}
|
|
2478
2528
|
async function getProjectId() {
|
|
2479
|
-
const envPath =
|
|
2529
|
+
const envPath = path7.resolve(process.cwd(), ".env");
|
|
2480
2530
|
let projectId;
|
|
2481
|
-
if (await
|
|
2482
|
-
const envContent = await
|
|
2531
|
+
if (await fs7.pathExists(envPath)) {
|
|
2532
|
+
const envContent = await fs7.readFile(envPath, "utf-8");
|
|
2483
2533
|
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
2484
2534
|
projectId = match?.[1]?.trim();
|
|
2485
2535
|
}
|
|
@@ -2636,15 +2686,15 @@ function maskApiKey(key) {
|
|
|
2636
2686
|
// src/commands/migrate.ts
|
|
2637
2687
|
import chalk8 from "chalk";
|
|
2638
2688
|
import ora8 from "ora";
|
|
2639
|
-
import
|
|
2640
|
-
import
|
|
2689
|
+
import fs8 from "fs-extra";
|
|
2690
|
+
import path8 from "path";
|
|
2641
2691
|
import readline2 from "readline";
|
|
2642
2692
|
async function migrateSystemColumnsCommand(options) {
|
|
2643
2693
|
const credentials = await requireAuth();
|
|
2644
|
-
const envPath =
|
|
2694
|
+
const envPath = path8.resolve(process.cwd(), ".env");
|
|
2645
2695
|
let projectId;
|
|
2646
|
-
if (await
|
|
2647
|
-
const envContent = await
|
|
2696
|
+
if (await fs8.pathExists(envPath)) {
|
|
2697
|
+
const envContent = await fs8.readFile(envPath, "utf-8");
|
|
2648
2698
|
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
2649
2699
|
projectId = match?.[1]?.trim();
|
|
2650
2700
|
}
|
|
@@ -2782,9 +2832,10 @@ function askConfirmation(prompt) {
|
|
|
2782
2832
|
}
|
|
2783
2833
|
|
|
2784
2834
|
// src/index.ts
|
|
2835
|
+
loadDotenv();
|
|
2785
2836
|
var __filename = fileURLToPath(import.meta.url);
|
|
2786
|
-
var __dirname =
|
|
2787
|
-
var pkg =
|
|
2837
|
+
var __dirname = path9.dirname(__filename);
|
|
2838
|
+
var pkg = fs9.readJsonSync(path9.resolve(__dirname, "../package.json"));
|
|
2788
2839
|
var program = new Command();
|
|
2789
2840
|
program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version(pkg.version).enablePositionalOptions();
|
|
2790
2841
|
program.command("init [name]").description("Create a new Tether project").option("-t, --template <template>", "Project template (vue, svelte, react, vanilla)", "vue").action(initCommand);
|