zitejs 0.9.104 → 0.9.106
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/auth/index.js +16 -4
- package/dist/cjs/auth/index.test.js +39 -0
- package/dist/cjs/sync/lib.d.ts +2 -13
- package/dist/cjs/sync/lib.js +62 -65
- package/dist/cjs/sync/sdkNames.d.ts +111 -0
- package/dist/cjs/sync/sdkNames.js +279 -0
- package/dist/cjs/sync/sdkNames.test.d.ts +1 -0
- package/dist/cjs/sync/sdkNames.test.js +205 -0
- package/dist/esm/auth/index.js +16 -4
- package/dist/esm/auth/index.test.js +39 -0
- package/dist/esm/sync/lib.d.ts +2 -13
- package/dist/esm/sync/lib.js +52 -56
- package/dist/esm/sync/sdkNames.d.ts +111 -0
- package/dist/esm/sync/sdkNames.js +269 -0
- package/dist/esm/sync/sdkNames.test.d.ts +1 -0
- package/dist/esm/sync/sdkNames.test.js +203 -0
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { generateSchema, generateDbTs } from "./lib.js";
|
|
3
|
+
import { normalizeSchemaNames } from "./sdkNames.js";
|
|
4
|
+
// `generateSchema` warns on every rename; silence it so the suite stays
|
|
5
|
+
// readable, and assert on the emitted names instead.
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.restoreAllMocks();
|
|
11
|
+
});
|
|
12
|
+
const field = (id, name) => ({ id, name, type: "single_line_text", order: 0 });
|
|
13
|
+
const database = (tables) => ({
|
|
14
|
+
id: "base_1",
|
|
15
|
+
name: "Base",
|
|
16
|
+
tables,
|
|
17
|
+
createdAt: "",
|
|
18
|
+
updatedAt: "",
|
|
19
|
+
url: "",
|
|
20
|
+
});
|
|
21
|
+
/** Every key in the emitted `export const zite = { … }` literal. */
|
|
22
|
+
const ziteKeys = (source) => {
|
|
23
|
+
const body = source.slice(source.indexOf("export const zite = {"));
|
|
24
|
+
return [...body.matchAll(/^ {2}([A-Za-z_$][\w$]*):/gm)].map((m) => m[1]);
|
|
25
|
+
};
|
|
26
|
+
/** Every property key on one emitted record type. */
|
|
27
|
+
const recordTypeKeys = (source, typeName) => {
|
|
28
|
+
const start = source.indexOf(`export type ${typeName} = {`);
|
|
29
|
+
const body = source.slice(start, source.indexOf("};", start));
|
|
30
|
+
return [...body.matchAll(/^ {2}([A-Za-z_$][\w$]*)\??:/gm)].map((m) => m[1]);
|
|
31
|
+
};
|
|
32
|
+
describe("reserved platform accessors", () => {
|
|
33
|
+
// Sudzy Dashboard: a table named "Notifications" emitted a second
|
|
34
|
+
// `notifications:` key, and the platform client — written last — won. The
|
|
35
|
+
// table lost findAll and typed as `{ create(NotificationsCreateParams) }`.
|
|
36
|
+
it("moves a table off the notifications accessor", () => {
|
|
37
|
+
const schema = generateSchema(database([{ id: "tbl_1", name: "Notifications", fields: [] }]));
|
|
38
|
+
expect(schema.tables[0].sdkName).toBe("notifications2");
|
|
39
|
+
const keys = ziteKeys(generateDbTs(schema));
|
|
40
|
+
expect(keys).toContain("notifications2");
|
|
41
|
+
expect(keys.filter((k) => k === "notifications")).toHaveLength(1);
|
|
42
|
+
expect(new Set(keys).size).toBe(keys.length);
|
|
43
|
+
});
|
|
44
|
+
it.each(["sql", "auth", "notifications"])("keeps the platform %s accessor for the platform", (reserved) => {
|
|
45
|
+
const schema = generateSchema(database([{ id: "tbl_1", name: reserved, fields: [] }]));
|
|
46
|
+
expect(schema.tables[0].sdkName).toBe(`${reserved}2`);
|
|
47
|
+
const keys = ziteKeys(generateDbTs(schema));
|
|
48
|
+
expect(new Set(keys).size).toBe(keys.length);
|
|
49
|
+
});
|
|
50
|
+
it.each(["__proto__", "constructor", "prototype"])("refuses %s as a table accessor", (hazard) => {
|
|
51
|
+
const schema = generateSchema(database([{ id: "tbl_1", name: hazard, fields: [] }]));
|
|
52
|
+
expect(schema.tables[0].sdkName).not.toBe(hazard);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("collision de-duplication", () => {
|
|
56
|
+
// Nevada Car Coalition: 1.0's generator suffixed colliding names, so the app
|
|
57
|
+
// was written against `category1` / `sponsor1`. 2.0 had no accumulator at
|
|
58
|
+
// all, so the second field silently overwrote the first.
|
|
59
|
+
it("keeps both fields when two display names normalize alike", () => {
|
|
60
|
+
const schema = generateSchema(database([
|
|
61
|
+
{
|
|
62
|
+
id: "tbl_1",
|
|
63
|
+
name: "K9 Winners",
|
|
64
|
+
fields: [field("fld_1", "Category"), field("fld_2", "category")],
|
|
65
|
+
},
|
|
66
|
+
]));
|
|
67
|
+
const names = schema.tables[0].fields.map((f) => f.sdkName);
|
|
68
|
+
expect(names).toEqual(["category", "category2"]);
|
|
69
|
+
const keys = recordTypeKeys(generateDbTs(schema), "K9WinnersRecordType");
|
|
70
|
+
expect(keys).toEqual(["id", "category", "category2"]);
|
|
71
|
+
});
|
|
72
|
+
it("de-duplicates two tables that normalize alike", () => {
|
|
73
|
+
const schema = generateSchema(database([
|
|
74
|
+
{ id: "tbl_1", name: "Orders", fields: [] },
|
|
75
|
+
{ id: "tbl_2", name: "orders", fields: [] },
|
|
76
|
+
]));
|
|
77
|
+
expect(schema.tables.map((t) => t.sdkName)).toEqual(["orders", "orders2"]);
|
|
78
|
+
});
|
|
79
|
+
// Two sdkNames sharing a PascalCase form emit `FooRecordType` twice, which is
|
|
80
|
+
// a duplicate identifier even though the accessors differ.
|
|
81
|
+
it("separates tables whose class names would collide", () => {
|
|
82
|
+
const source = generateDbTs({
|
|
83
|
+
tables: [
|
|
84
|
+
{ id: "tbl_1", sdkName: "orders", fields: [] },
|
|
85
|
+
{ id: "tbl_2", sdkName: "Orders", fields: [] },
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
const declared = [...source.matchAll(/export type (\w+RecordType)/g)].map((m) => m[1]);
|
|
89
|
+
expect(new Set(declared).size).toBe(declared.length);
|
|
90
|
+
});
|
|
91
|
+
it("reserves id so a field named ID stays readable", () => {
|
|
92
|
+
const schema = generateSchema(database([{ id: "tbl_1", name: "Rows", fields: [field("fld_1", "ID")] }]));
|
|
93
|
+
expect(schema.tables[0].fields[0].sdkName).toBe("id2");
|
|
94
|
+
expect(recordTypeKeys(generateDbTs(schema), "RowsRecordType")).toEqual([
|
|
95
|
+
"id",
|
|
96
|
+
"id2",
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe("name stability", () => {
|
|
101
|
+
const existing = {
|
|
102
|
+
tables: [
|
|
103
|
+
{
|
|
104
|
+
id: "tbl_1",
|
|
105
|
+
sdkName: "priceList",
|
|
106
|
+
fields: [
|
|
107
|
+
{ id: "fld_1", sdkName: "srp", definition: { type: "number" } },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
// Kelly: the app was written against `srp`, and the field's display name has
|
|
113
|
+
// drifted since. A locked name is what the app compiles against.
|
|
114
|
+
it("preserves a locked name over the one the display name would produce", () => {
|
|
115
|
+
const schema = generateSchema(database([
|
|
116
|
+
{
|
|
117
|
+
id: "tbl_1",
|
|
118
|
+
name: "Price List",
|
|
119
|
+
fields: [field("fld_1", "SRP (Suggested Retail Price)")],
|
|
120
|
+
},
|
|
121
|
+
]), existing);
|
|
122
|
+
expect(schema.tables[0].fields[0].sdkName).toBe("srp");
|
|
123
|
+
});
|
|
124
|
+
it("does not let a new field displace a locked one", () => {
|
|
125
|
+
const schema = generateSchema(database([
|
|
126
|
+
{
|
|
127
|
+
id: "tbl_1",
|
|
128
|
+
name: "Price List",
|
|
129
|
+
// The new field's fresh name is exactly what fld_1 holds.
|
|
130
|
+
fields: [field("fld_2", "SRP"), field("fld_1", "Anything")],
|
|
131
|
+
},
|
|
132
|
+
]), existing);
|
|
133
|
+
const byId = Object.fromEntries(schema.tables[0].fields.map((f) => [f.id, f.sdkName]));
|
|
134
|
+
expect(byId.fld_1).toBe("srp");
|
|
135
|
+
expect(byId.fld_2).toBe("srp2");
|
|
136
|
+
});
|
|
137
|
+
it("is stable across repeated generation", () => {
|
|
138
|
+
const db = database([
|
|
139
|
+
{
|
|
140
|
+
id: "tbl_1",
|
|
141
|
+
name: "Notifications",
|
|
142
|
+
fields: [field("fld_1", "Category"), field("fld_2", "category")],
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
const first = generateSchema(db);
|
|
146
|
+
const second = generateSchema(db, first);
|
|
147
|
+
const third = generateSchema(db, second);
|
|
148
|
+
expect(second).toEqual(first);
|
|
149
|
+
expect(third).toEqual(first);
|
|
150
|
+
});
|
|
151
|
+
it("recomputes a name that is not a valid identifier", () => {
|
|
152
|
+
const schema = generateSchema(database([
|
|
153
|
+
{ id: "tbl_1", name: "Rows", fields: [field("fld_1", "Time")] },
|
|
154
|
+
]), {
|
|
155
|
+
tables: [
|
|
156
|
+
{
|
|
157
|
+
id: "tbl_1",
|
|
158
|
+
sdkName: "rows",
|
|
159
|
+
fields: [
|
|
160
|
+
{
|
|
161
|
+
id: "fld_1",
|
|
162
|
+
sdkName: "timeSeconds)",
|
|
163
|
+
definition: { type: "number" },
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
expect(schema.tables[0].fields[0].sdkName).toBe("time");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe("normalizeSchemaNames", () => {
|
|
173
|
+
it("is a no-op on a well-formed schema", () => {
|
|
174
|
+
const schema = {
|
|
175
|
+
tables: [
|
|
176
|
+
{
|
|
177
|
+
id: "tbl_1",
|
|
178
|
+
sdkName: "orders",
|
|
179
|
+
fields: [
|
|
180
|
+
{ id: "fld_1", sdkName: "total", definition: { type: "number" } },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
const result = normalizeSchemaNames(schema);
|
|
186
|
+
expect(result.renames).toEqual([]);
|
|
187
|
+
expect(result.schema).toEqual(schema);
|
|
188
|
+
});
|
|
189
|
+
// A schema committed before allocation existed. The sandbox's boot-time
|
|
190
|
+
// `zitejs generate` reads it with no generateSchema pass in front, so this is
|
|
191
|
+
// the only thing standing between it and a TS1117 it cannot build past.
|
|
192
|
+
it("repairs a committed schema that already holds the collision", () => {
|
|
193
|
+
const source = generateDbTs({
|
|
194
|
+
tables: [
|
|
195
|
+
{ id: "tbl_1", sdkName: "notifications", fields: [] },
|
|
196
|
+
{ id: "tbl_2", sdkName: "orders", fields: [] },
|
|
197
|
+
],
|
|
198
|
+
});
|
|
199
|
+
const keys = ziteKeys(source);
|
|
200
|
+
expect(new Set(keys).size).toBe(keys.length);
|
|
201
|
+
expect(keys).toContain("notifications2");
|
|
202
|
+
});
|
|
203
|
+
});
|