zitejs 0.9.99 → 0.9.100

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.
@@ -10,33 +10,47 @@ exports.generateBackendWrapperTs = generateBackendWrapperTs;
10
10
  exports.generateEmailSdk = generateEmailSdk;
11
11
  const parser_1 = require("@babel/parser");
12
12
  const AUTH_USERS_TABLE_ID = "zite_user";
13
- /** What a field's value looks like when a record is READ back. */
13
+ /**
14
+ * What a field's value looks like when a record is READ back.
15
+ *
16
+ * `| null` on the nullable ones is load-bearing, not pedantry: every field
17
+ * column is created nullable with no default (`recordsTableManager`), and only
18
+ * the six text types get their NULL rewritten to `''` on the way out
19
+ * (`normalizeEmptyStringFields` keys on `emptyValue === ''`). The product's own
20
+ * test asserts `fld_number` and `fld_checkbox` read back as `null`. Typing them
21
+ * non-null let `task.estimate.toFixed(2)` compile and throw.
22
+ */
14
23
  const FIELD_TYPE_MAP = {
24
+ // Text: NULL is rewritten to '' on read, so these genuinely can't be null.
15
25
  single_line_text: "string",
16
26
  long_text: "string",
17
27
  rich_text: "string",
18
28
  email: "string",
19
29
  url: "string",
20
30
  phone_number: "string",
21
- number: "number",
22
- currency: "number",
23
- percent: "number",
24
- rating: "number",
25
- duration: "number",
26
- single_select: "string",
27
- multiple_select: "string[]",
28
- checkbox: "boolean",
31
+ number: "number | null",
32
+ currency: "number | null",
33
+ percent: "number | null",
34
+ rating: "number | null",
35
+ // DECIMAL seconds, unlike Airtable's, which is formatted to "HH:mm:ss".
36
+ duration: "number | null",
37
+ checkbox: "boolean | null",
38
+ single_select: "string | null",
39
+ multiple_select: "string[] | null",
29
40
  date: "string | null",
30
41
  datetime: "string | null",
31
- attachments: "ZiteAttachment[]",
32
- // base-runner normalizes both of these to an array on write and stores them
33
- // that way, so a read never produces a bare string.
34
- linked_record: "string[]",
35
- user: "string[]",
42
+ attachments: "ZiteAttachment[] | null",
43
+ // `string | string[]`, matching the pre-monorepo type. base-runner does force
44
+ // an array on read today, but 1.0 code is written with `typeof x === 'string'`
45
+ // guards — narrowing to `string[]` turns those branches into `never` and
46
+ // fails the app's typecheck for no gain.
47
+ linked_record: "string | string[]",
48
+ user: "string | string[]",
36
49
  lookup: "unknown",
37
50
  rollup: "unknown",
38
51
  autonumber: "number",
39
- source: "string",
52
+ // JSONB change metadata (`{ type: "PublicAPI", apiKeyId: 3 }`), not a string.
53
+ source: "unknown",
40
54
  formula: "unknown",
41
55
  created_at: "string",
42
56
  updated_at: "string",
@@ -47,9 +61,18 @@ const FIELD_TYPE_MAP = {
47
61
  * looser input than it stores.
48
62
  */
49
63
  const FIELD_INPUT_TYPE_MAP = {
50
- attachments: "ZiteAttachmentInput[]",
51
- linked_record: "string | string[] | null",
52
- user: "string | string[] | null",
64
+ attachments: "ZiteAttachmentInput[] | ZiteAttachmentInput",
65
+ linked_record: "string | string[]",
66
+ user: "string | string[]",
67
+ multiple_select: "string | string[]",
68
+ date: "string | Date",
69
+ datetime: "string | Date",
70
+ number: "string | number",
71
+ currency: "string | number",
72
+ percent: "string | number",
73
+ rating: "string | number",
74
+ duration: "string | number",
75
+ checkbox: "string | boolean",
53
76
  };
54
77
  /**
55
78
  * Computed and system-managed fields, omitted from the generated input type
@@ -156,6 +179,13 @@ function keepValidSdkName(sdkName) {
156
179
  return undefined;
157
180
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(sdkName) ? sdkName : undefined;
158
181
  }
182
+ /**
183
+ * `null` is how you CLEAR a cell — every scalar write schema in
184
+ * `flexible-inputs.ts` ends in `.nullable()` and update runs with
185
+ * `allowExplicitNull`. A write type without it makes the documented way to
186
+ * empty a field a compile error.
187
+ */
188
+ const withNull = (type) => type.includes("null") ? type : `${type} | null`;
159
189
  function tsTypeForSchemaField(def, variant = "read") {
160
190
  if (def.type === "single_select" || def.type === "multiple_select") {
161
191
  const options = def.template
@@ -166,13 +196,19 @@ function tsTypeForSchemaField(def, variant = "read") {
166
196
  .map((o) => `"${o.label.replace(/"/g, '\\"')}"`)
167
197
  .join(" | ");
168
198
  const union = `${literals} | string`;
169
- if (def.type === "multiple_select")
170
- return `(${union})[]`;
171
- return union;
199
+ const read = def.type === "multiple_select" ? `(${union})[] | null` : `${union} | null`;
200
+ if (variant === "write") {
201
+ return def.type === "multiple_select"
202
+ ? `${union} | (${union})[] | null`
203
+ : `${union} | null`;
204
+ }
205
+ return read;
172
206
  }
173
207
  }
174
- if (variant === "write" && FIELD_INPUT_TYPE_MAP[def.type]) {
175
- return FIELD_INPUT_TYPE_MAP[def.type];
208
+ if (variant === "write") {
209
+ const write = FIELD_INPUT_TYPE_MAP[def.type] ?? FIELD_TYPE_MAP[def.type];
210
+ if (write)
211
+ return withNull(write);
176
212
  }
177
213
  if (FIELD_TYPE_MAP[def.type])
178
214
  return FIELD_TYPE_MAP[def.type];
@@ -474,7 +510,11 @@ function generateDbTs(schema) {
474
510
  if (jsdoc) {
475
511
  lines.push(` /** ${jsdoc} */`);
476
512
  }
477
- lines.push(` ${field.sdkName}: ${tsType};`);
513
+ // Optional, matching the pre-monorepo type (`required: ['id']` — "none of
514
+ // these are required to be defined aside from 'id'"). A `fields:`
515
+ // projection omits every key it wasn't asked for, so any field really can
516
+ // be absent on a read.
517
+ lines.push(` ${field.sdkName}?: ${tsType};`);
478
518
  }
479
519
  lines.push("};");
480
520
  lines.push("");
@@ -712,14 +752,18 @@ const AIRTABLE_FIELD_TYPE_MAP = {
712
752
  currency: "number",
713
753
  percent: "number",
714
754
  rating: "number",
715
- duration: "number",
755
+ // Formatted to "HH:mm:ss" before it reaches the app — `formatAirtableDuration`
756
+ // in BOTH dispatches. Unlike the Zite DB's, which is DECIMAL seconds.
757
+ duration: "string",
716
758
  checkbox: "boolean",
717
759
  date: "string",
718
760
  dateTime: "string",
719
761
  singleSelect: "string",
720
762
  multipleSelects: "string[]",
721
763
  multipleAttachments: "AirtableAttachment[]",
722
- multipleRecordLinks: "string[]",
764
+ // `string | string[]`, matching the pre-monorepo type — see the note on the
765
+ // Zite DB's `linked_record`.
766
+ multipleRecordLinks: "string | string[]",
723
767
  singleCollaborator: "{ id: string; email: string; name?: string }",
724
768
  multipleCollaborators: "Array<{ id: string; email: string; name?: string }>",
725
769
  // Read-only fields
@@ -732,7 +776,8 @@ const AIRTABLE_FIELD_TYPE_MAP = {
732
776
  lastModifiedTime: "string",
733
777
  createdBy: "{ id: string; email: string; name?: string }",
734
778
  lastModifiedBy: "{ id: string; email: string; name?: string }",
735
- button: "unknown",
779
+ // Both dispatches extract the URL string off the button object.
780
+ button: "string",
736
781
  externalSyncSource: "unknown",
737
782
  aiText: "string",
738
783
  };
@@ -1,32 +1,46 @@
1
1
  import { parse } from "@babel/parser";
2
2
  const AUTH_USERS_TABLE_ID = "zite_user";
3
- /** What a field's value looks like when a record is READ back. */
3
+ /**
4
+ * What a field's value looks like when a record is READ back.
5
+ *
6
+ * `| null` on the nullable ones is load-bearing, not pedantry: every field
7
+ * column is created nullable with no default (`recordsTableManager`), and only
8
+ * the six text types get their NULL rewritten to `''` on the way out
9
+ * (`normalizeEmptyStringFields` keys on `emptyValue === ''`). The product's own
10
+ * test asserts `fld_number` and `fld_checkbox` read back as `null`. Typing them
11
+ * non-null let `task.estimate.toFixed(2)` compile and throw.
12
+ */
4
13
  const FIELD_TYPE_MAP = {
14
+ // Text: NULL is rewritten to '' on read, so these genuinely can't be null.
5
15
  single_line_text: "string",
6
16
  long_text: "string",
7
17
  rich_text: "string",
8
18
  email: "string",
9
19
  url: "string",
10
20
  phone_number: "string",
11
- number: "number",
12
- currency: "number",
13
- percent: "number",
14
- rating: "number",
15
- duration: "number",
16
- single_select: "string",
17
- multiple_select: "string[]",
18
- checkbox: "boolean",
21
+ number: "number | null",
22
+ currency: "number | null",
23
+ percent: "number | null",
24
+ rating: "number | null",
25
+ // DECIMAL seconds, unlike Airtable's, which is formatted to "HH:mm:ss".
26
+ duration: "number | null",
27
+ checkbox: "boolean | null",
28
+ single_select: "string | null",
29
+ multiple_select: "string[] | null",
19
30
  date: "string | null",
20
31
  datetime: "string | null",
21
- attachments: "ZiteAttachment[]",
22
- // base-runner normalizes both of these to an array on write and stores them
23
- // that way, so a read never produces a bare string.
24
- linked_record: "string[]",
25
- user: "string[]",
32
+ attachments: "ZiteAttachment[] | null",
33
+ // `string | string[]`, matching the pre-monorepo type. base-runner does force
34
+ // an array on read today, but 1.0 code is written with `typeof x === 'string'`
35
+ // guards — narrowing to `string[]` turns those branches into `never` and
36
+ // fails the app's typecheck for no gain.
37
+ linked_record: "string | string[]",
38
+ user: "string | string[]",
26
39
  lookup: "unknown",
27
40
  rollup: "unknown",
28
41
  autonumber: "number",
29
- source: "string",
42
+ // JSONB change metadata (`{ type: "PublicAPI", apiKeyId: 3 }`), not a string.
43
+ source: "unknown",
30
44
  formula: "unknown",
31
45
  created_at: "string",
32
46
  updated_at: "string",
@@ -37,9 +51,18 @@ const FIELD_TYPE_MAP = {
37
51
  * looser input than it stores.
38
52
  */
39
53
  const FIELD_INPUT_TYPE_MAP = {
40
- attachments: "ZiteAttachmentInput[]",
41
- linked_record: "string | string[] | null",
42
- user: "string | string[] | null",
54
+ attachments: "ZiteAttachmentInput[] | ZiteAttachmentInput",
55
+ linked_record: "string | string[]",
56
+ user: "string | string[]",
57
+ multiple_select: "string | string[]",
58
+ date: "string | Date",
59
+ datetime: "string | Date",
60
+ number: "string | number",
61
+ currency: "string | number",
62
+ percent: "string | number",
63
+ rating: "string | number",
64
+ duration: "string | number",
65
+ checkbox: "string | boolean",
43
66
  };
44
67
  /**
45
68
  * Computed and system-managed fields, omitted from the generated input type
@@ -146,6 +169,13 @@ function keepValidSdkName(sdkName) {
146
169
  return undefined;
147
170
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(sdkName) ? sdkName : undefined;
148
171
  }
172
+ /**
173
+ * `null` is how you CLEAR a cell — every scalar write schema in
174
+ * `flexible-inputs.ts` ends in `.nullable()` and update runs with
175
+ * `allowExplicitNull`. A write type without it makes the documented way to
176
+ * empty a field a compile error.
177
+ */
178
+ const withNull = (type) => type.includes("null") ? type : `${type} | null`;
149
179
  function tsTypeForSchemaField(def, variant = "read") {
150
180
  if (def.type === "single_select" || def.type === "multiple_select") {
151
181
  const options = def.template
@@ -156,13 +186,19 @@ function tsTypeForSchemaField(def, variant = "read") {
156
186
  .map((o) => `"${o.label.replace(/"/g, '\\"')}"`)
157
187
  .join(" | ");
158
188
  const union = `${literals} | string`;
159
- if (def.type === "multiple_select")
160
- return `(${union})[]`;
161
- return union;
189
+ const read = def.type === "multiple_select" ? `(${union})[] | null` : `${union} | null`;
190
+ if (variant === "write") {
191
+ return def.type === "multiple_select"
192
+ ? `${union} | (${union})[] | null`
193
+ : `${union} | null`;
194
+ }
195
+ return read;
162
196
  }
163
197
  }
164
- if (variant === "write" && FIELD_INPUT_TYPE_MAP[def.type]) {
165
- return FIELD_INPUT_TYPE_MAP[def.type];
198
+ if (variant === "write") {
199
+ const write = FIELD_INPUT_TYPE_MAP[def.type] ?? FIELD_TYPE_MAP[def.type];
200
+ if (write)
201
+ return withNull(write);
166
202
  }
167
203
  if (FIELD_TYPE_MAP[def.type])
168
204
  return FIELD_TYPE_MAP[def.type];
@@ -464,7 +500,11 @@ export function generateDbTs(schema) {
464
500
  if (jsdoc) {
465
501
  lines.push(` /** ${jsdoc} */`);
466
502
  }
467
- lines.push(` ${field.sdkName}: ${tsType};`);
503
+ // Optional, matching the pre-monorepo type (`required: ['id']` — "none of
504
+ // these are required to be defined aside from 'id'"). A `fields:`
505
+ // projection omits every key it wasn't asked for, so any field really can
506
+ // be absent on a read.
507
+ lines.push(` ${field.sdkName}?: ${tsType};`);
468
508
  }
469
509
  lines.push("};");
470
510
  lines.push("");
@@ -702,14 +742,18 @@ const AIRTABLE_FIELD_TYPE_MAP = {
702
742
  currency: "number",
703
743
  percent: "number",
704
744
  rating: "number",
705
- duration: "number",
745
+ // Formatted to "HH:mm:ss" before it reaches the app — `formatAirtableDuration`
746
+ // in BOTH dispatches. Unlike the Zite DB's, which is DECIMAL seconds.
747
+ duration: "string",
706
748
  checkbox: "boolean",
707
749
  date: "string",
708
750
  dateTime: "string",
709
751
  singleSelect: "string",
710
752
  multipleSelects: "string[]",
711
753
  multipleAttachments: "AirtableAttachment[]",
712
- multipleRecordLinks: "string[]",
754
+ // `string | string[]`, matching the pre-monorepo type — see the note on the
755
+ // Zite DB's `linked_record`.
756
+ multipleRecordLinks: "string | string[]",
713
757
  singleCollaborator: "{ id: string; email: string; name?: string }",
714
758
  multipleCollaborators: "Array<{ id: string; email: string; name?: string }>",
715
759
  // Read-only fields
@@ -722,7 +766,8 @@ const AIRTABLE_FIELD_TYPE_MAP = {
722
766
  lastModifiedTime: "string",
723
767
  createdBy: "{ id: string; email: string; name?: string }",
724
768
  lastModifiedBy: "{ id: string; email: string; name?: string }",
725
- button: "unknown",
769
+ // Both dispatches extract the URL string off the button object.
770
+ button: "string",
726
771
  externalSyncSource: "unknown",
727
772
  aiText: "string",
728
773
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.99",
3
+ "version": "0.9.100",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",