zitejs 0.9.40 → 0.9.42
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/cjs/sync/index.js +18 -6
- package/dist/cjs/sync/lib.js +182 -33
- package/dist/esm/client/index.d.ts +236 -236
- package/dist/esm/sync/index.js +19 -7
- package/dist/esm/sync/lib.d.ts +27 -18
- package/dist/esm/sync/lib.js +182 -33
- package/dist/esm/types/fields.d.ts +329 -894
- package/dist/esm/types/tables.d.ts +659 -4125
- package/package.json +2 -2
package/dist/esm/sync/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
-
import { generateSchema, generateDbTs, generateApiTs } from "./lib.js";
|
|
4
|
+
import { generateSchema, generateDbTs, generateApiTs, } from "./lib.js";
|
|
5
5
|
const DB_ENVIRONMENTS = {
|
|
6
6
|
production: "https://tables.fillout.com/api/v1",
|
|
7
7
|
staging: "https://tables.filloutstaging.com/api/v1",
|
|
8
8
|
local: "http://localhost:2507/api/v1",
|
|
9
9
|
};
|
|
10
10
|
const env = process.env.ZITE_ENV ?? "production";
|
|
11
|
-
const BASE_URL = process.env.ZITE_DB_URL ??
|
|
11
|
+
const BASE_URL = process.env.ZITE_DB_URL ??
|
|
12
|
+
DB_ENVIRONMENTS[env] ??
|
|
13
|
+
DB_ENVIRONMENTS.production;
|
|
12
14
|
const TOKEN = process.env.ZITE_DB_TOKEN ?? "";
|
|
13
|
-
async function
|
|
15
|
+
async function fetchDatabase(baseId) {
|
|
14
16
|
const url = `${BASE_URL}/bases/${encodeURIComponent(baseId)}`;
|
|
15
17
|
const res = await fetch(url, {
|
|
16
18
|
headers: { Authorization: `Bearer ${TOKEN}` },
|
|
@@ -31,6 +33,15 @@ function resolveBaseId() {
|
|
|
31
33
|
return undefined;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
36
|
+
function readExistingSchema() {
|
|
37
|
+
try {
|
|
38
|
+
if (existsSync("zite.schema.json")) {
|
|
39
|
+
return JSON.parse(readFileSync("zite.schema.json", "utf-8"));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
34
45
|
export async function runSync() {
|
|
35
46
|
if (!TOKEN) {
|
|
36
47
|
throw new Error("ZITE_DB_TOKEN is required.");
|
|
@@ -40,12 +51,13 @@ export async function runSync() {
|
|
|
40
51
|
throw new Error("Could not determine base ID.");
|
|
41
52
|
}
|
|
42
53
|
console.log(`Syncing database ${baseId}...`);
|
|
43
|
-
const
|
|
44
|
-
console.log(`Found ${
|
|
45
|
-
const
|
|
54
|
+
const database = await fetchDatabase(baseId);
|
|
55
|
+
console.log(`Found ${database.tables?.length ?? 0} tables`);
|
|
56
|
+
const existingSchema = readExistingSchema();
|
|
57
|
+
const schema = generateSchema(database, existingSchema);
|
|
46
58
|
writeFileSync("zite.schema.json", JSON.stringify(schema, null, 2));
|
|
47
59
|
console.log("Wrote zite.schema.json");
|
|
48
|
-
const dbTs = generateDbTs(
|
|
60
|
+
const dbTs = generateDbTs(schema);
|
|
49
61
|
const dotZite = ".zite";
|
|
50
62
|
mkdirSync(dotZite, { recursive: true });
|
|
51
63
|
writeFileSync(join(dotZite, "db.ts"), dbTs);
|
package/dist/esm/sync/lib.d.ts
CHANGED
|
@@ -1,26 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import type { PublicFieldDefinition } from "../types/fields.js";
|
|
2
|
+
import type { Database } from "../types/tables.js";
|
|
3
|
+
export type ZiteSchemaField = {
|
|
4
|
+
id: string;
|
|
5
|
+
sdkName: string;
|
|
6
|
+
definition: PublicFieldDefinition;
|
|
7
|
+
};
|
|
8
|
+
export type ZiteSchemaTable = {
|
|
9
|
+
id: string;
|
|
10
|
+
sdkName: string;
|
|
11
|
+
primaryFieldId?: string;
|
|
12
|
+
fields: ZiteSchemaField[];
|
|
11
13
|
};
|
|
12
14
|
export type ZiteSchema = {
|
|
13
|
-
tables:
|
|
14
|
-
id: string;
|
|
15
|
-
fields: Record<string, {
|
|
16
|
-
id: string;
|
|
17
|
-
}>;
|
|
18
|
-
}>;
|
|
15
|
+
tables: ZiteSchemaTable[];
|
|
19
16
|
};
|
|
20
17
|
export declare function toPascalCase(name: string): string;
|
|
21
18
|
export declare function toCamelCase(name: string): string;
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Build a ZiteSchema from a Database API response.
|
|
21
|
+
*
|
|
22
|
+
* If `existingSchema` is provided, locked SDK names are preserved for
|
|
23
|
+
* tables/fields that already exist (matched by ID). New tables/fields
|
|
24
|
+
* get fresh camelCase SDK names. This is how `zitejs sync` preserves
|
|
25
|
+
* name stability across schema changes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function generateSchema(database: Database, existingSchema?: ZiteSchema): ZiteSchema;
|
|
28
|
+
/**
|
|
29
|
+
* Generate .zite/db.ts purely from ZiteSchema.
|
|
30
|
+
* Pure function — no external data needed beyond what's in the schema file.
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateDbTs(schema: ZiteSchema): string;
|
|
24
33
|
export declare function generateApiTs(endpointFiles: string[]): string | null;
|
|
25
34
|
export declare function generateUserTs(usersTableFields?: Array<{
|
|
26
35
|
name: string;
|
package/dist/esm/sync/lib.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const FIELD_TYPE_MAP = {
|
|
2
2
|
single_line_text: "string",
|
|
3
3
|
long_text: "string",
|
|
4
|
+
rich_text: "string",
|
|
4
5
|
email: "string",
|
|
5
6
|
url: "string",
|
|
6
7
|
phone_number: "string",
|
|
@@ -19,6 +20,25 @@ const FIELD_TYPE_MAP = {
|
|
|
19
20
|
lookup: "unknown",
|
|
20
21
|
autonumber: "number",
|
|
21
22
|
source: "string",
|
|
23
|
+
formula: "unknown",
|
|
24
|
+
created_at: "string",
|
|
25
|
+
updated_at: "string",
|
|
26
|
+
};
|
|
27
|
+
const MAX_SELECT_OPTIONS = 100;
|
|
28
|
+
const NUMBER_FORMAT_EXAMPLES = {
|
|
29
|
+
local: "1,000,000.50",
|
|
30
|
+
comma_period: "1,000,000.50",
|
|
31
|
+
period_comma: "1.000.000,50",
|
|
32
|
+
space_comma: "1 000 000,50",
|
|
33
|
+
space_period: "1 000 000.50",
|
|
34
|
+
no_separator: "1000000.50",
|
|
35
|
+
};
|
|
36
|
+
const DURATION_FORMAT_EXAMPLES = {
|
|
37
|
+
"h:mm": "1:23",
|
|
38
|
+
"h:mm:ss": "1:23:03",
|
|
39
|
+
"h:mm:ss.s": "1:23:03.0",
|
|
40
|
+
"h:mm:ss.ss": "1:23:03.00",
|
|
41
|
+
"h:mm:ss.sss": "1:23:03.000",
|
|
22
42
|
};
|
|
23
43
|
export function toPascalCase(name) {
|
|
24
44
|
return name
|
|
@@ -29,33 +49,157 @@ export function toCamelCase(name) {
|
|
|
29
49
|
const pascal = toPascalCase(name);
|
|
30
50
|
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
31
51
|
}
|
|
32
|
-
function
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
function tsTypeForSchemaField(def) {
|
|
53
|
+
if (def.type === "single_select" || def.type === "multiple_select") {
|
|
54
|
+
const options = def.template
|
|
55
|
+
?.options;
|
|
56
|
+
if (options && options.length > 0) {
|
|
57
|
+
const literals = options
|
|
58
|
+
.slice(0, MAX_SELECT_OPTIONS)
|
|
59
|
+
.map((o) => `"${o.label.replace(/"/g, '\\"')}"`)
|
|
60
|
+
.join(" | ");
|
|
61
|
+
const union = `${literals} | string`;
|
|
62
|
+
if (def.type === "multiple_select")
|
|
63
|
+
return `(${union})[]`;
|
|
64
|
+
return union;
|
|
65
|
+
}
|
|
41
66
|
}
|
|
67
|
+
if (FIELD_TYPE_MAP[def.type])
|
|
68
|
+
return FIELD_TYPE_MAP[def.type];
|
|
42
69
|
return "string";
|
|
43
70
|
}
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
function fieldJsdoc(schemaField, table) {
|
|
72
|
+
const def = schemaField.definition;
|
|
73
|
+
const parts = [];
|
|
74
|
+
if (table.primaryFieldId === schemaField.id)
|
|
75
|
+
parts.push("Primary field");
|
|
76
|
+
if (def.type === "created_at" ||
|
|
77
|
+
def.type === "updated_at" ||
|
|
78
|
+
def.type === "lookup" ||
|
|
79
|
+
def.type === "formula" ||
|
|
80
|
+
def.type === "autonumber") {
|
|
81
|
+
parts.push("Read-only");
|
|
82
|
+
}
|
|
83
|
+
if (def.type === "linked_record") {
|
|
84
|
+
const tpl = def.template;
|
|
85
|
+
if (tpl.tableId)
|
|
86
|
+
parts.push(`Links to table ${tpl.tableId}`);
|
|
87
|
+
if (tpl.allowMultiple === false)
|
|
88
|
+
parts.push("Single record only");
|
|
89
|
+
}
|
|
90
|
+
parts.push(`"${def.name}"`);
|
|
91
|
+
if (def.type === "date") {
|
|
92
|
+
const tpl = def.template;
|
|
93
|
+
if (tpl.dateFormat)
|
|
94
|
+
parts.push(`Date-only field (YYYY-MM-DD string), display as "${tpl.dateFormat}" format`);
|
|
95
|
+
}
|
|
96
|
+
if (def.type === "datetime") {
|
|
97
|
+
const tpl = def.template;
|
|
98
|
+
const timeParts = [
|
|
99
|
+
"Date+time field (ISO 8601 timestamp)",
|
|
100
|
+
];
|
|
101
|
+
if (tpl.dateFormat)
|
|
102
|
+
timeParts.push(`date: ${tpl.dateFormat}`);
|
|
103
|
+
if (tpl.timeFormat)
|
|
104
|
+
timeParts.push(`time: ${tpl.timeFormat}`);
|
|
105
|
+
if (tpl.timezone)
|
|
106
|
+
timeParts.push(`tz: ${tpl.timezone}`);
|
|
107
|
+
if (tpl.displayTimeZone)
|
|
108
|
+
timeParts.push("timezone displayed to user");
|
|
109
|
+
parts.push(timeParts.join(", "));
|
|
110
|
+
}
|
|
111
|
+
if (def.type === "currency") {
|
|
112
|
+
const tpl = def.template;
|
|
113
|
+
if (tpl.currencySymbol) {
|
|
114
|
+
const formatHint = NUMBER_FORMAT_EXAMPLES[tpl.numberFormat ?? "local"] ?? "";
|
|
115
|
+
parts.push(`Value is a raw number; display as currency with symbol "${tpl.currencySymbol}", ${tpl.decimalPlaces ?? 2} decimal places, format: ${tpl.numberFormat ?? "local"} (e.g. ${formatHint})`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (def.type === "number") {
|
|
119
|
+
const tpl = def.template;
|
|
120
|
+
const formatHint = NUMBER_FORMAT_EXAMPLES[tpl.numberFormat ?? "local"] ?? "";
|
|
121
|
+
parts.push(`${tpl.decimalPlaces ?? 0} decimal places, format: ${tpl.numberFormat ?? "local"} (e.g. ${formatHint})`);
|
|
122
|
+
}
|
|
123
|
+
if (def.type === "percent") {
|
|
124
|
+
const tpl = def.template;
|
|
125
|
+
if (tpl.decimalPlaces !== undefined)
|
|
126
|
+
parts.push(`${tpl.decimalPlaces} decimal places`);
|
|
127
|
+
}
|
|
128
|
+
if (def.type === "duration") {
|
|
129
|
+
const tpl = def.template;
|
|
130
|
+
if (tpl.format) {
|
|
131
|
+
const example = DURATION_FORMAT_EXAMPLES[tpl.format] ?? "";
|
|
132
|
+
parts.push(`Value is in seconds; display in "${tpl.format}" format (${example})`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (def.type === "rating") {
|
|
136
|
+
const tpl = def.template;
|
|
137
|
+
if (tpl.maxRating)
|
|
138
|
+
parts.push(`Max rating: ${tpl.maxRating}`);
|
|
139
|
+
}
|
|
140
|
+
if (def.type === "created_at") {
|
|
141
|
+
parts.push("Auto-set when record was created (ISO 8601)");
|
|
142
|
+
}
|
|
143
|
+
if (def.type === "updated_at") {
|
|
144
|
+
parts.push("Auto-set when record was last modified (ISO 8601)");
|
|
145
|
+
}
|
|
146
|
+
if ((def.type === "single_select" || def.type === "multiple_select") &&
|
|
147
|
+
def.template?.options) {
|
|
148
|
+
const count = (def.template.options ?? []).length;
|
|
149
|
+
if (count > MAX_SELECT_OPTIONS) {
|
|
150
|
+
parts.push(`${count - MAX_SELECT_OPTIONS} more options omitted`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (parts.length <= 1)
|
|
154
|
+
return undefined;
|
|
155
|
+
return parts.join(". ");
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Build a ZiteSchema from a Database API response.
|
|
159
|
+
*
|
|
160
|
+
* If `existingSchema` is provided, locked SDK names are preserved for
|
|
161
|
+
* tables/fields that already exist (matched by ID). New tables/fields
|
|
162
|
+
* get fresh camelCase SDK names. This is how `zitejs sync` preserves
|
|
163
|
+
* name stability across schema changes.
|
|
164
|
+
*/
|
|
165
|
+
export function generateSchema(database, existingSchema) {
|
|
166
|
+
const existingTableById = new Map();
|
|
167
|
+
for (const t of existingSchema?.tables ?? []) {
|
|
168
|
+
existingTableById.set(t.id, t);
|
|
169
|
+
}
|
|
170
|
+
const tables = [];
|
|
171
|
+
for (const table of database.tables) {
|
|
172
|
+
const existingTable = existingTableById.get(table.id);
|
|
173
|
+
const existingFieldById = new Map();
|
|
174
|
+
for (const f of existingTable?.fields ?? []) {
|
|
175
|
+
existingFieldById.set(f.id, f);
|
|
176
|
+
}
|
|
177
|
+
const fields = [];
|
|
178
|
+
for (const field of table.fields) {
|
|
179
|
+
const existing = existingFieldById.get(field.id);
|
|
180
|
+
const { id: _id, order: _order, ...definition } = field;
|
|
181
|
+
fields.push({
|
|
182
|
+
id: field.id,
|
|
183
|
+
sdkName: existing?.sdkName ?? toCamelCase(field.name),
|
|
184
|
+
definition,
|
|
185
|
+
});
|
|
51
186
|
}
|
|
52
|
-
|
|
187
|
+
tables.push({
|
|
188
|
+
id: table.id,
|
|
189
|
+
sdkName: existingTable?.sdkName ?? toCamelCase(table.name),
|
|
190
|
+
primaryFieldId: table.primaryFieldId,
|
|
191
|
+
fields,
|
|
192
|
+
});
|
|
53
193
|
}
|
|
54
|
-
return
|
|
194
|
+
return { tables };
|
|
55
195
|
}
|
|
56
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Generate .zite/db.ts purely from ZiteSchema.
|
|
198
|
+
* Pure function — no external data needed beyond what's in the schema file.
|
|
199
|
+
*/
|
|
200
|
+
export function generateDbTs(schema) {
|
|
57
201
|
const lines = [];
|
|
58
|
-
lines.push("// Auto-generated by zitejs
|
|
202
|
+
lines.push("// Auto-generated by zitejs generate. Do not edit manually.");
|
|
59
203
|
lines.push("//");
|
|
60
204
|
lines.push("// Usage in endpoint files (src/api/*.ts):");
|
|
61
205
|
lines.push('// import { zite } from "zitejs/db";');
|
|
@@ -87,26 +231,28 @@ export function generateDbTs(base, schema) {
|
|
|
87
231
|
lines.push('// - Load the "zite:sql" skill for full SQL reference (formulas, lookups, etc.)');
|
|
88
232
|
lines.push("import { createTableClient, createSqlClient } from 'zitejs/runtime';");
|
|
89
233
|
lines.push("");
|
|
90
|
-
for (const table of
|
|
91
|
-
const className = toPascalCase(table.
|
|
234
|
+
for (const table of schema.tables) {
|
|
235
|
+
const className = toPascalCase(table.sdkName);
|
|
92
236
|
const recordType = `${className}RecordType`;
|
|
93
237
|
lines.push(`export type ${recordType} = {`);
|
|
94
238
|
lines.push(" id: string;");
|
|
95
|
-
for (const field of table.fields
|
|
96
|
-
|
|
97
|
-
if (fieldName === "id")
|
|
239
|
+
for (const field of table.fields) {
|
|
240
|
+
if (field.sdkName === "id")
|
|
98
241
|
continue;
|
|
99
|
-
const tsType =
|
|
100
|
-
|
|
242
|
+
const tsType = tsTypeForSchemaField(field.definition);
|
|
243
|
+
const jsdoc = fieldJsdoc(field, table);
|
|
244
|
+
if (jsdoc) {
|
|
245
|
+
lines.push(` /** ${jsdoc} */`);
|
|
246
|
+
}
|
|
247
|
+
lines.push(` ${field.sdkName}: ${tsType};`);
|
|
101
248
|
}
|
|
102
249
|
lines.push("};");
|
|
103
250
|
lines.push("");
|
|
104
251
|
}
|
|
105
252
|
lines.push("export const zite = {");
|
|
106
|
-
for (const table of
|
|
107
|
-
const className = toPascalCase(table.
|
|
108
|
-
|
|
109
|
-
lines.push(` ${propName}: createTableClient<${className}RecordType>('${className}'),`);
|
|
253
|
+
for (const table of schema.tables) {
|
|
254
|
+
const className = toPascalCase(table.sdkName);
|
|
255
|
+
lines.push(` ${table.sdkName}: createTableClient<${className}RecordType>('${className}'),`);
|
|
110
256
|
}
|
|
111
257
|
lines.push(` sql: createSqlClient(),`);
|
|
112
258
|
lines.push("};");
|
|
@@ -134,14 +280,12 @@ export function generateApiTs(endpointFiles) {
|
|
|
134
280
|
lines.push(`export const ${name} = createCaller(${name}Endpoint, '${name}');`);
|
|
135
281
|
}
|
|
136
282
|
lines.push("");
|
|
137
|
-
// Inferred input/output types per endpoint (e.g. GetDashboardInputType, GetDashboardOutputType)
|
|
138
283
|
for (const name of endpointNames) {
|
|
139
284
|
const pascal = toPascalCase(name);
|
|
140
285
|
lines.push(`export type ${pascal}InputType = Parameters<typeof ${name}Endpoint.execute>[0]['input'];`);
|
|
141
286
|
lines.push(`export type ${pascal}OutputType = Awaited<ReturnType<typeof ${name}Endpoint.execute>>;`);
|
|
142
287
|
}
|
|
143
288
|
lines.push("");
|
|
144
|
-
// Also export as a single api object
|
|
145
289
|
lines.push("export const api = {");
|
|
146
290
|
for (const name of endpointNames) {
|
|
147
291
|
lines.push(` ${name},`);
|
|
@@ -150,6 +294,11 @@ export function generateApiTs(endpointFiles) {
|
|
|
150
294
|
lines.push("");
|
|
151
295
|
return lines.join("\n");
|
|
152
296
|
}
|
|
297
|
+
function tsTypeForField(field) {
|
|
298
|
+
if (FIELD_TYPE_MAP[field.type])
|
|
299
|
+
return FIELD_TYPE_MAP[field.type];
|
|
300
|
+
return "string";
|
|
301
|
+
}
|
|
153
302
|
export function generateUserTs(usersTableFields) {
|
|
154
303
|
const lines = [
|
|
155
304
|
"// Auto-generated by zitejs sync. Do not edit manually.",
|