zitejs 0.9.111 → 0.9.113

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.
@@ -85,6 +85,7 @@ const PREBUNDLED_LIBS = {
85
85
  'twilio': '__twilio__.js',
86
86
  'intercom-client': '__intercom__.js',
87
87
  '@google/generative-ai': '__gemini__.js',
88
+ '@elevenlabs/elevenlabs-js': '__elevenlabs__.js',
88
89
  };
89
90
  const BASE_BUILD_OPTIONS = {
90
91
  bundle: true,
@@ -9,6 +9,7 @@ export type ZiteSchemaField = {
9
9
  export type ZiteSchemaTable = {
10
10
  id: string;
11
11
  sdkName: string;
12
+ name?: string;
12
13
  primaryFieldId?: string;
13
14
  fields: ZiteSchemaField[];
14
15
  };
@@ -45,6 +46,7 @@ export type AirtableLockField = {
45
46
  export type AirtableLockTable = {
46
47
  id: string;
47
48
  sdkName: string;
49
+ name?: string;
48
50
  primaryFieldId: string;
49
51
  fields: AirtableLockField[];
50
52
  };
@@ -268,9 +268,12 @@ function fieldJsdoc(schemaField, table, schema) {
268
268
  const tpl = def.template;
269
269
  if (tpl.tableId) {
270
270
  // The SDK name, not the raw `tbl...` id — the id appears nowhere the
271
- // reader can act on, while the SDK name is the property on `zite`.
271
+ // reader can act on, while the SDK name is the property on `zite`. Its
272
+ // display name rides along, since after a rename the two differ.
272
273
  const linked = schema?.tables.find((t) => t.id === tpl.tableId);
273
- parts.push(`Links to ${linked ? linked.sdkName : tpl.tableId}`);
274
+ parts.push(linked
275
+ ? `Links to ${linked.sdkName}${linked.name ? ` ("${linked.name}")` : ""}`
276
+ : `Links to ${tpl.tableId}`);
274
277
  }
275
278
  if (tpl.allowMultiple === false)
276
279
  parts.push("Single record only");
@@ -383,6 +386,10 @@ function generateSchema(database, existingSchema) {
383
386
  table: {
384
387
  id: table.id,
385
388
  sdkName: "",
389
+ // Always the live name — never `existingTable?.name`. Locking it the way
390
+ // `sdkName` is locked would freeze it at the first name the table ever
391
+ // had, which is the one thing this is here to avoid.
392
+ name: table.name,
386
393
  primaryFieldId: table.primaryFieldId,
387
394
  fields: [],
388
395
  },
@@ -441,6 +448,13 @@ function buildLinkTableComments(schema) {
441
448
  const tablesById = new Map();
442
449
  for (const t of schema.tables)
443
450
  tablesById.set(t.id, t);
451
+ // The link name itself is a literal SQL identifier ("use these exact names"),
452
+ // so it stays sdkName-derived; the display names ride alongside as a gloss.
453
+ const displayByPascal = new Map();
454
+ for (const t of schema.tables) {
455
+ if (t.name)
456
+ displayByPascal.set((0, sdkNames_js_1.toPascalCase)(t.sdkName), t.name);
457
+ }
444
458
  const seen = new Set();
445
459
  const entries = [];
446
460
  for (const table of schema.tables) {
@@ -470,7 +484,15 @@ function buildLinkTableComments(schema) {
470
484
  const col2 = isSelf
471
485
  ? `target${second}Id`
472
486
  : `${second.charAt(0).toLowerCase()}${second.slice(1)}Id`;
473
- entries.push({ name: linkName, cols: [col1, col2] });
487
+ const firstName = displayByPascal.get(first);
488
+ const secondName = displayByPascal.get(second);
489
+ entries.push({
490
+ name: linkName,
491
+ cols: [col1, col2],
492
+ gloss: firstName && secondName
493
+ ? `joins "${firstName}" and "${secondName}"`
494
+ : undefined,
495
+ });
474
496
  }
475
497
  }
476
498
  if (entries.length === 0)
@@ -480,7 +502,8 @@ function buildLinkTableComments(schema) {
480
502
  "// Link tables for zite.sql() JOINs (use these exact names):",
481
503
  ];
482
504
  for (const e of entries) {
483
- lines.push(`// "${commentSafe(e.name)}" — columns: "${commentSafe(e.cols[0])}", "${commentSafe(e.cols[1])}"`);
505
+ lines.push(`// "${commentSafe(e.name)}" — columns: "${commentSafe(e.cols[0])}", "${commentSafe(e.cols[1])}"` +
506
+ (e.gloss ? ` (${commentSafe(e.gloss)})` : ""));
484
507
  }
485
508
  return lines;
486
509
  }
@@ -567,7 +590,12 @@ function generateDbTs(inputSchema) {
567
590
  for (const table of tables) {
568
591
  const className = (0, sdkNames_js_1.toPascalCase)(table.sdkName);
569
592
  const recordType = `${className}RecordType`;
570
- lines.push(`/** A ${table.sdkName} record as it is read back. */`);
593
+ // A schema written before `name` existed keeps the old wording exactly —
594
+ // sandbox boot regenerates unconditionally, so any drift here would show up
595
+ // as an unexplained diff in every project's next commit.
596
+ lines.push(table.name
597
+ ? `/** A record in the "${commentSafe(table.name)}" table, as it is read back. */`
598
+ : `/** A ${table.sdkName} record as it is read back. */`);
571
599
  lines.push(`export type ${recordType} = {`);
572
600
  lines.push(" id: string;");
573
601
  for (const field of table.fields) {
@@ -591,7 +619,9 @@ function generateDbTs(inputSchema) {
591
619
  // input than they store (an attachments field takes a URL string; a linked
592
620
  // record takes one id or many).
593
621
  const writableFields = table.fields.filter((f) => f.sdkName !== "id" && !READ_ONLY_FIELD_TYPES.has(f.definition.type));
594
- lines.push(`/** What you may write when creating or updating a ${table.sdkName}. */`);
622
+ lines.push(table.name
623
+ ? `/** What you may write when creating or updating a record in the "${commentSafe(table.name)}" table. */`
624
+ : `/** What you may write when creating or updating a ${table.sdkName}. */`);
595
625
  lines.push(`export type ${className}RecordInput = {`);
596
626
  for (const field of writableFields) {
597
627
  lines.push(` ${field.sdkName}: ${tsTypeForSchemaField(field.definition, "write")};`);
@@ -603,7 +633,8 @@ function generateDbTs(inputSchema) {
603
633
  lines.push("export const zite = {");
604
634
  for (const table of tables) {
605
635
  const className = (0, sdkNames_js_1.toPascalCase)(table.sdkName);
606
- lines.push(` ${table.sdkName}: createTableClient<${className}RecordType, ${className}RecordInput>('${className}'),`);
636
+ lines.push(` ${table.sdkName}: createTableClient<${className}RecordType, ${className}RecordInput>('${className}'),` +
637
+ (table.name ? ` // "${commentSafe(table.name)}"` : ""));
607
638
  }
608
639
  lines.push(` sql: createSqlClient(),`);
609
640
  // No `notifications` here: it is a platform primitive, not something backed
@@ -1002,7 +1033,9 @@ function airtableFieldJsdoc(field, table, lock) {
1002
1033
  const linkedTableId = field.config?.linkedTableId;
1003
1034
  if (linkedTableId) {
1004
1035
  const linked = lock?.tables.find((t) => t.id === linkedTableId);
1005
- parts.push(`Links to ${linked ? linked.sdkName : linkedTableId}`);
1036
+ parts.push(linked
1037
+ ? `Links to ${linked.sdkName}${linked.name ? ` ("${linked.name}")` : ""}`
1038
+ : `Links to ${linkedTableId}`);
1006
1039
  }
1007
1040
  if (field.config?.prefersSingleRecordLink)
1008
1041
  parts.push("Single record only");
@@ -1160,7 +1193,9 @@ function generateAirtableTs(inputLock) {
1160
1193
  lines.push(...AIRTABLE_ATTACHMENT_TYPE);
1161
1194
  for (const table of lock.tables) {
1162
1195
  const recordType = `${table.sdkName}RecordType`;
1163
- lines.push(`/** A ${commentSafe(table.sdkName)} record as it is read back. */`);
1196
+ lines.push(table.name
1197
+ ? `/** A record in the "${commentSafe(table.name)}" table, as it is read back. */`
1198
+ : `/** A ${commentSafe(table.sdkName)} record as it is read back. */`);
1164
1199
  lines.push(`export type ${recordType} = {`);
1165
1200
  lines.push(" id: string;");
1166
1201
  for (const field of table.fields) {
@@ -1182,7 +1217,9 @@ function generateAirtableTs(inputLock) {
1182
1217
  // Read-only fields are omitted rather than typed: Airtable rejects a write
1183
1218
  // to a formula, rollup, lookup or autonumber with a 422.
1184
1219
  const writableFields = table.fields.filter((f) => f.sdkName !== "id" && !READ_ONLY_AIRTABLE_FIELDS.has(f.type));
1185
- lines.push(`/** What you may write when creating or updating a ${commentSafe(table.sdkName)}. */`);
1220
+ lines.push(table.name
1221
+ ? `/** What you may write when creating or updating a record in the "${commentSafe(table.name)}" table. */`
1222
+ : `/** What you may write when creating or updating a ${commentSafe(table.sdkName)}. */`);
1186
1223
  lines.push(`export type ${table.sdkName}RecordInput = {`);
1187
1224
  for (const field of writableFields) {
1188
1225
  lines.push(` ${field.sdkName}: ${airtableTsType(field, lock, 0, "write")};`);
@@ -1192,7 +1229,8 @@ function generateAirtableTs(inputLock) {
1192
1229
  lines.push(`export const ${table.sdkName} = createAirtableClient<${recordType}, ${table.sdkName}RecordInput>(`);
1193
1230
  lines.push(` '${lock.integrationId}',`);
1194
1231
  lines.push(` '${table.sdkName}',`);
1195
- lines.push(` { tableId: '${table.id}' },`);
1232
+ lines.push(` { tableId: '${table.id}' },` +
1233
+ (table.name ? ` // "${commentSafe(table.name)}"` : ""));
1196
1234
  lines.push(`);`);
1197
1235
  lines.push("");
1198
1236
  }
@@ -282,6 +282,56 @@ const duplicateIdentifierErrorsIn = (source) => {
282
282
  (0, vitest_1.expect)(syntaxErrorsIn((0, lib_js_1.generateDbTs)(schemaWithFieldNamed(name)))).toEqual([]);
283
283
  });
284
284
  });
285
+ (0, vitest_1.describe)('generateDbTs table names in comments', () => {
286
+ const schemaWithTableNamed = (name) => ({
287
+ tables: [
288
+ {
289
+ id: 'tbl1',
290
+ sdkName: 'table1',
291
+ ...(name === undefined ? {} : { name }),
292
+ primaryFieldId: 'fld1',
293
+ fields: [
294
+ {
295
+ id: 'fld1',
296
+ sdkName: 'slot',
297
+ definition: {
298
+ type: 'single_line_text',
299
+ name: 'Slot',
300
+ template: {},
301
+ },
302
+ },
303
+ ],
304
+ },
305
+ ],
306
+ });
307
+ // The whole point: `sdkName` is locked to the table's id, so after a rename
308
+ // `table1` is the only thing a reader sees. The display name is what says
309
+ // which table that is.
310
+ (0, vitest_1.it)('names the table beside its locked sdkName', () => {
311
+ const out = (0, lib_js_1.generateDbTs)(schemaWithTableNamed('Time Slots'));
312
+ (0, vitest_1.expect)(out).toContain('/** A record in the "Time Slots" table, as it is read back. */');
313
+ (0, vitest_1.expect)(out).toContain('/** What you may write when creating or updating a record in the "Time Slots" table. */');
314
+ (0, vitest_1.expect)(out).toContain(`table1: createTableClient<Table1RecordType, Table1RecordInput>('Table1'), // "Time Slots"`);
315
+ });
316
+ // Sandbox boot regenerates unconditionally against the committed schema, so a
317
+ // schema written before `name` existed has to keep producing what it did
318
+ // before — otherwise every project's next commit carries an unexplained diff.
319
+ (0, vitest_1.it)('emits the pre-name wording when the schema carries no name', () => {
320
+ const out = (0, lib_js_1.generateDbTs)(schemaWithTableNamed(undefined));
321
+ (0, vitest_1.expect)(out).toContain('/** A table1 record as it is read back. */');
322
+ (0, vitest_1.expect)(out).toContain('/** What you may write when creating or updating a table1. */');
323
+ (0, vitest_1.expect)(out).toContain(`('Table1'),\n`);
324
+ (0, vitest_1.expect)(out).not.toContain('" table, as it is read back');
325
+ });
326
+ // Same hazard as a field name, one level up: a table's display name is user
327
+ // text and now reaches a JSDoc.
328
+ vitest_1.it.each([
329
+ ['a comment-close sequence', 'Slots */ console.log(1); /*'],
330
+ ['a newline', 'Time\nSlots'],
331
+ ])('emits parseable TypeScript for a table named with %s', (_what, name) => {
332
+ (0, vitest_1.expect)(syntaxErrorsIn((0, lib_js_1.generateDbTs)(schemaWithTableNamed(name)))).toEqual([]);
333
+ });
334
+ });
285
335
  (0, vitest_1.describe)('generateApiTs endpoint file names', () => {
286
336
  // An endpoint filename is the LLM's raw `writeFile` path argument — nothing
287
337
  // validates its characters — and it lands in an import specifier, a comment
@@ -207,3 +207,31 @@ const recordTypeKeys = (source, typeName) => {
207
207
  (0, vitest_1.expect)(keys).toContain("sql2");
208
208
  });
209
209
  });
210
+ (0, vitest_1.describe)("table display names", () => {
211
+ // `sdkName` is locked to the id so user code keeps compiling; `name` is the
212
+ // opposite and must always take the live value. Locking it too — the easy
213
+ // mistake, since the line above it does exactly that — freezes it at the
214
+ // table's first name and defeats the point.
215
+ (0, vitest_1.it)("follows a rename while the sdkName stays locked", () => {
216
+ const first = (0, lib_js_1.generateSchema)(database([{ id: "tbl_1", name: "Slots", fields: [field("fld_1", "At")] }]));
217
+ (0, vitest_1.expect)(first.tables[0].sdkName).toBe("slots");
218
+ (0, vitest_1.expect)(first.tables[0].name).toBe("Slots");
219
+ const renamed = (0, lib_js_1.generateSchema)(database([
220
+ { id: "tbl_1", name: "Time Slots", fields: [field("fld_1", "At")] },
221
+ ]), first);
222
+ (0, vitest_1.expect)(renamed.tables[0].sdkName).toBe("slots");
223
+ (0, vitest_1.expect)(renamed.tables[0].name).toBe("Time Slots");
224
+ (0, vitest_1.expect)((0, lib_js_1.generateDbTs)(renamed)).toContain('/** A record in the "Time Slots" table, as it is read back. */');
225
+ });
226
+ // `buildLegacySchemaSeed` (restly, migration) builds a seed carrying only
227
+ // `sdkName`. Reading `name` off the seed would emit `undefined` for every
228
+ // table on every newly migrated project.
229
+ (0, vitest_1.it)("takes the live name even when the seed has none", () => {
230
+ const seed = {
231
+ tables: [{ id: "tbl_1", sdkName: "salesDeals", fields: [] }],
232
+ };
233
+ const schema = (0, lib_js_1.generateSchema)(database([{ id: "tbl_1", name: "Deals", fields: [] }]), seed);
234
+ (0, vitest_1.expect)(schema.tables[0].sdkName).toBe("salesDeals");
235
+ (0, vitest_1.expect)(schema.tables[0].name).toBe("Deals");
236
+ });
237
+ });
@@ -49,6 +49,7 @@ const PREBUNDLED_LIBS = {
49
49
  'twilio': '__twilio__.js',
50
50
  'intercom-client': '__intercom__.js',
51
51
  '@google/generative-ai': '__gemini__.js',
52
+ '@elevenlabs/elevenlabs-js': '__elevenlabs__.js',
52
53
  };
53
54
  const BASE_BUILD_OPTIONS = {
54
55
  bundle: true,
package/dist/esm/cli.js CHANGED
File without changes
@@ -9,6 +9,7 @@ export type ZiteSchemaField = {
9
9
  export type ZiteSchemaTable = {
10
10
  id: string;
11
11
  sdkName: string;
12
+ name?: string;
12
13
  primaryFieldId?: string;
13
14
  fields: ZiteSchemaField[];
14
15
  };
@@ -45,6 +46,7 @@ export type AirtableLockField = {
45
46
  export type AirtableLockTable = {
46
47
  id: string;
47
48
  sdkName: string;
49
+ name?: string;
48
50
  primaryFieldId: string;
49
51
  fields: AirtableLockField[];
50
52
  };
@@ -255,9 +255,12 @@ function fieldJsdoc(schemaField, table, schema) {
255
255
  const tpl = def.template;
256
256
  if (tpl.tableId) {
257
257
  // The SDK name, not the raw `tbl...` id — the id appears nowhere the
258
- // reader can act on, while the SDK name is the property on `zite`.
258
+ // reader can act on, while the SDK name is the property on `zite`. Its
259
+ // display name rides along, since after a rename the two differ.
259
260
  const linked = schema?.tables.find((t) => t.id === tpl.tableId);
260
- parts.push(`Links to ${linked ? linked.sdkName : tpl.tableId}`);
261
+ parts.push(linked
262
+ ? `Links to ${linked.sdkName}${linked.name ? ` ("${linked.name}")` : ""}`
263
+ : `Links to ${tpl.tableId}`);
261
264
  }
262
265
  if (tpl.allowMultiple === false)
263
266
  parts.push("Single record only");
@@ -370,6 +373,10 @@ export function generateSchema(database, existingSchema) {
370
373
  table: {
371
374
  id: table.id,
372
375
  sdkName: "",
376
+ // Always the live name — never `existingTable?.name`. Locking it the way
377
+ // `sdkName` is locked would freeze it at the first name the table ever
378
+ // had, which is the one thing this is here to avoid.
379
+ name: table.name,
373
380
  primaryFieldId: table.primaryFieldId,
374
381
  fields: [],
375
382
  },
@@ -428,6 +435,13 @@ function buildLinkTableComments(schema) {
428
435
  const tablesById = new Map();
429
436
  for (const t of schema.tables)
430
437
  tablesById.set(t.id, t);
438
+ // The link name itself is a literal SQL identifier ("use these exact names"),
439
+ // so it stays sdkName-derived; the display names ride alongside as a gloss.
440
+ const displayByPascal = new Map();
441
+ for (const t of schema.tables) {
442
+ if (t.name)
443
+ displayByPascal.set(toPascalCase(t.sdkName), t.name);
444
+ }
431
445
  const seen = new Set();
432
446
  const entries = [];
433
447
  for (const table of schema.tables) {
@@ -457,7 +471,15 @@ function buildLinkTableComments(schema) {
457
471
  const col2 = isSelf
458
472
  ? `target${second}Id`
459
473
  : `${second.charAt(0).toLowerCase()}${second.slice(1)}Id`;
460
- entries.push({ name: linkName, cols: [col1, col2] });
474
+ const firstName = displayByPascal.get(first);
475
+ const secondName = displayByPascal.get(second);
476
+ entries.push({
477
+ name: linkName,
478
+ cols: [col1, col2],
479
+ gloss: firstName && secondName
480
+ ? `joins "${firstName}" and "${secondName}"`
481
+ : undefined,
482
+ });
461
483
  }
462
484
  }
463
485
  if (entries.length === 0)
@@ -467,7 +489,8 @@ function buildLinkTableComments(schema) {
467
489
  "// Link tables for zite.sql() JOINs (use these exact names):",
468
490
  ];
469
491
  for (const e of entries) {
470
- lines.push(`// "${commentSafe(e.name)}" — columns: "${commentSafe(e.cols[0])}", "${commentSafe(e.cols[1])}"`);
492
+ lines.push(`// "${commentSafe(e.name)}" — columns: "${commentSafe(e.cols[0])}", "${commentSafe(e.cols[1])}"` +
493
+ (e.gloss ? ` (${commentSafe(e.gloss)})` : ""));
471
494
  }
472
495
  return lines;
473
496
  }
@@ -554,7 +577,12 @@ export function generateDbTs(inputSchema) {
554
577
  for (const table of tables) {
555
578
  const className = toPascalCase(table.sdkName);
556
579
  const recordType = `${className}RecordType`;
557
- lines.push(`/** A ${table.sdkName} record as it is read back. */`);
580
+ // A schema written before `name` existed keeps the old wording exactly —
581
+ // sandbox boot regenerates unconditionally, so any drift here would show up
582
+ // as an unexplained diff in every project's next commit.
583
+ lines.push(table.name
584
+ ? `/** A record in the "${commentSafe(table.name)}" table, as it is read back. */`
585
+ : `/** A ${table.sdkName} record as it is read back. */`);
558
586
  lines.push(`export type ${recordType} = {`);
559
587
  lines.push(" id: string;");
560
588
  for (const field of table.fields) {
@@ -578,7 +606,9 @@ export function generateDbTs(inputSchema) {
578
606
  // input than they store (an attachments field takes a URL string; a linked
579
607
  // record takes one id or many).
580
608
  const writableFields = table.fields.filter((f) => f.sdkName !== "id" && !READ_ONLY_FIELD_TYPES.has(f.definition.type));
581
- lines.push(`/** What you may write when creating or updating a ${table.sdkName}. */`);
609
+ lines.push(table.name
610
+ ? `/** What you may write when creating or updating a record in the "${commentSafe(table.name)}" table. */`
611
+ : `/** What you may write when creating or updating a ${table.sdkName}. */`);
582
612
  lines.push(`export type ${className}RecordInput = {`);
583
613
  for (const field of writableFields) {
584
614
  lines.push(` ${field.sdkName}: ${tsTypeForSchemaField(field.definition, "write")};`);
@@ -590,7 +620,8 @@ export function generateDbTs(inputSchema) {
590
620
  lines.push("export const zite = {");
591
621
  for (const table of tables) {
592
622
  const className = toPascalCase(table.sdkName);
593
- lines.push(` ${table.sdkName}: createTableClient<${className}RecordType, ${className}RecordInput>('${className}'),`);
623
+ lines.push(` ${table.sdkName}: createTableClient<${className}RecordType, ${className}RecordInput>('${className}'),` +
624
+ (table.name ? ` // "${commentSafe(table.name)}"` : ""));
594
625
  }
595
626
  lines.push(` sql: createSqlClient(),`);
596
627
  // No `notifications` here: it is a platform primitive, not something backed
@@ -989,7 +1020,9 @@ function airtableFieldJsdoc(field, table, lock) {
989
1020
  const linkedTableId = field.config?.linkedTableId;
990
1021
  if (linkedTableId) {
991
1022
  const linked = lock?.tables.find((t) => t.id === linkedTableId);
992
- parts.push(`Links to ${linked ? linked.sdkName : linkedTableId}`);
1023
+ parts.push(linked
1024
+ ? `Links to ${linked.sdkName}${linked.name ? ` ("${linked.name}")` : ""}`
1025
+ : `Links to ${linkedTableId}`);
993
1026
  }
994
1027
  if (field.config?.prefersSingleRecordLink)
995
1028
  parts.push("Single record only");
@@ -1147,7 +1180,9 @@ export function generateAirtableTs(inputLock) {
1147
1180
  lines.push(...AIRTABLE_ATTACHMENT_TYPE);
1148
1181
  for (const table of lock.tables) {
1149
1182
  const recordType = `${table.sdkName}RecordType`;
1150
- lines.push(`/** A ${commentSafe(table.sdkName)} record as it is read back. */`);
1183
+ lines.push(table.name
1184
+ ? `/** A record in the "${commentSafe(table.name)}" table, as it is read back. */`
1185
+ : `/** A ${commentSafe(table.sdkName)} record as it is read back. */`);
1151
1186
  lines.push(`export type ${recordType} = {`);
1152
1187
  lines.push(" id: string;");
1153
1188
  for (const field of table.fields) {
@@ -1169,7 +1204,9 @@ export function generateAirtableTs(inputLock) {
1169
1204
  // Read-only fields are omitted rather than typed: Airtable rejects a write
1170
1205
  // to a formula, rollup, lookup or autonumber with a 422.
1171
1206
  const writableFields = table.fields.filter((f) => f.sdkName !== "id" && !READ_ONLY_AIRTABLE_FIELDS.has(f.type));
1172
- lines.push(`/** What you may write when creating or updating a ${commentSafe(table.sdkName)}. */`);
1207
+ lines.push(table.name
1208
+ ? `/** What you may write when creating or updating a record in the "${commentSafe(table.name)}" table. */`
1209
+ : `/** What you may write when creating or updating a ${commentSafe(table.sdkName)}. */`);
1173
1210
  lines.push(`export type ${table.sdkName}RecordInput = {`);
1174
1211
  for (const field of writableFields) {
1175
1212
  lines.push(` ${field.sdkName}: ${airtableTsType(field, lock, 0, "write")};`);
@@ -1179,7 +1216,8 @@ export function generateAirtableTs(inputLock) {
1179
1216
  lines.push(`export const ${table.sdkName} = createAirtableClient<${recordType}, ${table.sdkName}RecordInput>(`);
1180
1217
  lines.push(` '${lock.integrationId}',`);
1181
1218
  lines.push(` '${table.sdkName}',`);
1182
- lines.push(` { tableId: '${table.id}' },`);
1219
+ lines.push(` { tableId: '${table.id}' },` +
1220
+ (table.name ? ` // "${commentSafe(table.name)}"` : ""));
1183
1221
  lines.push(`);`);
1184
1222
  lines.push("");
1185
1223
  }
@@ -277,6 +277,56 @@ describe('generateDbTs field names in comments', () => {
277
277
  expect(syntaxErrorsIn(generateDbTs(schemaWithFieldNamed(name)))).toEqual([]);
278
278
  });
279
279
  });
280
+ describe('generateDbTs table names in comments', () => {
281
+ const schemaWithTableNamed = (name) => ({
282
+ tables: [
283
+ {
284
+ id: 'tbl1',
285
+ sdkName: 'table1',
286
+ ...(name === undefined ? {} : { name }),
287
+ primaryFieldId: 'fld1',
288
+ fields: [
289
+ {
290
+ id: 'fld1',
291
+ sdkName: 'slot',
292
+ definition: {
293
+ type: 'single_line_text',
294
+ name: 'Slot',
295
+ template: {},
296
+ },
297
+ },
298
+ ],
299
+ },
300
+ ],
301
+ });
302
+ // The whole point: `sdkName` is locked to the table's id, so after a rename
303
+ // `table1` is the only thing a reader sees. The display name is what says
304
+ // which table that is.
305
+ it('names the table beside its locked sdkName', () => {
306
+ const out = generateDbTs(schemaWithTableNamed('Time Slots'));
307
+ expect(out).toContain('/** A record in the "Time Slots" table, as it is read back. */');
308
+ expect(out).toContain('/** What you may write when creating or updating a record in the "Time Slots" table. */');
309
+ expect(out).toContain(`table1: createTableClient<Table1RecordType, Table1RecordInput>('Table1'), // "Time Slots"`);
310
+ });
311
+ // Sandbox boot regenerates unconditionally against the committed schema, so a
312
+ // schema written before `name` existed has to keep producing what it did
313
+ // before — otherwise every project's next commit carries an unexplained diff.
314
+ it('emits the pre-name wording when the schema carries no name', () => {
315
+ const out = generateDbTs(schemaWithTableNamed(undefined));
316
+ expect(out).toContain('/** A table1 record as it is read back. */');
317
+ expect(out).toContain('/** What you may write when creating or updating a table1. */');
318
+ expect(out).toContain(`('Table1'),\n`);
319
+ expect(out).not.toContain('" table, as it is read back');
320
+ });
321
+ // Same hazard as a field name, one level up: a table's display name is user
322
+ // text and now reaches a JSDoc.
323
+ it.each([
324
+ ['a comment-close sequence', 'Slots */ console.log(1); /*'],
325
+ ['a newline', 'Time\nSlots'],
326
+ ])('emits parseable TypeScript for a table named with %s', (_what, name) => {
327
+ expect(syntaxErrorsIn(generateDbTs(schemaWithTableNamed(name)))).toEqual([]);
328
+ });
329
+ });
280
330
  describe('generateApiTs endpoint file names', () => {
281
331
  // An endpoint filename is the LLM's raw `writeFile` path argument — nothing
282
332
  // validates its characters — and it lands in an import specifier, a comment
@@ -205,3 +205,31 @@ describe("normalizeSchemaNames", () => {
205
205
  expect(keys).toContain("sql2");
206
206
  });
207
207
  });
208
+ describe("table display names", () => {
209
+ // `sdkName` is locked to the id so user code keeps compiling; `name` is the
210
+ // opposite and must always take the live value. Locking it too — the easy
211
+ // mistake, since the line above it does exactly that — freezes it at the
212
+ // table's first name and defeats the point.
213
+ it("follows a rename while the sdkName stays locked", () => {
214
+ const first = generateSchema(database([{ id: "tbl_1", name: "Slots", fields: [field("fld_1", "At")] }]));
215
+ expect(first.tables[0].sdkName).toBe("slots");
216
+ expect(first.tables[0].name).toBe("Slots");
217
+ const renamed = generateSchema(database([
218
+ { id: "tbl_1", name: "Time Slots", fields: [field("fld_1", "At")] },
219
+ ]), first);
220
+ expect(renamed.tables[0].sdkName).toBe("slots");
221
+ expect(renamed.tables[0].name).toBe("Time Slots");
222
+ expect(generateDbTs(renamed)).toContain('/** A record in the "Time Slots" table, as it is read back. */');
223
+ });
224
+ // `buildLegacySchemaSeed` (restly, migration) builds a seed carrying only
225
+ // `sdkName`. Reading `name` off the seed would emit `undefined` for every
226
+ // table on every newly migrated project.
227
+ it("takes the live name even when the seed has none", () => {
228
+ const seed = {
229
+ tables: [{ id: "tbl_1", sdkName: "salesDeals", fields: [] }],
230
+ };
231
+ const schema = generateSchema(database([{ id: "tbl_1", name: "Deals", fields: [] }]), seed);
232
+ expect(schema.tables[0].sdkName).toBe("salesDeals");
233
+ expect(schema.tables[0].name).toBe("Deals");
234
+ });
235
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.111",
3
+ "version": "0.9.113",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createCaller = void 0;
4
- var index_js_1 = require("../caller/index.js");
5
- Object.defineProperty(exports, "createCaller", { enumerable: true, get: function () { return index_js_1.createCaller; } });
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createTableClient = void 0;
4
- var index_js_1 = require("../runtime/index.js");
5
- Object.defineProperty(exports, "createTableClient", { enumerable: true, get: function () { return index_js_1.createTableClient; } });
@@ -1,2 +0,0 @@
1
- export { createCaller } from '../caller/index.js';
2
- export type { EndpointConfig } from '../caller/index.js';
@@ -1 +0,0 @@
1
- export { createCaller } from '../caller/index.js';
@@ -1,2 +0,0 @@
1
- export { createTableClient } from '../runtime/index.js';
2
- export type { TableClient } from '../runtime/index.js';
@@ -1 +0,0 @@
1
- export { createTableClient } from '../runtime/index.js';