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.
- package/dist/index.js +137 -54
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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`));
|