zitejs 0.9.101 → 0.9.102

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.
@@ -151,7 +151,9 @@ const DATE_FORMAT_EXAMPLES = {
151
151
  };
152
152
  const withDateFormatExample = (format) => {
153
153
  const example = DATE_FORMAT_EXAMPLES[format];
154
- return example ? `"${format}" format (e.g. ${example})` : `"${format}" format`;
154
+ return example
155
+ ? `"${format}" format (e.g. ${example})`
156
+ : `"${format}" format`;
155
157
  };
156
158
  function toPascalCase(name) {
157
159
  const pascal = name
@@ -196,7 +198,9 @@ function tsTypeForSchemaField(def, variant = "read") {
196
198
  .map((o) => `"${o.label.replace(/"/g, '\\"')}"`)
197
199
  .join(" | ");
198
200
  const union = `${literals} | string`;
199
- const read = def.type === "multiple_select" ? `(${union})[] | null` : `${union} | null`;
201
+ const read = def.type === "multiple_select"
202
+ ? `(${union})[] | null`
203
+ : `${union} | null`;
200
204
  if (variant === "write") {
201
205
  return def.type === "multiple_select"
202
206
  ? `${union} | (${union})[] | null`
@@ -447,7 +451,7 @@ function generateDbTs(schema) {
447
451
  lines.push("// .findAll({ filters?, sort?, offset?, limit?, fields? }) → { records: T[], hasMore: boolean }");
448
452
  lines.push("// filters: { fieldName: value } for equality, or { fieldName: { <op>: value } }");
449
453
  lines.push("// operators: contains, not, in, notIn, lt, lte, gt, gte");
450
- lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', '// empty `notIn: []` applies no filter.');
454
+ lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', "// empty `notIn: []` applies no filter.");
451
455
  lines.push("// sort is currently IGNORED here — it is stripped before the request.", "// (it does work on zite.auth.findAllUsers). Order in SQL instead.");
452
456
  lines.push("// limit defaults to 500, max 2000; offset is a row count (a number)");
453
457
  lines.push("// .findOne({ id?, filters?, fields? }) → T | undefined");
@@ -620,12 +624,52 @@ function inspectEndpointFile(source) {
620
624
  * words are legal object keys.
621
625
  */
622
626
  const RESERVED_IDENTIFIERS = new Set([
623
- "await", "break", "case", "catch", "class", "const", "continue", "debugger",
624
- "default", "delete", "do", "else", "enum", "export", "extends", "false",
625
- "finally", "for", "function", "if", "implements", "import", "in",
626
- "instanceof", "interface", "let", "new", "null", "package", "private",
627
- "protected", "public", "return", "static", "super", "switch", "this",
628
- "throw", "true", "try", "typeof", "var", "void", "while", "with", "yield",
627
+ "await",
628
+ "break",
629
+ "case",
630
+ "catch",
631
+ "class",
632
+ "const",
633
+ "continue",
634
+ "debugger",
635
+ "default",
636
+ "delete",
637
+ "do",
638
+ "else",
639
+ "enum",
640
+ "export",
641
+ "extends",
642
+ "false",
643
+ "finally",
644
+ "for",
645
+ "function",
646
+ "if",
647
+ "implements",
648
+ "import",
649
+ "in",
650
+ "instanceof",
651
+ "interface",
652
+ "let",
653
+ "new",
654
+ "null",
655
+ "package",
656
+ "private",
657
+ "protected",
658
+ "public",
659
+ "return",
660
+ "static",
661
+ "super",
662
+ "switch",
663
+ "this",
664
+ "throw",
665
+ "true",
666
+ "try",
667
+ "typeof",
668
+ "var",
669
+ "void",
670
+ "while",
671
+ "with",
672
+ "yield",
629
673
  ]);
630
674
  const toSafeIdentifier = (camelName) => RESERVED_IDENTIFIERS.has(camelName) ? `_${camelName}` : camelName;
631
675
  function generateApiTs(endpointFiles) {
@@ -955,7 +999,7 @@ function generateAirtableTs(lock) {
955
999
  '// each carries its current user-facing name in quotes (// "Task Name"), which is',
956
1000
  "// the source of truth for what it means.",
957
1001
  "// Values are RAW; format them for display using each field's comment.",
958
- "// Fields marked \"Links to X\" hold Airtable record ids (rec...) from",
1002
+ '// Fields marked "Links to X" hold Airtable record ids (rec...) from',
959
1003
  "// findOne/findAll — never invent one.",
960
1004
  "// Read-only fields are absent from the *RecordInput types: Airtable rejects a",
961
1005
  "// write to a computed one (formula, rollup, lookup, autonumber, the created/",
@@ -1066,11 +1110,31 @@ function generateBackendWrapperTs(envVarNames = []) {
1066
1110
  " * compile and throw. `Omit` keeps any index signature merged in by",
1067
1111
  " * `.zite/user-extensions.d.ts`, so a migrated user-sync app still reads its",
1068
1112
  " * synced columns off this.",
1113
+ " *",
1114
+ " * The `Pick` intersection is not redundant. On an app that DOES merge in",
1115
+ " * `[key: string]: any`, `keyof User` widens to `string`, `Exclude` then",
1116
+ " * removes nothing, and the `Omit` collapses to a bare index signature —",
1117
+ " * which would silently degrade `user.id` from `string` to `any`. The `Pick`",
1118
+ " * names those members explicitly, so they survive the collapse.",
1119
+ " *",
1120
+ " * Only `id` and `email` are pinned that way, deliberately. On a user-sync",
1121
+ " * app `context.user` IS the synced row (`buildUserContext` returns it",
1122
+ " * whole), and only those two are guaranteed by the runtime: the id is the",
1123
+ " * record id and the email is merged in explicitly. `firstName`/`lastName`",
1124
+ " * come from whatever the app mapped those columns to, so pinning them to",
1125
+ " * `string` would reject a legitimate read on a table that types them",
1126
+ " * differently. Non-widened apps get both from the `Omit` regardless.",
1127
+ " *",
1128
+ " * On such an app `user.createdAt` still compiles, as `any`. That is",
1129
+ " * deliberate: a synced user table may legitimately have its own",
1130
+ " * `createdAt` column, and typing it `never` to close the hole would break",
1131
+ " * exactly the legacy apps `user-extensions.d.ts` exists to keep compiling.",
1069
1132
  " */",
1070
1133
  "export type ZiteEndpointUser = Omit<",
1071
1134
  " User,",
1072
1135
  " 'name' | 'emailVerified' | 'image' | 'createdAt' | 'updatedAt'",
1073
- ">;",
1136
+ "> &",
1137
+ " Pick<User, 'id' | 'email'>;",
1074
1138
  "",
1075
1139
  'export interface ZiteRequestContext extends Omit<_ZiteRequestContext, "user"> {',
1076
1140
  " user: ZiteEndpointUser;",
@@ -141,7 +141,9 @@ const DATE_FORMAT_EXAMPLES = {
141
141
  };
142
142
  const withDateFormatExample = (format) => {
143
143
  const example = DATE_FORMAT_EXAMPLES[format];
144
- return example ? `"${format}" format (e.g. ${example})` : `"${format}" format`;
144
+ return example
145
+ ? `"${format}" format (e.g. ${example})`
146
+ : `"${format}" format`;
145
147
  };
146
148
  export function toPascalCase(name) {
147
149
  const pascal = name
@@ -186,7 +188,9 @@ function tsTypeForSchemaField(def, variant = "read") {
186
188
  .map((o) => `"${o.label.replace(/"/g, '\\"')}"`)
187
189
  .join(" | ");
188
190
  const union = `${literals} | string`;
189
- const read = def.type === "multiple_select" ? `(${union})[] | null` : `${union} | null`;
191
+ const read = def.type === "multiple_select"
192
+ ? `(${union})[] | null`
193
+ : `${union} | null`;
190
194
  if (variant === "write") {
191
195
  return def.type === "multiple_select"
192
196
  ? `${union} | (${union})[] | null`
@@ -437,7 +441,7 @@ export function generateDbTs(schema) {
437
441
  lines.push("// .findAll({ filters?, sort?, offset?, limit?, fields? }) → { records: T[], hasMore: boolean }");
438
442
  lines.push("// filters: { fieldName: value } for equality, or { fieldName: { <op>: value } }");
439
443
  lines.push("// operators: contains, not, in, notIn, lt, lte, gt, gte");
440
- lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', '// empty `notIn: []` applies no filter.');
444
+ lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', "// empty `notIn: []` applies no filter.");
441
445
  lines.push("// sort is currently IGNORED here — it is stripped before the request.", "// (it does work on zite.auth.findAllUsers). Order in SQL instead.");
442
446
  lines.push("// limit defaults to 500, max 2000; offset is a row count (a number)");
443
447
  lines.push("// .findOne({ id?, filters?, fields? }) → T | undefined");
@@ -610,12 +614,52 @@ function inspectEndpointFile(source) {
610
614
  * words are legal object keys.
611
615
  */
612
616
  const RESERVED_IDENTIFIERS = new Set([
613
- "await", "break", "case", "catch", "class", "const", "continue", "debugger",
614
- "default", "delete", "do", "else", "enum", "export", "extends", "false",
615
- "finally", "for", "function", "if", "implements", "import", "in",
616
- "instanceof", "interface", "let", "new", "null", "package", "private",
617
- "protected", "public", "return", "static", "super", "switch", "this",
618
- "throw", "true", "try", "typeof", "var", "void", "while", "with", "yield",
617
+ "await",
618
+ "break",
619
+ "case",
620
+ "catch",
621
+ "class",
622
+ "const",
623
+ "continue",
624
+ "debugger",
625
+ "default",
626
+ "delete",
627
+ "do",
628
+ "else",
629
+ "enum",
630
+ "export",
631
+ "extends",
632
+ "false",
633
+ "finally",
634
+ "for",
635
+ "function",
636
+ "if",
637
+ "implements",
638
+ "import",
639
+ "in",
640
+ "instanceof",
641
+ "interface",
642
+ "let",
643
+ "new",
644
+ "null",
645
+ "package",
646
+ "private",
647
+ "protected",
648
+ "public",
649
+ "return",
650
+ "static",
651
+ "super",
652
+ "switch",
653
+ "this",
654
+ "throw",
655
+ "true",
656
+ "try",
657
+ "typeof",
658
+ "var",
659
+ "void",
660
+ "while",
661
+ "with",
662
+ "yield",
619
663
  ]);
620
664
  const toSafeIdentifier = (camelName) => RESERVED_IDENTIFIERS.has(camelName) ? `_${camelName}` : camelName;
621
665
  export function generateApiTs(endpointFiles) {
@@ -945,7 +989,7 @@ export function generateAirtableTs(lock) {
945
989
  '// each carries its current user-facing name in quotes (// "Task Name"), which is',
946
990
  "// the source of truth for what it means.",
947
991
  "// Values are RAW; format them for display using each field's comment.",
948
- "// Fields marked \"Links to X\" hold Airtable record ids (rec...) from",
992
+ '// Fields marked "Links to X" hold Airtable record ids (rec...) from',
949
993
  "// findOne/findAll — never invent one.",
950
994
  "// Read-only fields are absent from the *RecordInput types: Airtable rejects a",
951
995
  "// write to a computed one (formula, rollup, lookup, autonumber, the created/",
@@ -1056,11 +1100,31 @@ export function generateBackendWrapperTs(envVarNames = []) {
1056
1100
  " * compile and throw. `Omit` keeps any index signature merged in by",
1057
1101
  " * `.zite/user-extensions.d.ts`, so a migrated user-sync app still reads its",
1058
1102
  " * synced columns off this.",
1103
+ " *",
1104
+ " * The `Pick` intersection is not redundant. On an app that DOES merge in",
1105
+ " * `[key: string]: any`, `keyof User` widens to `string`, `Exclude` then",
1106
+ " * removes nothing, and the `Omit` collapses to a bare index signature —",
1107
+ " * which would silently degrade `user.id` from `string` to `any`. The `Pick`",
1108
+ " * names those members explicitly, so they survive the collapse.",
1109
+ " *",
1110
+ " * Only `id` and `email` are pinned that way, deliberately. On a user-sync",
1111
+ " * app `context.user` IS the synced row (`buildUserContext` returns it",
1112
+ " * whole), and only those two are guaranteed by the runtime: the id is the",
1113
+ " * record id and the email is merged in explicitly. `firstName`/`lastName`",
1114
+ " * come from whatever the app mapped those columns to, so pinning them to",
1115
+ " * `string` would reject a legitimate read on a table that types them",
1116
+ " * differently. Non-widened apps get both from the `Omit` regardless.",
1117
+ " *",
1118
+ " * On such an app `user.createdAt` still compiles, as `any`. That is",
1119
+ " * deliberate: a synced user table may legitimately have its own",
1120
+ " * `createdAt` column, and typing it `never` to close the hole would break",
1121
+ " * exactly the legacy apps `user-extensions.d.ts` exists to keep compiling.",
1059
1122
  " */",
1060
1123
  "export type ZiteEndpointUser = Omit<",
1061
1124
  " User,",
1062
1125
  " 'name' | 'emailVerified' | 'image' | 'createdAt' | 'updatedAt'",
1063
- ">;",
1126
+ "> &",
1127
+ " Pick<User, 'id' | 'email'>;",
1064
1128
  "",
1065
1129
  'export interface ZiteRequestContext extends Omit<_ZiteRequestContext, "user"> {',
1066
1130
  " user: ZiteEndpointUser;",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.101",
3
+ "version": "0.9.102",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",