@tinybirdco/sdk 0.0.54 → 0.0.56
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/api/build.test.js +23 -0
- package/dist/api/build.test.js.map +1 -1
- package/dist/api/deploy.d.ts +17 -10
- package/dist/api/deploy.d.ts.map +1 -1
- package/dist/api/deploy.js +86 -68
- package/dist/api/deploy.js.map +1 -1
- package/dist/api/deploy.test.js +119 -55
- package/dist/api/deploy.test.js.map +1 -1
- package/dist/schema/types.d.ts +34 -25
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +15 -3
- package/dist/schema/types.js.map +1 -1
- package/dist/schema/types.test.js +230 -94
- package/dist/schema/types.test.js.map +1 -1
- package/package.json +1 -1
- package/src/api/build.test.ts +30 -0
- package/src/api/deploy.test.ts +201 -62
- package/src/api/deploy.ts +119 -89
- package/src/schema/types.test.ts +294 -96
- package/src/schema/types.ts +145 -68
package/src/schema/types.test.ts
CHANGED
|
@@ -1,226 +1,424 @@
|
|
|
1
|
-
import { describe, it, expect } from
|
|
2
|
-
import { t, isTypeValidator, getTinybirdType, getModifiers } from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { describe, it, expect, expectTypeOf } from "vitest";
|
|
2
|
+
import { t, isTypeValidator, getTinybirdType, getModifiers } from "./types.js";
|
|
3
|
+
import { defineDatasource } from "./datasource.js";
|
|
4
|
+
import { engine } from "./engines.js";
|
|
5
|
+
import type { InferRow } from "../infer/index.js";
|
|
6
|
+
|
|
7
|
+
describe("Type Validators (t.*)", () => {
|
|
8
|
+
describe("Basic types", () => {
|
|
9
|
+
it("generates String type", () => {
|
|
7
10
|
const type = t.string();
|
|
8
|
-
expect(type._tinybirdType).toBe(
|
|
11
|
+
expect(type._tinybirdType).toBe("String");
|
|
9
12
|
});
|
|
10
13
|
|
|
11
|
-
it(
|
|
14
|
+
it("generates Int32 type", () => {
|
|
12
15
|
const type = t.int32();
|
|
13
|
-
expect(type._tinybirdType).toBe(
|
|
16
|
+
expect(type._tinybirdType).toBe("Int32");
|
|
14
17
|
});
|
|
15
18
|
|
|
16
|
-
it(
|
|
19
|
+
it("generates DateTime type", () => {
|
|
17
20
|
const type = t.dateTime();
|
|
18
|
-
expect(type._tinybirdType).toBe(
|
|
21
|
+
expect(type._tinybirdType).toBe("DateTime");
|
|
19
22
|
});
|
|
20
23
|
|
|
21
|
-
it(
|
|
22
|
-
const type = t.dateTime(
|
|
24
|
+
it("generates DateTime with timezone", () => {
|
|
25
|
+
const type = t.dateTime("UTC");
|
|
23
26
|
expect(type._tinybirdType).toBe("DateTime('UTC')");
|
|
24
27
|
});
|
|
25
28
|
|
|
26
|
-
it(
|
|
29
|
+
it("generates Bool type", () => {
|
|
27
30
|
const type = t.bool();
|
|
28
|
-
expect(type._tinybirdType).toBe(
|
|
31
|
+
expect(type._tinybirdType).toBe("Bool");
|
|
29
32
|
});
|
|
30
33
|
|
|
31
|
-
it(
|
|
34
|
+
it("generates UUID type", () => {
|
|
32
35
|
const type = t.uuid();
|
|
33
|
-
expect(type._tinybirdType).toBe(
|
|
36
|
+
expect(type._tinybirdType).toBe("UUID");
|
|
34
37
|
});
|
|
35
38
|
|
|
36
|
-
it(
|
|
39
|
+
it("generates Float64 type", () => {
|
|
37
40
|
const type = t.float64();
|
|
38
|
-
expect(type._tinybirdType).toBe(
|
|
41
|
+
expect(type._tinybirdType).toBe("Float64");
|
|
39
42
|
});
|
|
40
43
|
|
|
41
|
-
it(
|
|
44
|
+
it("generates UInt64 type", () => {
|
|
42
45
|
const type = t.uint64();
|
|
43
|
-
expect(type._tinybirdType).toBe(
|
|
46
|
+
expect(type._tinybirdType).toBe("UInt64");
|
|
44
47
|
});
|
|
45
48
|
});
|
|
46
49
|
|
|
47
|
-
describe(
|
|
48
|
-
it(
|
|
50
|
+
describe("Nullable modifier", () => {
|
|
51
|
+
it("wraps type in Nullable", () => {
|
|
49
52
|
const type = t.string().nullable();
|
|
50
|
-
expect(type._tinybirdType).toBe(
|
|
53
|
+
expect(type._tinybirdType).toBe("Nullable(String)");
|
|
51
54
|
});
|
|
52
55
|
|
|
53
|
-
it(
|
|
56
|
+
it("wraps Int32 in Nullable", () => {
|
|
54
57
|
const type = t.int32().nullable();
|
|
55
|
-
expect(type._tinybirdType).toBe(
|
|
58
|
+
expect(type._tinybirdType).toBe("Nullable(Int32)");
|
|
56
59
|
});
|
|
57
60
|
|
|
58
|
-
it(
|
|
61
|
+
it("sets nullable modifier", () => {
|
|
59
62
|
const type = t.string().nullable();
|
|
60
63
|
expect(type._modifiers.nullable).toBe(true);
|
|
61
64
|
});
|
|
62
65
|
});
|
|
63
66
|
|
|
64
|
-
describe(
|
|
65
|
-
it(
|
|
67
|
+
describe("LowCardinality modifier", () => {
|
|
68
|
+
it("wraps type in LowCardinality", () => {
|
|
66
69
|
const type = t.string().lowCardinality();
|
|
67
|
-
expect(type._tinybirdType).toBe(
|
|
70
|
+
expect(type._tinybirdType).toBe("LowCardinality(String)");
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
it(
|
|
73
|
+
it("sets lowCardinality modifier", () => {
|
|
71
74
|
const type = t.string().lowCardinality();
|
|
72
75
|
expect(type._modifiers.lowCardinality).toBe(true);
|
|
73
76
|
});
|
|
74
77
|
});
|
|
75
78
|
|
|
76
|
-
describe(
|
|
77
|
-
it(
|
|
79
|
+
describe("LowCardinality + Nullable ordering", () => {
|
|
80
|
+
it("generates LowCardinality(Nullable(X)) when chaining .lowCardinality().nullable()", () => {
|
|
78
81
|
const type = t.string().lowCardinality().nullable();
|
|
79
|
-
expect(type._tinybirdType).toBe(
|
|
82
|
+
expect(type._tinybirdType).toBe("LowCardinality(Nullable(String))");
|
|
80
83
|
});
|
|
81
84
|
|
|
82
|
-
it(
|
|
85
|
+
it("generates LowCardinality(Nullable(X)) when chaining .nullable().lowCardinality()", () => {
|
|
83
86
|
const type = t.string().nullable().lowCardinality();
|
|
84
|
-
expect(type._tinybirdType).toBe(
|
|
87
|
+
expect(type._tinybirdType).toBe("LowCardinality(Nullable(String))");
|
|
85
88
|
});
|
|
86
89
|
|
|
87
|
-
it(
|
|
90
|
+
it("preserves both modifiers when chained", () => {
|
|
88
91
|
const type = t.string().lowCardinality().nullable();
|
|
89
92
|
expect(type._modifiers.lowCardinality).toBe(true);
|
|
90
93
|
expect(type._modifiers.nullable).toBe(true);
|
|
91
94
|
});
|
|
92
95
|
});
|
|
93
96
|
|
|
94
|
-
describe(
|
|
95
|
-
it(
|
|
96
|
-
const type = t.string().default(
|
|
97
|
+
describe("Default values", () => {
|
|
98
|
+
it("sets hasDefault modifier", () => {
|
|
99
|
+
const type = t.string().default("test");
|
|
97
100
|
expect(type._modifiers.hasDefault).toBe(true);
|
|
98
101
|
});
|
|
99
102
|
|
|
100
|
-
it(
|
|
101
|
-
const type = t.string().default(
|
|
102
|
-
expect(type._modifiers.defaultValue).toBe(
|
|
103
|
+
it("stores defaultValue in modifiers", () => {
|
|
104
|
+
const type = t.string().default("test");
|
|
105
|
+
expect(type._modifiers.defaultValue).toBe("test");
|
|
103
106
|
});
|
|
104
107
|
|
|
105
|
-
it(
|
|
108
|
+
it("works with numeric defaults", () => {
|
|
106
109
|
const type = t.int32().default(42);
|
|
107
110
|
expect(type._modifiers.defaultValue).toBe(42);
|
|
108
111
|
});
|
|
109
112
|
});
|
|
110
113
|
|
|
111
|
-
describe(
|
|
112
|
-
it(
|
|
113
|
-
const type = t.string().codec(
|
|
114
|
-
expect(type._modifiers.codec).toBe(
|
|
114
|
+
describe("Codec modifier", () => {
|
|
115
|
+
it("sets codec in modifiers", () => {
|
|
116
|
+
const type = t.string().codec("LZ4");
|
|
117
|
+
expect(type._modifiers.codec).toBe("LZ4");
|
|
115
118
|
});
|
|
116
119
|
});
|
|
117
120
|
|
|
118
|
-
describe(
|
|
119
|
-
it(
|
|
120
|
-
const type = t.string().jsonPath(
|
|
121
|
-
expect(type._modifiers.jsonPath).toBe(
|
|
121
|
+
describe("jsonPath modifier", () => {
|
|
122
|
+
it("sets jsonPath in modifiers", () => {
|
|
123
|
+
const type = t.string().jsonPath("$.payload.id");
|
|
124
|
+
expect(type._modifiers.jsonPath).toBe("$.payload.id");
|
|
122
125
|
});
|
|
123
126
|
|
|
124
|
-
it(
|
|
125
|
-
const type = t.string().nullable().jsonPath(
|
|
126
|
-
expect(type._tinybirdType).toBe(
|
|
127
|
+
it("supports chaining with other modifiers", () => {
|
|
128
|
+
const type = t.string().nullable().jsonPath("$.user.name");
|
|
129
|
+
expect(type._tinybirdType).toBe("Nullable(String)");
|
|
127
130
|
expect(type._modifiers.nullable).toBe(true);
|
|
128
|
-
expect(type._modifiers.jsonPath).toBe(
|
|
131
|
+
expect(type._modifiers.jsonPath).toBe("$.user.name");
|
|
129
132
|
});
|
|
130
133
|
});
|
|
131
134
|
|
|
132
|
-
describe(
|
|
133
|
-
it(
|
|
135
|
+
describe("Complex types", () => {
|
|
136
|
+
it("generates Array type", () => {
|
|
134
137
|
const type = t.array(t.string());
|
|
135
|
-
expect(type._tinybirdType).toBe(
|
|
138
|
+
expect(type._tinybirdType).toBe("Array(String)");
|
|
136
139
|
});
|
|
137
140
|
|
|
138
|
-
it(
|
|
141
|
+
it("generates nested Array type", () => {
|
|
139
142
|
const type = t.array(t.int32());
|
|
140
|
-
expect(type._tinybirdType).toBe(
|
|
143
|
+
expect(type._tinybirdType).toBe("Array(Int32)");
|
|
141
144
|
});
|
|
142
145
|
|
|
143
|
-
it(
|
|
146
|
+
it("generates Map type", () => {
|
|
144
147
|
const type = t.map(t.string(), t.int32());
|
|
145
|
-
expect(type._tinybirdType).toBe(
|
|
148
|
+
expect(type._tinybirdType).toBe("Map(String, Int32)");
|
|
146
149
|
});
|
|
147
150
|
|
|
148
|
-
it(
|
|
151
|
+
it("generates Decimal type", () => {
|
|
149
152
|
const type = t.decimal(10, 2);
|
|
150
|
-
expect(type._tinybirdType).toBe(
|
|
153
|
+
expect(type._tinybirdType).toBe("Decimal(10, 2)");
|
|
151
154
|
});
|
|
152
155
|
|
|
153
|
-
it(
|
|
156
|
+
it("generates FixedString type", () => {
|
|
154
157
|
const type = t.fixedString(3);
|
|
155
|
-
expect(type._tinybirdType).toBe(
|
|
158
|
+
expect(type._tinybirdType).toBe("FixedString(3)");
|
|
156
159
|
});
|
|
157
160
|
|
|
158
|
-
it(
|
|
161
|
+
it("generates Tuple type", () => {
|
|
159
162
|
const type = t.tuple(t.string(), t.int32());
|
|
160
|
-
expect(type._tinybirdType).toBe(
|
|
163
|
+
expect(type._tinybirdType).toBe("Tuple(String, Int32)");
|
|
161
164
|
});
|
|
162
165
|
|
|
163
|
-
it(
|
|
166
|
+
it("generates DateTime64 type", () => {
|
|
164
167
|
const type = t.dateTime64(3);
|
|
165
|
-
expect(type._tinybirdType).toBe(
|
|
168
|
+
expect(type._tinybirdType).toBe("DateTime64(3)");
|
|
166
169
|
});
|
|
167
170
|
|
|
168
|
-
it(
|
|
169
|
-
const type = t.dateTime64(3,
|
|
171
|
+
it("generates DateTime64 with timezone", () => {
|
|
172
|
+
const type = t.dateTime64(3, "UTC");
|
|
170
173
|
expect(type._tinybirdType).toBe("DateTime64(3, 'UTC')");
|
|
171
174
|
});
|
|
172
175
|
});
|
|
173
176
|
|
|
174
|
-
describe(
|
|
175
|
-
it(
|
|
177
|
+
describe("Helper functions", () => {
|
|
178
|
+
it("isTypeValidator returns true for validators", () => {
|
|
176
179
|
expect(isTypeValidator(t.string())).toBe(true);
|
|
177
180
|
});
|
|
178
181
|
|
|
179
|
-
it(
|
|
180
|
-
expect(isTypeValidator(
|
|
182
|
+
it("isTypeValidator returns false for non-validators", () => {
|
|
183
|
+
expect(isTypeValidator("string")).toBe(false);
|
|
181
184
|
expect(isTypeValidator({})).toBe(false);
|
|
182
185
|
expect(isTypeValidator(null)).toBe(false);
|
|
183
186
|
});
|
|
184
187
|
|
|
185
|
-
it(
|
|
186
|
-
expect(getTinybirdType(t.string())).toBe(
|
|
188
|
+
it("getTinybirdType returns type string", () => {
|
|
189
|
+
expect(getTinybirdType(t.string())).toBe("String");
|
|
187
190
|
});
|
|
188
191
|
|
|
189
|
-
it(
|
|
192
|
+
it("getModifiers returns modifiers object", () => {
|
|
190
193
|
const modifiers = getModifiers(t.string().nullable());
|
|
191
194
|
expect(modifiers.nullable).toBe(true);
|
|
192
195
|
});
|
|
193
196
|
});
|
|
194
197
|
|
|
195
|
-
describe(
|
|
196
|
-
it(
|
|
197
|
-
const type = t.string().lowCardinality().default(
|
|
198
|
-
expect(type._tinybirdType).toBe(
|
|
198
|
+
describe("Chained modifiers", () => {
|
|
199
|
+
it("supports multiple modifiers", () => {
|
|
200
|
+
const type = t.string().lowCardinality().default("test");
|
|
201
|
+
expect(type._tinybirdType).toBe("LowCardinality(String)");
|
|
199
202
|
expect(type._modifiers.lowCardinality).toBe(true);
|
|
200
203
|
expect(type._modifiers.hasDefault).toBe(true);
|
|
201
|
-
expect(type._modifiers.defaultValue).toBe(
|
|
204
|
+
expect(type._modifiers.defaultValue).toBe("test");
|
|
202
205
|
});
|
|
203
206
|
});
|
|
204
207
|
|
|
205
|
-
describe(
|
|
206
|
-
it(
|
|
207
|
-
const type = t.enum8(
|
|
208
|
-
expect(type._tinybirdType).toBe(
|
|
208
|
+
describe("Enum types", () => {
|
|
209
|
+
it("generates Enum8 with value mapping", () => {
|
|
210
|
+
const type = t.enum8("active", "inactive", "pending");
|
|
211
|
+
expect(type._tinybirdType).toBe(
|
|
212
|
+
"Enum8('active' = 1, 'inactive' = 2, 'pending' = 3)",
|
|
213
|
+
);
|
|
209
214
|
});
|
|
210
215
|
|
|
211
|
-
it(
|
|
212
|
-
const type = t.enum16(
|
|
213
|
-
expect(type._tinybirdType).toBe(
|
|
216
|
+
it("generates Enum16 with value mapping", () => {
|
|
217
|
+
const type = t.enum16("draft", "published", "archived");
|
|
218
|
+
expect(type._tinybirdType).toBe(
|
|
219
|
+
"Enum16('draft' = 1, 'published' = 2, 'archived' = 3)",
|
|
220
|
+
);
|
|
214
221
|
});
|
|
215
222
|
|
|
216
|
-
it(
|
|
217
|
-
const type = t.enum8("it's ok",
|
|
223
|
+
it("escapes single quotes in enum values", () => {
|
|
224
|
+
const type = t.enum8("it's ok", "normal");
|
|
218
225
|
expect(type._tinybirdType).toBe("Enum8('it\\'s ok' = 1, 'normal' = 2)");
|
|
219
226
|
});
|
|
220
227
|
|
|
221
|
-
it(
|
|
222
|
-
const type = t.enum8(
|
|
228
|
+
it("handles single enum value", () => {
|
|
229
|
+
const type = t.enum8("only");
|
|
223
230
|
expect(type._tinybirdType).toBe("Enum8('only' = 1)");
|
|
224
231
|
});
|
|
225
232
|
});
|
|
233
|
+
|
|
234
|
+
describe("Custom type generics", () => {
|
|
235
|
+
// Branded/nominal type helpers for testing
|
|
236
|
+
type UserId = string & { readonly __brand: "UserId" };
|
|
237
|
+
type TraceId = string & { readonly __brand: "TraceId" };
|
|
238
|
+
type Timestamp = string & { readonly __brand: "Timestamp" };
|
|
239
|
+
type Count = number & { readonly __brand: "Count" };
|
|
240
|
+
type Price = number & { readonly __brand: "Price" };
|
|
241
|
+
type BigId = bigint & { readonly __brand: "BigId" };
|
|
242
|
+
type IsActive = boolean & { readonly __brand: "IsActive" };
|
|
243
|
+
|
|
244
|
+
describe("runtime behavior unchanged", () => {
|
|
245
|
+
it("string with generic produces same _tinybirdType", () => {
|
|
246
|
+
expect(t.string<UserId>()._tinybirdType).toBe(t.string()._tinybirdType);
|
|
247
|
+
expect(t.string<UserId>()._tinybirdType).toBe("String");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("int32 with generic produces same _tinybirdType", () => {
|
|
251
|
+
expect(t.int32<Count>()._tinybirdType).toBe(t.int32()._tinybirdType);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("uuid with generic produces same _tinybirdType", () => {
|
|
255
|
+
expect(t.uuid<TraceId>()._tinybirdType).toBe("UUID");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("dateTime with generic produces same _tinybirdType", () => {
|
|
259
|
+
expect(t.dateTime<Timestamp>()._tinybirdType).toBe("DateTime");
|
|
260
|
+
expect(t.dateTime<Timestamp>("UTC")._tinybirdType).toBe(
|
|
261
|
+
"DateTime('UTC')",
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("bool with generic produces same _tinybirdType", () => {
|
|
266
|
+
expect(t.bool<IsActive>()._tinybirdType).toBe("Bool");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("int128 with generic produces same _tinybirdType", () => {
|
|
270
|
+
expect(t.int128<BigId>()._tinybirdType).toBe("Int128");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("decimal with generic produces same _tinybirdType", () => {
|
|
274
|
+
expect(t.decimal<Price>(10, 2)._tinybirdType).toBe("Decimal(10, 2)");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("fixedString with generic produces same _tinybirdType", () => {
|
|
278
|
+
type CountryCode = string & { readonly __brand: "CountryCode" };
|
|
279
|
+
expect(t.fixedString<CountryCode>(2)._tinybirdType).toBe(
|
|
280
|
+
"FixedString(2)",
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe("modifiers work with custom generics", () => {
|
|
286
|
+
it("nullable", () => {
|
|
287
|
+
const v = t.string<UserId>().nullable();
|
|
288
|
+
expect(v._tinybirdType).toBe("Nullable(String)");
|
|
289
|
+
expect(v._modifiers.nullable).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("lowCardinality", () => {
|
|
293
|
+
expect(t.string<UserId>().lowCardinality()._tinybirdType).toBe(
|
|
294
|
+
"LowCardinality(String)",
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("default", () => {
|
|
299
|
+
const v = t.string<UserId>().default("fallback" as UserId);
|
|
300
|
+
expect(v._modifiers.hasDefault).toBe(true);
|
|
301
|
+
expect(v._modifiers.defaultValue).toBe("fallback");
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("type inference", () => {
|
|
306
|
+
it("validators without generics still infer base types", () => {
|
|
307
|
+
expectTypeOf(t.string()._type).toEqualTypeOf<string>();
|
|
308
|
+
expectTypeOf(t.int32()._type).toEqualTypeOf<number>();
|
|
309
|
+
expectTypeOf(t.bool()._type).toEqualTypeOf<boolean>();
|
|
310
|
+
expectTypeOf(t.int128()._type).toEqualTypeOf<bigint>();
|
|
311
|
+
expectTypeOf(t.uuid()._type).toEqualTypeOf<string>();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("validators with generics infer the custom type", () => {
|
|
315
|
+
expectTypeOf(t.string<UserId>()._type).toEqualTypeOf<UserId>();
|
|
316
|
+
expectTypeOf(t.uuid<TraceId>()._type).toEqualTypeOf<TraceId>();
|
|
317
|
+
expectTypeOf(t.int32<Count>()._type).toEqualTypeOf<Count>();
|
|
318
|
+
expectTypeOf(t.bool<IsActive>()._type).toEqualTypeOf<IsActive>();
|
|
319
|
+
expectTypeOf(t.int128<BigId>()._type).toEqualTypeOf<BigId>();
|
|
320
|
+
expectTypeOf(t.dateTime<Timestamp>()._type).toEqualTypeOf<Timestamp>();
|
|
321
|
+
expectTypeOf(
|
|
322
|
+
t.dateTime<Timestamp>("UTC")._type,
|
|
323
|
+
).toEqualTypeOf<Timestamp>();
|
|
324
|
+
expectTypeOf(
|
|
325
|
+
t.dateTime64<Timestamp>(3)._type,
|
|
326
|
+
).toEqualTypeOf<Timestamp>();
|
|
327
|
+
expectTypeOf(t.decimal<Price>(10, 2)._type).toEqualTypeOf<Price>();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("custom types flow through nullable", () => {
|
|
331
|
+
expectTypeOf(
|
|
332
|
+
t.string<UserId>().nullable()._type,
|
|
333
|
+
).toEqualTypeOf<UserId | null>();
|
|
334
|
+
expectTypeOf(
|
|
335
|
+
t.int32<Count>().nullable()._type,
|
|
336
|
+
).toEqualTypeOf<Count | null>();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("custom types flow through lowCardinality", () => {
|
|
340
|
+
expectTypeOf(
|
|
341
|
+
t.string<UserId>().lowCardinality()._type,
|
|
342
|
+
).toEqualTypeOf<UserId>();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("custom types flow through InferRow", () => {
|
|
346
|
+
const ds = defineDatasource("test_custom_types", {
|
|
347
|
+
schema: {
|
|
348
|
+
user_id: t.string<UserId>(),
|
|
349
|
+
event_count: t.int32<Count>(),
|
|
350
|
+
created_at: t.dateTime<Timestamp>(),
|
|
351
|
+
name: t.string(),
|
|
352
|
+
},
|
|
353
|
+
engine: engine.mergeTree({ sortingKey: ["user_id"] }),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
type Row = InferRow<typeof ds>;
|
|
357
|
+
|
|
358
|
+
expectTypeOf<Row["user_id"]>().toEqualTypeOf<UserId>();
|
|
359
|
+
expectTypeOf<Row["event_count"]>().toEqualTypeOf<Count>();
|
|
360
|
+
expectTypeOf<Row["created_at"]>().toEqualTypeOf<Timestamp>();
|
|
361
|
+
expectTypeOf<Row["name"]>().toEqualTypeOf<string>();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("rejects generics that violate base type constraint", () => {
|
|
365
|
+
// @ts-expect-error - number does not extend string
|
|
366
|
+
t.string<number>();
|
|
367
|
+
|
|
368
|
+
// @ts-expect-error - string does not extend number
|
|
369
|
+
t.int32<string>();
|
|
370
|
+
|
|
371
|
+
// @ts-expect-error - string does not extend boolean
|
|
372
|
+
t.bool<string>();
|
|
373
|
+
|
|
374
|
+
// @ts-expect-error - number does not extend bigint
|
|
375
|
+
t.int128<number>();
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe("all validators accept custom generics", () => {
|
|
380
|
+
it("string-based validators", () => {
|
|
381
|
+
type S = string & { readonly __brand: "S" };
|
|
382
|
+
expectTypeOf(t.string<S>()._type).toEqualTypeOf<S>();
|
|
383
|
+
expectTypeOf(t.fixedString<S>(10)._type).toEqualTypeOf<S>();
|
|
384
|
+
expectTypeOf(t.uuid<S>()._type).toEqualTypeOf<S>();
|
|
385
|
+
expectTypeOf(t.ipv4<S>()._type).toEqualTypeOf<S>();
|
|
386
|
+
expectTypeOf(t.ipv6<S>()._type).toEqualTypeOf<S>();
|
|
387
|
+
expectTypeOf(t.date<S>()._type).toEqualTypeOf<S>();
|
|
388
|
+
expectTypeOf(t.date32<S>()._type).toEqualTypeOf<S>();
|
|
389
|
+
expectTypeOf(t.dateTime<S>()._type).toEqualTypeOf<S>();
|
|
390
|
+
expectTypeOf(t.dateTime<S>("UTC")._type).toEqualTypeOf<S>();
|
|
391
|
+
expectTypeOf(t.dateTime64<S>()._type).toEqualTypeOf<S>();
|
|
392
|
+
expectTypeOf(t.dateTime64<S>(6, "UTC")._type).toEqualTypeOf<S>();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("number-based validators", () => {
|
|
396
|
+
type N = number & { readonly __brand: "N" };
|
|
397
|
+
expectTypeOf(t.int8<N>()._type).toEqualTypeOf<N>();
|
|
398
|
+
expectTypeOf(t.int16<N>()._type).toEqualTypeOf<N>();
|
|
399
|
+
expectTypeOf(t.int32<N>()._type).toEqualTypeOf<N>();
|
|
400
|
+
expectTypeOf(t.int64<N>()._type).toEqualTypeOf<N>();
|
|
401
|
+
expectTypeOf(t.uint8<N>()._type).toEqualTypeOf<N>();
|
|
402
|
+
expectTypeOf(t.uint16<N>()._type).toEqualTypeOf<N>();
|
|
403
|
+
expectTypeOf(t.uint32<N>()._type).toEqualTypeOf<N>();
|
|
404
|
+
expectTypeOf(t.uint64<N>()._type).toEqualTypeOf<N>();
|
|
405
|
+
expectTypeOf(t.float32<N>()._type).toEqualTypeOf<N>();
|
|
406
|
+
expectTypeOf(t.float64<N>()._type).toEqualTypeOf<N>();
|
|
407
|
+
expectTypeOf(t.decimal<N>(10, 2)._type).toEqualTypeOf<N>();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("bigint-based validators", () => {
|
|
411
|
+
type B = bigint & { readonly __brand: "B" };
|
|
412
|
+
expectTypeOf(t.int128<B>()._type).toEqualTypeOf<B>();
|
|
413
|
+
expectTypeOf(t.int256<B>()._type).toEqualTypeOf<B>();
|
|
414
|
+
expectTypeOf(t.uint128<B>()._type).toEqualTypeOf<B>();
|
|
415
|
+
expectTypeOf(t.uint256<B>()._type).toEqualTypeOf<B>();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("boolean-based validators", () => {
|
|
419
|
+
type Bool = boolean & { readonly __brand: "Bool" };
|
|
420
|
+
expectTypeOf(t.bool<Bool>()._type).toEqualTypeOf<Bool>();
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
});
|
|
226
424
|
});
|