tthr 0.0.19 → 0.0.20

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 +137 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1567,6 +1567,127 @@ import chalk5 from "chalk";
1567
1567
  import ora4 from "ora";
1568
1568
  import fs6 from "fs-extra";
1569
1569
  import path6 from "path";
1570
+ function parseSchemaFile(source) {
1571
+ const tables = [];
1572
+ const schemaMatch = source.match(/defineSchema\s*\(\s*\{([\s\S]*)\}\s*\)/);
1573
+ if (!schemaMatch) return tables;
1574
+ const schemaContent = schemaMatch[1];
1575
+ const tableStartRegex = /(\w+)\s*:\s*\{/g;
1576
+ let match;
1577
+ while ((match = tableStartRegex.exec(schemaContent)) !== null) {
1578
+ const tableName = match[1];
1579
+ const startOffset = match.index + match[0].length;
1580
+ let braceCount = 1;
1581
+ let endOffset = startOffset;
1582
+ for (let i = startOffset; i < schemaContent.length && braceCount > 0; i++) {
1583
+ const char = schemaContent[i];
1584
+ if (char === "{") braceCount++;
1585
+ else if (char === "}") braceCount--;
1586
+ endOffset = i;
1587
+ }
1588
+ const columnsContent = schemaContent.slice(startOffset, endOffset);
1589
+ const columns = parseColumns(columnsContent);
1590
+ tables.push({ name: tableName, columns });
1591
+ }
1592
+ return tables;
1593
+ }
1594
+ function parseColumns(content) {
1595
+ const columns = [];
1596
+ const columnRegex = /(\w+)\s*:\s*(\w+)\s*\(\s*\)([^,\n}]*)/g;
1597
+ let match;
1598
+ while ((match = columnRegex.exec(content)) !== null) {
1599
+ const name = match[1];
1600
+ const schemaType = match[2];
1601
+ const modifiers = match[3] || "";
1602
+ const nullable = !modifiers.includes(".notNull()") && !modifiers.includes(".primaryKey()");
1603
+ const primaryKey = modifiers.includes(".primaryKey()");
1604
+ columns.push({
1605
+ name,
1606
+ type: schemaTypeToTS(schemaType),
1607
+ nullable,
1608
+ primaryKey
1609
+ });
1610
+ }
1611
+ return columns;
1612
+ }
1613
+ function schemaTypeToTS(schemaType) {
1614
+ switch (schemaType) {
1615
+ case "text":
1616
+ return "string";
1617
+ case "integer":
1618
+ return "number";
1619
+ case "real":
1620
+ return "number";
1621
+ case "boolean":
1622
+ return "boolean";
1623
+ case "timestamp":
1624
+ return "string";
1625
+ case "json":
1626
+ return "unknown";
1627
+ case "blob":
1628
+ return "Uint8Array";
1629
+ default:
1630
+ return "unknown";
1631
+ }
1632
+ }
1633
+ function tableNameToInterface(tableName) {
1634
+ let name = tableName;
1635
+ if (name.endsWith("ies")) {
1636
+ name = name.slice(0, -3) + "y";
1637
+ } else if (name.endsWith("s") && !name.endsWith("ss")) {
1638
+ name = name.slice(0, -1);
1639
+ }
1640
+ return name.charAt(0).toUpperCase() + name.slice(1);
1641
+ }
1642
+ function generateDbFile(tables) {
1643
+ const lines = [
1644
+ "// Auto-generated by Tether CLI - do not edit manually",
1645
+ `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1646
+ "",
1647
+ "import { createDatabaseProxy, type TetherDatabase } from '@tthr/client';",
1648
+ ""
1649
+ ];
1650
+ for (const table of tables) {
1651
+ const interfaceName = tableNameToInterface(table.name);
1652
+ lines.push(`export interface ${interfaceName} {`);
1653
+ for (const col of table.columns) {
1654
+ const typeStr = col.nullable ? `${col.type} | null` : col.type;
1655
+ lines.push(` ${col.name}: ${typeStr};`);
1656
+ }
1657
+ lines.push("}");
1658
+ lines.push("");
1659
+ }
1660
+ lines.push("export interface Schema {");
1661
+ for (const table of tables) {
1662
+ const interfaceName = tableNameToInterface(table.name);
1663
+ lines.push(` ${table.name}: ${interfaceName};`);
1664
+ }
1665
+ lines.push("}");
1666
+ lines.push("");
1667
+ lines.push("// Database client with typed tables");
1668
+ lines.push("// This is a proxy that will be populated by the Tether runtime");
1669
+ lines.push("export const db: TetherDatabase<Schema> = createDatabaseProxy<Schema>();");
1670
+ lines.push("");
1671
+ return lines.join("\n");
1672
+ }
1673
+ function generateApiFile(functionsDir, tables) {
1674
+ const lines = [
1675
+ "// Auto-generated by Tether CLI - do not edit manually",
1676
+ `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1677
+ "",
1678
+ "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
+ "",
1685
+ "// API client proxy - will be populated by the Tether runtime",
1686
+ "export const api: Api = createApiProxy<Api>();",
1687
+ ""
1688
+ ];
1689
+ return lines.join("\n");
1690
+ }
1570
1691
  async function generateCommand() {
1571
1692
  await requireAuth();
1572
1693
  const configPath = path6.resolve(process.cwd(), "tether.config.ts");
@@ -1581,71 +1702,29 @@ async function generateCommand() {
1581
1702
  const config = await loadConfig();
1582
1703
  const schemaPath = resolvePath(config.schema);
1583
1704
  const outputDir = resolvePath(config.output);
1705
+ const functionsDir = resolvePath(config.functions);
1584
1706
  if (!await fs6.pathExists(schemaPath)) {
1585
1707
  spinner.fail("Schema file not found");
1586
1708
  console.log(chalk5.dim(`Expected: ${schemaPath}
1587
1709
  `));
1588
1710
  process.exit(1);
1589
1711
  }
1590
- spinner.text = "Generating types...";
1712
+ const schemaSource = await fs6.readFile(schemaPath, "utf-8");
1713
+ const tables = parseSchemaFile(schemaSource);
1714
+ if (tables.length === 0) {
1715
+ spinner.warn("No tables found in schema");
1716
+ console.log(chalk5.dim(" Make sure your schema uses defineSchema({ ... })\n"));
1717
+ return;
1718
+ }
1719
+ spinner.text = `Generating types for ${tables.length} table(s)...`;
1591
1720
  await fs6.ensureDir(outputDir);
1592
1721
  await fs6.writeFile(
1593
1722
  path6.join(outputDir, "db.ts"),
1594
- `// Auto-generated by Tether CLI - do not edit manually
1595
- // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1596
-
1597
- import { createDatabaseProxy, type TetherDatabase } from '@tthr/client';
1598
-
1599
- export interface Post {
1600
- id: string;
1601
- title: string;
1602
- content: string | null;
1603
- authorId: string;
1604
- createdAt: string;
1605
- updatedAt: string;
1606
- }
1607
-
1608
- export interface Comment {
1609
- id: string;
1610
- postId: string;
1611
- content: string;
1612
- authorId: string;
1613
- createdAt: string;
1614
- }
1615
-
1616
- export interface Schema {
1617
- posts: Post;
1618
- comments: Comment;
1619
- }
1620
-
1621
- // Database client with typed tables
1622
- // This is a proxy that will be populated by the Tether runtime
1623
- export const db: TetherDatabase<Schema> = createDatabaseProxy<Schema>();
1624
- `
1723
+ generateDbFile(tables)
1625
1724
  );
1626
1725
  await fs6.writeFile(
1627
1726
  path6.join(outputDir, "api.ts"),
1628
- `// Auto-generated by Tether CLI - do not edit manually
1629
- // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1630
-
1631
- import { createApiProxy } from '@tthr/client';
1632
- import type { Schema } from './db';
1633
-
1634
- export interface PostsApi {
1635
- list: (args?: { limit?: number }) => Promise<Schema['posts'][]>;
1636
- get: (args: { id: string }) => Promise<Schema['posts'] | null>;
1637
- create: (args: { title: string; content?: string }) => Promise<{ id: string }>;
1638
- update: (args: { id: string; title?: string; content?: string }) => Promise<void>;
1639
- remove: (args: { id: string }) => Promise<void>;
1640
- }
1641
-
1642
- export interface Api {
1643
- posts: PostsApi;
1644
- }
1645
-
1646
- // API client proxy - will be populated by the Tether runtime
1647
- export const api: Api = createApiProxy<Api>();
1648
- `
1727
+ generateApiFile(functionsDir, tables)
1649
1728
  );
1650
1729
  await fs6.writeFile(
1651
1730
  path6.join(outputDir, "index.ts"),
@@ -1654,7 +1733,11 @@ export * from './db';
1654
1733
  export * from './api';
1655
1734
  `
1656
1735
  );
1657
- spinner.succeed("Types generated");
1736
+ spinner.succeed(`Types generated for ${tables.length} table(s)`);
1737
+ console.log("\n" + chalk5.green("\u2713") + " Tables:");
1738
+ for (const table of tables) {
1739
+ console.log(chalk5.dim(` - ${table.name} (${table.columns.length} columns)`));
1740
+ }
1658
1741
  const relativeOutput = path6.relative(process.cwd(), outputDir);
1659
1742
  console.log("\n" + chalk5.green("\u2713") + " Generated files:");
1660
1743
  console.log(chalk5.dim(` ${relativeOutput}/db.ts`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tthr",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "description": "Tether CLI - project scaffolding, migrations, and deployment",
5
5
  "type": "module",
6
6
  "bin": {