zitejs 0.9.100 → 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.
@@ -213,11 +213,12 @@ function getSdkImportSource(baseDir) {
213
213
  /**
214
214
  * `zitejs/db`, `zitejs/api`, `zitejs/integrations` and `zitejs/email` are
215
215
  * tsconfig aliases onto generated files, not real package subpaths — they are
216
- * absent from the `exports` map, and `zitejs` isn't in PREBUNDLED_LIBS either.
217
- * So marking one external doesn't defer resolution, it guarantees a module
218
- * that fails to instantiate the first time the deployed endpoint is invoked,
219
- * long after the build reported success. Fail here instead, where the message
220
- * can name the file that's missing.
216
+ * absent from the `exports` map. `zitejs/backend` IS a real subpath export, but
217
+ * it lands here too and `zitejs` is not in PREBUNDLED_LIBS, so nothing would
218
+ * resolve it in the worker either. Either way, marking one external doesn't
219
+ * defer resolution: it guarantees a module that fails to instantiate the first
220
+ * time the deployed endpoint is invoked, long after the build reported success.
221
+ * Fail here instead, where the message can name the file that's missing.
221
222
  */
222
223
  const unresolvedAlias = (specifier, expectedPath) => {
223
224
  throw new Error(`Cannot resolve '${specifier}': expected generated file at ${expectedPath}. ` +
@@ -90,10 +90,18 @@ export interface TableFindAllOptions<T = Record<string, unknown>> {
90
90
  */
91
91
  sort?: unknown[];
92
92
  filters?: RecordFilters<T>;
93
+ /**
94
+ * The nested filter format (`filters` is the legacy flat one). IGNORED here
95
+ * for the same reason as `sort` — not in `DatabasesFindAllParamsSchema`, so
96
+ * zod strips it — but kept so code already passing it still compiles. It IS
97
+ * honored by `zite.auth.findAllUsers`.
98
+ */
99
+ filter?: unknown;
93
100
  fields?: Array<FieldSelector<T>>;
94
101
  }
95
102
  export interface BulkCreateResult<T> {
96
- success: boolean;
103
+ /** Absent when `records: []` was passed — the dispatch short-circuits before setting it. */
104
+ success?: boolean;
97
105
  records: T[];
98
106
  }
99
107
  export interface UpdateResult<T> {
@@ -173,7 +181,16 @@ export type FindAllAuthUsersResult<T extends AuthUser = AuthUser> = {
173
181
  total: number;
174
182
  hasMore: boolean;
175
183
  };
176
- export type UpdateAuthUserProfile = Partial<Omit<AuthUser, "id" | "email" | "name">>;
184
+ /**
185
+ * `null` clears a value — `publicUpdateAuthUserInputSchema` accepts
186
+ * `string | null | undefined` on each of these, and null is the only way to
187
+ * unset a name.
188
+ */
189
+ export type UpdateAuthUserProfile = {
190
+ firstName?: string | null;
191
+ lastName?: string | null;
192
+ image?: string | null;
193
+ };
177
194
  export interface AuthClient<T extends AuthUser = AuthUser> {
178
195
  findAllUsers(options?: FindAllAuthUsersOptions): Promise<FindAllAuthUsersResult<T>>;
179
196
  updateUserProfile(userId: string, profile: UpdateAuthUserProfile): Promise<T>;
@@ -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/",
@@ -1058,8 +1102,42 @@ function generateBackendWrapperTs(envVarNames = []) {
1058
1102
  // Narrows `user` to this app's generated User. ZiteScheduledContext is
1059
1103
  // re-exported unchanged — a scheduled fire has no user at all, so there is
1060
1104
  // nothing to narrow.
1105
+ "/**",
1106
+ " * The user an endpoint sees. NOT the full `User` from zitejs/auth — that is",
1107
+ " * the browser's better-auth session, and the runtime never sends its",
1108
+ " * `name`, `emailVerified`, `image`, `createdAt` or `updatedAt` to an",
1109
+ " * endpoint. Typing those here let `context.user.createdAt.getFullYear()`",
1110
+ " * compile and throw. `Omit` keeps any index signature merged in by",
1111
+ " * `.zite/user-extensions.d.ts`, so a migrated user-sync app still reads its",
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.",
1132
+ " */",
1133
+ "export type ZiteEndpointUser = Omit<",
1134
+ " User,",
1135
+ " 'name' | 'emailVerified' | 'image' | 'createdAt' | 'updatedAt'",
1136
+ "> &",
1137
+ " Pick<User, 'id' | 'email'>;",
1138
+ "",
1061
1139
  'export interface ZiteRequestContext extends Omit<_ZiteRequestContext, "user"> {',
1062
- " user: User;",
1140
+ " user: ZiteEndpointUser;",
1063
1141
  "}",
1064
1142
  "",
1065
1143
  "type SchemaLike<TOut, TIn = TOut> = { _output: TOut; _input: TIn; parse: (data: unknown) => TOut };",
@@ -177,11 +177,12 @@ function getSdkImportSource(baseDir) {
177
177
  /**
178
178
  * `zitejs/db`, `zitejs/api`, `zitejs/integrations` and `zitejs/email` are
179
179
  * tsconfig aliases onto generated files, not real package subpaths — they are
180
- * absent from the `exports` map, and `zitejs` isn't in PREBUNDLED_LIBS either.
181
- * So marking one external doesn't defer resolution, it guarantees a module
182
- * that fails to instantiate the first time the deployed endpoint is invoked,
183
- * long after the build reported success. Fail here instead, where the message
184
- * can name the file that's missing.
180
+ * absent from the `exports` map. `zitejs/backend` IS a real subpath export, but
181
+ * it lands here too and `zitejs` is not in PREBUNDLED_LIBS, so nothing would
182
+ * resolve it in the worker either. Either way, marking one external doesn't
183
+ * defer resolution: it guarantees a module that fails to instantiate the first
184
+ * time the deployed endpoint is invoked, long after the build reported success.
185
+ * Fail here instead, where the message can name the file that's missing.
185
186
  */
186
187
  const unresolvedAlias = (specifier, expectedPath) => {
187
188
  throw new Error(`Cannot resolve '${specifier}': expected generated file at ${expectedPath}. ` +
@@ -90,10 +90,18 @@ export interface TableFindAllOptions<T = Record<string, unknown>> {
90
90
  */
91
91
  sort?: unknown[];
92
92
  filters?: RecordFilters<T>;
93
+ /**
94
+ * The nested filter format (`filters` is the legacy flat one). IGNORED here
95
+ * for the same reason as `sort` — not in `DatabasesFindAllParamsSchema`, so
96
+ * zod strips it — but kept so code already passing it still compiles. It IS
97
+ * honored by `zite.auth.findAllUsers`.
98
+ */
99
+ filter?: unknown;
93
100
  fields?: Array<FieldSelector<T>>;
94
101
  }
95
102
  export interface BulkCreateResult<T> {
96
- success: boolean;
103
+ /** Absent when `records: []` was passed — the dispatch short-circuits before setting it. */
104
+ success?: boolean;
97
105
  records: T[];
98
106
  }
99
107
  export interface UpdateResult<T> {
@@ -173,7 +181,16 @@ export type FindAllAuthUsersResult<T extends AuthUser = AuthUser> = {
173
181
  total: number;
174
182
  hasMore: boolean;
175
183
  };
176
- export type UpdateAuthUserProfile = Partial<Omit<AuthUser, "id" | "email" | "name">>;
184
+ /**
185
+ * `null` clears a value — `publicUpdateAuthUserInputSchema` accepts
186
+ * `string | null | undefined` on each of these, and null is the only way to
187
+ * unset a name.
188
+ */
189
+ export type UpdateAuthUserProfile = {
190
+ firstName?: string | null;
191
+ lastName?: string | null;
192
+ image?: string | null;
193
+ };
177
194
  export interface AuthClient<T extends AuthUser = AuthUser> {
178
195
  findAllUsers(options?: FindAllAuthUsersOptions): Promise<FindAllAuthUsersResult<T>>;
179
196
  updateUserProfile(userId: string, profile: UpdateAuthUserProfile): Promise<T>;
@@ -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/",
@@ -1048,8 +1092,42 @@ export function generateBackendWrapperTs(envVarNames = []) {
1048
1092
  // Narrows `user` to this app's generated User. ZiteScheduledContext is
1049
1093
  // re-exported unchanged — a scheduled fire has no user at all, so there is
1050
1094
  // nothing to narrow.
1095
+ "/**",
1096
+ " * The user an endpoint sees. NOT the full `User` from zitejs/auth — that is",
1097
+ " * the browser's better-auth session, and the runtime never sends its",
1098
+ " * `name`, `emailVerified`, `image`, `createdAt` or `updatedAt` to an",
1099
+ " * endpoint. Typing those here let `context.user.createdAt.getFullYear()`",
1100
+ " * compile and throw. `Omit` keeps any index signature merged in by",
1101
+ " * `.zite/user-extensions.d.ts`, so a migrated user-sync app still reads its",
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.",
1122
+ " */",
1123
+ "export type ZiteEndpointUser = Omit<",
1124
+ " User,",
1125
+ " 'name' | 'emailVerified' | 'image' | 'createdAt' | 'updatedAt'",
1126
+ "> &",
1127
+ " Pick<User, 'id' | 'email'>;",
1128
+ "",
1051
1129
  'export interface ZiteRequestContext extends Omit<_ZiteRequestContext, "user"> {',
1052
- " user: User;",
1130
+ " user: ZiteEndpointUser;",
1053
1131
  "}",
1054
1132
  "",
1055
1133
  "type SchemaLike<TOut, TIn = TOut> = { _output: TOut; _input: TIn; parse: (data: unknown) => TOut };",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.100",
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",