@palantir/pack.schema 0.7.0 → 0.9.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.
@@ -14,9 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import type { SchemaBuilder } from "./defineMigration.js";
17
+ import type { FieldDeprecation, SchemaBuilder } from "./defineMigration.js";
18
18
  import { applyMigration } from "./defineMigration.js";
19
19
  import type { ModelDefs } from "./defs.js";
20
+ import { isRecordDef } from "./utils.js";
20
21
 
21
22
  export interface InitialSchema<T extends ModelDefs = ModelDefs> {
22
23
  readonly type: "initial";
@@ -30,12 +31,19 @@ export interface FieldMigration {
30
31
 
31
32
  export type VersionMigrations = Record<string, Record<string, FieldMigration>>;
32
33
 
34
+ export interface FieldDeprecationInfo {
35
+ readonly fromVersion: number;
36
+ readonly message?: string;
37
+ }
38
+ export type VersionDeprecations = Record<string, Record<string, FieldDeprecationInfo>>;
39
+
33
40
  export interface VersionedSchema<T extends ModelDefs = ModelDefs> {
34
41
  readonly type: "versioned";
35
42
  readonly models: T;
36
43
  readonly version: number;
37
44
  readonly previous: SchemaDefinition;
38
45
  readonly migrations?: VersionMigrations;
46
+ readonly deprecations?: VersionDeprecations;
39
47
  }
40
48
 
41
49
  export type SchemaDefinition<T extends ModelDefs = ModelDefs> =
@@ -67,18 +75,18 @@ export interface SchemaVersionBuilder<T extends ModelDefs> {
67
75
  }
68
76
 
69
77
  /**
70
- * Merge two `VersionMigrations` records. Per-model field migrations from
71
- * `override` take precedence over `base` for the same field, but the maps are
72
- * combined at both the model and field level so explicit `withMigrations`
73
- * calls can supplement (not just replace) the sugar from `addField` options.
78
+ * Shallow-merge two `[modelKey][fieldName]` maps; `override` wins per field.
79
+ * Used for both migrations (so `withMigrations` supplements `addField` sugar)
80
+ * and deprecations (so each version's deprecations accumulate forward). The
81
+ * maps are combined at both the model and field level.
74
82
  */
75
- function mergeMigrations(
76
- base: VersionMigrations | undefined,
77
- override: VersionMigrations | undefined,
78
- ): VersionMigrations | undefined {
83
+ function mergeByModelField<V>(
84
+ base: Record<string, Record<string, V>> | undefined,
85
+ override: Record<string, Record<string, V>> | undefined,
86
+ ): Record<string, Record<string, V>> | undefined {
79
87
  if (base == null) return override;
80
88
  if (override == null) return base;
81
- const result: VersionMigrations = { ...base };
89
+ const result: Record<string, Record<string, V>> = { ...base };
82
90
  for (const [modelKey, fields] of Object.entries(override)) {
83
91
  result[modelKey] = { ...result[modelKey], ...fields };
84
92
  }
@@ -87,7 +95,7 @@ function mergeMigrations(
87
95
 
88
96
  /**
89
97
  * Drop migration entries that no longer correspond to a field on a record in
90
- * `models`. Required because `mergeMigrations` is monotonic — a later
98
+ * `models`. Required because `mergeByModelField` is monotonic — a later
91
99
  * `addSchemaUpdate` that removes or retypes a field would otherwise leave a
92
100
  * stale entry in the accumulator. Entries are dropped when:
93
101
  * - the model key is no longer present,
@@ -102,7 +110,7 @@ function pruneMigrations(
102
110
  const result: VersionMigrations = {};
103
111
  for (const [modelKey, fields] of Object.entries(migrations)) {
104
112
  const def = models[modelKey];
105
- if (def == null || def.type !== "record") continue;
113
+ if (!isRecordDef(def)) continue;
106
114
  const recordFields = def.fields;
107
115
  const kept: Record<string, FieldMigration> = {};
108
116
  for (const [fieldName, migration] of Object.entries(fields)) {
@@ -117,44 +125,82 @@ function pruneMigrations(
117
125
  return Object.keys(result).length > 0 ? result : undefined;
118
126
  }
119
127
 
128
+ /**
129
+ * Convert the version-agnostic `FieldDeprecations` collected by `applyMigration`
130
+ * (just `{ message? }`) into `VersionDeprecations` by stamping `fromVersion`.
131
+ */
132
+ function stampDeprecationVersions(
133
+ deprecations: Record<string, Record<string, FieldDeprecation>> | undefined,
134
+ version: number,
135
+ ): VersionDeprecations | undefined {
136
+ if (deprecations == null) return undefined;
137
+ const result: VersionDeprecations = {};
138
+ for (const [modelKey, fields] of Object.entries(deprecations)) {
139
+ const out: Record<string, FieldDeprecationInfo> = {};
140
+ for (const [fieldName, info] of Object.entries(fields)) {
141
+ out[fieldName] = { fromVersion: version, ...info };
142
+ }
143
+ result[modelKey] = out;
144
+ }
145
+ return result;
146
+ }
147
+
120
148
  class SchemaVersionBuilderImpl<T extends ModelDefs> implements SchemaVersionBuilder<T> {
121
149
  private readonly models: T;
122
150
  private readonly version: number;
123
151
  private readonly previous: SchemaDefinition;
124
152
  private readonly _migrations: VersionMigrations | undefined;
153
+ private readonly _deprecations: VersionDeprecations | undefined;
125
154
 
126
155
  constructor(
127
156
  models: T,
128
157
  version: number,
129
158
  previous: SchemaDefinition,
130
- migrations?: VersionMigrations,
159
+ migrations: VersionMigrations | undefined,
160
+ deprecations?: VersionDeprecations,
131
161
  ) {
132
162
  this.models = models;
133
163
  this.version = version;
134
164
  this.previous = previous;
135
165
  this._migrations = migrations;
166
+ this._deprecations = deprecations;
136
167
  }
137
168
 
138
169
  addSchemaUpdate<S extends ModelDefs>(
139
170
  update: SchemaUpdate<T, S>,
140
171
  ): SchemaVersionBuilder<T & S> {
141
- // applyMigration returns both the merged models and any sugar-form
142
- // upgrades collected via `addField(name, type, { derivedFrom, forward })`.
143
- const { models, upgrades } = applyMigration(this.models, update.migration);
144
- const merged = mergeMigrations(
172
+ // applyMigration returns the merged models, any sugar-form upgrades from
173
+ // `addField(name, type, { derivedFrom })`, and any `deprecateField` calls.
174
+ const { models, upgrades, deprecations } = applyMigration(this.models, update.migration);
175
+ const merged = mergeByModelField(
145
176
  this._migrations,
146
177
  upgrades as VersionMigrations | undefined,
147
178
  );
148
- // Reconcile against the new models: a later update that removes or
149
- // retypes a field must drop the prior step's stale migration entry.
179
+ // Reconcile against the new models: a later update that retypes a field
180
+ // must drop the prior step's stale migration entry.
150
181
  const pruned = pruneMigrations(merged, models);
151
- return new SchemaVersionBuilderImpl(models, this.version, this.previous, pruned);
182
+ // Attach this version to each freshly-deprecated field, then merge forward.
183
+ const thisVersionDeprecations = stampDeprecationVersions(deprecations, this.version);
184
+ const mergedDeprecations = mergeByModelField(this._deprecations, thisVersionDeprecations);
185
+ return new SchemaVersionBuilderImpl(
186
+ models,
187
+ this.version,
188
+ this.previous,
189
+ pruned,
190
+ mergedDeprecations,
191
+ );
152
192
  }
153
193
 
154
194
  withMigrations(migrations: VersionMigrations): SchemaVersionBuilder<T> {
155
- const merged = mergeMigrations(this._migrations, migrations);
195
+ const merged = mergeByModelField(this._migrations, migrations);
156
196
  const pruned = pruneMigrations(merged, this.models);
157
- return new SchemaVersionBuilderImpl(this.models, this.version, this.previous, pruned);
197
+ return new SchemaVersionBuilderImpl(
198
+ this.models,
199
+ this.version,
200
+ this.previous,
201
+ pruned,
202
+ this._deprecations,
203
+ );
158
204
  }
159
205
 
160
206
  build(): VersionedSchema<T> {
@@ -164,6 +210,7 @@ class SchemaVersionBuilderImpl<T extends ModelDefs> implements SchemaVersionBuil
164
210
  version: this.version,
165
211
  previous: this.previous,
166
212
  ...(this._migrations != null ? { migrations: this._migrations } : {}),
213
+ ...(this._deprecations != null ? { deprecations: this._deprecations } : {}),
167
214
  };
168
215
  }
169
216
  }
@@ -171,5 +218,13 @@ class SchemaVersionBuilderImpl<T extends ModelDefs> implements SchemaVersionBuil
171
218
  export function nextSchema<T extends ModelDefs>(
172
219
  previous: SchemaDefinition<T>,
173
220
  ): SchemaVersionBuilder<T> {
174
- return new SchemaVersionBuilderImpl(previous.models, previous.version + 1, previous);
221
+ const version = previous.version + 1;
222
+ const deprecations = "deprecations" in previous ? previous.deprecations : undefined;
223
+ return new SchemaVersionBuilderImpl(
224
+ previous.models,
225
+ version,
226
+ previous,
227
+ undefined,
228
+ deprecations,
229
+ );
175
230
  }