zitejs 0.1.0 → 0.2.0

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/package.json +6 -2
  2. package/sync/index.js +221 -0
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
+ "bin": {
7
+ "zitejs": "./sync/index.js"
8
+ },
6
9
  "exports": {
7
10
  "./db": {
8
11
  "types": "./db/index.d.ts",
@@ -35,7 +38,8 @@
35
38
  "auth",
36
39
  "upload",
37
40
  "pdf",
38
- "schedules"
41
+ "schedules",
42
+ "sync"
39
43
  ],
40
44
  "keywords": ["zite", "zitejs", "framework", "database", "vibe-coding"],
41
45
  "license": "MIT",
package/sync/index.js ADDED
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * zitejs sync — fetches your Zite Database schema and generates
5
+ * a typed .zite/db.ts client with a fetch()-based runtime.
6
+ *
7
+ * Usage:
8
+ * ZITE_DB_TOKEN=sk_prod_... npx zitejs sync
9
+ *
10
+ * Environment:
11
+ * ZITE_DB_TOKEN — API token (required)
12
+ * ZITE_DB_URL — Base URL (default: https://tables.fillout.com/api/v1)
13
+ * ZITE_BASE_ID — Database ID (auto-detected from zite.config.json if omitted)
14
+ */
15
+
16
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
17
+ import { join, dirname } from 'path';
18
+
19
+ const BASE_URL =
20
+ process.env.ZITE_DB_URL ?? 'https://tables.fillout.com/api/v1';
21
+ const TOKEN = process.env.ZITE_DB_TOKEN ?? '';
22
+
23
+ // Field type → TypeScript type mapping
24
+ const FIELD_TYPE_MAP = {
25
+ single_line_text: 'string',
26
+ long_text: 'string',
27
+ email: 'string',
28
+ url: 'string',
29
+ phone_number: 'string',
30
+ number: 'number',
31
+ currency: 'number',
32
+ percent: 'number',
33
+ rating: 'number',
34
+ duration: 'number',
35
+ single_select: 'string',
36
+ multiple_select: 'string[]',
37
+ checkbox: 'boolean',
38
+ date: 'string',
39
+ datetime: 'string',
40
+ attachments: 'Array<{ url: string; name?: string }>',
41
+ linked_record: 'string[]',
42
+ lookup: 'unknown',
43
+ autonumber: 'number',
44
+ source: 'string',
45
+ };
46
+
47
+ function toLowerCamel(name) {
48
+ return name.charAt(0).toLowerCase() + name.slice(1);
49
+ }
50
+
51
+ function toPascalCase(name) {
52
+ return name
53
+ .replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase())
54
+ .replace(/^(.)/, (_, c) => c.toUpperCase());
55
+ }
56
+
57
+ function toCamelCase(name) {
58
+ const pascal = toPascalCase(name);
59
+ return toLowerCamel(pascal);
60
+ }
61
+
62
+ function tsTypeForField(field) {
63
+ return FIELD_TYPE_MAP[field.type] ?? 'unknown';
64
+ }
65
+
66
+ async function fetchBaseMetadata(baseId) {
67
+ const url = `${BASE_URL}/bases/${encodeURIComponent(baseId)}`;
68
+ const res = await fetch(url, {
69
+ headers: { Authorization: `Bearer ${TOKEN}` },
70
+ });
71
+ if (!res.ok) {
72
+ throw new Error(
73
+ `Failed to fetch base metadata (${res.status}): ${await res.text()}`,
74
+ );
75
+ }
76
+ return res.json();
77
+ }
78
+
79
+ function generateSchema(base) {
80
+ const schema = { tables: {} };
81
+ for (const table of base.tables ?? []) {
82
+ const tableName = toCamelCase(table.name);
83
+ const fields = {};
84
+ for (const field of table.fields ?? []) {
85
+ fields[toCamelCase(field.name)] = { id: field.id };
86
+ }
87
+ schema.tables[tableName] = { id: table.id, fields };
88
+ }
89
+ return schema;
90
+ }
91
+
92
+ function generateDbTs(base, schema, baseId) {
93
+ const lines = [];
94
+
95
+ lines.push('// Auto-generated by zitejs sync. Do not edit manually.');
96
+ lines.push('');
97
+
98
+ // Runtime
99
+ lines.push(`const BASE_URL = process.env.ZITE_DB_URL ?? 'https://tables.fillout.com/api/v1';`);
100
+ lines.push(`const TOKEN = process.env.ZITE_DB_TOKEN ?? '';`);
101
+ lines.push('');
102
+ lines.push('async function dbFetch(method: string, path: string, body?: unknown) {');
103
+ lines.push(' const res = await fetch(BASE_URL + path, {');
104
+ lines.push(' method,');
105
+ lines.push(' headers: {');
106
+ lines.push(' Authorization: `Bearer ${TOKEN}`,');
107
+ lines.push(" ...(body ? { 'Content-Type': 'application/json' } : {}),");
108
+ lines.push(' },');
109
+ lines.push(' body: body ? JSON.stringify(body) : undefined,');
110
+ lines.push(' });');
111
+ lines.push(' if (!res.ok) {');
112
+ lines.push(" const text = await res.text().catch(() => '');");
113
+ lines.push(' throw new Error(`Zite DB request failed (${res.status}): ${text}`);');
114
+ lines.push(' }');
115
+ lines.push(' return res.json();');
116
+ lines.push('}');
117
+ lines.push('');
118
+
119
+ // Per-table types + client
120
+ for (const table of base.tables ?? []) {
121
+ const className = toPascalCase(table.name);
122
+ const propName = toCamelCase(table.name);
123
+ const recordType = `${className}RecordType`;
124
+
125
+ // Record type
126
+ lines.push(`export type ${recordType} = {`);
127
+ lines.push(' id: string;');
128
+ for (const field of table.fields ?? []) {
129
+ const fieldName = toCamelCase(field.name);
130
+ const tsType = tsTypeForField(field);
131
+ lines.push(` ${fieldName}: ${tsType};`);
132
+ }
133
+ lines.push(' createdAt: string;');
134
+ lines.push(' updatedAt: string;');
135
+ lines.push('};');
136
+ lines.push('');
137
+ }
138
+
139
+ // createTableClient helper
140
+ lines.push('function createTableClient<T>(baseId: string, tableId: string) {');
141
+ lines.push(' const base = `/bases/${encodeURIComponent(baseId)}/tables/${encodeURIComponent(tableId)}`;');
142
+ lines.push(' return {');
143
+ lines.push(' findMany: (options?: { limit?: number; offset?: number; sort?: unknown[]; filter?: unknown }): Promise<{ records: T[]; total: number; hasMore: boolean }> =>');
144
+ lines.push(' dbFetch("POST", `${base}/records/list`, options),');
145
+ lines.push(' findOne: (recordId: string): Promise<T> =>');
146
+ lines.push(' dbFetch("GET", `${base}/records/${encodeURIComponent(recordId)}`),');
147
+ lines.push(' create: (data: Partial<T>): Promise<T> =>');
148
+ lines.push(' dbFetch("POST", `${base}/records`, { record: data }),');
149
+ lines.push(' update: (recordId: string, data: Partial<T>): Promise<T> =>');
150
+ lines.push(' dbFetch("PATCH", `${base}/records/${encodeURIComponent(recordId)}`, { record: data }),');
151
+ lines.push(' delete: (recordId: string): Promise<{ deleted: true }> =>');
152
+ lines.push(' dbFetch("DELETE", `${base}/records/${encodeURIComponent(recordId)}`),');
153
+ lines.push(' bulkCreate: (records: Partial<T>[]): Promise<T[]> =>');
154
+ lines.push(' dbFetch("POST", `${base}/records/bulk`, { records }),');
155
+ lines.push(' };');
156
+ lines.push('}');
157
+ lines.push('');
158
+
159
+ // Singleton
160
+ lines.push(`const BASE_ID = '${baseId}';`);
161
+ lines.push('');
162
+ lines.push('export const zite = {');
163
+ for (const table of base.tables ?? []) {
164
+ const className = toPascalCase(table.name);
165
+ const propName = toCamelCase(table.name);
166
+ const tableId = schema.tables[propName]?.id ?? table.id;
167
+ lines.push(` ${propName}: createTableClient<${className}RecordType>(BASE_ID, '${tableId}'),`);
168
+ }
169
+ lines.push('};');
170
+ lines.push('');
171
+
172
+ return lines.join('\n');
173
+ }
174
+
175
+ async function main() {
176
+ if (!TOKEN) {
177
+ console.error('Error: ZITE_DB_TOKEN is required. Set it in your .env or environment.');
178
+ process.exit(1);
179
+ }
180
+
181
+ // Find base ID from zite.config.json or env
182
+ let baseId = process.env.ZITE_BASE_ID;
183
+ if (!baseId) {
184
+ try {
185
+ const config = JSON.parse(readFileSync('zite.config.json', 'utf-8'));
186
+ baseId = config.project?.basePublicIdentifier;
187
+ } catch {
188
+ // ignore
189
+ }
190
+ }
191
+
192
+ if (!baseId) {
193
+ console.error('Error: Could not determine base ID. Set ZITE_BASE_ID or ensure zite.config.json exists.');
194
+ process.exit(1);
195
+ }
196
+
197
+ console.log(`Syncing database ${baseId}...`);
198
+
199
+ const base = await fetchBaseMetadata(baseId);
200
+ console.log(`Found ${base.tables?.length ?? 0} tables`);
201
+
202
+ const schema = generateSchema(base);
203
+
204
+ // Write zite.schema.json
205
+ writeFileSync('zite.schema.json', JSON.stringify(schema, null, 2));
206
+ console.log('Wrote zite.schema.json');
207
+
208
+ // Write .zite/db.ts
209
+ const dbTs = generateDbTs(base, schema, baseId);
210
+ const dbPath = join('.zite', 'db.ts');
211
+ mkdirSync(dirname(dbPath), { recursive: true });
212
+ writeFileSync(dbPath, dbTs);
213
+ console.log(`Wrote ${dbPath}`);
214
+
215
+ console.log('Done!');
216
+ }
217
+
218
+ main().catch(err => {
219
+ console.error('Sync failed:', err.message);
220
+ process.exit(1);
221
+ });