@palantir/pack.schema 0.0.1-beta.1
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/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-transpileBrowser.log +5 -0
- package/.turbo/turbo-transpileCjs.log +5 -0
- package/.turbo/turbo-transpileEsm.log +5 -0
- package/.turbo/turbo-transpileTypes.log +5 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/LICENSE.txt +13 -0
- package/README.md +87 -0
- package/build/browser/index.js +176 -0
- package/build/browser/index.js.map +1 -0
- package/build/cjs/index.cjs +192 -0
- package/build/cjs/index.cjs.map +1 -0
- package/build/cjs/index.d.cts +131 -0
- package/build/esm/index.js +176 -0
- package/build/esm/index.js.map +1 -0
- package/build/types/__tests__/defineMigration.test.d.ts +1 -0
- package/build/types/__tests__/defineMigration.test.d.ts.map +1 -0
- package/build/types/__tests__/defineRecord.test.d.ts +1 -0
- package/build/types/__tests__/defineRecord.test.d.ts.map +1 -0
- package/build/types/__tests__/defineUnion.test.d.ts +1 -0
- package/build/types/__tests__/defineUnion.test.d.ts.map +1 -0
- package/build/types/__tests__/integration.test.d.ts +1 -0
- package/build/types/__tests__/integration.test.d.ts.map +1 -0
- package/build/types/__tests__/primitives.test.d.ts +1 -0
- package/build/types/__tests__/primitives.test.d.ts.map +1 -0
- package/build/types/__tests__/testTypeUtils.d.ts +74 -0
- package/build/types/__tests__/testTypeUtils.d.ts.map +1 -0
- package/build/types/defineMigration.d.ts +21 -0
- package/build/types/defineMigration.d.ts.map +1 -0
- package/build/types/defineRecord.d.ts +14 -0
- package/build/types/defineRecord.d.ts.map +1 -0
- package/build/types/defineUnion.d.ts +12 -0
- package/build/types/defineUnion.d.ts.map +1 -0
- package/build/types/defs.d.ts +24 -0
- package/build/types/defs.d.ts.map +1 -0
- package/build/types/index.d.ts +6 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/primitives.d.ts +41 -0
- package/build/types/primitives.d.ts.map +1 -0
- package/build/types/utils.d.ts +20 -0
- package/build/types/utils.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/__tests__/defineMigration.test.ts +265 -0
- package/src/__tests__/defineRecord.test.ts +167 -0
- package/src/__tests__/defineUnion.test.ts +231 -0
- package/src/__tests__/integration.test.ts +126 -0
- package/src/__tests__/primitives.test.ts +153 -0
- package/src/__tests__/testTypeUtils.ts +92 -0
- package/src/defineMigration.ts +159 -0
- package/src/defineRecord.ts +83 -0
- package/src/defineUnion.ts +58 -0
- package/src/defs.ts +46 -0
- package/src/index.ts +22 -0
- package/src/primitives.ts +80 -0
- package/src/utils.ts +62 -0
- package/tsconfig.json +21 -0
- package/vitest.config.mjs +26 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@palantir/pack.schema",
|
|
3
|
+
"version": "0.0.1-beta.1",
|
|
4
|
+
"description": "API package for PACK schema functionality",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/palantir/pack.git"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"browser": "./build/browser/index.js",
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./build/types/index.d.ts",
|
|
15
|
+
"default": "./build/esm/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": "./build/cjs/index.js",
|
|
18
|
+
"default": "./build/browser/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./*": {
|
|
21
|
+
"browser": "./build/browser/public/*.js",
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./build/types/public/*.d.ts",
|
|
24
|
+
"default": "./build/js/public/*.js"
|
|
25
|
+
},
|
|
26
|
+
"require": "./build/cjs/public/*.js",
|
|
27
|
+
"default": "./build/browser/public/*.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"rimraf": "^6.0.1",
|
|
32
|
+
"tslib": "^2.8.1",
|
|
33
|
+
"typescript": "^5.9.2",
|
|
34
|
+
"@palantir/pack.monorepo.tsconfig": "~0.4.0-beta.1"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"main": "./build/cjs/index.js",
|
|
40
|
+
"module": "./build/esm/index.js",
|
|
41
|
+
"types": "./build/cjs/index.d.ts",
|
|
42
|
+
"type": "module",
|
|
43
|
+
"scripts": {
|
|
44
|
+
"clean": "rimraf .turbo build dist lib test-output *.tgz tsconfig.tsbuildinfo",
|
|
45
|
+
"lint": "eslint ./src ; dprint check --config $(find-up dprint.json) --allow-no-files",
|
|
46
|
+
"lint:fix": "eslint ./src --fix ; dprint fmt --config $(find-up dprint.json) --allow-no-files",
|
|
47
|
+
"test": "vitest run --passWithNoTests -u",
|
|
48
|
+
"test:watch": "vitest --passWithNoTests",
|
|
49
|
+
"transpileBrowser": "monorepo-transpile -f esm -m bundle -t browser",
|
|
50
|
+
"transpileCjs": "monorepo-transpile -f cjs -m bundle -t node",
|
|
51
|
+
"transpileEsm": "monorepo-transpile -f esm -m bundle -t node",
|
|
52
|
+
"transpileTypes": "monorepo-transpile -f esm -m types -t node",
|
|
53
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
import { defineMigration } from "../defineMigration.js";
|
|
19
|
+
import { defineRecord } from "../defineRecord.js";
|
|
20
|
+
import { defineUnion } from "../defineUnion.js";
|
|
21
|
+
import * as P from "../primitives.js";
|
|
22
|
+
import { assertExactKeys, assertHasKeys, assertTypeEquals } from "./testTypeUtils.js";
|
|
23
|
+
|
|
24
|
+
describe("defineMigration", () => {
|
|
25
|
+
it("should create a new schema by extending record fields", () => {
|
|
26
|
+
const v1Schema = {
|
|
27
|
+
Person: defineRecord("Person", {
|
|
28
|
+
docs: "A person",
|
|
29
|
+
fields: {
|
|
30
|
+
name: P.String,
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
36
|
+
EnhancedPerson: schema.Person.addField("age", P.Double).build(),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
expect(v2Schema.Person).toBe(v1Schema.Person);
|
|
40
|
+
expect(v2Schema.EnhancedPerson.name).toBe("Person");
|
|
41
|
+
expect(v2Schema.EnhancedPerson.fields.name).toEqual({ type: "string" });
|
|
42
|
+
expect(v2Schema.EnhancedPerson.fields.age).toEqual({ type: "double" });
|
|
43
|
+
|
|
44
|
+
assertHasKeys<typeof v2Schema, "Person" | "EnhancedPerson">();
|
|
45
|
+
assertExactKeys<typeof v2Schema.EnhancedPerson.fields, "name" | "age">();
|
|
46
|
+
assertTypeEquals<typeof v2Schema.EnhancedPerson.fields.name, P.String>();
|
|
47
|
+
assertTypeEquals<typeof v2Schema.EnhancedPerson.fields.age, P.Double>();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should handle multiple field additions", () => {
|
|
51
|
+
const v1Schema = {
|
|
52
|
+
User: defineRecord("User", {
|
|
53
|
+
docs: "A user",
|
|
54
|
+
fields: {
|
|
55
|
+
username: P.String,
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
61
|
+
ExtendedUser: schema.User.addField("email", P.String)
|
|
62
|
+
.addField("age", P.Optional(P.Double))
|
|
63
|
+
.build(),
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
expect(v2Schema.ExtendedUser.fields.username).toEqual({ type: "string" });
|
|
67
|
+
expect(v2Schema.ExtendedUser.fields.email).toEqual({ type: "string" });
|
|
68
|
+
expect(v2Schema.ExtendedUser.fields.age).toEqual({
|
|
69
|
+
type: "optional",
|
|
70
|
+
item: { type: "double" },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
assertExactKeys<typeof v2Schema.ExtendedUser.fields, "username" | "email" | "age">();
|
|
74
|
+
assertTypeEquals<typeof v2Schema.ExtendedUser.fields.username, P.String>();
|
|
75
|
+
assertTypeEquals<typeof v2Schema.ExtendedUser.fields.email, P.String>();
|
|
76
|
+
assertTypeEquals<typeof v2Schema.ExtendedUser.fields.age, P.Optional<P.Double>>();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should extend union types", () => {
|
|
80
|
+
const CircleRecord = defineRecord("Circle", {
|
|
81
|
+
docs: "A circle",
|
|
82
|
+
fields: {
|
|
83
|
+
radius: P.Double,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const RectangleRecord = defineRecord("Rectangle", {
|
|
88
|
+
docs: "A rectangle",
|
|
89
|
+
fields: {
|
|
90
|
+
width: P.Double,
|
|
91
|
+
height: P.Double,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const v1Schema = {
|
|
96
|
+
Shape: defineUnion("Shape", {
|
|
97
|
+
docs: "A shape",
|
|
98
|
+
variants: {
|
|
99
|
+
circle: CircleRecord,
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
105
|
+
ExtendedShape: schema.Shape.addVariant("rectangle", RectangleRecord).build(),
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
expect(v2Schema.ExtendedShape.variants.circle).toEqual({
|
|
109
|
+
type: "ref",
|
|
110
|
+
name: "Circle",
|
|
111
|
+
refType: "record",
|
|
112
|
+
});
|
|
113
|
+
expect(v2Schema.ExtendedShape.variants.rectangle).toEqual({
|
|
114
|
+
type: "ref",
|
|
115
|
+
name: "Rectangle",
|
|
116
|
+
refType: "record",
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
assertExactKeys<typeof v2Schema.ExtendedShape.variants, "circle" | "rectangle">();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should handle multiple union member additions", () => {
|
|
123
|
+
const TextRecord = defineRecord("Text", {
|
|
124
|
+
docs: "Text value",
|
|
125
|
+
fields: { value: P.String },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const NumberRecord = defineRecord("Number", {
|
|
129
|
+
docs: "Number value",
|
|
130
|
+
fields: { value: P.Double },
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const OptionalTextRecord = defineRecord("OptionalText", {
|
|
134
|
+
docs: "Optional text value",
|
|
135
|
+
fields: { value: P.Optional(P.String) },
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const v1Schema = {
|
|
139
|
+
Value: defineUnion("Value", {
|
|
140
|
+
docs: "A value",
|
|
141
|
+
variants: {
|
|
142
|
+
text: TextRecord,
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
148
|
+
ExtendedValue: schema.Value.addVariant("number", NumberRecord)
|
|
149
|
+
.addVariant("optional_text", OptionalTextRecord)
|
|
150
|
+
.build(),
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
expect(v2Schema.ExtendedValue.variants.text).toEqual({
|
|
154
|
+
type: "ref",
|
|
155
|
+
name: "Text",
|
|
156
|
+
refType: "record",
|
|
157
|
+
});
|
|
158
|
+
expect(v2Schema.ExtendedValue.variants.number).toEqual({
|
|
159
|
+
type: "ref",
|
|
160
|
+
name: "Number",
|
|
161
|
+
refType: "record",
|
|
162
|
+
});
|
|
163
|
+
expect(v2Schema.ExtendedValue.variants.optional_text).toEqual({
|
|
164
|
+
type: "ref",
|
|
165
|
+
name: "OptionalText",
|
|
166
|
+
refType: "record",
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
assertExactKeys<typeof v2Schema.ExtendedValue.variants, "text" | "number" | "optional_text">();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should preserve original schema alongside new definitions", () => {
|
|
173
|
+
const originalRecord = defineRecord("Original", {
|
|
174
|
+
docs: "Original record",
|
|
175
|
+
fields: {
|
|
176
|
+
value: P.String,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const v1Schema = {
|
|
181
|
+
Original: originalRecord,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
185
|
+
Modified: schema.Original.addField("extra", P.Double).build(),
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
expect(v2Schema.Original).toBe(originalRecord);
|
|
189
|
+
expect(v2Schema.Modified.fields.value).toEqual({ type: "string" });
|
|
190
|
+
expect(v2Schema.Modified.fields.extra).toEqual({ type: "double" });
|
|
191
|
+
|
|
192
|
+
assertHasKeys<typeof v2Schema, "Original" | "Modified">();
|
|
193
|
+
assertExactKeys<typeof v2Schema.Modified.fields, "value" | "extra">();
|
|
194
|
+
assertTypeEquals<typeof v2Schema.Modified.fields.value, P.String>();
|
|
195
|
+
assertTypeEquals<typeof v2Schema.Modified.fields.extra, P.Double>();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should handle mixed record and union migrations", () => {
|
|
199
|
+
const PersonRecord = defineRecord("Person", {
|
|
200
|
+
docs: "A person",
|
|
201
|
+
fields: {
|
|
202
|
+
name: P.String,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const OrganizationRecord = defineRecord("Organization", {
|
|
207
|
+
docs: "An organization",
|
|
208
|
+
fields: {
|
|
209
|
+
orgName: P.String,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const v1Schema = {
|
|
214
|
+
Person: PersonRecord,
|
|
215
|
+
Entity: defineUnion("Entity", {
|
|
216
|
+
docs: "An entity",
|
|
217
|
+
variants: {
|
|
218
|
+
person: PersonRecord,
|
|
219
|
+
},
|
|
220
|
+
}),
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
224
|
+
ExtendedPerson: schema.Person.addField("age", P.Double).build(),
|
|
225
|
+
ExtendedEntity: schema.Entity.addVariant("organization", OrganizationRecord).build(),
|
|
226
|
+
}));
|
|
227
|
+
|
|
228
|
+
expect(v2Schema.ExtendedPerson.fields.name).toEqual({ type: "string" });
|
|
229
|
+
expect(v2Schema.ExtendedPerson.fields.age).toEqual({ type: "double" });
|
|
230
|
+
expect(v2Schema.ExtendedEntity.variants.person).toEqual({
|
|
231
|
+
type: "ref",
|
|
232
|
+
name: "Person",
|
|
233
|
+
refType: "record",
|
|
234
|
+
});
|
|
235
|
+
expect(v2Schema.ExtendedEntity.variants.organization).toEqual({
|
|
236
|
+
type: "ref",
|
|
237
|
+
name: "Organization",
|
|
238
|
+
refType: "record",
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
assertHasKeys<typeof v2Schema, "Person" | "Entity" | "ExtendedPerson" | "ExtendedEntity">();
|
|
242
|
+
assertExactKeys<typeof v2Schema.ExtendedPerson.fields, "name" | "age">();
|
|
243
|
+
assertExactKeys<typeof v2Schema.ExtendedEntity.variants, "person" | "organization">();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should maintain correct references and names", () => {
|
|
247
|
+
const v1Schema = {
|
|
248
|
+
TestRecord: defineRecord("TestRecord", {
|
|
249
|
+
docs: "Test record",
|
|
250
|
+
fields: {
|
|
251
|
+
field1: P.String,
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
257
|
+
ModifiedRecord: schema.TestRecord.addField("field2", P.Double).build(),
|
|
258
|
+
}));
|
|
259
|
+
|
|
260
|
+
expect(v2Schema.ModifiedRecord.name).toBe("TestRecord");
|
|
261
|
+
expect(v2Schema.ModifiedRecord.type).toBe("record");
|
|
262
|
+
expect(v2Schema.ModifiedRecord.fields.field1).toEqual({ type: "string" });
|
|
263
|
+
expect(v2Schema.ModifiedRecord.fields.field2).toEqual({ type: "double" });
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
import { defineRecord } from "../defineRecord.js";
|
|
19
|
+
import type { Array, Double, Optional, String } from "../primitives.js";
|
|
20
|
+
import * as P from "../primitives.js";
|
|
21
|
+
import { assertExactKeys, assertTypeEquals } from "./testTypeUtils.js";
|
|
22
|
+
|
|
23
|
+
describe("defineRecord", () => {
|
|
24
|
+
it("should create a basic record definition", () => {
|
|
25
|
+
const PersonRecord = defineRecord("Person", {
|
|
26
|
+
docs: "A person record",
|
|
27
|
+
fields: {
|
|
28
|
+
name: P.String,
|
|
29
|
+
age: P.Double,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(PersonRecord.type).toBe("record");
|
|
34
|
+
expect(PersonRecord.name).toBe("Person");
|
|
35
|
+
expect(PersonRecord.docs).toBe("A person record");
|
|
36
|
+
expect(PersonRecord.fields.name).toEqual({ type: "string" });
|
|
37
|
+
expect(PersonRecord.fields.age).toEqual({ type: "double" });
|
|
38
|
+
|
|
39
|
+
assertExactKeys<typeof PersonRecord.fields, "name" | "age">();
|
|
40
|
+
assertTypeEquals<typeof PersonRecord.fields.name, String>();
|
|
41
|
+
assertTypeEquals<typeof PersonRecord.fields.age, Double>();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should handle optional fields", () => {
|
|
45
|
+
const UserRecord = defineRecord("User", {
|
|
46
|
+
docs: "A user record",
|
|
47
|
+
fields: {
|
|
48
|
+
username: P.String,
|
|
49
|
+
email: P.Optional(P.String),
|
|
50
|
+
age: P.Optional(P.Double),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(UserRecord.fields.username).toEqual({ type: "string" });
|
|
55
|
+
expect(UserRecord.fields.email).toEqual({
|
|
56
|
+
type: "optional",
|
|
57
|
+
item: { type: "string" },
|
|
58
|
+
});
|
|
59
|
+
expect(UserRecord.fields.age).toEqual({
|
|
60
|
+
type: "optional",
|
|
61
|
+
item: { type: "double" },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
assertExactKeys<typeof UserRecord.fields, "username" | "email" | "age">();
|
|
65
|
+
assertTypeEquals<typeof UserRecord.fields.username, String>();
|
|
66
|
+
assertTypeEquals<typeof UserRecord.fields.email, Optional<String>>();
|
|
67
|
+
assertTypeEquals<typeof UserRecord.fields.age, Optional<Double>>();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should handle array fields", () => {
|
|
71
|
+
const TodoListRecord = defineRecord("TodoList", {
|
|
72
|
+
docs: "A todo list",
|
|
73
|
+
fields: {
|
|
74
|
+
title: P.String,
|
|
75
|
+
items: P.Array(P.String),
|
|
76
|
+
priorities: P.Array(P.Double),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(TodoListRecord.fields.items).toEqual({
|
|
81
|
+
type: "array",
|
|
82
|
+
items: { type: "string" },
|
|
83
|
+
});
|
|
84
|
+
expect(TodoListRecord.fields.priorities).toEqual({
|
|
85
|
+
type: "array",
|
|
86
|
+
items: { type: "double" },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
assertExactKeys<typeof TodoListRecord.fields, "title" | "items" | "priorities">();
|
|
90
|
+
assertTypeEquals<typeof TodoListRecord.fields.title, String>();
|
|
91
|
+
assertTypeEquals<typeof TodoListRecord.fields.items, Array<String>>();
|
|
92
|
+
assertTypeEquals<typeof TodoListRecord.fields.priorities, Array<Double>>();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should handle nested optional arrays", () => {
|
|
96
|
+
const ComplexRecord = defineRecord("Complex", {
|
|
97
|
+
docs: "A complex record",
|
|
98
|
+
fields: {
|
|
99
|
+
optionalStringArray: P.Optional(P.Array(P.String)),
|
|
100
|
+
arrayOfOptionalStrings: P.Array(P.Optional(P.String)),
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(ComplexRecord.fields.optionalStringArray).toEqual({
|
|
105
|
+
type: "optional",
|
|
106
|
+
item: {
|
|
107
|
+
type: "array",
|
|
108
|
+
items: { type: "string" },
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
expect(ComplexRecord.fields.arrayOfOptionalStrings).toEqual({
|
|
112
|
+
type: "array",
|
|
113
|
+
items: {
|
|
114
|
+
type: "optional",
|
|
115
|
+
item: { type: "string" },
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
assertExactKeys<
|
|
120
|
+
typeof ComplexRecord.fields,
|
|
121
|
+
"optionalStringArray" | "arrayOfOptionalStrings"
|
|
122
|
+
>();
|
|
123
|
+
assertTypeEquals<
|
|
124
|
+
typeof ComplexRecord.fields.optionalStringArray,
|
|
125
|
+
P.Optional<P.Array<P.String>>
|
|
126
|
+
>();
|
|
127
|
+
assertTypeEquals<
|
|
128
|
+
typeof ComplexRecord.fields.arrayOfOptionalStrings,
|
|
129
|
+
P.Array<P.Optional<P.String>>
|
|
130
|
+
>();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should handle record references", () => {
|
|
134
|
+
const AuthorRecord = defineRecord("Author", {
|
|
135
|
+
docs: "An author",
|
|
136
|
+
fields: {
|
|
137
|
+
name: P.String,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const BookRecord = defineRecord("Book", {
|
|
142
|
+
docs: "A book",
|
|
143
|
+
fields: {
|
|
144
|
+
title: P.String,
|
|
145
|
+
author: AuthorRecord,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(BookRecord.fields.author).toEqual({
|
|
150
|
+
type: "ref",
|
|
151
|
+
refType: "record",
|
|
152
|
+
name: "Author",
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should support function references for self-referencing structures", () => {
|
|
157
|
+
const NodeRecord = defineRecord("Node", {
|
|
158
|
+
docs: "A node in a graph",
|
|
159
|
+
fields: {
|
|
160
|
+
value: P.String,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(NodeRecord.name).toBe("Node");
|
|
165
|
+
expect(NodeRecord.fields.value).toEqual({ type: "string" });
|
|
166
|
+
});
|
|
167
|
+
});
|