@leon740727/type-schema 0.0.3

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/src/type.ts ADDED
@@ -0,0 +1,367 @@
1
+ import { mergeRight } from "ramda";
2
+ import { addQuestionMarks, flatten, resolveTuple } from "./util";
3
+
4
+ /**
5
+ * schema 有幾種類別
6
+ * 1. atom: 沒有 inner schema 描述的值。例如 number, string ...
7
+ * 注意,array 或 object 也可以是 atom 的,只要其值沒有另外的 schema 描述
8
+ * 2. compound: 裡面的值有另外的 inner schema 來描述。又分成二種 object, array
9
+ */
10
+
11
+ export enum SchemaType {
12
+ atom,
13
+ array, // compound array
14
+ object, // compound object
15
+ tuple,
16
+ union,
17
+ }
18
+
19
+ type Attr = { [field: string]: any };
20
+
21
+ export class AtomSchema<VT, VT2> {
22
+ constructor(
23
+ readonly type: SchemaType.atom,
24
+ readonly value: VT,
25
+ readonly isNullable: boolean,
26
+ readonly isOptional: boolean,
27
+ readonly isa: (v) => string | null, // 傳回錯誤訊息
28
+ readonly transform: (v: VT) => VT2,
29
+ readonly attr: Attr // 額外附加的屬性
30
+ ) {}
31
+
32
+ nullable() {
33
+ return new AtomSchema<VT | null, VT2>(
34
+ SchemaType.atom,
35
+ this.value,
36
+ true,
37
+ this.isOptional,
38
+ this.isa,
39
+ this.transform,
40
+ this.attr
41
+ );
42
+ }
43
+
44
+ optional() {
45
+ return new AtomSchema<VT | undefined, VT2>(
46
+ SchemaType.atom,
47
+ this.value,
48
+ this.isNullable,
49
+ true,
50
+ this.isa,
51
+ this.transform,
52
+ this.attr
53
+ );
54
+ }
55
+
56
+ transformer<T2>(fn: (v: NonNullable<VT>) => T2) {
57
+ return new AtomSchema<VT, T2>(
58
+ SchemaType.atom,
59
+ this.value,
60
+ this.isNullable,
61
+ this.isOptional,
62
+ this.isa,
63
+ fn,
64
+ this.attr
65
+ );
66
+ }
67
+
68
+ set(attr: Attr) {
69
+ const newAttr = mergeRight(this.attr, attr);
70
+ return new AtomSchema<VT, VT2>(
71
+ SchemaType.atom,
72
+ this.value,
73
+ this.isNullable,
74
+ this.isOptional,
75
+ this.isa,
76
+ this.transform,
77
+ newAttr
78
+ );
79
+ }
80
+ }
81
+
82
+ export class ArraySchema<
83
+ InnerSchema extends InnerSchemaForArraySchema | null | undefined
84
+ > {
85
+ readonly innerSchema: InnerSchema;
86
+
87
+ constructor(
88
+ readonly type: SchemaType.array,
89
+ readonly itemSchema: Schema,
90
+ readonly isNullable: boolean,
91
+ readonly isOptional: boolean,
92
+ readonly attr: Attr // 額外附加的屬性
93
+ ) {}
94
+
95
+ nullable() {
96
+ return new ArraySchema<InnerSchema | null>(
97
+ SchemaType.array,
98
+ this.itemSchema,
99
+ true,
100
+ this.isOptional,
101
+ this.attr
102
+ );
103
+ }
104
+
105
+ optional() {
106
+ return new ArraySchema<InnerSchema | undefined>(
107
+ SchemaType.array,
108
+ this.itemSchema,
109
+ this.isNullable,
110
+ true,
111
+ this.attr
112
+ );
113
+ }
114
+
115
+ set(attr: Attr) {
116
+ const newAttr = mergeRight(this.attr, attr);
117
+ return new ArraySchema<InnerSchema>(
118
+ SchemaType.array,
119
+ this.itemSchema,
120
+ this.isNullable,
121
+ this.isOptional,
122
+ newAttr
123
+ );
124
+ }
125
+ }
126
+
127
+ export class ObjectSchema<
128
+ InnerSchema extends InnerSchemaForObjectSchema | null | undefined
129
+ > {
130
+ constructor(
131
+ readonly type: SchemaType.object,
132
+ readonly innerSchema: InnerSchema,
133
+ readonly isNullable: boolean,
134
+ readonly isOptional: boolean,
135
+ readonly attr: Attr // 額外附加的屬性
136
+ ) {}
137
+
138
+ nullable() {
139
+ return new ObjectSchema<InnerSchema | null>(
140
+ SchemaType.object,
141
+ this.innerSchema,
142
+ true,
143
+ this.isOptional,
144
+ this.attr
145
+ );
146
+ }
147
+
148
+ optional() {
149
+ return new ObjectSchema<InnerSchema | undefined>(
150
+ SchemaType.object,
151
+ this.innerSchema,
152
+ this.isNullable,
153
+ true,
154
+ this.attr
155
+ );
156
+ }
157
+
158
+ set(attr: Attr) {
159
+ const newAttr = mergeRight(this.attr, attr);
160
+ return new ObjectSchema<InnerSchema>(
161
+ SchemaType.object,
162
+ this.innerSchema,
163
+ this.isNullable,
164
+ this.isOptional,
165
+ newAttr
166
+ );
167
+ }
168
+ }
169
+
170
+ class TupleSchema<
171
+ InnerSchema extends [Schema, ...Schema[]] | null | undefined
172
+ > {
173
+ constructor(
174
+ readonly type: SchemaType.tuple,
175
+ readonly innerSchema: InnerSchema,
176
+ readonly isNullable: boolean,
177
+ readonly isOptional: boolean,
178
+ readonly attr: Attr // 額外附加的屬性
179
+ ) {}
180
+
181
+ nullable() {
182
+ return new TupleSchema<InnerSchema | null>(
183
+ SchemaType.tuple,
184
+ this.innerSchema,
185
+ true,
186
+ this.isOptional,
187
+ this.attr
188
+ );
189
+ }
190
+
191
+ optional() {
192
+ return new TupleSchema<InnerSchema | undefined>(
193
+ SchemaType.tuple,
194
+ this.innerSchema,
195
+ this.isNullable,
196
+ true,
197
+ this.attr
198
+ );
199
+ }
200
+
201
+ set(attr: Attr) {
202
+ const newAttr = mergeRight(this.attr, attr);
203
+ return new TupleSchema<InnerSchema>(
204
+ SchemaType.tuple,
205
+ this.innerSchema,
206
+ this.isNullable,
207
+ this.isOptional,
208
+ newAttr
209
+ );
210
+ }
211
+ }
212
+
213
+ class UnionSchema<InnerSchema extends Schema[] | null | undefined> {
214
+ constructor(
215
+ readonly type: SchemaType.union,
216
+ readonly innerSchema: InnerSchema,
217
+ readonly isNullable: boolean,
218
+ readonly isOptional: boolean,
219
+ readonly attr: Attr // 額外附加的屬性
220
+ ) {}
221
+
222
+ nullable() {
223
+ return new UnionSchema<InnerSchema | null>(
224
+ SchemaType.union,
225
+ this.innerSchema,
226
+ true,
227
+ this.isOptional,
228
+ this.attr
229
+ );
230
+ }
231
+
232
+ optional() {
233
+ return new UnionSchema<InnerSchema | undefined>(
234
+ SchemaType.union,
235
+ this.innerSchema,
236
+ this.isNullable,
237
+ true,
238
+ this.attr
239
+ );
240
+ }
241
+
242
+ set(attr: Attr) {
243
+ const newAttr = mergeRight(this.attr, attr);
244
+ return new UnionSchema<InnerSchema>(
245
+ SchemaType.union,
246
+ this.innerSchema,
247
+ this.isNullable,
248
+ this.isOptional,
249
+ newAttr
250
+ );
251
+ }
252
+ }
253
+
254
+ export type Schema =
255
+ | AtomSchema<any, any>
256
+ | ArraySchema<InnerSchemaForArraySchema | null | undefined>
257
+ | TupleSchema<[Schema, ...Schema[]] | null | undefined>
258
+ | UnionSchema<Schema[] | null | undefined>
259
+ | ObjectSchema<InnerSchemaForObjectSchema | null | undefined>;
260
+
261
+ type InnerSchemaForArraySchema = Schema[];
262
+
263
+ export type InnerSchemaForObjectSchema = {
264
+ [field: string]: Schema;
265
+ };
266
+
267
+ export namespace Schema {
268
+ export function value<T>(isa: (v) => string | null) {
269
+ return new AtomSchema<T, T>(
270
+ SchemaType.atom,
271
+ null as any,
272
+ false,
273
+ false,
274
+ isa,
275
+ (v: T) => v,
276
+ {}
277
+ );
278
+ }
279
+
280
+ export function array<S extends Schema>(itemSchema: S) {
281
+ return new ArraySchema<S[]>(SchemaType.array, itemSchema, false, false, {});
282
+ }
283
+
284
+ export function object<S extends InnerSchemaForObjectSchema>(innerSchema: S) {
285
+ return new ObjectSchema<S>(
286
+ SchemaType.object,
287
+ innerSchema,
288
+ false,
289
+ false,
290
+ {}
291
+ );
292
+ }
293
+
294
+ export function tuple<tuple extends [Schema, ...Schema[]]>(schemas: tuple) {
295
+ return new TupleSchema<tuple>(SchemaType.tuple, schemas, false, false, {});
296
+ }
297
+
298
+ export function union<union extends Schema[]>(schemas: union) {
299
+ return new UnionSchema<union>(SchemaType.union, schemas, false, false, {});
300
+ }
301
+ }
302
+
303
+ export type fetchAtom<T extends Schema> = T extends { type: SchemaType.atom }
304
+ ? T
305
+ : never;
306
+ export type fetchArray<T extends Schema> = T extends { type: SchemaType.array }
307
+ ? T
308
+ : never;
309
+ export type fetchObject<T extends Schema> = T extends {
310
+ type: SchemaType.object;
311
+ }
312
+ ? T
313
+ : never;
314
+ export type fetchTuple<T extends Schema> = Extract<T, TupleSchema<any>>;
315
+ export type fetchUnion<T extends Schema> = Extract<T, UnionSchema<any>>;
316
+
317
+ export type build<S extends Schema, transformed extends boolean> = S extends {
318
+ type: SchemaType.object;
319
+ }
320
+ ? buildObj<fetchObject<S>["innerSchema"], transformed>
321
+ : S extends { type: SchemaType.array }
322
+ ? buildArray<fetchArray<S>["innerSchema"], transformed>
323
+ : S extends { type: SchemaType.tuple }
324
+ ? buildTuple<fetchTuple<S>["innerSchema"], transformed>
325
+ : S extends { type: SchemaType.union }
326
+ ? buildUnion<fetchUnion<S>["innerSchema"], transformed>
327
+ : S extends { type: SchemaType.atom }
328
+ ? buildAtom<fetchAtom<S>, transformed>
329
+ : never;
330
+
331
+ type buildObj<
332
+ OS extends InnerSchemaForObjectSchema | null | undefined,
333
+ transformed extends boolean
334
+ > = OS extends InnerSchemaForObjectSchema
335
+ ? flatten<addQuestionMarks<{ [f in keyof OS]: build<OS[f], transformed> }>>
336
+ : OS;
337
+
338
+ type buildArray<
339
+ S extends InnerSchemaForArraySchema | null | undefined,
340
+ transformed extends boolean
341
+ > = S extends InnerSchemaForArraySchema ? build<S[number], transformed>[] : S;
342
+
343
+ type buildTuple<
344
+ S extends any[] | null | undefined,
345
+ transformed extends boolean
346
+ > = S extends [Schema, ...Schema[]]
347
+ ? [
348
+ build<resolveTuple<S>[0], transformed>,
349
+ ...buildTuple<resolveTuple<S>[1], transformed>
350
+ ]
351
+ : S;
352
+
353
+ type buildUnion<
354
+ S extends Schema[] | null | undefined,
355
+ transformed extends boolean
356
+ > = S extends Schema[] ? build<S[number], transformed> : S;
357
+
358
+ type buildAtom<
359
+ S extends Schema | null | undefined,
360
+ transformed extends boolean
361
+ > = S extends Schema
362
+ ? transformed extends true
363
+ ?
364
+ | ReturnType<fetchAtom<S>["transform"]>
365
+ | Extract<fetchAtom<S>["value"], null | undefined>
366
+ : fetchAtom<S>["value"]
367
+ : S;
package/src/util.ts ADDED
@@ -0,0 +1,35 @@
1
+ // ref: https://github.com/colinhacks/zod
2
+
3
+ export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;
4
+
5
+ export type addQuestionMarks<T extends object> = {
6
+ [K in requiredKeys<T>]: T[K];
7
+ } & {
8
+ [K in optionalKeys<T>]?: T[K];
9
+ };
10
+
11
+ export type resolveTuple<tuple> = tuple extends [infer h, ...infer r]
12
+ ? [h, r]
13
+ : never;
14
+
15
+ type identity<T> = T;
16
+
17
+ type optionalKeys<T extends object> = {
18
+ [k in keyof T]: undefined extends T[k] ? k : never;
19
+ }[keyof T];
20
+
21
+ type requiredKeys<T extends object> = {
22
+ [k in keyof T]: undefined extends T[k] ? never : k;
23
+ }[keyof T];
24
+
25
+ class AssertError extends Error {}
26
+
27
+ export function assert(condition: unknown, message: string): asserts condition {
28
+ if (!condition) {
29
+ throw new AssertError(message);
30
+ }
31
+ }
32
+
33
+ export function pair<A, B>(a: A, b: B): [A, B] {
34
+ return [a, b];
35
+ }
package/test/enums.ts ADDED
@@ -0,0 +1,32 @@
1
+ import * as assert from "assert";
2
+ import * as Schema from "../src/index";
3
+
4
+ declare const describe, it;
5
+
6
+ describe("enums", () => {
7
+ const schema = Schema.enums([1, 2, "yes", "no"]);
8
+ type Types = Schema.buildType<typeof schema>;
9
+
10
+ it("type", () => {
11
+ const t1: Types = 1;
12
+ const t2: Types = 2;
13
+ const t3: Types = "yes";
14
+ const t4: Types = "no";
15
+ //@ts-expect-error
16
+ const t5: Types = 3;
17
+ //@ts-expect-error
18
+ const t6: Types = "error";
19
+ });
20
+
21
+ it("transform", () => {
22
+ assert.strictEqual(Schema.transform(schema, 2)[0], null);
23
+ assert.strictEqual(
24
+ Schema.transform(schema, 3)[0],
25
+ "not a valid enum value, value should be one of [1,2,yes,no]"
26
+ );
27
+ assert.strictEqual(
28
+ Schema.transform(schema, "e")[0],
29
+ "not a valid enum value, value should be one of [1,2,yes,no]"
30
+ );
31
+ });
32
+ });
@@ -0,0 +1,84 @@
1
+ import * as assert from "assert";
2
+ import * as Schema from "../src/index";
3
+
4
+ declare const describe, it;
5
+
6
+ describe("nullable field", () => {
7
+ describe("atom", () => {
8
+ const schema = Schema.object({
9
+ name: Schema.string(),
10
+ age: Schema.string().transformer((v) => parseInt(v)),
11
+ });
12
+
13
+ const nullableSchema = Schema.object({
14
+ name: Schema.string(),
15
+ age: Schema.string()
16
+ .nullable()
17
+ .transformer((v) => parseInt(v)),
18
+ });
19
+
20
+ const o = {
21
+ name: "jack",
22
+ age: null,
23
+ };
24
+
25
+ it("is null -- only check", () => {
26
+ assert.ok(Schema.check(schema, o) !== null);
27
+ assert.ok(Schema.check(nullableSchema, o) === null);
28
+ });
29
+
30
+ it("is null -- transform", () => {
31
+ const [error, _] = Schema.transform(schema, o);
32
+ assert.ok(error !== null);
33
+
34
+ const [__, m] = Schema.transform(nullableSchema, o);
35
+ assert.ok(m !== null);
36
+ assert.ok(m.age === null);
37
+ });
38
+ });
39
+
40
+ describe("array", () => {
41
+ const schema = Schema.object({
42
+ name: Schema.string(),
43
+ friends: Schema.array(Schema.number()),
44
+ });
45
+
46
+ const nullableSchema = Schema.object({
47
+ name: Schema.string(),
48
+ friends: Schema.array(Schema.number()).nullable(),
49
+ });
50
+
51
+ const o = {
52
+ name: "jack",
53
+ friends: null,
54
+ };
55
+
56
+ it("is null -- only check", () => {
57
+ assert.ok(Schema.check(schema, o) !== null);
58
+ assert.ok(Schema.check(nullableSchema, o) === null);
59
+ });
60
+
61
+ it("is null -- transform", () => {
62
+ const [error, _] = Schema.transform(schema, o);
63
+ assert.ok(error !== null);
64
+
65
+ const [__, m] = Schema.transform(nullableSchema, o);
66
+ assert.ok(m !== null);
67
+ assert.ok(m.friends === null);
68
+ });
69
+ });
70
+ });
71
+
72
+ it("pass null to a non-nullable schema", () => {
73
+ const arraySchema = Schema.array(Schema.string());
74
+
75
+ const objSchema = Schema.object({
76
+ name: Schema.string(),
77
+ });
78
+
79
+ assert.strictEqual(Schema.check(objSchema, null), "is not an object");
80
+ const [_, m] = Schema.transform(objSchema, null);
81
+ assert.ok(m === null);
82
+ assert.strictEqual(Schema.check(arraySchema, null), "is not an Array");
83
+ assert.strictEqual(Schema.check(Schema.string(), null), "is not a string");
84
+ });
package/test/tuple.ts ADDED
@@ -0,0 +1,63 @@
1
+ import * as Schema from "../src/index";
2
+ import { assert } from "../src/util";
3
+
4
+ declare const describe, it;
5
+
6
+ describe("tuple", () => {
7
+ const colorSchema = Schema.tuple([
8
+ Schema.object({
9
+ name: Schema.string(),
10
+ }).nullable(),
11
+ Schema.number(),
12
+ Schema.number(),
13
+ Schema.number(),
14
+ ]);
15
+ type color = Schema.buildType<typeof colorSchema>;
16
+
17
+ it("type", () => {
18
+ type name = color[0];
19
+ type r = color[1];
20
+ type g = color[2];
21
+ type b = color[3];
22
+ //@ts-expect-error
23
+ type x = color[4];
24
+
25
+ const n1: name = null;
26
+ const n2: name = { name: "" };
27
+ //@ts-expect-error
28
+ const n3: name = {};
29
+
30
+ const b: b = 5;
31
+ //@ts-expect-error
32
+ const b1: b = null;
33
+
34
+ const e1 = Schema.check(colorSchema, [{ name: "red" }, 255, 0]);
35
+ assert(e1 === "tuple size error", "e1");
36
+ const e2 = Schema.check(colorSchema, [{ name: "red" }, 255, "0", 0]);
37
+ assert(e2 === "tuple item 2 is wrong (is not a number)", "e2");
38
+ const e3 = Schema.check(colorSchema, [{ name: "red" }, 255, 0, 0]);
39
+ assert(e3 === null, "e3");
40
+ });
41
+
42
+ it("compound", () => {
43
+ const car = Schema.object({
44
+ name: Schema.string(),
45
+ color: Schema.tuple([
46
+ Schema.object({
47
+ name: Schema.string(),
48
+ }).nullable(),
49
+ Schema.number().transformer((v) => v.toString()),
50
+ Schema.number().transformer((v) => v.toString()),
51
+ Schema.number().transformer((v) => v.toString()),
52
+ ]),
53
+ });
54
+ type car = Schema.buildType<typeof car>;
55
+ const [e, c] = Schema.transform(car, {
56
+ name: "toyota",
57
+ color: [{ name: "red" }, 255, 0, 0],
58
+ });
59
+ assert(c !== null, "e1");
60
+ assert(c.color[0]!.name === "red", "e2");
61
+ assert(c.color[1] === "255", "e3");
62
+ });
63
+ });
package/test/union.ts ADDED
@@ -0,0 +1,112 @@
1
+ import * as m from "../src/index";
2
+ import { assert } from "../src/util";
3
+
4
+ declare const describe, it;
5
+
6
+ describe("tuple", () => {
7
+ it("setting usecase", () => {
8
+ const setting = m.union([
9
+ m.object({
10
+ type: m.enums<"position">(["position"]),
11
+ value: m.enums<"left" | "right">(["left", "right"]),
12
+ }),
13
+ m.object({
14
+ type: m.enums<"width">(["width"]),
15
+ value: m.number(),
16
+ }),
17
+ ]);
18
+ type setting = m.buildType<typeof setting>;
19
+ const a: setting = { type: "position", value: "left" };
20
+ const b: setting = { type: "width", value: 5 };
21
+ //@ts-expect-error
22
+ const c: setting = { type: "position", value: "top" };
23
+ //@ts-expect-error
24
+ const d: setting = { type: "height", value: 5 };
25
+
26
+ const datas = [
27
+ { type: "position", value: "left" },
28
+ { type: "position", value: "top" },
29
+ { type: "width", value: 5 },
30
+ ];
31
+ const valids = datas
32
+ .map((i) => m.transform(setting, i)[1])
33
+ .filter((i): i is NonNullable<typeof i> => i !== null);
34
+ assert(valids.length === 2, "");
35
+ });
36
+
37
+ it("simple", () => {
38
+ const color = m.object({
39
+ name: m.string(),
40
+ rgb: m.tuple([m.number(), m.number(), m.number()]),
41
+ });
42
+ const schema = m.union([
43
+ m.string(),
44
+ m.boolean().transformer((_) => 1),
45
+ m.array(color),
46
+ ]);
47
+ type u = m.buildType<typeof schema>;
48
+
49
+ const a: u = "";
50
+ const b: u = 1;
51
+ const c: u = [{ name: "red", rgb: [1, 2, 3] }];
52
+ //@ts-expect-error
53
+ const d: u = true;
54
+ //@ts-expect-error
55
+ const e: u = { name: "red", rgb: [1, 2, 3] };
56
+ //@ts-expect-error
57
+ const f: u = [{ name: "red", rgb: [1, 2, 3, 4] }];
58
+
59
+ assert(m.check(schema, "") === null, "c1");
60
+ assert(m.check(schema, false) === null, "c2");
61
+ assert(m.check(schema, 1) !== null, "c3");
62
+
63
+ assert(m.transform(schema, true)[1] === 1, "t1");
64
+ assert(
65
+ m.transform(schema, [{ name: "red", rgb: [1, 2, 3] }])[1]![0].name ===
66
+ "red",
67
+ "t2"
68
+ );
69
+
70
+ assertOk(m.transform(schema, true), "t3");
71
+ assertOk(m.transform(schema, [{ name: "red", rgb: [1, 2, 3] }]), "t4");
72
+ assertError(m.transform(schema, 1), "t5");
73
+ assertError(m.transform(schema, [{ name: "red", rgb: [1, 2, "3"] }]), "t6");
74
+ });
75
+
76
+ it("compound", () => {
77
+ const carSchema = m.object({
78
+ name: m.string(),
79
+ color: m.union([
80
+ m.object({
81
+ name: m.enums<"red">(["red"]),
82
+ }),
83
+ m.object({
84
+ name: m.enums<"black">(["black"]),
85
+ }),
86
+ ]),
87
+ });
88
+ type car = m.buildType<typeof carSchema>;
89
+ const a: car = { name: "toyota", color: { name: "red" } };
90
+ //@ts-expect-error
91
+ const b: car = { name: "toyota", color: { name: "blue" } };
92
+
93
+ assertOk(
94
+ m.transform(carSchema, { name: "toyota", color: { name: "red" } }),
95
+ "e1"
96
+ );
97
+ assertError(
98
+ m.transform(carSchema, { name: "toyota", color: { name: "blue" } }),
99
+ "e2"
100
+ );
101
+ });
102
+ });
103
+
104
+ function assertOk<data>(result: [null, data] | [string, null], msg: string) {
105
+ const [error, data] = result;
106
+ assert(error === null, msg);
107
+ }
108
+
109
+ function assertError<data>(result: [null, data] | [string, null], msg: string) {
110
+ const [error, data] = result;
111
+ assert(error !== null, msg);
112
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "dst/",
4
+ "declaration": true,
5
+ "strictNullChecks": true,
6
+ "target": "ES6",
7
+ "module": "commonjs",
8
+ // "jsx": "react",
9
+ },
10
+ "files": [
11
+ "src/index.ts",
12
+ "test.ts",
13
+ ],
14
+ "include": [
15
+ "test",
16
+ ],
17
+ }