@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
|
@@ -0,0 +1,231 @@
|
|
|
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 { defineUnion } from "../defineUnion.js";
|
|
20
|
+
import * as P from "../primitives.js";
|
|
21
|
+
import { assertExactKeys, assertTypeEquals } from "./testTypeUtils.js";
|
|
22
|
+
|
|
23
|
+
describe("defineUnion", () => {
|
|
24
|
+
it("should create a basic union definition with record types", () => {
|
|
25
|
+
const TextRecord = defineRecord("Text", {
|
|
26
|
+
docs: "A text value",
|
|
27
|
+
fields: {
|
|
28
|
+
value: P.String,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const NumberRecord = defineRecord("Number", {
|
|
33
|
+
docs: "A number value",
|
|
34
|
+
fields: {
|
|
35
|
+
value: P.Double,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const ValueUnion = defineUnion("Value", {
|
|
40
|
+
docs: "A value union",
|
|
41
|
+
variants: {
|
|
42
|
+
text: TextRecord,
|
|
43
|
+
number: NumberRecord,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(ValueUnion.type).toBe("union");
|
|
48
|
+
expect(ValueUnion.name).toBe("Value");
|
|
49
|
+
expect(ValueUnion.discriminant).toBe("type");
|
|
50
|
+
expect(ValueUnion.variants.text).toEqual({
|
|
51
|
+
type: "ref",
|
|
52
|
+
name: "Text",
|
|
53
|
+
refType: "record",
|
|
54
|
+
});
|
|
55
|
+
expect(ValueUnion.variants.number).toEqual({
|
|
56
|
+
type: "ref",
|
|
57
|
+
name: "Number",
|
|
58
|
+
refType: "record",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
assertExactKeys<typeof ValueUnion.variants, "text" | "number">();
|
|
62
|
+
assertTypeEquals<typeof ValueUnion.variants.text, P.Ref>();
|
|
63
|
+
assertTypeEquals<typeof ValueUnion.variants.number, P.Ref>();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should create a union definition with record types", () => {
|
|
67
|
+
const CircleRecord = defineRecord("Circle", {
|
|
68
|
+
docs: "A circle",
|
|
69
|
+
fields: {
|
|
70
|
+
radius: P.Double,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const RectangleRecord = defineRecord("Rectangle", {
|
|
75
|
+
docs: "A rectangle",
|
|
76
|
+
fields: {
|
|
77
|
+
width: P.Double,
|
|
78
|
+
height: P.Double,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const ShapeUnion = defineUnion("Shape", {
|
|
83
|
+
docs: "A shape union",
|
|
84
|
+
variants: {
|
|
85
|
+
circle: CircleRecord,
|
|
86
|
+
rectangle: RectangleRecord,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(ShapeUnion.type).toBe("union");
|
|
91
|
+
expect(ShapeUnion.variants.circle).toEqual({
|
|
92
|
+
type: "ref",
|
|
93
|
+
name: "Circle",
|
|
94
|
+
refType: "record",
|
|
95
|
+
});
|
|
96
|
+
expect(ShapeUnion.variants.rectangle).toEqual({
|
|
97
|
+
type: "ref",
|
|
98
|
+
name: "Rectangle",
|
|
99
|
+
refType: "record",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
assertExactKeys<typeof ShapeUnion.variants, "circle" | "rectangle">();
|
|
103
|
+
assertTypeEquals<typeof ShapeUnion.variants.circle, P.Ref>();
|
|
104
|
+
assertTypeEquals<typeof ShapeUnion.variants.rectangle, P.Ref>();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should create a union with nested unions", () => {
|
|
108
|
+
const PersonRecord = defineRecord("Person", {
|
|
109
|
+
docs: "A person",
|
|
110
|
+
fields: {
|
|
111
|
+
name: P.String,
|
|
112
|
+
age: P.Double,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const AnimalRecord = defineRecord("Animal", {
|
|
117
|
+
docs: "An animal",
|
|
118
|
+
fields: {
|
|
119
|
+
species: P.String,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const LivingBeingUnion = defineUnion("LivingBeing", {
|
|
124
|
+
docs: "A living being",
|
|
125
|
+
variants: {
|
|
126
|
+
person: PersonRecord,
|
|
127
|
+
animal: AnimalRecord,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const MachineRecord = defineRecord("Machine", {
|
|
132
|
+
docs: "A machine",
|
|
133
|
+
fields: {
|
|
134
|
+
model: P.String,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const EntityUnion = defineUnion("Entity", {
|
|
139
|
+
docs: "An entity union",
|
|
140
|
+
variants: {
|
|
141
|
+
livingBeing: LivingBeingUnion,
|
|
142
|
+
machine: MachineRecord,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(EntityUnion.variants.livingBeing).toEqual({
|
|
147
|
+
type: "ref",
|
|
148
|
+
name: "LivingBeing",
|
|
149
|
+
refType: "union",
|
|
150
|
+
});
|
|
151
|
+
expect(EntityUnion.variants.machine).toEqual({
|
|
152
|
+
type: "ref",
|
|
153
|
+
name: "Machine",
|
|
154
|
+
refType: "record",
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
assertExactKeys<typeof EntityUnion.variants, "livingBeing" | "machine">();
|
|
158
|
+
assertTypeEquals<typeof EntityUnion.variants.livingBeing, P.Ref>();
|
|
159
|
+
assertTypeEquals<typeof EntityUnion.variants.machine, P.Ref>();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should have correct union name", () => {
|
|
163
|
+
const Option1Record = defineRecord("Option1", {
|
|
164
|
+
docs: "Option 1",
|
|
165
|
+
fields: {
|
|
166
|
+
value: P.String,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const Option2Record = defineRecord("Option2", {
|
|
171
|
+
docs: "Option 2",
|
|
172
|
+
fields: {
|
|
173
|
+
value: P.Double,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const TestUnion = defineUnion("TestUnion", {
|
|
178
|
+
docs: "A test union",
|
|
179
|
+
variants: {
|
|
180
|
+
option1: Option1Record,
|
|
181
|
+
option2: Option2Record,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(TestUnion.name).toBe("TestUnion");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should preserve all item keys", () => {
|
|
189
|
+
const FirstRecord = defineRecord("First", {
|
|
190
|
+
docs: "First option",
|
|
191
|
+
fields: { value: P.String },
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const SecondRecord = defineRecord("Second", {
|
|
195
|
+
docs: "Second option",
|
|
196
|
+
fields: { value: P.Double },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const ThirdRecord = defineRecord("Third", {
|
|
200
|
+
docs: "Third option",
|
|
201
|
+
fields: { value: P.String },
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const FourthRecord = defineRecord("Fourth", {
|
|
205
|
+
docs: "Fourth option",
|
|
206
|
+
fields: { value: P.Double },
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const MultiOptionUnion = defineUnion("MultiOption", {
|
|
210
|
+
docs: "A multi-option union",
|
|
211
|
+
variants: {
|
|
212
|
+
first: FirstRecord,
|
|
213
|
+
second: SecondRecord,
|
|
214
|
+
third: ThirdRecord,
|
|
215
|
+
fourth: FourthRecord,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
expect(Object.keys(MultiOptionUnion.variants)).toEqual([
|
|
220
|
+
"first",
|
|
221
|
+
"second",
|
|
222
|
+
"third",
|
|
223
|
+
"fourth",
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
assertExactKeys<
|
|
227
|
+
typeof MultiOptionUnion.variants,
|
|
228
|
+
"first" | "second" | "third" | "fourth"
|
|
229
|
+
>();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
|
|
23
|
+
describe("Integration Tests", () => {
|
|
24
|
+
describe("Basic record relationships", () => {
|
|
25
|
+
it("should create records with direct references", () => {
|
|
26
|
+
const AuthorRecord = defineRecord("Author", {
|
|
27
|
+
docs: "An author",
|
|
28
|
+
fields: {
|
|
29
|
+
name: P.String,
|
|
30
|
+
email: P.String,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const BookRecord = defineRecord("Book", {
|
|
35
|
+
docs: "A book",
|
|
36
|
+
fields: {
|
|
37
|
+
title: P.String,
|
|
38
|
+
author: AuthorRecord,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(BookRecord.fields.author).toEqual({
|
|
43
|
+
type: "ref",
|
|
44
|
+
name: "Author",
|
|
45
|
+
refType: "record",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(BookRecord.fields.title).toEqual({
|
|
49
|
+
type: "string",
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should create unions with record references", () => {
|
|
54
|
+
const PersonRecord = defineRecord("Person", {
|
|
55
|
+
docs: "A person entity",
|
|
56
|
+
fields: {
|
|
57
|
+
name: P.String,
|
|
58
|
+
age: P.Double,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const OrganizationRecord = defineRecord("Organization", {
|
|
63
|
+
docs: "An organization entity",
|
|
64
|
+
fields: {
|
|
65
|
+
name: P.String,
|
|
66
|
+
industry: P.String,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const EntityUnion = defineUnion("Entity", {
|
|
71
|
+
docs: "An entity",
|
|
72
|
+
variants: {
|
|
73
|
+
person: PersonRecord,
|
|
74
|
+
organization: OrganizationRecord,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(EntityUnion.variants.person).toEqual({
|
|
79
|
+
type: "ref",
|
|
80
|
+
name: "Person",
|
|
81
|
+
refType: "record",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(EntityUnion.variants.organization).toEqual({
|
|
85
|
+
type: "ref",
|
|
86
|
+
name: "Organization",
|
|
87
|
+
refType: "record",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("Schema evolution with migrations", () => {
|
|
93
|
+
it("should evolve a schema through multiple versions", () => {
|
|
94
|
+
// Version 1: Basic user schema
|
|
95
|
+
const v1Schema = {
|
|
96
|
+
User: defineRecord("User", {
|
|
97
|
+
docs: "User account",
|
|
98
|
+
fields: {
|
|
99
|
+
username: P.String,
|
|
100
|
+
email: P.String,
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Version 2: Add profile information
|
|
106
|
+
const v2Schema = defineMigration(v1Schema, schema => ({
|
|
107
|
+
UserWithProfile: schema.User.addField("firstName", P.String)
|
|
108
|
+
.addField("lastName", P.String)
|
|
109
|
+
.addField("age", P.Optional(P.Double))
|
|
110
|
+
.build(),
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
expect(v2Schema.User).toBe(v1Schema.User);
|
|
114
|
+
expect(v2Schema.UserWithProfile.fields.username).toEqual({
|
|
115
|
+
type: "string",
|
|
116
|
+
});
|
|
117
|
+
expect(v2Schema.UserWithProfile.fields.firstName).toEqual({
|
|
118
|
+
type: "string",
|
|
119
|
+
});
|
|
120
|
+
expect(v2Schema.UserWithProfile.fields.age).toEqual({
|
|
121
|
+
type: "optional",
|
|
122
|
+
item: { type: "double" },
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
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 * as P from "../primitives.js";
|
|
19
|
+
|
|
20
|
+
describe("primitives", () => {
|
|
21
|
+
describe("String", () => {
|
|
22
|
+
it("should create a string type", () => {
|
|
23
|
+
expect(P.String).toEqual({ type: "string" });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should have correct type property", () => {
|
|
27
|
+
expect(P.String.type).toBe("string");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("Double", () => {
|
|
32
|
+
it("should create a double type", () => {
|
|
33
|
+
expect(P.Double).toEqual({ type: "double" });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should have correct type property", () => {
|
|
37
|
+
expect(P.Double.type).toBe("double");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("Array", () => {
|
|
42
|
+
it("should create an array of strings", () => {
|
|
43
|
+
const stringArray = P.Array(P.String);
|
|
44
|
+
expect(stringArray).toEqual({
|
|
45
|
+
type: "array",
|
|
46
|
+
items: { type: "string" },
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should create an array of doubles", () => {
|
|
51
|
+
const doubleArray = P.Array(P.Double);
|
|
52
|
+
expect(doubleArray).toEqual({
|
|
53
|
+
type: "array",
|
|
54
|
+
items: { type: "double" },
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should create nested arrays", () => {
|
|
59
|
+
const nestedArray = P.Array(P.Array(P.String));
|
|
60
|
+
expect(nestedArray).toEqual({
|
|
61
|
+
type: "array",
|
|
62
|
+
items: {
|
|
63
|
+
type: "array",
|
|
64
|
+
items: { type: "string" },
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should create arrays of optional types", () => {
|
|
70
|
+
const optionalStringArray = P.Array(P.Optional(P.String));
|
|
71
|
+
expect(optionalStringArray).toEqual({
|
|
72
|
+
type: "array",
|
|
73
|
+
items: {
|
|
74
|
+
type: "optional",
|
|
75
|
+
item: { type: "string" },
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("Optional", () => {
|
|
82
|
+
it("should create an optional string", () => {
|
|
83
|
+
const optionalString = P.Optional(P.String);
|
|
84
|
+
expect(optionalString).toEqual({
|
|
85
|
+
type: "optional",
|
|
86
|
+
item: { type: "string" },
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should create an optional double", () => {
|
|
91
|
+
const optionalDouble = P.Optional(P.Double);
|
|
92
|
+
expect(optionalDouble).toEqual({
|
|
93
|
+
type: "optional",
|
|
94
|
+
item: { type: "double" },
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should create optional arrays", () => {
|
|
99
|
+
const optionalArray = P.Optional(P.Array(P.String));
|
|
100
|
+
expect(optionalArray).toEqual({
|
|
101
|
+
type: "optional",
|
|
102
|
+
item: {
|
|
103
|
+
type: "array",
|
|
104
|
+
items: { type: "string" },
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should handle nested optional types", () => {
|
|
110
|
+
const doubleOptional = P.Optional(P.Optional(P.String));
|
|
111
|
+
expect(doubleOptional).toEqual({
|
|
112
|
+
type: "optional",
|
|
113
|
+
item: {
|
|
114
|
+
type: "optional",
|
|
115
|
+
item: { type: "string" },
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("Type combinations", () => {
|
|
122
|
+
it("should create complex nested structures", () => {
|
|
123
|
+
const complexType = P.Optional(P.Array(P.Optional(P.Array(P.String))));
|
|
124
|
+
expect(complexType).toEqual({
|
|
125
|
+
type: "optional",
|
|
126
|
+
item: {
|
|
127
|
+
type: "array",
|
|
128
|
+
items: {
|
|
129
|
+
type: "optional",
|
|
130
|
+
item: {
|
|
131
|
+
type: "array",
|
|
132
|
+
items: { type: "string" },
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should maintain immutability of primitive types", () => {
|
|
140
|
+
const string1 = P.String;
|
|
141
|
+
const string2 = P.String;
|
|
142
|
+
expect(string1).toBe(string2);
|
|
143
|
+
expect(string1 === string2).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should create new instances for constructed types", () => {
|
|
147
|
+
const array1 = P.Array(P.String);
|
|
148
|
+
const array2 = P.Array(P.String);
|
|
149
|
+
expect(array1).toEqual(array2);
|
|
150
|
+
expect(array1 === array2).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* Checks if two types are exactly equal
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
|
|
22
|
+
? true
|
|
23
|
+
: false;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Asserts that two types are exactly equal. Causes a compile error if not.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // This will compile only if PersonRecord.fields.name is exactly String
|
|
30
|
+
* assertTypeEquals<typeof PersonRecord.fields.name, String>();
|
|
31
|
+
*/
|
|
32
|
+
export function assertTypeEquals<T, U>(
|
|
33
|
+
_witness?: Equals<T, U> extends true ? unknown : never,
|
|
34
|
+
): void {}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Checks if T extends U
|
|
38
|
+
*/
|
|
39
|
+
export type Extends<T, U> = T extends U ? true : false;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Asserts that T extends U. Causes a compile error if not.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // This will compile only if T extends Record<string, unknown>
|
|
46
|
+
* assertTypeExtends<T, Record<string, unknown>>();
|
|
47
|
+
*/
|
|
48
|
+
export function assertTypeExtends<T, U>(
|
|
49
|
+
_witness?: Extends<T, U> extends true ? unknown : never,
|
|
50
|
+
): void {}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Asserts that a type has the specified keys. Causes a compile error if not.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // This will compile only if PersonRecord.fields has exactly keys 'name' and 'age'
|
|
57
|
+
* assertExactKeys<typeof PersonRecord.fields, 'name' | 'age'>();
|
|
58
|
+
*/
|
|
59
|
+
export function assertExactKeys<T, K extends string>(
|
|
60
|
+
_witness?: Equals<keyof T, K> extends true ? unknown : never,
|
|
61
|
+
): void {}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Asserts that a type has at least the specified keys. Causes a compile error if not.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* // This will compile only if PersonRecord.fields has at least keys 'name' and 'age'
|
|
68
|
+
* assertHasKeys<typeof PersonRecord.fields, 'name' | 'age'>();
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
export function assertHasKeys<T, K extends string>(
|
|
72
|
+
_witness?: K extends keyof T ? unknown : never,
|
|
73
|
+
): void {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Assert that a value is never. Useful for exhaustiveness checks.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* function handleShape(shape: Shape) {
|
|
80
|
+
* if (shape.type === 'circle') {
|
|
81
|
+
* // handle circle
|
|
82
|
+
* } else if (shape.type === 'rectangle') {
|
|
83
|
+
* // handle rectangle
|
|
84
|
+
* } else {
|
|
85
|
+
* // This will cause a compile error if we forget to handle a shape type
|
|
86
|
+
* assertNever(shape);
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
export function assertNever(_value: never): never {
|
|
91
|
+
throw new Error("Should be unreachable");
|
|
92
|
+
}
|