tthr 0.3.6 → 0.3.8
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/chunk-FUIPVG5Y.js +692 -0
- package/dist/generate-OKE6YGA3.js +8 -0
- package/dist/index.js +83 -25
- package/package.json +1 -1
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
// src/commands/generate.ts
|
|
2
|
+
import chalk2 from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import fs3 from "fs-extra";
|
|
5
|
+
import path3 from "path";
|
|
6
|
+
|
|
7
|
+
// src/utils/auth.ts
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import path from "path";
|
|
11
|
+
var CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || "", ".tether");
|
|
12
|
+
var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
|
|
13
|
+
var isDev = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
|
|
14
|
+
var API_URL = isDev ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
|
|
15
|
+
var REFRESH_THRESHOLD_DAYS = 7;
|
|
16
|
+
async function getCredentials() {
|
|
17
|
+
try {
|
|
18
|
+
if (await fs.pathExists(CREDENTIALS_FILE)) {
|
|
19
|
+
return await fs.readJSON(CREDENTIALS_FILE);
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
async function refreshSession(credentials) {
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(`${API_URL}/auth/session/refresh`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
"Authorization": `Bearer ${credentials.accessToken}`
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const data = await response.json();
|
|
38
|
+
const updated = {
|
|
39
|
+
...credentials,
|
|
40
|
+
expiresAt: data.expiresAt
|
|
41
|
+
};
|
|
42
|
+
await saveCredentials(updated);
|
|
43
|
+
return updated;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function requireAuth() {
|
|
49
|
+
const credentials = await getCredentials();
|
|
50
|
+
if (!credentials) {
|
|
51
|
+
console.error(chalk.red("\n\u2717 Not logged in\n"));
|
|
52
|
+
console.log(chalk.dim("Run `tthr login` to authenticate\n"));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (credentials.expiresAt) {
|
|
56
|
+
const expiresAt = new Date(credentials.expiresAt);
|
|
57
|
+
const now = /* @__PURE__ */ new Date();
|
|
58
|
+
if (now > expiresAt) {
|
|
59
|
+
await clearCredentials();
|
|
60
|
+
console.error(chalk.red("\n\u2717 Session expired \u2014 you have been signed out\n"));
|
|
61
|
+
console.log(chalk.dim("Run `tthr login` to authenticate\n"));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const daysUntilExpiry = (expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24);
|
|
65
|
+
if (daysUntilExpiry <= REFRESH_THRESHOLD_DAYS) {
|
|
66
|
+
const refreshed = await refreshSession(credentials);
|
|
67
|
+
if (refreshed) {
|
|
68
|
+
return refreshed;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return credentials;
|
|
73
|
+
}
|
|
74
|
+
async function saveCredentials(credentials) {
|
|
75
|
+
await fs.ensureDir(CONFIG_DIR);
|
|
76
|
+
await fs.writeJSON(CREDENTIALS_FILE, credentials, { spaces: 2, mode: 384 });
|
|
77
|
+
}
|
|
78
|
+
async function clearCredentials() {
|
|
79
|
+
try {
|
|
80
|
+
await fs.remove(CREDENTIALS_FILE);
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/utils/config.ts
|
|
86
|
+
import fs2 from "fs-extra";
|
|
87
|
+
import path2 from "path";
|
|
88
|
+
var DEFAULT_CONFIG = {
|
|
89
|
+
schema: "./tether/schema.ts",
|
|
90
|
+
functions: "./tether/functions",
|
|
91
|
+
output: "./tether/_generated",
|
|
92
|
+
dev: {
|
|
93
|
+
port: 3001,
|
|
94
|
+
host: "localhost"
|
|
95
|
+
},
|
|
96
|
+
database: {
|
|
97
|
+
walMode: true
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
101
|
+
const configPath = path2.resolve(cwd, "tether.config.ts");
|
|
102
|
+
if (!await fs2.pathExists(configPath)) {
|
|
103
|
+
return DEFAULT_CONFIG;
|
|
104
|
+
}
|
|
105
|
+
const configSource = await fs2.readFile(configPath, "utf-8");
|
|
106
|
+
const config = {};
|
|
107
|
+
const schemaMatch = configSource.match(/schema\s*:\s*['"]([^'"]+)['"]/);
|
|
108
|
+
if (schemaMatch) {
|
|
109
|
+
config.schema = schemaMatch[1];
|
|
110
|
+
}
|
|
111
|
+
const functionsMatch = configSource.match(/functions\s*:\s*['"]([^'"]+)['"]/);
|
|
112
|
+
if (functionsMatch) {
|
|
113
|
+
config.functions = functionsMatch[1];
|
|
114
|
+
}
|
|
115
|
+
const outputMatch = configSource.match(/output\s*:\s*['"]([^'"]+)['"]/);
|
|
116
|
+
if (outputMatch) {
|
|
117
|
+
config.output = outputMatch[1];
|
|
118
|
+
}
|
|
119
|
+
const projectIdMatch = configSource.match(/projectId\s*:\s*['"]([^'"]+)['"]/);
|
|
120
|
+
if (projectIdMatch) {
|
|
121
|
+
config.projectId = projectIdMatch[1];
|
|
122
|
+
}
|
|
123
|
+
const urlMatch = configSource.match(/(?:^|\n)\s*url\s*:\s*['"]([^'"]+)['"]/);
|
|
124
|
+
if (urlMatch) {
|
|
125
|
+
config.url = urlMatch[1];
|
|
126
|
+
}
|
|
127
|
+
const envMatch = configSource.match(/environment\s*:\s*['"]([^'"]+)['"]/);
|
|
128
|
+
if (envMatch) {
|
|
129
|
+
config.environment = envMatch[1];
|
|
130
|
+
}
|
|
131
|
+
const portMatch = configSource.match(/port\s*:\s*(\d+)/);
|
|
132
|
+
if (portMatch) {
|
|
133
|
+
config.dev = { ...config.dev, port: parseInt(portMatch[1], 10) };
|
|
134
|
+
}
|
|
135
|
+
const hostMatch = configSource.match(/host\s*:\s*['"]([^'"]+)['"]/);
|
|
136
|
+
if (hostMatch) {
|
|
137
|
+
config.dev = { ...config.dev, host: hostMatch[1] };
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
...DEFAULT_CONFIG,
|
|
141
|
+
...config,
|
|
142
|
+
dev: { ...DEFAULT_CONFIG.dev, ...config.dev },
|
|
143
|
+
database: { ...DEFAULT_CONFIG.database, ...config.database }
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function resolvePath(configPath, cwd = process.cwd()) {
|
|
147
|
+
const normalised = configPath.replace(/^\.\//, "");
|
|
148
|
+
return path2.resolve(cwd, normalised);
|
|
149
|
+
}
|
|
150
|
+
async function detectFramework(cwd = process.cwd()) {
|
|
151
|
+
const packageJsonPath = path2.resolve(cwd, "package.json");
|
|
152
|
+
if (!await fs2.pathExists(packageJsonPath)) {
|
|
153
|
+
return "unknown";
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const packageJson = await fs2.readJson(packageJsonPath);
|
|
157
|
+
const deps = {
|
|
158
|
+
...packageJson.dependencies,
|
|
159
|
+
...packageJson.devDependencies
|
|
160
|
+
};
|
|
161
|
+
if (deps.nuxt || deps["@nuxt/kit"]) {
|
|
162
|
+
return "nuxt";
|
|
163
|
+
}
|
|
164
|
+
if (deps.next) {
|
|
165
|
+
return "next";
|
|
166
|
+
}
|
|
167
|
+
if (deps["@sveltejs/kit"]) {
|
|
168
|
+
return "sveltekit";
|
|
169
|
+
}
|
|
170
|
+
if (deps.vite && !deps.nuxt && !deps.next && !deps["@sveltejs/kit"]) {
|
|
171
|
+
return "vite";
|
|
172
|
+
}
|
|
173
|
+
return "vanilla";
|
|
174
|
+
} catch {
|
|
175
|
+
return "unknown";
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function getFrameworkDevCommand(framework) {
|
|
179
|
+
switch (framework) {
|
|
180
|
+
case "nuxt":
|
|
181
|
+
return "nuxt dev";
|
|
182
|
+
case "next":
|
|
183
|
+
return "next dev";
|
|
184
|
+
case "sveltekit":
|
|
185
|
+
return "vite dev";
|
|
186
|
+
case "vite":
|
|
187
|
+
return "vite dev";
|
|
188
|
+
case "vanilla":
|
|
189
|
+
return null;
|
|
190
|
+
// No default dev server for vanilla
|
|
191
|
+
default:
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/commands/generate.ts
|
|
197
|
+
var isDev2 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
|
|
198
|
+
var API_URL2 = isDev2 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
|
|
199
|
+
function parseSchemaFile(source) {
|
|
200
|
+
const tables = [];
|
|
201
|
+
const schemaMatch = source.match(/defineSchema\s*\(\s*\{([\s\S]*)\}\s*\)/);
|
|
202
|
+
if (!schemaMatch) return tables;
|
|
203
|
+
const schemaContent = schemaMatch[1];
|
|
204
|
+
const tableStartRegex = /(\w+)\s*:\s*\{/g;
|
|
205
|
+
let match;
|
|
206
|
+
while ((match = tableStartRegex.exec(schemaContent)) !== null) {
|
|
207
|
+
const tableName = match[1];
|
|
208
|
+
const startOffset = match.index + match[0].length;
|
|
209
|
+
let braceCount = 1;
|
|
210
|
+
let endOffset = startOffset;
|
|
211
|
+
for (let i = startOffset; i < schemaContent.length && braceCount > 0; i++) {
|
|
212
|
+
const char = schemaContent[i];
|
|
213
|
+
if (char === "{") braceCount++;
|
|
214
|
+
else if (char === "}") braceCount--;
|
|
215
|
+
endOffset = i;
|
|
216
|
+
}
|
|
217
|
+
const columnsContent = schemaContent.slice(startOffset, endOffset);
|
|
218
|
+
const columns = parseColumns(columnsContent);
|
|
219
|
+
tables.push({ name: tableName, columns });
|
|
220
|
+
}
|
|
221
|
+
return tables;
|
|
222
|
+
}
|
|
223
|
+
function parseColumns(content) {
|
|
224
|
+
const columns = [];
|
|
225
|
+
const columnRegex = /(\w+)\s*:\s*(\w+)(?:<([^>]+)>)?\s*\(\s*\)((?:\[.*?\]|[^,\n}])*)/g;
|
|
226
|
+
let match;
|
|
227
|
+
while ((match = columnRegex.exec(content)) !== null) {
|
|
228
|
+
const name = match[1];
|
|
229
|
+
const schemaType = match[2];
|
|
230
|
+
const genericType = match[3];
|
|
231
|
+
const modifiers = match[4] || "";
|
|
232
|
+
const nullable = !modifiers.includes(".notNull()");
|
|
233
|
+
const oneOfMatch = modifiers.match(/\.oneOf\s*\(\s*\[(.*?)\]\s*\)/);
|
|
234
|
+
let oneOf;
|
|
235
|
+
if (oneOfMatch) {
|
|
236
|
+
oneOf = [...oneOfMatch[1].matchAll(/['"]([^'"]+)['"]/g)].map((m) => m[1]);
|
|
237
|
+
}
|
|
238
|
+
columns.push({
|
|
239
|
+
name,
|
|
240
|
+
type: schemaTypeToTS(schemaType),
|
|
241
|
+
nullable,
|
|
242
|
+
jsonType: genericType,
|
|
243
|
+
// Store the generic type for json columns
|
|
244
|
+
oneOf
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return columns;
|
|
248
|
+
}
|
|
249
|
+
function schemaTypeToTS(schemaType) {
|
|
250
|
+
switch (schemaType) {
|
|
251
|
+
case "text":
|
|
252
|
+
return "string";
|
|
253
|
+
case "integer":
|
|
254
|
+
return "number";
|
|
255
|
+
case "real":
|
|
256
|
+
return "number";
|
|
257
|
+
case "boolean":
|
|
258
|
+
return "boolean";
|
|
259
|
+
case "timestamp":
|
|
260
|
+
return "string";
|
|
261
|
+
case "json":
|
|
262
|
+
return "unknown";
|
|
263
|
+
case "blob":
|
|
264
|
+
return "Uint8Array";
|
|
265
|
+
case "asset":
|
|
266
|
+
return "TetherAsset";
|
|
267
|
+
// Asset object returned by API
|
|
268
|
+
default:
|
|
269
|
+
return "unknown";
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function extractSingleType(source, typeName) {
|
|
273
|
+
const interfaceStart = new RegExp(`(?:export\\s+)?interface\\s+${typeName}\\s*\\{`);
|
|
274
|
+
const interfaceMatch = interfaceStart.exec(source);
|
|
275
|
+
if (interfaceMatch) {
|
|
276
|
+
const braceStart = interfaceMatch.index + interfaceMatch[0].length;
|
|
277
|
+
let braceCount = 1;
|
|
278
|
+
let endIdx = braceStart;
|
|
279
|
+
for (let i = braceStart; i < source.length && braceCount > 0; i++) {
|
|
280
|
+
if (source[i] === "{") braceCount++;
|
|
281
|
+
else if (source[i] === "}") braceCount--;
|
|
282
|
+
endIdx = i;
|
|
283
|
+
}
|
|
284
|
+
return source.slice(interfaceMatch.index, endIdx + 1).replace(/^export\s+/, "");
|
|
285
|
+
}
|
|
286
|
+
const typeStart = new RegExp(`(?:export\\s+)?type\\s+${typeName}\\s*=`);
|
|
287
|
+
const typeMatch = typeStart.exec(source);
|
|
288
|
+
if (typeMatch) {
|
|
289
|
+
const semiIdx = source.indexOf(";", typeMatch.index);
|
|
290
|
+
if (semiIdx !== -1) {
|
|
291
|
+
return source.slice(typeMatch.index, semiIdx + 1).replace(/^export\s+/, "");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
function extractTypeDefinitions(source, typeNames) {
|
|
297
|
+
if (typeNames.size === 0) return [];
|
|
298
|
+
const allDefinedTypes = /* @__PURE__ */ new Set();
|
|
299
|
+
const typeDefRegex = /(?:export\s+)?(?:interface|type)\s+(\w+)/g;
|
|
300
|
+
let m;
|
|
301
|
+
while ((m = typeDefRegex.exec(source)) !== null) {
|
|
302
|
+
allDefinedTypes.add(m[1]);
|
|
303
|
+
}
|
|
304
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
305
|
+
const queue = [...typeNames];
|
|
306
|
+
while (queue.length > 0) {
|
|
307
|
+
const name = queue.shift();
|
|
308
|
+
if (resolved.has(name)) continue;
|
|
309
|
+
const block = extractSingleType(source, name);
|
|
310
|
+
if (!block) continue;
|
|
311
|
+
resolved.set(name, block);
|
|
312
|
+
for (const definedType of allDefinedTypes) {
|
|
313
|
+
if (!resolved.has(definedType) && !queue.includes(definedType)) {
|
|
314
|
+
const refRegex = new RegExp(`\\b${definedType}\\b`);
|
|
315
|
+
if (refRegex.test(block)) {
|
|
316
|
+
queue.push(definedType);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const ordered = [];
|
|
322
|
+
const visited = /* @__PURE__ */ new Set();
|
|
323
|
+
function visit(name) {
|
|
324
|
+
if (visited.has(name)) return;
|
|
325
|
+
visited.add(name);
|
|
326
|
+
const block = resolved.get(name);
|
|
327
|
+
if (!block) return;
|
|
328
|
+
for (const dep of resolved.keys()) {
|
|
329
|
+
if (dep !== name && new RegExp(`\\b${dep}\\b`).test(block)) {
|
|
330
|
+
visit(dep);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
ordered.push(name);
|
|
334
|
+
}
|
|
335
|
+
for (const name of resolved.keys()) {
|
|
336
|
+
visit(name);
|
|
337
|
+
}
|
|
338
|
+
return ordered.map((name) => resolved.get(name));
|
|
339
|
+
}
|
|
340
|
+
function tableNameToInterface(tableName) {
|
|
341
|
+
let name = tableName;
|
|
342
|
+
if (name.endsWith("ies")) {
|
|
343
|
+
name = name.slice(0, -3) + "y";
|
|
344
|
+
} else if (name.endsWith("s") && !name.endsWith("ss")) {
|
|
345
|
+
name = name.slice(0, -1);
|
|
346
|
+
}
|
|
347
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
348
|
+
}
|
|
349
|
+
function generateDbFile(tables, schemaSource) {
|
|
350
|
+
const TS_PRIMITIVES = /* @__PURE__ */ new Set(["number", "string", "boolean", "unknown", "any", "void", "never", "undefined", "null", "object"]);
|
|
351
|
+
const jsonTypes = /* @__PURE__ */ new Set();
|
|
352
|
+
for (const table of tables) {
|
|
353
|
+
for (const col of table.columns) {
|
|
354
|
+
if (col.jsonType) {
|
|
355
|
+
const baseType = col.jsonType.replace(/\s*\|\s*null/g, "").replace(/\[\]/g, "").trim();
|
|
356
|
+
if (!TS_PRIMITIVES.has(baseType)) {
|
|
357
|
+
jsonTypes.add(baseType);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const lines = [
|
|
363
|
+
"// Auto-generated by Tether CLI - do not edit manually",
|
|
364
|
+
`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
365
|
+
"",
|
|
366
|
+
"import { createDatabaseProxy, type TetherDatabase } from '@tthr/client';",
|
|
367
|
+
"import {",
|
|
368
|
+
" type QueryDefinition,",
|
|
369
|
+
" type MutationDefinition,",
|
|
370
|
+
" z,",
|
|
371
|
+
"} from '@tthr/server';",
|
|
372
|
+
"import type { TetherEnv } from './env';"
|
|
373
|
+
];
|
|
374
|
+
if (jsonTypes.size > 0) {
|
|
375
|
+
const typeBlocks = extractTypeDefinitions(schemaSource, jsonTypes);
|
|
376
|
+
if (typeBlocks.length > 0) {
|
|
377
|
+
lines.push("");
|
|
378
|
+
lines.push("// Schema types (inlined from schema.ts)");
|
|
379
|
+
for (const block of typeBlocks) {
|
|
380
|
+
lines.push(block);
|
|
381
|
+
lines.push("");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
lines.push("");
|
|
386
|
+
lines.push("// Asset type returned by the API for asset columns");
|
|
387
|
+
lines.push("export interface TetherAsset {");
|
|
388
|
+
lines.push(" id: string;");
|
|
389
|
+
lines.push(" filename: string;");
|
|
390
|
+
lines.push(" contentType: string;");
|
|
391
|
+
lines.push(" size: number;");
|
|
392
|
+
lines.push(" url: string;");
|
|
393
|
+
lines.push(" createdAt: string;");
|
|
394
|
+
lines.push("}");
|
|
395
|
+
lines.push("");
|
|
396
|
+
for (const table of tables) {
|
|
397
|
+
const interfaceName = tableNameToInterface(table.name);
|
|
398
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
399
|
+
lines.push(" _id: string;");
|
|
400
|
+
for (const col of table.columns) {
|
|
401
|
+
let colType;
|
|
402
|
+
if (col.oneOf && col.oneOf.length > 0) {
|
|
403
|
+
colType = col.oneOf.map((v) => `'${v}'`).join(" | ");
|
|
404
|
+
} else {
|
|
405
|
+
colType = col.jsonType || col.type;
|
|
406
|
+
}
|
|
407
|
+
const typeStr = col.nullable ? `${colType} | null` : colType;
|
|
408
|
+
const optional = col.nullable ? "?" : "";
|
|
409
|
+
lines.push(` ${col.name}${optional}: ${typeStr};`);
|
|
410
|
+
}
|
|
411
|
+
lines.push(" _createdAt: string;");
|
|
412
|
+
lines.push(" _updatedAt?: string | null;");
|
|
413
|
+
lines.push(" _deletedAt?: string | null;");
|
|
414
|
+
lines.push("}");
|
|
415
|
+
lines.push("");
|
|
416
|
+
}
|
|
417
|
+
lines.push("export interface Schema {");
|
|
418
|
+
for (const table of tables) {
|
|
419
|
+
const interfaceName = tableNameToInterface(table.name);
|
|
420
|
+
lines.push(` ${table.name}: ${interfaceName};`);
|
|
421
|
+
}
|
|
422
|
+
lines.push("}");
|
|
423
|
+
lines.push("");
|
|
424
|
+
lines.push("// Database client with typed tables");
|
|
425
|
+
lines.push("// This is a proxy that will be populated by the Tether runtime");
|
|
426
|
+
lines.push("export const db: TetherDatabase<Schema> = createDatabaseProxy<Schema>();");
|
|
427
|
+
lines.push("");
|
|
428
|
+
lines.push("// ============================================================================");
|
|
429
|
+
lines.push("// Typed function wrappers - use these instead of importing from @tthr/server");
|
|
430
|
+
lines.push("// This ensures the `db` and `env` parameters in handlers are properly typed");
|
|
431
|
+
lines.push("// ============================================================================");
|
|
432
|
+
lines.push("");
|
|
433
|
+
lines.push("/**");
|
|
434
|
+
lines.push(" * Define a query function with typed database and environment variable access.");
|
|
435
|
+
lines.push(" * The `db` and `env` parameters in the handler will have full type safety.");
|
|
436
|
+
lines.push(" */");
|
|
437
|
+
lines.push("export function query<TArgs = void, TResult = unknown>(");
|
|
438
|
+
lines.push(" definition: QueryDefinition<TArgs, TResult, Schema, TetherEnv>");
|
|
439
|
+
lines.push("): QueryDefinition<TArgs, TResult, Schema, TetherEnv> {");
|
|
440
|
+
lines.push(" return definition;");
|
|
441
|
+
lines.push("}");
|
|
442
|
+
lines.push("");
|
|
443
|
+
lines.push("/**");
|
|
444
|
+
lines.push(" * Define a mutation function with typed database and environment variable access.");
|
|
445
|
+
lines.push(" * The `db` and `env` parameters in the handler will have full type safety.");
|
|
446
|
+
lines.push(" */");
|
|
447
|
+
lines.push("export function mutation<TArgs = void, TResult = unknown>(");
|
|
448
|
+
lines.push(" definition: MutationDefinition<TArgs, TResult, Schema, TetherEnv>");
|
|
449
|
+
lines.push("): MutationDefinition<TArgs, TResult, Schema, TetherEnv> {");
|
|
450
|
+
lines.push(" return definition;");
|
|
451
|
+
lines.push("}");
|
|
452
|
+
lines.push("");
|
|
453
|
+
lines.push("// Re-export z for convenience");
|
|
454
|
+
lines.push("export { z };");
|
|
455
|
+
lines.push("");
|
|
456
|
+
return lines.join("\n");
|
|
457
|
+
}
|
|
458
|
+
async function parseFunctionsDir(functionsDir) {
|
|
459
|
+
const functions = [];
|
|
460
|
+
if (!await fs3.pathExists(functionsDir)) {
|
|
461
|
+
return functions;
|
|
462
|
+
}
|
|
463
|
+
const files = await fs3.readdir(functionsDir);
|
|
464
|
+
for (const file of files) {
|
|
465
|
+
if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
|
|
466
|
+
const filePath = path3.join(functionsDir, file);
|
|
467
|
+
const stat = await fs3.stat(filePath);
|
|
468
|
+
if (!stat.isFile()) continue;
|
|
469
|
+
const moduleName = file.replace(/\.(ts|js)$/, "");
|
|
470
|
+
const source = await fs3.readFile(filePath, "utf-8");
|
|
471
|
+
const exportRegex = /export\s+const\s+(\w+)\s*=\s*(query|mutation)\s*\(/g;
|
|
472
|
+
let match;
|
|
473
|
+
while ((match = exportRegex.exec(source)) !== null) {
|
|
474
|
+
functions.push({
|
|
475
|
+
name: match[1],
|
|
476
|
+
moduleName
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return functions;
|
|
481
|
+
}
|
|
482
|
+
async function generateApiFile(functionsDir) {
|
|
483
|
+
const functions = await parseFunctionsDir(functionsDir);
|
|
484
|
+
const moduleMap = /* @__PURE__ */ new Map();
|
|
485
|
+
for (const fn of functions) {
|
|
486
|
+
if (!moduleMap.has(fn.moduleName)) {
|
|
487
|
+
moduleMap.set(fn.moduleName, []);
|
|
488
|
+
}
|
|
489
|
+
moduleMap.get(fn.moduleName).push(fn.name);
|
|
490
|
+
}
|
|
491
|
+
const lines = [
|
|
492
|
+
"// Auto-generated by Tether CLI - do not edit manually",
|
|
493
|
+
`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
494
|
+
"",
|
|
495
|
+
"import { createApiProxy } from '@tthr/client';",
|
|
496
|
+
"",
|
|
497
|
+
"/**",
|
|
498
|
+
" * API function reference type for useQuery/useMutation.",
|
|
499
|
+
' * The _name property contains the function path (e.g., "users.list").',
|
|
500
|
+
" */",
|
|
501
|
+
"export interface ApiFunction<TArgs = unknown, TResult = unknown> {",
|
|
502
|
+
" _name: string;",
|
|
503
|
+
" _args?: TArgs;",
|
|
504
|
+
" _result?: TResult;",
|
|
505
|
+
"}",
|
|
506
|
+
""
|
|
507
|
+
];
|
|
508
|
+
if (moduleMap.size > 0) {
|
|
509
|
+
for (const [moduleName, fnNames] of moduleMap) {
|
|
510
|
+
const interfaceName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1) + "Api";
|
|
511
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
512
|
+
for (const fnName of fnNames) {
|
|
513
|
+
lines.push(` ${fnName}: ApiFunction;`);
|
|
514
|
+
}
|
|
515
|
+
lines.push("}");
|
|
516
|
+
lines.push("");
|
|
517
|
+
}
|
|
518
|
+
lines.push("export interface Api {");
|
|
519
|
+
for (const moduleName of moduleMap.keys()) {
|
|
520
|
+
const interfaceName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1) + "Api";
|
|
521
|
+
lines.push(` ${moduleName}: ${interfaceName};`);
|
|
522
|
+
}
|
|
523
|
+
lines.push("}");
|
|
524
|
+
} else {
|
|
525
|
+
lines.push("/**");
|
|
526
|
+
lines.push(" * Flexible API type that allows access to any function path.");
|
|
527
|
+
lines.push(" * Functions are accessed as api.moduleName.functionName");
|
|
528
|
+
lines.push(" * The actual types depend on your function definitions.");
|
|
529
|
+
lines.push(" */");
|
|
530
|
+
lines.push("export type Api = {");
|
|
531
|
+
lines.push(" [module: string]: {");
|
|
532
|
+
lines.push(" [fn: string]: ApiFunction;");
|
|
533
|
+
lines.push(" };");
|
|
534
|
+
lines.push("};");
|
|
535
|
+
}
|
|
536
|
+
lines.push("");
|
|
537
|
+
lines.push("// API client proxy - provides typed access to your functions");
|
|
538
|
+
lines.push("// On the client: returns { _name } references for useQuery/useMutation");
|
|
539
|
+
lines.push("// In Tether functions: calls the actual function implementation");
|
|
540
|
+
lines.push("export const api = createApiProxy<Api>();");
|
|
541
|
+
lines.push("");
|
|
542
|
+
return lines.join("\n");
|
|
543
|
+
}
|
|
544
|
+
async function fetchEnvVarKeys(projectId, environment) {
|
|
545
|
+
const credentials = await getCredentials();
|
|
546
|
+
if (!credentials) return [];
|
|
547
|
+
const envPath = environment !== "production" ? `/projects/${projectId}/env/${environment}/env-vars` : `/projects/${projectId}/env-vars`;
|
|
548
|
+
try {
|
|
549
|
+
const response = await fetch(`${API_URL2}${envPath}`, {
|
|
550
|
+
headers: {
|
|
551
|
+
"Authorization": `Bearer ${credentials.accessToken}`
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
if (!response.ok) return [];
|
|
555
|
+
const data = await response.json();
|
|
556
|
+
return (data.envVars || []).map((v) => v.key).sort();
|
|
557
|
+
} catch {
|
|
558
|
+
return [];
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function generateEnvFile(keys) {
|
|
562
|
+
const lines = [
|
|
563
|
+
"// Auto-generated by Tether CLI - do not edit manually",
|
|
564
|
+
`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
565
|
+
"",
|
|
566
|
+
"/**",
|
|
567
|
+
" * Typed environment variables for this project.",
|
|
568
|
+
" * Keys are fetched from the Tether API \u2014 values are never exposed.",
|
|
569
|
+
' * Use `env.get("KEY")` in your functions to access them.',
|
|
570
|
+
" */",
|
|
571
|
+
""
|
|
572
|
+
];
|
|
573
|
+
if (keys.length > 0) {
|
|
574
|
+
lines.push("export type EnvVarKey =");
|
|
575
|
+
for (let i = 0; i < keys.length; i++) {
|
|
576
|
+
const sep = i === keys.length - 1 ? ";" : "";
|
|
577
|
+
lines.push(` | '${keys[i]}'${sep}`);
|
|
578
|
+
}
|
|
579
|
+
lines.push("");
|
|
580
|
+
lines.push("export interface TetherEnv {");
|
|
581
|
+
for (const key of keys) {
|
|
582
|
+
lines.push(` ${key}: string;`);
|
|
583
|
+
}
|
|
584
|
+
lines.push("}");
|
|
585
|
+
} else {
|
|
586
|
+
lines.push("export type EnvVarKey = string;");
|
|
587
|
+
lines.push("");
|
|
588
|
+
lines.push("export interface TetherEnv {");
|
|
589
|
+
lines.push(" [key: string]: string;");
|
|
590
|
+
lines.push("}");
|
|
591
|
+
}
|
|
592
|
+
lines.push("");
|
|
593
|
+
return lines.join("\n");
|
|
594
|
+
}
|
|
595
|
+
async function generateTypes(options = {}) {
|
|
596
|
+
const config = await loadConfig();
|
|
597
|
+
const schemaPath = resolvePath(config.schema);
|
|
598
|
+
const outputDir = resolvePath(config.output);
|
|
599
|
+
const functionsDir = resolvePath(config.functions);
|
|
600
|
+
if (!await fs3.pathExists(schemaPath)) {
|
|
601
|
+
throw new Error(`Schema file not found: ${schemaPath}`);
|
|
602
|
+
}
|
|
603
|
+
const schemaSource = await fs3.readFile(schemaPath, "utf-8");
|
|
604
|
+
const tables = parseSchemaFile(schemaSource);
|
|
605
|
+
const environment = config.environment || "development";
|
|
606
|
+
let projectId;
|
|
607
|
+
const envFilePath = path3.resolve(process.cwd(), ".env");
|
|
608
|
+
if (await fs3.pathExists(envFilePath)) {
|
|
609
|
+
const envContent = await fs3.readFile(envFilePath, "utf-8");
|
|
610
|
+
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
611
|
+
projectId = match?.[1]?.trim();
|
|
612
|
+
}
|
|
613
|
+
const envVarKeys = projectId ? await fetchEnvVarKeys(projectId, environment) : [];
|
|
614
|
+
await fs3.ensureDir(outputDir);
|
|
615
|
+
await fs3.writeFile(
|
|
616
|
+
path3.join(outputDir, "db.ts"),
|
|
617
|
+
generateDbFile(tables, schemaSource)
|
|
618
|
+
);
|
|
619
|
+
await fs3.writeFile(
|
|
620
|
+
path3.join(outputDir, "api.ts"),
|
|
621
|
+
await generateApiFile(functionsDir)
|
|
622
|
+
);
|
|
623
|
+
await fs3.writeFile(
|
|
624
|
+
path3.join(outputDir, "env.ts"),
|
|
625
|
+
generateEnvFile(envVarKeys)
|
|
626
|
+
);
|
|
627
|
+
await fs3.writeFile(
|
|
628
|
+
path3.join(outputDir, "index.ts"),
|
|
629
|
+
`// Auto-generated by Tether CLI - do not edit manually
|
|
630
|
+
export * from './db';
|
|
631
|
+
export * from './api';
|
|
632
|
+
export * from './env';
|
|
633
|
+
`
|
|
634
|
+
);
|
|
635
|
+
return { tables, envVarKeys, outputDir };
|
|
636
|
+
}
|
|
637
|
+
async function generateCommand() {
|
|
638
|
+
await requireAuth();
|
|
639
|
+
const configPath = path3.resolve(process.cwd(), "tether.config.ts");
|
|
640
|
+
if (!await fs3.pathExists(configPath)) {
|
|
641
|
+
console.log(chalk2.red("\nError: Not a Tether project"));
|
|
642
|
+
console.log(chalk2.dim("Run `tthr init` to create a new project\n"));
|
|
643
|
+
process.exit(1);
|
|
644
|
+
}
|
|
645
|
+
console.log(chalk2.bold("\n\u26A1 Generating types from schema\n"));
|
|
646
|
+
const spinner = ora("Reading schema...").start();
|
|
647
|
+
try {
|
|
648
|
+
spinner.text = "Generating types...";
|
|
649
|
+
const { tables, envVarKeys, outputDir } = await generateTypes();
|
|
650
|
+
if (tables.length === 0) {
|
|
651
|
+
spinner.warn("No tables found in schema");
|
|
652
|
+
console.log(chalk2.dim(" Make sure your schema uses defineSchema({ ... })\n"));
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
spinner.succeed(`Types generated for ${tables.length} table(s)`);
|
|
656
|
+
console.log("\n" + chalk2.green("\u2713") + " Tables:");
|
|
657
|
+
for (const table of tables) {
|
|
658
|
+
console.log(chalk2.dim(` - ${table.name} (${table.columns.length} columns)`));
|
|
659
|
+
}
|
|
660
|
+
if (envVarKeys.length > 0) {
|
|
661
|
+
console.log("\n" + chalk2.green("\u2713") + ` Environment variables: ${envVarKeys.length} key(s)`);
|
|
662
|
+
for (const key of envVarKeys) {
|
|
663
|
+
console.log(chalk2.dim(` - ${key}`));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const relativeOutput = path3.relative(process.cwd(), outputDir);
|
|
667
|
+
console.log("\n" + chalk2.green("\u2713") + " Generated files:");
|
|
668
|
+
console.log(chalk2.dim(` ${relativeOutput}/db.ts`));
|
|
669
|
+
console.log(chalk2.dim(` ${relativeOutput}/api.ts`));
|
|
670
|
+
console.log(chalk2.dim(` ${relativeOutput}/env.ts`));
|
|
671
|
+
console.log(chalk2.dim(` ${relativeOutput}/index.ts
|
|
672
|
+
`));
|
|
673
|
+
} catch (error) {
|
|
674
|
+
spinner.fail("Failed to generate types");
|
|
675
|
+
console.error(chalk2.red(error instanceof Error ? error.message : "Unknown error"));
|
|
676
|
+
process.exit(1);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
export {
|
|
681
|
+
API_URL,
|
|
682
|
+
getCredentials,
|
|
683
|
+
requireAuth,
|
|
684
|
+
saveCredentials,
|
|
685
|
+
clearCredentials,
|
|
686
|
+
loadConfig,
|
|
687
|
+
resolvePath,
|
|
688
|
+
detectFramework,
|
|
689
|
+
getFrameworkDevCommand,
|
|
690
|
+
generateTypes,
|
|
691
|
+
generateCommand
|
|
692
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
requireAuth,
|
|
12
12
|
resolvePath,
|
|
13
13
|
saveCredentials
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FUIPVG5Y.js";
|
|
15
15
|
|
|
16
16
|
// src/index.ts
|
|
17
17
|
import { Command } from "commander";
|
|
@@ -43,16 +43,19 @@ async function deployCommand(options) {
|
|
|
43
43
|
console.log(chalk.dim("Run `tthr init` to create a new project\n"));
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
|
-
const
|
|
47
|
-
let projectId;
|
|
48
|
-
if (
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
const config = await loadConfig();
|
|
47
|
+
let projectId = config.projectId;
|
|
48
|
+
if (!projectId) {
|
|
49
|
+
const envPath = path.resolve(process.cwd(), ".env");
|
|
50
|
+
if (await fs.pathExists(envPath)) {
|
|
51
|
+
const envContent = await fs.readFile(envPath, "utf-8");
|
|
52
|
+
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
53
|
+
projectId = match?.[1]?.trim();
|
|
54
|
+
}
|
|
52
55
|
}
|
|
53
56
|
if (!projectId) {
|
|
54
57
|
console.log(chalk.red("\nError: Project ID not found"));
|
|
55
|
-
console.log(chalk.dim("
|
|
58
|
+
console.log(chalk.dim("Set projectId in tether.config.ts or TETHER_PROJECT_ID in your .env file\n"));
|
|
56
59
|
process.exit(1);
|
|
57
60
|
}
|
|
58
61
|
const environment = options.env || "development";
|
|
@@ -68,7 +71,6 @@ async function deployCommand(options) {
|
|
|
68
71
|
} catch (error) {
|
|
69
72
|
console.log(chalk.dim(" Skipping type generation (schema may not exist yet)"));
|
|
70
73
|
}
|
|
71
|
-
const config = await loadConfig();
|
|
72
74
|
const deploySchema = options.schema || !options.schema && !options.functions;
|
|
73
75
|
const deployFunctions = options.functions || !options.schema && !options.functions;
|
|
74
76
|
if (deploySchema) {
|
|
@@ -1224,8 +1226,8 @@ PUBLIC_TETHER_PROJECT_ID=\${TETHER_PROJECT_ID}
|
|
|
1224
1226
|
}
|
|
1225
1227
|
}
|
|
1226
1228
|
}
|
|
1227
|
-
function getInstallCommand(pm,
|
|
1228
|
-
const devFlag =
|
|
1229
|
+
function getInstallCommand(pm, isDev7 = false) {
|
|
1230
|
+
const devFlag = isDev7 ? pm === "npm" ? "-D" : pm === "yarn" ? "-D" : pm === "pnpm" ? "-D" : "-d" : "";
|
|
1229
1231
|
return `${pm} ${pm === "npm" ? "install" : "add"} ${devFlag}`.trim();
|
|
1230
1232
|
}
|
|
1231
1233
|
async function installTetherPackages(projectPath, template, packageManager) {
|
|
@@ -1670,9 +1672,12 @@ import fs3 from "fs-extra";
|
|
|
1670
1672
|
import path3 from "path";
|
|
1671
1673
|
import { spawn } from "child_process";
|
|
1672
1674
|
import { watch } from "chokidar";
|
|
1675
|
+
var isDev3 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
|
|
1676
|
+
var API_BASE = isDev3 ? "http://localhost:3001" : "https://tether-api.strands.gg";
|
|
1673
1677
|
var frameworkProcess = null;
|
|
1674
1678
|
var schemaWatcher = null;
|
|
1675
1679
|
var functionsWatcher = null;
|
|
1680
|
+
var envWs = null;
|
|
1676
1681
|
var isGenerating = false;
|
|
1677
1682
|
var pendingGenerate = false;
|
|
1678
1683
|
var isDeploying = false;
|
|
@@ -1684,7 +1689,7 @@ async function runGenerate(spinner) {
|
|
|
1684
1689
|
}
|
|
1685
1690
|
isGenerating = true;
|
|
1686
1691
|
try {
|
|
1687
|
-
const { generateTypes: generateTypes2 } = await import("./generate-
|
|
1692
|
+
const { generateTypes: generateTypes2 } = await import("./generate-OKE6YGA3.js");
|
|
1688
1693
|
spinner.text = "Regenerating types...";
|
|
1689
1694
|
await generateTypes2({ silent: true });
|
|
1690
1695
|
spinner.succeed("Types regenerated");
|
|
@@ -1791,6 +1796,44 @@ async function validateFunctions(functionsDir) {
|
|
|
1791
1796
|
}
|
|
1792
1797
|
return { valid: errors.length === 0, errors };
|
|
1793
1798
|
}
|
|
1799
|
+
function connectToEnvironment(projectId, environment, token, spinner) {
|
|
1800
|
+
const wsBase = API_BASE.replace("https://", "wss://").replace("http://", "ws://");
|
|
1801
|
+
const wsUrl = environment && environment !== "production" ? `${wsBase}/ws/${projectId}/${environment}?token=${encodeURIComponent(token)}` : `${wsBase}/ws/${projectId}?token=${encodeURIComponent(token)}`;
|
|
1802
|
+
let reconnectAttempts = 0;
|
|
1803
|
+
const maxReconnectAttempts = 10;
|
|
1804
|
+
function connect() {
|
|
1805
|
+
try {
|
|
1806
|
+
envWs = new WebSocket(wsUrl);
|
|
1807
|
+
} catch {
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
envWs.onopen = () => {
|
|
1811
|
+
reconnectAttempts = 0;
|
|
1812
|
+
};
|
|
1813
|
+
envWs.onmessage = async (event) => {
|
|
1814
|
+
try {
|
|
1815
|
+
const data = JSON.parse(typeof event.data === "string" ? event.data : event.data.toString());
|
|
1816
|
+
if (data.type === "env_change") {
|
|
1817
|
+
console.log(chalk3.cyan(`
|
|
1818
|
+
Environment variable ${data.action}: ${data.key}`));
|
|
1819
|
+
await runGenerate(spinner);
|
|
1820
|
+
}
|
|
1821
|
+
} catch {
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
envWs.onclose = () => {
|
|
1825
|
+
envWs = null;
|
|
1826
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
1827
|
+
reconnectAttempts++;
|
|
1828
|
+
const delay = Math.min(1e3 * Math.pow(2, reconnectAttempts - 1), 3e4);
|
|
1829
|
+
setTimeout(connect, delay);
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
envWs.onerror = () => {
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
connect();
|
|
1836
|
+
}
|
|
1794
1837
|
function startFrameworkDev(command, port, spinner, extraArgs = []) {
|
|
1795
1838
|
const [cmd, ...args] = command.split(" ");
|
|
1796
1839
|
const portArgs = ["--port", port];
|
|
@@ -1845,6 +1888,10 @@ function cleanup() {
|
|
|
1845
1888
|
functionsWatcher.close();
|
|
1846
1889
|
functionsWatcher = null;
|
|
1847
1890
|
}
|
|
1891
|
+
if (envWs) {
|
|
1892
|
+
envWs.close();
|
|
1893
|
+
envWs = null;
|
|
1894
|
+
}
|
|
1848
1895
|
}
|
|
1849
1896
|
async function devCommand(options) {
|
|
1850
1897
|
const credentials = await requireAuth();
|
|
@@ -1855,6 +1902,14 @@ async function devCommand(options) {
|
|
|
1855
1902
|
process.exit(1);
|
|
1856
1903
|
}
|
|
1857
1904
|
const config = await loadConfig();
|
|
1905
|
+
if (!config.projectId) {
|
|
1906
|
+
const envPath = path3.resolve(process.cwd(), ".env");
|
|
1907
|
+
if (await fs3.pathExists(envPath)) {
|
|
1908
|
+
const envContent = await fs3.readFile(envPath, "utf-8");
|
|
1909
|
+
const match = envContent.match(/TETHER_PROJECT_ID=(.+)/);
|
|
1910
|
+
if (match) config.projectId = match[1].trim();
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1858
1913
|
const port = options.port || String(config.dev?.port || 3001);
|
|
1859
1914
|
const schemaPath = resolvePath(config.schema);
|
|
1860
1915
|
const functionsDir = resolvePath(config.functions);
|
|
@@ -1904,7 +1959,7 @@ async function devCommand(options) {
|
|
|
1904
1959
|
}
|
|
1905
1960
|
}
|
|
1906
1961
|
spinner.text = "Generating types...";
|
|
1907
|
-
const { generateTypes: generateTypes2 } = await import("./generate-
|
|
1962
|
+
const { generateTypes: generateTypes2 } = await import("./generate-OKE6YGA3.js");
|
|
1908
1963
|
await generateTypes2({ silent: true });
|
|
1909
1964
|
spinner.succeed("Types generated");
|
|
1910
1965
|
if (config.projectId) {
|
|
@@ -1960,6 +2015,9 @@ async function devCommand(options) {
|
|
|
1960
2015
|
});
|
|
1961
2016
|
}
|
|
1962
2017
|
spinner.succeed("File watchers ready");
|
|
2018
|
+
if (config.projectId) {
|
|
2019
|
+
connectToEnvironment(config.projectId, environment, credentials.accessToken, spinner);
|
|
2020
|
+
}
|
|
1963
2021
|
if (devCmd && !options.skipFramework) {
|
|
1964
2022
|
spinner.start(`Starting ${framework} dev server...`);
|
|
1965
2023
|
frameworkProcess = startFrameworkDev(devCmd, port, spinner, options.extraArgs || []);
|
|
@@ -1986,9 +2044,9 @@ import ora4 from "ora";
|
|
|
1986
2044
|
import os from "os";
|
|
1987
2045
|
import { exec } from "child_process";
|
|
1988
2046
|
import readline from "readline";
|
|
1989
|
-
var
|
|
1990
|
-
var API_URL4 =
|
|
1991
|
-
var AUTH_URL =
|
|
2047
|
+
var isDev4 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
|
|
2048
|
+
var API_URL4 = isDev4 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
|
|
2049
|
+
var AUTH_URL = isDev4 ? "http://localhost:3000/cli" : "https://tthr.io/cli";
|
|
1992
2050
|
async function loginCommand() {
|
|
1993
2051
|
console.log(chalk4.bold("\u26A1 Login to Tether\n"));
|
|
1994
2052
|
const existing = await getCredentials();
|
|
@@ -2203,18 +2261,18 @@ function detectPackageManager() {
|
|
|
2203
2261
|
}
|
|
2204
2262
|
return "npm";
|
|
2205
2263
|
}
|
|
2206
|
-
function getInstallCommand2(pm, packages,
|
|
2264
|
+
function getInstallCommand2(pm, packages, isDev7) {
|
|
2207
2265
|
const packagesStr = packages.join(" ");
|
|
2208
2266
|
switch (pm) {
|
|
2209
2267
|
case "bun":
|
|
2210
|
-
return
|
|
2268
|
+
return isDev7 ? `bun add -d ${packagesStr}` : `bun add ${packagesStr}`;
|
|
2211
2269
|
case "pnpm":
|
|
2212
|
-
return
|
|
2270
|
+
return isDev7 ? `pnpm add -D ${packagesStr}` : `pnpm add ${packagesStr}`;
|
|
2213
2271
|
case "yarn":
|
|
2214
|
-
return
|
|
2272
|
+
return isDev7 ? `yarn add -D ${packagesStr}` : `yarn add ${packagesStr}`;
|
|
2215
2273
|
case "npm":
|
|
2216
2274
|
default:
|
|
2217
|
-
return
|
|
2275
|
+
return isDev7 ? `npm install -D ${packagesStr}` : `npm install ${packagesStr}`;
|
|
2218
2276
|
}
|
|
2219
2277
|
}
|
|
2220
2278
|
async function getLatestVersion2(packageName) {
|
|
@@ -2323,8 +2381,8 @@ import chalk6 from "chalk";
|
|
|
2323
2381
|
import ora6 from "ora";
|
|
2324
2382
|
import fs5 from "fs-extra";
|
|
2325
2383
|
import path5 from "path";
|
|
2326
|
-
var
|
|
2327
|
-
var API_URL5 =
|
|
2384
|
+
var isDev5 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
|
|
2385
|
+
var API_URL5 = isDev5 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
|
|
2328
2386
|
async function execCommand(sql) {
|
|
2329
2387
|
if (!sql || sql.trim() === "") {
|
|
2330
2388
|
console.log(chalk6.red("\nError: SQL query required"));
|
|
@@ -2396,8 +2454,8 @@ import chalk7 from "chalk";
|
|
|
2396
2454
|
import ora7 from "ora";
|
|
2397
2455
|
import fs6 from "fs-extra";
|
|
2398
2456
|
import path6 from "path";
|
|
2399
|
-
var
|
|
2400
|
-
var API_URL6 =
|
|
2457
|
+
var isDev6 = process.env.NODE_ENV === "development" || process.env.TETHER_DEV === "true";
|
|
2458
|
+
var API_URL6 = isDev6 ? "http://localhost:3001/api/v1" : "https://tether-api.strands.gg/api/v1";
|
|
2401
2459
|
async function getProjectId() {
|
|
2402
2460
|
const envPath = path6.resolve(process.cwd(), ".env");
|
|
2403
2461
|
let projectId;
|