tthr 0.0.20 → 0.0.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 +216 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1670,22 +1670,90 @@ function generateDbFile(tables) {
1670
1670
  lines.push("");
1671
1671
  return lines.join("\n");
1672
1672
  }
1673
- function generateApiFile(functionsDir, tables) {
1673
+ async function parseFunctionsDir(functionsDir) {
1674
+ const functions = [];
1675
+ if (!await fs6.pathExists(functionsDir)) {
1676
+ return functions;
1677
+ }
1678
+ const files = await fs6.readdir(functionsDir);
1679
+ for (const file of files) {
1680
+ if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
1681
+ const filePath = path6.join(functionsDir, file);
1682
+ const stat = await fs6.stat(filePath);
1683
+ if (!stat.isFile()) continue;
1684
+ const moduleName = file.replace(/\.(ts|js)$/, "");
1685
+ const source = await fs6.readFile(filePath, "utf-8");
1686
+ const exportRegex = /export\s+const\s+(\w+)\s*=\s*(query|mutation|action)\s*\(/g;
1687
+ let match;
1688
+ while ((match = exportRegex.exec(source)) !== null) {
1689
+ functions.push({
1690
+ name: match[1],
1691
+ moduleName
1692
+ });
1693
+ }
1694
+ }
1695
+ return functions;
1696
+ }
1697
+ async function generateApiFile(functionsDir) {
1698
+ const functions = await parseFunctionsDir(functionsDir);
1699
+ const moduleMap = /* @__PURE__ */ new Map();
1700
+ for (const fn of functions) {
1701
+ if (!moduleMap.has(fn.moduleName)) {
1702
+ moduleMap.set(fn.moduleName, []);
1703
+ }
1704
+ moduleMap.get(fn.moduleName).push(fn.name);
1705
+ }
1674
1706
  const lines = [
1675
1707
  "// Auto-generated by Tether CLI - do not edit manually",
1676
1708
  `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1677
1709
  "",
1678
1710
  "import { createApiProxy } from '@tthr/client';",
1679
- "import type { Schema } from './db';",
1680
- "",
1681
- "// API type placeholder - will be populated based on your functions",
1682
- "// eslint-disable-next-line @typescript-eslint/no-empty-object-type",
1683
- "export interface Api {}",
1684
1711
  "",
1685
- "// API client proxy - will be populated by the Tether runtime",
1686
- "export const api: Api = createApiProxy<Api>();",
1712
+ "/**",
1713
+ " * API function reference type for useQuery/useMutation.",
1714
+ ' * The _name property contains the function path (e.g., "users.list").',
1715
+ " */",
1716
+ "export interface ApiFunction<TArgs = unknown, TResult = unknown> {",
1717
+ " _name: string;",
1718
+ " _args?: TArgs;",
1719
+ " _result?: TResult;",
1720
+ "}",
1687
1721
  ""
1688
1722
  ];
1723
+ if (moduleMap.size > 0) {
1724
+ for (const [moduleName, fnNames] of moduleMap) {
1725
+ const interfaceName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1) + "Api";
1726
+ lines.push(`export interface ${interfaceName} {`);
1727
+ for (const fnName of fnNames) {
1728
+ lines.push(` ${fnName}: ApiFunction;`);
1729
+ }
1730
+ lines.push("}");
1731
+ lines.push("");
1732
+ }
1733
+ lines.push("export interface Api {");
1734
+ for (const moduleName of moduleMap.keys()) {
1735
+ const interfaceName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1) + "Api";
1736
+ lines.push(` ${moduleName}: ${interfaceName};`);
1737
+ }
1738
+ lines.push("}");
1739
+ } else {
1740
+ lines.push("/**");
1741
+ lines.push(" * Flexible API type that allows access to any function path.");
1742
+ lines.push(" * Functions are accessed as api.moduleName.functionName");
1743
+ lines.push(" * The actual types depend on your function definitions.");
1744
+ lines.push(" */");
1745
+ lines.push("export type Api = {");
1746
+ lines.push(" [module: string]: {");
1747
+ lines.push(" [fn: string]: ApiFunction;");
1748
+ lines.push(" };");
1749
+ lines.push("};");
1750
+ }
1751
+ lines.push("");
1752
+ lines.push("// API client proxy - provides typed access to your functions");
1753
+ lines.push("// On the client: returns { _name } references for useQuery/useMutation");
1754
+ lines.push("// In Tether functions: calls the actual function implementation");
1755
+ lines.push("export const api = createApiProxy<Api>();");
1756
+ lines.push("");
1689
1757
  return lines.join("\n");
1690
1758
  }
1691
1759
  async function generateCommand() {
@@ -1724,7 +1792,7 @@ async function generateCommand() {
1724
1792
  );
1725
1793
  await fs6.writeFile(
1726
1794
  path6.join(outputDir, "api.ts"),
1727
- generateApiFile(functionsDir, tables)
1795
+ await generateApiFile(functionsDir)
1728
1796
  );
1729
1797
  await fs6.writeFile(
1730
1798
  path6.join(outputDir, "index.ts"),
@@ -2049,6 +2117,144 @@ function sleep(ms) {
2049
2117
  return new Promise((resolve) => setTimeout(resolve, ms));
2050
2118
  }
2051
2119
 
2120
+ // src/commands/update.ts
2121
+ import chalk8 from "chalk";
2122
+ import ora7 from "ora";
2123
+ import fs8 from "fs-extra";
2124
+ import path8 from "path";
2125
+ import { execSync as execSync2 } from "child_process";
2126
+ var TETHER_PACKAGES = [
2127
+ "@tthr/client",
2128
+ "@tthr/vue",
2129
+ "@tthr/svelte",
2130
+ "@tthr/react",
2131
+ "@tthr/server",
2132
+ "@tthr/schema"
2133
+ ];
2134
+ function detectPackageManager() {
2135
+ const cwd = process.cwd();
2136
+ if (fs8.existsSync(path8.join(cwd, "bun.lockb")) || fs8.existsSync(path8.join(cwd, "bun.lock"))) {
2137
+ return "bun";
2138
+ }
2139
+ if (fs8.existsSync(path8.join(cwd, "pnpm-lock.yaml"))) {
2140
+ return "pnpm";
2141
+ }
2142
+ if (fs8.existsSync(path8.join(cwd, "yarn.lock"))) {
2143
+ return "yarn";
2144
+ }
2145
+ return "npm";
2146
+ }
2147
+ function getInstallCommand2(pm, packages, isDev4) {
2148
+ const packagesStr = packages.join(" ");
2149
+ switch (pm) {
2150
+ case "bun":
2151
+ return isDev4 ? `bun add -d ${packagesStr}` : `bun add ${packagesStr}`;
2152
+ case "pnpm":
2153
+ return isDev4 ? `pnpm add -D ${packagesStr}` : `pnpm add ${packagesStr}`;
2154
+ case "yarn":
2155
+ return isDev4 ? `yarn add -D ${packagesStr}` : `yarn add ${packagesStr}`;
2156
+ case "npm":
2157
+ default:
2158
+ return isDev4 ? `npm install -D ${packagesStr}` : `npm install ${packagesStr}`;
2159
+ }
2160
+ }
2161
+ async function getLatestVersion2(packageName) {
2162
+ try {
2163
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
2164
+ if (!response.ok) return null;
2165
+ const data = await response.json();
2166
+ return data.version;
2167
+ } catch {
2168
+ return null;
2169
+ }
2170
+ }
2171
+ async function updateCommand(options) {
2172
+ const packageJsonPath = path8.resolve(process.cwd(), "package.json");
2173
+ if (!await fs8.pathExists(packageJsonPath)) {
2174
+ console.log(chalk8.red("\nError: No package.json found"));
2175
+ console.log(chalk8.dim("Make sure you're in the root of your project\n"));
2176
+ process.exit(1);
2177
+ }
2178
+ console.log(chalk8.bold("\n\u26A1 Updating Tether packages\n"));
2179
+ const packageJson = await fs8.readJson(packageJsonPath);
2180
+ const deps = packageJson.dependencies || {};
2181
+ const devDeps = packageJson.devDependencies || {};
2182
+ const installedDeps = [];
2183
+ for (const pkg of TETHER_PACKAGES) {
2184
+ if (deps[pkg]) {
2185
+ installedDeps.push({ name: pkg, current: deps[pkg], isDev: false });
2186
+ }
2187
+ if (devDeps[pkg]) {
2188
+ installedDeps.push({ name: pkg, current: devDeps[pkg], isDev: true });
2189
+ }
2190
+ }
2191
+ if (installedDeps.length === 0) {
2192
+ console.log(chalk8.yellow(" No Tether packages found in package.json"));
2193
+ console.log(chalk8.dim(" Tether packages start with @tthr/\n"));
2194
+ return;
2195
+ }
2196
+ console.log(chalk8.dim(` Found ${installedDeps.length} Tether package(s):
2197
+ `));
2198
+ const spinner = ora7("Checking for updates...").start();
2199
+ const packagesToUpdate = [];
2200
+ for (const pkg of installedDeps) {
2201
+ const latestVersion = await getLatestVersion2(pkg.name);
2202
+ if (latestVersion) {
2203
+ const currentClean = pkg.current.replace(/^[\^~>=<]+/, "");
2204
+ if (currentClean !== latestVersion) {
2205
+ packagesToUpdate.push({
2206
+ name: pkg.name,
2207
+ current: currentClean,
2208
+ latest: latestVersion,
2209
+ isDev: pkg.isDev
2210
+ });
2211
+ } else {
2212
+ console.log(chalk8.dim(` ${pkg.name}@${currentClean} (up to date)`));
2213
+ }
2214
+ }
2215
+ }
2216
+ spinner.stop();
2217
+ if (packagesToUpdate.length === 0) {
2218
+ console.log(chalk8.green("\n\u2713 All Tether packages are up to date\n"));
2219
+ return;
2220
+ }
2221
+ console.log(chalk8.cyan("\n Updates available:\n"));
2222
+ for (const pkg of packagesToUpdate) {
2223
+ console.log(
2224
+ chalk8.white(` ${pkg.name}`) + chalk8.red(` ${pkg.current}`) + chalk8.dim(" \u2192 ") + chalk8.green(`${pkg.latest}`) + (pkg.isDev ? chalk8.dim(" (dev)") : "")
2225
+ );
2226
+ }
2227
+ if (options.dryRun) {
2228
+ console.log(chalk8.yellow("\n Dry run - no packages were updated\n"));
2229
+ return;
2230
+ }
2231
+ const pm = detectPackageManager();
2232
+ console.log(chalk8.dim(`
2233
+ Using ${pm} to update packages...
2234
+ `));
2235
+ const depsToUpdate = packagesToUpdate.filter((p) => !p.isDev).map((p) => `${p.name}@latest`);
2236
+ const devDepsToUpdate = packagesToUpdate.filter((p) => p.isDev).map((p) => `${p.name}@latest`);
2237
+ const updateSpinner = ora7("Installing updates...").start();
2238
+ try {
2239
+ if (depsToUpdate.length > 0) {
2240
+ const cmd = getInstallCommand2(pm, depsToUpdate, false);
2241
+ execSync2(cmd, { stdio: "pipe", cwd: process.cwd() });
2242
+ }
2243
+ if (devDepsToUpdate.length > 0) {
2244
+ const cmd = getInstallCommand2(pm, devDepsToUpdate, true);
2245
+ execSync2(cmd, { stdio: "pipe", cwd: process.cwd() });
2246
+ }
2247
+ updateSpinner.succeed("Packages updated successfully");
2248
+ console.log(chalk8.green(`
2249
+ \u2713 Updated ${packagesToUpdate.length} package(s)
2250
+ `));
2251
+ } catch (error) {
2252
+ updateSpinner.fail("Failed to update packages");
2253
+ console.error(chalk8.red(error instanceof Error ? error.message : "Unknown error"));
2254
+ process.exit(1);
2255
+ }
2256
+ }
2257
+
2052
2258
  // src/index.ts
2053
2259
  var program = new Command();
2054
2260
  program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version("0.0.1");
@@ -2060,4 +2266,5 @@ program.command("deploy").description("Deploy schema and functions to Tether").o
2060
2266
  program.command("login").description("Authenticate with Tether").action(loginCommand);
2061
2267
  program.command("logout").description("Sign out of Tether").action(logoutCommand);
2062
2268
  program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
2269
+ program.command("update").description("Update all Tether packages to the latest version").option("--dry-run", "Show what would be updated without updating").action(updateCommand);
2063
2270
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tthr",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "Tether CLI - project scaffolding, migrations, and deployment",
5
5
  "type": "module",
6
6
  "bin": {