@palantir/pack.document-schema.type-gen 0.12.0 → 0.14.0
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/build/esm/commands/asset/assetUpdateSchemaHandler.js +3 -2
- package/build/esm/commands/asset/assetUpdateSchemaHandler.js.map +1 -1
- package/build/esm/commands/ir/__tests__/resolveIrInput.test.js +88 -0
- package/build/esm/commands/ir/__tests__/resolveIrInput.test.js.map +1 -0
- package/build/esm/commands/ir/irDeployHandler.js +4 -3
- package/build/esm/commands/ir/irDeployHandler.js.map +1 -1
- package/build/esm/commands/ir/irGenAssetHandler.js +2 -31
- package/build/esm/commands/ir/irGenAssetHandler.js.map +1 -1
- package/build/esm/commands/ir/irGenTypesHandler.js +6 -6
- package/build/esm/commands/ir/irGenTypesHandler.js.map +1 -1
- package/build/esm/commands/ir/registerIrCommands.js +1 -1
- package/build/esm/commands/ir/registerIrCommands.js.map +1 -1
- package/build/esm/commands/ir/resolveIrInput.js +50 -0
- package/build/esm/commands/ir/resolveIrInput.js.map +1 -0
- package/build/esm/commands/schema/registerSchemaCommands.js +1 -1
- package/build/esm/commands/schema/registerSchemaCommands.js.map +1 -1
- package/build/esm/commands/schema/schemaIrHandler.js +14 -4
- package/build/esm/commands/schema/schemaIrHandler.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/custom-discriminant.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/custom-discriminant.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/external-refs.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/external-refs.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/simple-record.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/simple-record.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/union-types.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateModelsFromIr/union-types.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/array-and-map-types.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/array-and-map-types.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/boolean-fields.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/boolean-fields.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/custom-discriminant.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/custom-discriminant.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/datetime-fields.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/datetime-fields.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/simple-records.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/simple-records.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/union-before-records.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/union-before-records.js.map +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/union-types.js +1 -1
- package/build/esm/utils/ir/__tests__/__snapshots__/generateZodSchemasFromIr/union-types.js.map +1 -1
- package/build/esm/utils/schema/__tests__/__snapshots__/generateDocumentTypeFromSchema/with-name-and-description.js +1 -1
- package/build/esm/utils/schema/__tests__/__snapshots__/generateDocumentTypeFromSchema/with-name-and-description.js.map +1 -1
- package/build/esm/utils/schema/__tests__/__snapshots__/generateVersionsFromSchema/single-version.js.map +1 -1
- package/build/esm/utils/schema/__tests__/__snapshots__/generateVersionsFromSchema/three-version-chain.js.map +1 -1
- package/build/esm/utils/schema/__tests__/__snapshots__/generateVersionsFromSchema/two-version.js.map +1 -1
- package/build/esm/utils/schema/__tests__/fixtures.js +11 -0
- package/build/esm/utils/schema/__tests__/fixtures.js.map +1 -1
- package/build/esm/utils/schema/__tests__/generateInternalFromSchema.test.js +6 -1
- package/build/esm/utils/schema/__tests__/generateInternalFromSchema.test.js.map +1 -1
- package/build/esm/utils/schema/__tests__/provenanceScenarios.test.js +184 -0
- package/build/esm/utils/schema/__tests__/provenanceScenarios.test.js.map +1 -0
- package/build/esm/utils/schema/__tests__/resolveSchemaChain.test.js +135 -0
- package/build/esm/utils/schema/__tests__/resolveSchemaChain.test.js.map +1 -0
- package/build/esm/utils/schema/generateInternalFromSchema.js +55 -34
- package/build/esm/utils/schema/generateInternalFromSchema.js.map +1 -1
- package/build/esm/utils/schema/generateModelMetadataFromSchema.js +12 -10
- package/build/esm/utils/schema/generateModelMetadataFromSchema.js.map +1 -1
- package/build/esm/utils/schema/generateScopeFromSchema.js +15 -3
- package/build/esm/utils/schema/generateScopeFromSchema.js.map +1 -1
- package/build/esm/utils/schema/loadSchemaModule.js +2 -5
- package/build/esm/utils/schema/loadSchemaModule.js.map +1 -1
- package/build/esm/utils/schema/resolveSchemaChain.js +135 -29
- package/build/esm/utils/schema/resolveSchemaChain.js.map +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/arrayFields.js.map +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/customDiscriminant.js +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/customDiscriminant.js.map +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/generateTypeScriptInterfaces.js +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/generateTypeScriptInterfaces.js.map +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/refTypes.js.map +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/simpleRecord.js.map +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/simpleSteps.js +1 -1
- package/build/esm/utils/steps/__tests__/__snapshots__/types/simpleSteps.js.map +1 -1
- package/build/esm/utils/steps/convertStepsToIr.js +49 -24
- package/build/esm/utils/steps/convertStepsToIr.js.map +1 -1
- package/build/types/commands/asset/assetUpdateSchemaHandler.d.ts.map +1 -1
- package/build/types/commands/ir/__tests__/resolveIrInput.test.d.ts +2 -0
- package/build/types/commands/ir/__tests__/resolveIrInput.test.d.ts.map +1 -0
- package/build/types/commands/ir/irDeployHandler.d.ts.map +1 -1
- package/build/types/commands/ir/irGenAssetHandler.d.ts.map +1 -1
- package/build/types/commands/ir/irGenTypesHandler.d.ts.map +1 -1
- package/build/types/commands/ir/registerIrCommands.d.ts.map +1 -1
- package/build/types/commands/ir/resolveIrInput.d.ts +8 -0
- package/build/types/commands/ir/resolveIrInput.d.ts.map +1 -0
- package/build/types/commands/schema/registerSchemaCommands.d.ts.map +1 -1
- package/build/types/commands/schema/schemaIrHandler.d.ts +4 -3
- package/build/types/commands/schema/schemaIrHandler.d.ts.map +1 -1
- package/build/types/utils/schema/__tests__/fixtures.d.ts +1 -0
- package/build/types/utils/schema/__tests__/fixtures.d.ts.map +1 -1
- package/build/types/utils/schema/__tests__/provenanceScenarios.test.d.ts +2 -0
- package/build/types/utils/schema/__tests__/provenanceScenarios.test.d.ts.map +1 -0
- package/build/types/utils/schema/__tests__/resolveSchemaChain.test.d.ts +2 -0
- package/build/types/utils/schema/__tests__/resolveSchemaChain.test.d.ts.map +1 -0
- package/build/types/utils/schema/generateInternalFromSchema.d.ts.map +1 -1
- package/build/types/utils/schema/generateModelMetadataFromSchema.d.ts.map +1 -1
- package/build/types/utils/schema/generateScopeFromSchema.d.ts.map +1 -1
- package/build/types/utils/schema/loadSchemaModule.d.ts.map +1 -1
- package/build/types/utils/schema/resolveSchemaChain.d.ts +25 -5
- package/build/types/utils/schema/resolveSchemaChain.d.ts.map +1 -1
- package/build/types/utils/steps/convertStepsToIr.d.ts +19 -4
- package/build/types/utils/steps/convertStepsToIr.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 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 * as P from "@palantir/pack.schema";
|
|
18
|
+
import { defineRecord, defineSchema, defineSchemaUpdate, nextSchema } from "@palantir/pack.schema";
|
|
19
|
+
import { describe, expect, it } from "vitest";
|
|
20
|
+
import { generateInternalFromChain } from "../generateInternalFromSchema.js";
|
|
21
|
+
import { resolveSchemaChain, stripDeprecatedFieldsForSdk } from "../resolveSchemaChain.js";
|
|
22
|
+
function entry(chain, v) {
|
|
23
|
+
return chain.find(c => c.version === v);
|
|
24
|
+
}
|
|
25
|
+
function recordFields(e, key) {
|
|
26
|
+
const m = e.ir.models[key];
|
|
27
|
+
if (m.type !== "record") throw new Error(`${key} not a record`);
|
|
28
|
+
return Object.fromEntries(m.record.fields.map(f => [f.key, f]));
|
|
29
|
+
}
|
|
30
|
+
function unionMeta(e, key) {
|
|
31
|
+
const m = e.ir.models[key];
|
|
32
|
+
if (m.type !== "union") throw new Error(`${key} not a union`);
|
|
33
|
+
return m.union.metadata;
|
|
34
|
+
}
|
|
35
|
+
describe("provenance — scenarios beyond the demo", () => {
|
|
36
|
+
const v1 = defineSchema({
|
|
37
|
+
Box: defineRecord("Box", {
|
|
38
|
+
docs: "",
|
|
39
|
+
fields: {
|
|
40
|
+
a: P.Double
|
|
41
|
+
}
|
|
42
|
+
}),
|
|
43
|
+
OldUnion: P.defineUnion("OldUnion", {
|
|
44
|
+
docs: "",
|
|
45
|
+
variants: {
|
|
46
|
+
box: defineRecord("UVariant", {
|
|
47
|
+
docs: "",
|
|
48
|
+
fields: {
|
|
49
|
+
z: P.Double
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
const v2update = defineSchemaUpdate("v2", s => {
|
|
56
|
+
const Box = s.Box.addField("b", P.Optional(P.String)).build();
|
|
57
|
+
const Circle = defineRecord("Circle", {
|
|
58
|
+
docs: "",
|
|
59
|
+
fields: {
|
|
60
|
+
r: P.Double
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
const NewUnion = P.defineUnion("NewUnion", {
|
|
64
|
+
docs: "",
|
|
65
|
+
variants: {
|
|
66
|
+
c: Circle
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
Box,
|
|
71
|
+
Circle,
|
|
72
|
+
NewUnion
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
const v2 = nextSchema(v1).addSchemaUpdate(v2update).build();
|
|
76
|
+
const v3update = defineSchemaUpdate("v3", s => ({
|
|
77
|
+
Box: s.Box.addField("c", P.Optional(P.Double)).build()
|
|
78
|
+
}));
|
|
79
|
+
const v3 = nextSchema(v2).addSchemaUpdate(v3update).build();
|
|
80
|
+
it("union defined in v1 stays addedInVersion 1 in every version", () => {
|
|
81
|
+
const {
|
|
82
|
+
chain
|
|
83
|
+
} = resolveSchemaChain(v3);
|
|
84
|
+
for (const v of [1, 2, 3]) {
|
|
85
|
+
expect(unionMeta(entry(chain, v), "OldUnion").addedInVersion).toBe(1);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
it("union added in v2 gets addedInVersion 2", () => {
|
|
89
|
+
const {
|
|
90
|
+
chain
|
|
91
|
+
} = resolveSchemaChain(v3);
|
|
92
|
+
expect(entry(chain, 1).ir.models.NewUnion).toBeUndefined();
|
|
93
|
+
expect(unionMeta(entry(chain, 2), "NewUnion").addedInVersion).toBe(2);
|
|
94
|
+
expect(unionMeta(entry(chain, 3), "NewUnion").addedInVersion).toBe(2);
|
|
95
|
+
});
|
|
96
|
+
it("record added in v2 gets its fields stamped addedInVersion 2", () => {
|
|
97
|
+
const {
|
|
98
|
+
chain
|
|
99
|
+
} = resolveSchemaChain(v3);
|
|
100
|
+
expect(entry(chain, 1).ir.models.Circle).toBeUndefined();
|
|
101
|
+
expect(recordFields(entry(chain, 2), "Circle").r.metadata.addedInVersion).toBe(2);
|
|
102
|
+
expect(recordFields(entry(chain, 3), "Circle").r.metadata.addedInVersion).toBe(2);
|
|
103
|
+
});
|
|
104
|
+
it("field added in v3 to a v1 record gets addedInVersion 3; older fields keep theirs", () => {
|
|
105
|
+
const {
|
|
106
|
+
chain
|
|
107
|
+
} = resolveSchemaChain(v3);
|
|
108
|
+
const f = recordFields(entry(chain, 3), "Box");
|
|
109
|
+
expect(f.b.metadata.addedInVersion).toBe(2);
|
|
110
|
+
expect(f.c.metadata.addedInVersion).toBe(3);
|
|
111
|
+
});
|
|
112
|
+
it("hand-built schema provenance is derived from first appearance in the chain", () => {
|
|
113
|
+
const hand = {
|
|
114
|
+
type: "versioned",
|
|
115
|
+
version: 2,
|
|
116
|
+
previous: {
|
|
117
|
+
type: "initial",
|
|
118
|
+
version: 1,
|
|
119
|
+
models: v1.models
|
|
120
|
+
},
|
|
121
|
+
models: {
|
|
122
|
+
Box: defineRecord("Box", {
|
|
123
|
+
docs: "",
|
|
124
|
+
fields: {
|
|
125
|
+
a: P.Double,
|
|
126
|
+
b: P.Double
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const {
|
|
132
|
+
chain
|
|
133
|
+
} = resolveSchemaChain(hand);
|
|
134
|
+
expect(recordFields(entry(chain, 2), "Box").a.metadata.addedInVersion).toBe(1);
|
|
135
|
+
expect(recordFields(entry(chain, 2), "Box").b.metadata.addedInVersion).toBe(2);
|
|
136
|
+
expect(recordFields(entry(chain, 1), "Box").a.metadata.addedInVersion).toBe(1);
|
|
137
|
+
});
|
|
138
|
+
it("upgrade lens still records removedFields + derivedFrom for a deprecated field", () => {
|
|
139
|
+
const b1 = defineSchema({
|
|
140
|
+
Box: defineRecord("Box", {
|
|
141
|
+
docs: "",
|
|
142
|
+
fields: {
|
|
143
|
+
a: P.Double,
|
|
144
|
+
color: P.Optional(P.String)
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
});
|
|
148
|
+
const split = defineSchemaUpdate("split", s => ({
|
|
149
|
+
Box: s.Box.addField("fillColor", P.Optional(P.String), {
|
|
150
|
+
derivedFrom: ["color"]
|
|
151
|
+
}).deprecateField("color").build()
|
|
152
|
+
}));
|
|
153
|
+
const b2 = nextSchema(b1).addSchemaUpdate(split).build();
|
|
154
|
+
const resolved = resolveSchemaChain(b2);
|
|
155
|
+
const stripped = {
|
|
156
|
+
...resolved,
|
|
157
|
+
chain: stripDeprecatedFieldsForSdk(resolved.chain)
|
|
158
|
+
};
|
|
159
|
+
const internal = generateInternalFromChain(stripped);
|
|
160
|
+
expect(internal.upgrades).toContain("removedFields: [\"color\"]");
|
|
161
|
+
expect(internal.upgrades).toContain("derivedFrom: [\"color\"]");
|
|
162
|
+
});
|
|
163
|
+
it("deprecation with and without message both serialize (spread fix #5)", () => {
|
|
164
|
+
const b1 = defineSchema({
|
|
165
|
+
Box: defineRecord("Box", {
|
|
166
|
+
docs: "",
|
|
167
|
+
fields: {
|
|
168
|
+
x: P.Optional(P.String),
|
|
169
|
+
y: P.Optional(P.String)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
});
|
|
173
|
+
const dep = defineSchemaUpdate("dep", s => ({
|
|
174
|
+
Box: s.Box.deprecateField("x", "gone").deprecateField("y").build()
|
|
175
|
+
}));
|
|
176
|
+
const b2 = nextSchema(b1).addSchemaUpdate(dep).build();
|
|
177
|
+
const f = recordFields(entry(resolveSchemaChain(b2).chain, 2), "Box");
|
|
178
|
+
expect(f.x.metadata.deprecatedFromVersion).toBe(2);
|
|
179
|
+
expect(f.x.metadata.deprecatedMessage).toBe("gone");
|
|
180
|
+
expect(f.y.metadata.deprecatedFromVersion).toBe(2);
|
|
181
|
+
expect(f.y.metadata.deprecatedMessage ?? undefined).toBeUndefined();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=provenanceScenarios.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenanceScenarios.test.js","names":["P","defineRecord","defineSchema","defineSchemaUpdate","nextSchema","describe","expect","it","generateInternalFromChain","resolveSchemaChain","stripDeprecatedFieldsForSdk","entry","chain","v","find","c","version","recordFields","e","key","m","ir","models","type","Error","Object","fromEntries","record","fields","map","f","unionMeta","union","metadata","v1","Box","docs","a","Double","OldUnion","defineUnion","variants","box","z","v2update","s","addField","Optional","String","build","Circle","r","NewUnion","v2","addSchemaUpdate","v3update","v3","addedInVersion","toBe","toBeUndefined","b","hand","previous","b1","color","split","derivedFrom","deprecateField","b2","resolved","stripped","internal","upgrades","toContain","x","y","dep","deprecatedFromVersion","deprecatedMessage","undefined"],"sources":["provenanceScenarios.test.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as P from \"@palantir/pack.schema\";\nimport { defineRecord, defineSchema, defineSchemaUpdate, nextSchema } from \"@palantir/pack.schema\";\nimport { describe, expect, it } from \"vitest\";\nimport type {\n IFieldDef,\n IModelDef,\n} from \"../../../lib/pack-docschema-api/pack-docschema-ir/index.js\";\nimport { generateInternalFromChain } from \"../generateInternalFromSchema.js\";\nimport {\n resolveSchemaChain,\n stripDeprecatedFieldsForSdk,\n type VersionedIrEntry,\n} from \"../resolveSchemaChain.js\";\n\nfunction entry(chain: VersionedIrEntry[], v: number): VersionedIrEntry {\n return chain.find(c => c.version === v)!;\n}\nfunction recordFields(e: VersionedIrEntry, key: string): Record<string, IFieldDef> {\n const m: IModelDef = e.ir.models[key]!;\n if (m.type !== \"record\") throw new Error(`${key} not a record`);\n return Object.fromEntries(m.record.fields.map(f => [f.key, f]));\n}\nfunction unionMeta(e: VersionedIrEntry, key: string) {\n const m: IModelDef = e.ir.models[key]!;\n if (m.type !== \"union\") throw new Error(`${key} not a union`);\n return m.union.metadata;\n}\n\ndescribe(\"provenance — scenarios beyond the demo\", () => {\n const v1 = defineSchema({\n Box: defineRecord(\"Box\", { docs: \"\", fields: { a: P.Double } }),\n OldUnion: P.defineUnion(\"OldUnion\", {\n docs: \"\",\n variants: { box: defineRecord(\"UVariant\", { docs: \"\", fields: { z: P.Double } }) },\n }),\n });\n\n const v2update = defineSchemaUpdate(\"v2\", (s: P.SchemaBuilder<typeof v1.models>) => {\n const Box = s.Box.addField(\"b\", P.Optional(P.String)).build();\n const Circle = defineRecord(\"Circle\", { docs: \"\", fields: { r: P.Double } });\n const NewUnion = P.defineUnion(\"NewUnion\", {\n docs: \"\",\n variants: { c: Circle },\n });\n return { Box, Circle, NewUnion };\n });\n const v2 = nextSchema(v1).addSchemaUpdate(v2update).build();\n\n const v3update = defineSchemaUpdate(\"v3\", (s: P.SchemaBuilder<typeof v2.models>) => ({\n Box: s.Box.addField(\"c\", P.Optional(P.Double)).build(),\n }));\n const v3 = nextSchema(v2).addSchemaUpdate(v3update).build();\n\n it(\"union defined in v1 stays addedInVersion 1 in every version\", () => {\n const { chain } = resolveSchemaChain(v3);\n for (const v of [1, 2, 3]) {\n expect(unionMeta(entry(chain, v), \"OldUnion\").addedInVersion).toBe(1);\n }\n });\n\n it(\"union added in v2 gets addedInVersion 2\", () => {\n const { chain } = resolveSchemaChain(v3);\n expect(entry(chain, 1).ir.models.NewUnion).toBeUndefined();\n expect(unionMeta(entry(chain, 2), \"NewUnion\").addedInVersion).toBe(2);\n expect(unionMeta(entry(chain, 3), \"NewUnion\").addedInVersion).toBe(2);\n });\n\n it(\"record added in v2 gets its fields stamped addedInVersion 2\", () => {\n const { chain } = resolveSchemaChain(v3);\n expect(entry(chain, 1).ir.models.Circle).toBeUndefined();\n expect(recordFields(entry(chain, 2), \"Circle\").r!.metadata.addedInVersion).toBe(2);\n expect(recordFields(entry(chain, 3), \"Circle\").r!.metadata.addedInVersion).toBe(2);\n });\n\n it(\"field added in v3 to a v1 record gets addedInVersion 3; older fields keep theirs\", () => {\n const { chain } = resolveSchemaChain(v3);\n const f = recordFields(entry(chain, 3), \"Box\");\n expect(f.b!.metadata.addedInVersion).toBe(2);\n expect(f.c!.metadata.addedInVersion).toBe(3);\n });\n\n it(\"hand-built schema provenance is derived from first appearance in the chain\", () => {\n const hand: P.SchemaDefinition = {\n type: \"versioned\",\n version: 2,\n previous: { type: \"initial\", version: 1, models: v1.models },\n models: { Box: defineRecord(\"Box\", { docs: \"\", fields: { a: P.Double, b: P.Double } }) },\n };\n const { chain } = resolveSchemaChain(hand);\n expect(recordFields(entry(chain, 2), \"Box\").a!.metadata.addedInVersion).toBe(1);\n expect(recordFields(entry(chain, 2), \"Box\").b!.metadata.addedInVersion).toBe(2);\n expect(recordFields(entry(chain, 1), \"Box\").a!.metadata.addedInVersion).toBe(1);\n });\n\n it(\"upgrade lens still records removedFields + derivedFrom for a deprecated field\", () => {\n const b1 = defineSchema({\n Box: defineRecord(\"Box\", { docs: \"\", fields: { a: P.Double, color: P.Optional(P.String) } }),\n });\n const split = defineSchemaUpdate(\"split\", (s: P.SchemaBuilder<typeof b1.models>) => ({\n Box: s.Box\n .addField(\"fillColor\", P.Optional(P.String), { derivedFrom: [\"color\"] })\n .deprecateField(\"color\")\n .build(),\n }));\n const b2 = nextSchema(b1).addSchemaUpdate(split).build();\n\n const resolved = resolveSchemaChain(b2);\n const stripped = { ...resolved, chain: stripDeprecatedFieldsForSdk(resolved.chain) };\n const internal = generateInternalFromChain(stripped);\n expect(internal.upgrades).toContain(\"removedFields: [\\\"color\\\"]\");\n expect(internal.upgrades).toContain(\"derivedFrom: [\\\"color\\\"]\");\n });\n\n it(\"deprecation with and without message both serialize (spread fix #5)\", () => {\n const b1 = defineSchema({\n Box: defineRecord(\"Box\", {\n docs: \"\",\n fields: { x: P.Optional(P.String), y: P.Optional(P.String) },\n }),\n });\n const dep = defineSchemaUpdate(\"dep\", (s: P.SchemaBuilder<typeof b1.models>) => ({\n Box: s.Box.deprecateField(\"x\", \"gone\").deprecateField(\"y\").build(),\n }));\n const b2 = nextSchema(b1).addSchemaUpdate(dep).build();\n const f = recordFields(entry(resolveSchemaChain(b2).chain, 2), \"Box\");\n expect(f.x!.metadata.deprecatedFromVersion).toBe(2);\n expect(f.x!.metadata.deprecatedMessage).toBe(\"gone\");\n expect(f.y!.metadata.deprecatedFromVersion).toBe(2);\n expect(f.y!.metadata.deprecatedMessage ?? undefined).toBeUndefined();\n });\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAO,KAAKA,CAAC,MAAM,uBAAuB;AAC1C,SAASC,YAAY,EAAEC,YAAY,EAAEC,kBAAkB,EAAEC,UAAU,QAAQ,uBAAuB;AAClG,SAASC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAK7C,SAASC,yBAAyB,QAAQ,kCAAkC;AAC5E,SACEC,kBAAkB,EAClBC,2BAA2B,QAEtB,0BAA0B;AAEjC,SAASC,KAAKA,CAACC,KAAyB,EAAEC,CAAS,EAAoB;EACrE,OAAOD,KAAK,CAACE,IAAI,CAACC,CAAC,IAAIA,CAAC,CAACC,OAAO,KAAKH,CAAC,CAAC;AACzC;AACA,SAASI,YAAYA,CAACC,CAAmB,EAAEC,GAAW,EAA6B;EACjF,MAAMC,CAAY,GAAGF,CAAC,CAACG,EAAE,CAACC,MAAM,CAACH,GAAG,CAAE;EACtC,IAAIC,CAAC,CAACG,IAAI,KAAK,QAAQ,EAAE,MAAM,IAAIC,KAAK,CAAC,GAAGL,GAAG,eAAe,CAAC;EAC/D,OAAOM,MAAM,CAACC,WAAW,CAACN,CAAC,CAACO,MAAM,CAACC,MAAM,CAACC,GAAG,CAACC,CAAC,IAAI,CAACA,CAAC,CAACX,GAAG,EAAEW,CAAC,CAAC,CAAC,CAAC;AACjE;AACA,SAASC,SAASA,CAACb,CAAmB,EAAEC,GAAW,EAAE;EACnD,MAAMC,CAAY,GAAGF,CAAC,CAACG,EAAE,CAACC,MAAM,CAACH,GAAG,CAAE;EACtC,IAAIC,CAAC,CAACG,IAAI,KAAK,OAAO,EAAE,MAAM,IAAIC,KAAK,CAAC,GAAGL,GAAG,cAAc,CAAC;EAC7D,OAAOC,CAAC,CAACY,KAAK,CAACC,QAAQ;AACzB;AAEA5B,QAAQ,CAAC,wCAAwC,EAAE,MAAM;EACvD,MAAM6B,EAAE,GAAGhC,YAAY,CAAC;IACtBiC,GAAG,EAAElC,YAAY,CAAC,KAAK,EAAE;MAAEmC,IAAI,EAAE,EAAE;MAAER,MAAM,EAAE;QAAES,CAAC,EAAErC,CAAC,CAACsC;MAAO;IAAE,CAAC,CAAC;IAC/DC,QAAQ,EAAEvC,CAAC,CAACwC,WAAW,CAAC,UAAU,EAAE;MAClCJ,IAAI,EAAE,EAAE;MACRK,QAAQ,EAAE;QAAEC,GAAG,EAAEzC,YAAY,CAAC,UAAU,EAAE;UAAEmC,IAAI,EAAE,EAAE;UAAER,MAAM,EAAE;YAAEe,CAAC,EAAE3C,CAAC,CAACsC;UAAO;QAAE,CAAC;MAAE;IACnF,CAAC;EACH,CAAC,CAAC;EAEF,MAAMM,QAAQ,GAAGzC,kBAAkB,CAAC,IAAI,EAAG0C,CAAoC,IAAK;IAClF,MAAMV,GAAG,GAAGU,CAAC,CAACV,GAAG,CAACW,QAAQ,CAAC,GAAG,EAAE9C,CAAC,CAAC+C,QAAQ,CAAC/C,CAAC,CAACgD,MAAM,CAAC,CAAC,CAACC,KAAK,CAAC,CAAC;IAC7D,MAAMC,MAAM,GAAGjD,YAAY,CAAC,QAAQ,EAAE;MAAEmC,IAAI,EAAE,EAAE;MAAER,MAAM,EAAE;QAAEuB,CAAC,EAAEnD,CAAC,CAACsC;MAAO;IAAE,CAAC,CAAC;IAC5E,MAAMc,QAAQ,GAAGpD,CAAC,CAACwC,WAAW,CAAC,UAAU,EAAE;MACzCJ,IAAI,EAAE,EAAE;MACRK,QAAQ,EAAE;QAAE1B,CAAC,EAAEmC;MAAO;IACxB,CAAC,CAAC;IACF,OAAO;MAAEf,GAAG;MAAEe,MAAM;MAAEE;IAAS,CAAC;EAClC,CAAC,CAAC;EACF,MAAMC,EAAE,GAAGjD,UAAU,CAAC8B,EAAE,CAAC,CAACoB,eAAe,CAACV,QAAQ,CAAC,CAACK,KAAK,CAAC,CAAC;EAE3D,MAAMM,QAAQ,GAAGpD,kBAAkB,CAAC,IAAI,EAAG0C,CAAoC,KAAM;IACnFV,GAAG,EAAEU,CAAC,CAACV,GAAG,CAACW,QAAQ,CAAC,GAAG,EAAE9C,CAAC,CAAC+C,QAAQ,CAAC/C,CAAC,CAACsC,MAAM,CAAC,CAAC,CAACW,KAAK,CAAC;EACvD,CAAC,CAAC,CAAC;EACH,MAAMO,EAAE,GAAGpD,UAAU,CAACiD,EAAE,CAAC,CAACC,eAAe,CAACC,QAAQ,CAAC,CAACN,KAAK,CAAC,CAAC;EAE3D1C,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACtE,MAAM;MAAEK;IAAM,CAAC,GAAGH,kBAAkB,CAAC+C,EAAE,CAAC;IACxC,KAAK,MAAM3C,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE;MACzBP,MAAM,CAACyB,SAAS,CAACpB,KAAK,CAACC,KAAK,EAAEC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC4C,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;IACvE;EACF,CAAC,CAAC;EAEFnD,EAAE,CAAC,yCAAyC,EAAE,MAAM;IAClD,MAAM;MAAEK;IAAM,CAAC,GAAGH,kBAAkB,CAAC+C,EAAE,CAAC;IACxClD,MAAM,CAACK,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,CAACS,EAAE,CAACC,MAAM,CAAC8B,QAAQ,CAAC,CAACO,aAAa,CAAC,CAAC;IAC1DrD,MAAM,CAACyB,SAAS,CAACpB,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC6C,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;IACrEpD,MAAM,CAACyB,SAAS,CAACpB,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC6C,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;EACvE,CAAC,CAAC;EAEFnD,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACtE,MAAM;MAAEK;IAAM,CAAC,GAAGH,kBAAkB,CAAC+C,EAAE,CAAC;IACxClD,MAAM,CAACK,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,CAACS,EAAE,CAACC,MAAM,CAAC4B,MAAM,CAAC,CAACS,aAAa,CAAC,CAAC;IACxDrD,MAAM,CAACW,YAAY,CAACN,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAACuC,CAAC,CAAElB,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;IAClFpD,MAAM,CAACW,YAAY,CAACN,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAACuC,CAAC,CAAElB,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;EACpF,CAAC,CAAC;EAEFnD,EAAE,CAAC,kFAAkF,EAAE,MAAM;IAC3F,MAAM;MAAEK;IAAM,CAAC,GAAGH,kBAAkB,CAAC+C,EAAE,CAAC;IACxC,MAAM1B,CAAC,GAAGb,YAAY,CAACN,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC;IAC9CN,MAAM,CAACwB,CAAC,CAAC8B,CAAC,CAAE3B,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;IAC5CpD,MAAM,CAACwB,CAAC,CAACf,CAAC,CAAEkB,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;EAC9C,CAAC,CAAC;EAEFnD,EAAE,CAAC,4EAA4E,EAAE,MAAM;IACrF,MAAMsD,IAAwB,GAAG;MAC/BtC,IAAI,EAAE,WAAW;MACjBP,OAAO,EAAE,CAAC;MACV8C,QAAQ,EAAE;QAAEvC,IAAI,EAAE,SAAS;QAAEP,OAAO,EAAE,CAAC;QAAEM,MAAM,EAAEY,EAAE,CAACZ;MAAO,CAAC;MAC5DA,MAAM,EAAE;QAAEa,GAAG,EAAElC,YAAY,CAAC,KAAK,EAAE;UAAEmC,IAAI,EAAE,EAAE;UAAER,MAAM,EAAE;YAAES,CAAC,EAAErC,CAAC,CAACsC,MAAM;YAAEsB,CAAC,EAAE5D,CAAC,CAACsC;UAAO;QAAE,CAAC;MAAE;IACzF,CAAC;IACD,MAAM;MAAE1B;IAAM,CAAC,GAAGH,kBAAkB,CAACoD,IAAI,CAAC;IAC1CvD,MAAM,CAACW,YAAY,CAACN,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAACyB,CAAC,CAAEJ,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;IAC/EpD,MAAM,CAACW,YAAY,CAACN,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAACgD,CAAC,CAAE3B,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;IAC/EpD,MAAM,CAACW,YAAY,CAACN,KAAK,CAACC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAACyB,CAAC,CAAEJ,QAAQ,CAACwB,cAAc,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC;EACjF,CAAC,CAAC;EAEFnD,EAAE,CAAC,+EAA+E,EAAE,MAAM;IACxF,MAAMwD,EAAE,GAAG7D,YAAY,CAAC;MACtBiC,GAAG,EAAElC,YAAY,CAAC,KAAK,EAAE;QAAEmC,IAAI,EAAE,EAAE;QAAER,MAAM,EAAE;UAAES,CAAC,EAAErC,CAAC,CAACsC,MAAM;UAAE0B,KAAK,EAAEhE,CAAC,CAAC+C,QAAQ,CAAC/C,CAAC,CAACgD,MAAM;QAAE;MAAE,CAAC;IAC7F,CAAC,CAAC;IACF,MAAMiB,KAAK,GAAG9D,kBAAkB,CAAC,OAAO,EAAG0C,CAAoC,KAAM;MACnFV,GAAG,EAAEU,CAAC,CAACV,GAAG,CACPW,QAAQ,CAAC,WAAW,EAAE9C,CAAC,CAAC+C,QAAQ,CAAC/C,CAAC,CAACgD,MAAM,CAAC,EAAE;QAAEkB,WAAW,EAAE,CAAC,OAAO;MAAE,CAAC,CAAC,CACvEC,cAAc,CAAC,OAAO,CAAC,CACvBlB,KAAK,CAAC;IACX,CAAC,CAAC,CAAC;IACH,MAAMmB,EAAE,GAAGhE,UAAU,CAAC2D,EAAE,CAAC,CAACT,eAAe,CAACW,KAAK,CAAC,CAAChB,KAAK,CAAC,CAAC;IAExD,MAAMoB,QAAQ,GAAG5D,kBAAkB,CAAC2D,EAAE,CAAC;IACvC,MAAME,QAAQ,GAAG;MAAE,GAAGD,QAAQ;MAAEzD,KAAK,EAAEF,2BAA2B,CAAC2D,QAAQ,CAACzD,KAAK;IAAE,CAAC;IACpF,MAAM2D,QAAQ,GAAG/D,yBAAyB,CAAC8D,QAAQ,CAAC;IACpDhE,MAAM,CAACiE,QAAQ,CAACC,QAAQ,CAAC,CAACC,SAAS,CAAC,4BAA4B,CAAC;IACjEnE,MAAM,CAACiE,QAAQ,CAACC,QAAQ,CAAC,CAACC,SAAS,CAAC,0BAA0B,CAAC;EACjE,CAAC,CAAC;EAEFlE,EAAE,CAAC,qEAAqE,EAAE,MAAM;IAC9E,MAAMwD,EAAE,GAAG7D,YAAY,CAAC;MACtBiC,GAAG,EAAElC,YAAY,CAAC,KAAK,EAAE;QACvBmC,IAAI,EAAE,EAAE;QACRR,MAAM,EAAE;UAAE8C,CAAC,EAAE1E,CAAC,CAAC+C,QAAQ,CAAC/C,CAAC,CAACgD,MAAM,CAAC;UAAE2B,CAAC,EAAE3E,CAAC,CAAC+C,QAAQ,CAAC/C,CAAC,CAACgD,MAAM;QAAE;MAC7D,CAAC;IACH,CAAC,CAAC;IACF,MAAM4B,GAAG,GAAGzE,kBAAkB,CAAC,KAAK,EAAG0C,CAAoC,KAAM;MAC/EV,GAAG,EAAEU,CAAC,CAACV,GAAG,CAACgC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAACA,cAAc,CAAC,GAAG,CAAC,CAAClB,KAAK,CAAC;IACnE,CAAC,CAAC,CAAC;IACH,MAAMmB,EAAE,GAAGhE,UAAU,CAAC2D,EAAE,CAAC,CAACT,eAAe,CAACsB,GAAG,CAAC,CAAC3B,KAAK,CAAC,CAAC;IACtD,MAAMnB,CAAC,GAAGb,YAAY,CAACN,KAAK,CAACF,kBAAkB,CAAC2D,EAAE,CAAC,CAACxD,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC;IACrEN,MAAM,CAACwB,CAAC,CAAC4C,CAAC,CAAEzC,QAAQ,CAAC4C,qBAAqB,CAAC,CAACnB,IAAI,CAAC,CAAC,CAAC;IACnDpD,MAAM,CAACwB,CAAC,CAAC4C,CAAC,CAAEzC,QAAQ,CAAC6C,iBAAiB,CAAC,CAACpB,IAAI,CAAC,MAAM,CAAC;IACpDpD,MAAM,CAACwB,CAAC,CAAC6C,CAAC,CAAE1C,QAAQ,CAAC4C,qBAAqB,CAAC,CAACnB,IAAI,CAAC,CAAC,CAAC;IACnDpD,MAAM,CAACwB,CAAC,CAAC6C,CAAC,CAAE1C,QAAQ,CAAC6C,iBAAiB,IAAIC,SAAS,CAAC,CAACpB,aAAa,CAAC,CAAC;EACtE,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 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 * as P from "@palantir/pack.schema";
|
|
18
|
+
import { defineRecord, defineSchema, defineSchemaUpdate, nextSchema } from "@palantir/pack.schema";
|
|
19
|
+
import { describe, expect, it } from "vitest";
|
|
20
|
+
import { resolveSchemaChain, stripDeprecatedFieldsForSdk } from "../resolveSchemaChain.js";
|
|
21
|
+
|
|
22
|
+
/** Index a record model's fields by key for a given chain entry. */
|
|
23
|
+
function fieldsByKey(entry, modelKey) {
|
|
24
|
+
const model = entry.ir.models[modelKey];
|
|
25
|
+
if (model.type !== "record") throw new Error(`${modelKey} is not a record`);
|
|
26
|
+
return Object.fromEntries(model.record.fields.map(f => [f.key, f]));
|
|
27
|
+
}
|
|
28
|
+
describe("resolveSchemaChain provenance", () => {
|
|
29
|
+
const v1 = defineSchema({
|
|
30
|
+
ShapeBox: defineRecord("ShapeBox", {
|
|
31
|
+
docs: "A box",
|
|
32
|
+
fields: {
|
|
33
|
+
left: P.Double,
|
|
34
|
+
color: P.Optional(P.String)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
const colorSplit = defineSchemaUpdate("colorSplit", schema => ({
|
|
39
|
+
ShapeBox: schema.ShapeBox.addField("fillColor", P.Optional(P.String), {
|
|
40
|
+
derivedFrom: ["color"]
|
|
41
|
+
}).addField("strokeColor", P.Optional(P.String), {
|
|
42
|
+
derivedFrom: ["color"]
|
|
43
|
+
}).deprecateField("color", "use fillColor/strokeColor").build()
|
|
44
|
+
}));
|
|
45
|
+
const v2 = nextSchema(v1).addSchemaUpdate(colorSplit).build();
|
|
46
|
+
it("derives addedInVersion from first appearance in the chain", () => {
|
|
47
|
+
const {
|
|
48
|
+
chain
|
|
49
|
+
} = resolveSchemaChain(v2);
|
|
50
|
+
const v2Fields = fieldsByKey(chain.find(c => c.version === 2), "ShapeBox");
|
|
51
|
+
expect(v2Fields.left.metadata.addedInVersion).toBe(1);
|
|
52
|
+
expect(v2Fields.fillColor.metadata.addedInVersion).toBe(2);
|
|
53
|
+
expect(v2Fields.strokeColor.metadata.addedInVersion).toBe(2);
|
|
54
|
+
});
|
|
55
|
+
it("caps the chain at maxVersion", () => {
|
|
56
|
+
const {
|
|
57
|
+
chain,
|
|
58
|
+
latestVersion,
|
|
59
|
+
minVersion
|
|
60
|
+
} = resolveSchemaChain(v2, 1, {}, 1);
|
|
61
|
+
expect(chain.map(entry => entry.version)).toEqual([1]);
|
|
62
|
+
expect(latestVersion).toBe(1);
|
|
63
|
+
expect(minVersion).toBe(1);
|
|
64
|
+
});
|
|
65
|
+
it("rejects a maxVersion that is not in the chain", () => {
|
|
66
|
+
expect(() => resolveSchemaChain(v2, undefined, {}, 3)).toThrow("maxVersion 3 is not in the schema chain");
|
|
67
|
+
});
|
|
68
|
+
it("keeps a deprecated field present and stamps deprecatedFromVersion + message", () => {
|
|
69
|
+
const {
|
|
70
|
+
chain
|
|
71
|
+
} = resolveSchemaChain(v2);
|
|
72
|
+
const v2Fields = fieldsByKey(chain.find(c => c.version === 2), "ShapeBox");
|
|
73
|
+
expect(v2Fields.color).toBeDefined();
|
|
74
|
+
expect(v2Fields.color.metadata.addedInVersion).toBe(1);
|
|
75
|
+
expect(v2Fields.color.metadata.deprecatedFromVersion).toBe(2);
|
|
76
|
+
expect(v2Fields.color.metadata.deprecatedMessage).toBe("use fillColor/strokeColor");
|
|
77
|
+
});
|
|
78
|
+
it("does not mark the field deprecated in versions before the deprecation", () => {
|
|
79
|
+
const {
|
|
80
|
+
chain
|
|
81
|
+
} = resolveSchemaChain(v2);
|
|
82
|
+
const v1Fields = fieldsByKey(chain.find(c => c.version === 1), "ShapeBox");
|
|
83
|
+
expect(v1Fields.color).toBeDefined();
|
|
84
|
+
expect(v1Fields.color.metadata.deprecatedFromVersion ?? undefined).toBeUndefined();
|
|
85
|
+
expect(v1Fields.fillColor).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
it("stripDeprecatedFieldsForSdk drops deprecated fields from v>=deprecation only", () => {
|
|
88
|
+
const {
|
|
89
|
+
chain
|
|
90
|
+
} = resolveSchemaChain(v2);
|
|
91
|
+
const stripped = stripDeprecatedFieldsForSdk(chain);
|
|
92
|
+
const v1Fields = fieldsByKey(stripped.find(c => c.version === 1), "ShapeBox");
|
|
93
|
+
expect(v1Fields.color).toBeDefined();
|
|
94
|
+
const v2Fields = fieldsByKey(stripped.find(c => c.version === 2), "ShapeBox");
|
|
95
|
+
expect(v2Fields.color).toBeUndefined();
|
|
96
|
+
expect(v2Fields.fillColor).toBeDefined();
|
|
97
|
+
expect(fieldsByKey(chain.find(c => c.version === 2), "ShapeBox").color).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe("resolveSchemaChain — union provenance", () => {
|
|
101
|
+
const v1 = defineSchema({
|
|
102
|
+
Box: defineRecord("Box", {
|
|
103
|
+
docs: "",
|
|
104
|
+
fields: {
|
|
105
|
+
a: P.Double
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
NodeShape: P.defineUnion("NodeShape", {
|
|
109
|
+
docs: "shape",
|
|
110
|
+
variants: {
|
|
111
|
+
box: defineRecord("Inner", {
|
|
112
|
+
docs: "",
|
|
113
|
+
fields: {
|
|
114
|
+
z: P.Double
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
const addB = defineSchemaUpdate("addB", schema => ({
|
|
121
|
+
Box: schema.Box.addField("b", P.Optional(P.String)).build()
|
|
122
|
+
}));
|
|
123
|
+
const v2 = nextSchema(v1).addSchemaUpdate(addB).build();
|
|
124
|
+
it("a v1-defined union stays addedInVersion 1 in every version", () => {
|
|
125
|
+
const {
|
|
126
|
+
chain
|
|
127
|
+
} = resolveSchemaChain(v2);
|
|
128
|
+
for (const version of [1, 2]) {
|
|
129
|
+
const union = chain.find(c => c.version === version).ir.models.NodeShape;
|
|
130
|
+
if (union.type !== "union") throw new Error("NodeShape should be a union");
|
|
131
|
+
expect(union.union.metadata.addedInVersion).toBe(1);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=resolveSchemaChain.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveSchemaChain.test.js","names":["P","defineRecord","defineSchema","defineSchemaUpdate","nextSchema","describe","expect","it","resolveSchemaChain","stripDeprecatedFieldsForSdk","fieldsByKey","entry","modelKey","model","ir","models","type","Error","Object","fromEntries","record","fields","map","f","key","v1","ShapeBox","docs","left","Double","color","Optional","String","colorSplit","schema","addField","derivedFrom","deprecateField","build","v2","addSchemaUpdate","chain","v2Fields","find","c","version","metadata","addedInVersion","toBe","fillColor","strokeColor","latestVersion","minVersion","toEqual","undefined","toThrow","toBeDefined","deprecatedFromVersion","deprecatedMessage","v1Fields","toBeUndefined","stripped","Box","a","NodeShape","defineUnion","variants","box","z","addB","union"],"sources":["resolveSchemaChain.test.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as P from \"@palantir/pack.schema\";\nimport { defineRecord, defineSchema, defineSchemaUpdate, nextSchema } from \"@palantir/pack.schema\";\nimport { describe, expect, it } from \"vitest\";\nimport type {\n IFieldDef,\n IModelDef,\n} from \"../../../lib/pack-docschema-api/pack-docschema-ir/index.js\";\nimport {\n resolveSchemaChain,\n stripDeprecatedFieldsForSdk,\n type VersionedIrEntry,\n} from \"../resolveSchemaChain.js\";\n\n/** Index a record model's fields by key for a given chain entry. */\nfunction fieldsByKey(entry: VersionedIrEntry, modelKey: string): Record<string, IFieldDef> {\n const model: IModelDef = entry.ir.models[modelKey]!;\n if (model.type !== \"record\") throw new Error(`${modelKey} is not a record`);\n return Object.fromEntries(model.record.fields.map(f => [f.key, f]));\n}\n\ndescribe(\"resolveSchemaChain provenance\", () => {\n const v1 = defineSchema({\n ShapeBox: defineRecord(\"ShapeBox\", {\n docs: \"A box\",\n fields: { left: P.Double, color: P.Optional(P.String) },\n }),\n });\n\n const colorSplit = defineSchemaUpdate(\n \"colorSplit\",\n (schema: P.SchemaBuilder<typeof v1.models>) => ({\n ShapeBox: schema.ShapeBox\n .addField(\"fillColor\", P.Optional(P.String), { derivedFrom: [\"color\"] })\n .addField(\"strokeColor\", P.Optional(P.String), { derivedFrom: [\"color\"] })\n .deprecateField(\"color\", \"use fillColor/strokeColor\")\n .build(),\n }),\n );\n\n const v2 = nextSchema(v1).addSchemaUpdate(colorSplit).build();\n\n it(\"derives addedInVersion from first appearance in the chain\", () => {\n const { chain } = resolveSchemaChain(v2);\n const v2Fields = fieldsByKey(chain.find(c => c.version === 2)!, \"ShapeBox\");\n\n expect(v2Fields.left!.metadata.addedInVersion).toBe(1);\n expect(v2Fields.fillColor!.metadata.addedInVersion).toBe(2);\n expect(v2Fields.strokeColor!.metadata.addedInVersion).toBe(2);\n });\n\n it(\"caps the chain at maxVersion\", () => {\n const { chain, latestVersion, minVersion } = resolveSchemaChain(v2, 1, {}, 1);\n\n expect(chain.map(entry => entry.version)).toEqual([1]);\n expect(latestVersion).toBe(1);\n expect(minVersion).toBe(1);\n });\n\n it(\"rejects a maxVersion that is not in the chain\", () => {\n expect(() => resolveSchemaChain(v2, undefined, {}, 3)).toThrow(\n \"maxVersion 3 is not in the schema chain\",\n );\n });\n\n it(\"keeps a deprecated field present and stamps deprecatedFromVersion + message\", () => {\n const { chain } = resolveSchemaChain(v2);\n const v2Fields = fieldsByKey(chain.find(c => c.version === 2)!, \"ShapeBox\");\n\n expect(v2Fields.color).toBeDefined();\n expect(v2Fields.color!.metadata.addedInVersion).toBe(1);\n expect(v2Fields.color!.metadata.deprecatedFromVersion).toBe(2);\n expect(v2Fields.color!.metadata.deprecatedMessage).toBe(\"use fillColor/strokeColor\");\n });\n\n it(\"does not mark the field deprecated in versions before the deprecation\", () => {\n const { chain } = resolveSchemaChain(v2);\n const v1Fields = fieldsByKey(chain.find(c => c.version === 1)!, \"ShapeBox\");\n\n expect(v1Fields.color).toBeDefined();\n expect(v1Fields.color!.metadata.deprecatedFromVersion ?? undefined).toBeUndefined();\n expect(v1Fields.fillColor).toBeUndefined();\n });\n\n it(\"stripDeprecatedFieldsForSdk drops deprecated fields from v>=deprecation only\", () => {\n const { chain } = resolveSchemaChain(v2);\n const stripped = stripDeprecatedFieldsForSdk(chain);\n\n const v1Fields = fieldsByKey(stripped.find(c => c.version === 1)!, \"ShapeBox\");\n expect(v1Fields.color).toBeDefined();\n\n const v2Fields = fieldsByKey(stripped.find(c => c.version === 2)!, \"ShapeBox\");\n expect(v2Fields.color).toBeUndefined();\n expect(v2Fields.fillColor).toBeDefined();\n\n expect(fieldsByKey(chain.find(c => c.version === 2)!, \"ShapeBox\").color).toBeDefined();\n });\n});\n\ndescribe(\"resolveSchemaChain — union provenance\", () => {\n const v1 = defineSchema({\n Box: defineRecord(\"Box\", { docs: \"\", fields: { a: P.Double } }),\n NodeShape: P.defineUnion(\"NodeShape\", {\n docs: \"shape\",\n variants: { box: defineRecord(\"Inner\", { docs: \"\", fields: { z: P.Double } }) },\n }),\n });\n const addB = defineSchemaUpdate(\n \"addB\",\n (schema: P.SchemaBuilder<typeof v1.models>) => ({\n Box: schema.Box.addField(\"b\", P.Optional(P.String)).build(),\n }),\n );\n const v2 = nextSchema(v1).addSchemaUpdate(addB).build();\n\n it(\"a v1-defined union stays addedInVersion 1 in every version\", () => {\n const { chain } = resolveSchemaChain(v2);\n for (const version of [1, 2]) {\n const union = chain.find(c => c.version === version)!.ir.models.NodeShape!;\n if (union.type !== \"union\") throw new Error(\"NodeShape should be a union\");\n expect(union.union.metadata.addedInVersion).toBe(1);\n }\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -16,12 +16,18 @@
|
|
|
16
16
|
|
|
17
17
|
import { GENERATED_FILE_HEADER } from "../generatedFileHeader.js";
|
|
18
18
|
import { convertFieldTypeToDescriptor, convertFieldTypeToInternalTypeScript, convertFieldTypeToInternalZod } from "../ir/irFieldHelpers.js";
|
|
19
|
-
import {
|
|
19
|
+
import { chainNeedsUpgradeFns } from "./resolveSchemaChain.js";
|
|
20
20
|
|
|
21
21
|
/** Collected info about all fields for a model across all versions. */
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Represents an upgrade step for a model between version transitions.
|
|
25
|
+
*
|
|
26
|
+
* `fields` only contains entries that require lens treatment — derived fields
|
|
27
|
+
* (`derivedFrom.length > 0`) and required additive fields (`derivedFrom.length
|
|
28
|
+
* === 0 && !isOptional`). Optional additive fields are absent because
|
|
29
|
+
* `undefined` is a legal value at the current version and the lens has no
|
|
30
|
+
* work to do.
|
|
25
31
|
*/
|
|
26
32
|
|
|
27
33
|
/**
|
|
@@ -30,6 +36,9 @@ import { chainHasDerivedFields } from "./resolveSchemaChain.js";
|
|
|
30
36
|
function sortedEntries(map) {
|
|
31
37
|
return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
32
38
|
}
|
|
39
|
+
function getAddedInVersion(fieldInfo) {
|
|
40
|
+
return Math.min(...fieldInfo.presentInVersions);
|
|
41
|
+
}
|
|
33
42
|
|
|
34
43
|
/**
|
|
35
44
|
* Collect all record model info across all versions and compute upgrade steps.
|
|
@@ -87,25 +96,25 @@ function collectRecordModels(chain) {
|
|
|
87
96
|
const prevFields = new Set(prevModelDef.record.fields.map(f => f.key));
|
|
88
97
|
const currFields = new Set(currModelDef.record.fields.map(f => f.key));
|
|
89
98
|
|
|
90
|
-
// Fields added in this version
|
|
99
|
+
// Fields added in this version that require lens treatment. Optional
|
|
100
|
+
// additive fields are skipped — `undefined` is a legal v_current value
|
|
101
|
+
// for them, so the lens has nothing to invent.
|
|
91
102
|
const addedFields = new Map();
|
|
92
103
|
const recordUpgrades = currEntry.migrations?.[modelKey];
|
|
93
|
-
for (const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
}
|
|
104
|
+
for (const field of currModelDef.record.fields) {
|
|
105
|
+
const fieldKey = field.key;
|
|
106
|
+
if (prevFields.has(fieldKey)) continue;
|
|
107
|
+
const isOptional = field.isOptional === true;
|
|
108
|
+
const annotation = recordUpgrades?.[fieldKey];
|
|
109
|
+
const derivedFrom = annotation != null ? [...annotation.derivedFrom] : [];
|
|
110
|
+
if (derivedFrom.length === 0 && isOptional) continue;
|
|
111
|
+
addedFields.set(fieldKey, {
|
|
112
|
+
derivedFrom,
|
|
113
|
+
isOptional
|
|
114
|
+
});
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
// Fields
|
|
117
|
+
// Fields absent from this version relative to the previous one (i.e. deprecated fields).
|
|
109
118
|
const removedFields = [];
|
|
110
119
|
for (const fieldKey of prevFields) {
|
|
111
120
|
if (!currFields.has(fieldKey)) {
|
|
@@ -224,7 +233,7 @@ function generateUpgrades(allRecordModels, latestIr) {
|
|
|
224
233
|
output += ` allFields: {\n`;
|
|
225
234
|
for (const [fieldName, fieldInfo] of sortedEntries(model.allFields)) {
|
|
226
235
|
const typeDescriptor = convertFieldTypeToDescriptor(fieldInfo.fieldType, fieldInfo.isOptional);
|
|
227
|
-
output += ` ${fieldName}: { type: ${typeDescriptor} },\n`;
|
|
236
|
+
output += ` ${fieldName}: { type: ${typeDescriptor}, addedInVersion: ${getAddedInVersion(fieldInfo)} },\n`;
|
|
228
237
|
}
|
|
229
238
|
output += ` },\n`;
|
|
230
239
|
|
|
@@ -237,9 +246,6 @@ function generateUpgrades(allRecordModels, latestIr) {
|
|
|
237
246
|
for (const [fieldName, fieldMeta] of step.fields) {
|
|
238
247
|
output += ` ${fieldName}: {\n`;
|
|
239
248
|
output += ` derivedFrom: [${fieldMeta.derivedFrom.map(d => `"${d}"`).join(", ")}],\n`;
|
|
240
|
-
if (fieldMeta.default !== undefined) {
|
|
241
|
-
output += ` default: ${JSON.stringify(fieldMeta.default)},\n`;
|
|
242
|
-
}
|
|
243
249
|
output += ` },\n`;
|
|
244
250
|
}
|
|
245
251
|
output += ` },\n`;
|
|
@@ -292,16 +298,21 @@ function generateInternalSchemaContent(allRecordModels) {
|
|
|
292
298
|
* Generate _internal/upgradeFns.ts content: a typed `DocumentUpgradeFns`
|
|
293
299
|
* interface enumerating every upgrade function the app must supply at boot.
|
|
294
300
|
*
|
|
295
|
-
* For
|
|
296
|
-
*
|
|
297
|
-
* `
|
|
298
|
-
*
|
|
299
|
-
*
|
|
301
|
+
* For each `(modelName, v${version}, fieldName)` triple in a step's `fields`,
|
|
302
|
+
* emit one property:
|
|
303
|
+
* - Derived (`derivedFrom.length > 0`):
|
|
304
|
+
* `(oldFields: Pick<<Model>__v<prior>, derivedFrom[number]>) => <Model>__v<current>[fieldName]`.
|
|
305
|
+
* - Required additive (`derivedFrom.length === 0`):
|
|
306
|
+
* `() => <Model>__v<current>[fieldName]`.
|
|
307
|
+
*
|
|
308
|
+
* Optional additive fields are filtered out at step-collection time and don't
|
|
309
|
+
* appear here — `undefined` is a legal v<current> value for them, so the app
|
|
310
|
+
* has no obligation to invent one.
|
|
300
311
|
*
|
|
301
312
|
* The interface is consumed by the generated `DocumentModel` factory in
|
|
302
|
-
* `models.ts`, which requires it as a parameter when the schema
|
|
303
|
-
*
|
|
304
|
-
* (still exported for type-uniformity in downstream code).
|
|
313
|
+
* `models.ts`, which requires it as a parameter when the schema needs any
|
|
314
|
+
* upgrade functions. Schemas with no required fields added past v1 emit an
|
|
315
|
+
* empty interface (still exported for type-uniformity in downstream code).
|
|
305
316
|
*/
|
|
306
317
|
function generateUpgradeFns(chain, allRecordModels) {
|
|
307
318
|
const perModel = new Map();
|
|
@@ -313,7 +324,6 @@ function generateUpgradeFns(chain, allRecordModels) {
|
|
|
313
324
|
const priorVersion = chain[currentIdx - 1].version;
|
|
314
325
|
const fields = [];
|
|
315
326
|
for (const [fieldName, fieldMeta] of step.fields) {
|
|
316
|
-
if (fieldMeta.derivedFrom.length === 0) continue;
|
|
317
327
|
fields.push({
|
|
318
328
|
fieldName,
|
|
319
329
|
derivedFrom: [...fieldMeta.derivedFrom]
|
|
@@ -330,7 +340,10 @@ function generateUpgradeFns(chain, allRecordModels) {
|
|
|
330
340
|
priorVersion,
|
|
331
341
|
fields
|
|
332
342
|
});
|
|
333
|
-
|
|
343
|
+
// Prior-version type only needed when at least one field derives from it.
|
|
344
|
+
if (fields.some(f => f.derivedFrom.length > 0)) {
|
|
345
|
+
neededTypes.add(`${modelKey}__v${priorVersion}`);
|
|
346
|
+
}
|
|
334
347
|
neededTypes.add(`${modelKey}__v${step.addedInVersion}`);
|
|
335
348
|
}
|
|
336
349
|
}
|
|
@@ -343,7 +356,10 @@ function generateUpgradeFns(chain, allRecordModels) {
|
|
|
343
356
|
output += `/**\n`;
|
|
344
357
|
output += ` * Typed upgrade functions the application must supply at boot. Coverage is\n`;
|
|
345
358
|
output += ` * exhaustive: missing or extra entries fail type-check at the \`DocumentModel(...)\`\n`;
|
|
346
|
-
output += ` * call site.
|
|
359
|
+
output += ` * call site. Derived fields receive their declared source fields; required\n`;
|
|
360
|
+
output += ` * additive fields receive no arguments. Optional additive fields are absent\n`;
|
|
361
|
+
output += ` * — \`undefined\` is a legal value at the new version, so the runtime lens\n`;
|
|
362
|
+
output += ` * leaves them alone.\n`;
|
|
347
363
|
output += ` */\n`;
|
|
348
364
|
if (perModel.size === 0) {
|
|
349
365
|
output += `// eslint-disable-next-line @typescript-eslint/no-empty-object-type\n`;
|
|
@@ -355,8 +371,13 @@ function generateUpgradeFns(chain, allRecordModels) {
|
|
|
355
371
|
for (const step of steps) {
|
|
356
372
|
output += ` v${step.currentVersion}: {\n`;
|
|
357
373
|
for (const f of step.fields) {
|
|
358
|
-
const
|
|
359
|
-
|
|
374
|
+
const returnType = `${modelKey}__v${step.currentVersion}["${f.fieldName}"]`;
|
|
375
|
+
if (f.derivedFrom.length === 0) {
|
|
376
|
+
output += ` ${f.fieldName}: () => ${returnType};\n`;
|
|
377
|
+
} else {
|
|
378
|
+
const pickKeys = f.derivedFrom.map(d => `"${d}"`).join(" | ");
|
|
379
|
+
output += ` ${f.fieldName}: (oldFields: Pick<${modelKey}__v${step.priorVersion}, ${pickKeys}>) => ${returnType};\n`;
|
|
380
|
+
}
|
|
360
381
|
}
|
|
361
382
|
output += ` };\n`;
|
|
362
383
|
}
|
|
@@ -377,7 +398,7 @@ export function generateInternalFromChain(resolved) {
|
|
|
377
398
|
} = resolved;
|
|
378
399
|
const latestIr = chain[chain.length - 1].ir;
|
|
379
400
|
const allRecordModels = collectRecordModels(chain);
|
|
380
|
-
const internalTypes = generateInternalTypes(allRecordModels) + (
|
|
401
|
+
const internalTypes = generateInternalTypes(allRecordModels) + (chainNeedsUpgradeFns(chain) ? generatePerVersionInternalTypes(chain) : "");
|
|
381
402
|
const upgrades = generateUpgrades(allRecordModels, latestIr);
|
|
382
403
|
const internalSchema = generateInternalSchemaContent(allRecordModels);
|
|
383
404
|
const internalUpgradeFns = generateUpgradeFns(chain, allRecordModels);
|