@typespec/versioning 0.41.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/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib.d.ts +82 -0
- package/dist/src/lib.d.ts.map +1 -0
- package/dist/src/lib.js +80 -0
- package/dist/src/lib.js.map +1 -0
- package/dist/src/testing/index.d.ts +3 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +8 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/validate.d.ts +3 -0
- package/dist/src/validate.d.ts.map +1 -0
- package/dist/src/validate.js +408 -0
- package/dist/src/validate.js.map +1 -0
- package/dist/src/versioning.d.ts +159 -0
- package/dist/src/versioning.d.ts.map +1 -0
- package/dist/src/versioning.js +782 -0
- package/dist/src/versioning.js.map +1 -0
- package/lib/decorators.tsp +20 -0
- package/lib/versioning.tsp +200 -0
- package/package.json +71 -0
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
import { getNamespaceFullName, reportDeprecated, } from "@typespec/compiler";
|
|
2
|
+
import { createStateSymbol, reportDiagnostic } from "./lib.js";
|
|
3
|
+
const addedOnKey = createStateSymbol("addedOn");
|
|
4
|
+
const removedOnKey = createStateSymbol("removedOn");
|
|
5
|
+
const versionsKey = createStateSymbol("versions");
|
|
6
|
+
const versionDependencyKey = createStateSymbol("versionDependency");
|
|
7
|
+
const useDependencyNamespaceKey = createStateSymbol("useDependencyNamespace");
|
|
8
|
+
const useDependencyEnumKey = createStateSymbol("useDependencyEnum");
|
|
9
|
+
const renamedFromKey = createStateSymbol("renamedFrom");
|
|
10
|
+
const madeOptionalKey = createStateSymbol("madeOptional");
|
|
11
|
+
const typeChangedFromKey = createStateSymbol("typeChangedFrom");
|
|
12
|
+
const returnTypeChangedFromKey = createStateSymbol("returnTypeChangedFrom");
|
|
13
|
+
export const namespace = "TypeSpec.Versioning";
|
|
14
|
+
function checkIsVersion(program, enumMember, diagnosticTarget) {
|
|
15
|
+
const version = getVersionForEnumMember(program, enumMember);
|
|
16
|
+
if (!version) {
|
|
17
|
+
reportDiagnostic(program, {
|
|
18
|
+
code: "version-not-found",
|
|
19
|
+
target: diagnosticTarget,
|
|
20
|
+
format: { version: enumMember.name, enumName: enumMember.enum.name },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return version;
|
|
24
|
+
}
|
|
25
|
+
export function $added(context, t, v) {
|
|
26
|
+
var _a;
|
|
27
|
+
const { program } = context;
|
|
28
|
+
const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
|
|
29
|
+
if (!version) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// retrieve statemap to update or create a new one
|
|
33
|
+
const record = (_a = program.stateMap(addedOnKey).get(t)) !== null && _a !== void 0 ? _a : new Array();
|
|
34
|
+
record.push(version);
|
|
35
|
+
// ensure that records are stored in ascending order
|
|
36
|
+
record.sort((a, b) => a.index - b.index);
|
|
37
|
+
program.stateMap(addedOnKey).set(t, record);
|
|
38
|
+
}
|
|
39
|
+
export function $removed(context, t, v) {
|
|
40
|
+
var _a;
|
|
41
|
+
const { program } = context;
|
|
42
|
+
const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
|
|
43
|
+
if (!version) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// retrieve statemap to update or create a new one
|
|
47
|
+
const record = (_a = program.stateMap(removedOnKey).get(t)) !== null && _a !== void 0 ? _a : new Array();
|
|
48
|
+
record.push(version);
|
|
49
|
+
// ensure that records are stored in ascending order
|
|
50
|
+
record.sort((a, b) => a.index - b.index);
|
|
51
|
+
program.stateMap(removedOnKey).set(t, record);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns the mapping of versions to old type values, if applicable
|
|
55
|
+
* @param p TypeSpec program
|
|
56
|
+
* @param t type to query
|
|
57
|
+
* @returns Map of versions to old types, if any
|
|
58
|
+
*/
|
|
59
|
+
export function getTypeChangedFrom(p, t) {
|
|
60
|
+
return p.stateMap(typeChangedFromKey).get(t);
|
|
61
|
+
}
|
|
62
|
+
export function $typeChangedFrom(context, prop, v, oldType) {
|
|
63
|
+
var _a;
|
|
64
|
+
const { program } = context;
|
|
65
|
+
const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
|
|
66
|
+
if (!version) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// retrieve statemap to update or create a new one
|
|
70
|
+
let record = (_a = getTypeChangedFrom(program, prop)) !== null && _a !== void 0 ? _a : new Map();
|
|
71
|
+
record.set(version, oldType);
|
|
72
|
+
// ensure the map is sorted by version
|
|
73
|
+
record = new Map([...record.entries()].sort((a, b) => a[0].index - b[0].index));
|
|
74
|
+
program.stateMap(typeChangedFromKey).set(prop, record);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns the mapping of versions to old return type values, if applicable
|
|
78
|
+
* @param p TypeSpec program
|
|
79
|
+
* @param t type to query
|
|
80
|
+
* @returns Map of versions to old types, if any
|
|
81
|
+
*/
|
|
82
|
+
export function getReturnTypeChangedFrom(p, t) {
|
|
83
|
+
return p.stateMap(returnTypeChangedFromKey).get(t);
|
|
84
|
+
}
|
|
85
|
+
export function $returnTypeChangedFrom(context, op, v, oldReturnType) {
|
|
86
|
+
var _a;
|
|
87
|
+
const { program } = context;
|
|
88
|
+
const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
|
|
89
|
+
if (!version) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// retrieve statemap to update or create a new one
|
|
93
|
+
let record = (_a = getReturnTypeChangedFrom(program, op)) !== null && _a !== void 0 ? _a : new Map();
|
|
94
|
+
record.set(version, oldReturnType);
|
|
95
|
+
// ensure the map is sorted by version
|
|
96
|
+
record = new Map([...record.entries()].sort((a, b) => a[0].index - b[0].index));
|
|
97
|
+
program.stateMap(returnTypeChangedFromKey).set(op, record);
|
|
98
|
+
}
|
|
99
|
+
export function $renamedFrom(context, t, v, oldName) {
|
|
100
|
+
var _a;
|
|
101
|
+
const { program } = context;
|
|
102
|
+
const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
|
|
103
|
+
if (!version) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (oldName === "") {
|
|
107
|
+
reportDiagnostic(program, {
|
|
108
|
+
code: "invalid-renamed-from-value",
|
|
109
|
+
target: t,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// retrieve statemap to update or create a new one
|
|
113
|
+
const record = (_a = getRenamedFrom(program, t)) !== null && _a !== void 0 ? _a : [];
|
|
114
|
+
record.push({ version: version, oldName: oldName });
|
|
115
|
+
// ensure that records are stored in ascending order
|
|
116
|
+
record.sort((a, b) => a.version.index - b.version.index);
|
|
117
|
+
program.stateMap(renamedFromKey).set(t, record);
|
|
118
|
+
}
|
|
119
|
+
export function $madeOptional(context, t, v) {
|
|
120
|
+
const { program } = context;
|
|
121
|
+
const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
|
|
122
|
+
if (!version) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
program.stateMap(madeOptionalKey).set(t, version);
|
|
126
|
+
}
|
|
127
|
+
function toVersion(p, t, v) {
|
|
128
|
+
var _a;
|
|
129
|
+
const [namespace] = getVersions(p, t);
|
|
130
|
+
if (namespace === undefined) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return getVersionForNamespace(p, v, (_a = namespace.projectionBase) !== null && _a !== void 0 ? _a : namespace);
|
|
134
|
+
}
|
|
135
|
+
function getRenamedFrom(p, t) {
|
|
136
|
+
return p.stateMap(renamedFromKey).get(t);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* @deprecated since version 0.39.0. Use getRenamedFromVersions
|
|
140
|
+
* @returns version when the given type was added if applicable.
|
|
141
|
+
*/
|
|
142
|
+
export function getRenamedFromVersion(p, t) {
|
|
143
|
+
var _a;
|
|
144
|
+
reportDeprecated(p, "Deprecated: getRenamedFromVersion is deprecated. Use getRenamedFromVersions instead.", t);
|
|
145
|
+
return (_a = p.stateMap(renamedFromKey).get(t)) === null || _a === void 0 ? void 0 : _a[0].version;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @returns the list of versions for which this decorator has been applied
|
|
149
|
+
*/
|
|
150
|
+
export function getRenamedFromVersions(p, t) {
|
|
151
|
+
var _a;
|
|
152
|
+
return (_a = getRenamedFrom(p, t)) === null || _a === void 0 ? void 0 : _a.map((x) => x.version);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* @deprecated since version 0.39.0. Use getNameAtVersion instead.
|
|
156
|
+
* @returns get old renamed name if applicable.
|
|
157
|
+
*/
|
|
158
|
+
export function getRenamedFromOldName(p, t, v) {
|
|
159
|
+
var _a, _b;
|
|
160
|
+
reportDeprecated(p, "Deprecated: getRenamedFromOldName is deprecated. Use getNameAtVersion instead.", t);
|
|
161
|
+
return (_b = (_a = p.stateMap(renamedFromKey).get(t)) === null || _a === void 0 ? void 0 : _a[0].oldName) !== null && _b !== void 0 ? _b : "";
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* @returns get old name if applicable.
|
|
165
|
+
*/
|
|
166
|
+
export function getNameAtVersion(p, t, v) {
|
|
167
|
+
const version = toVersion(p, t, v);
|
|
168
|
+
const allValues = getRenamedFrom(p, t);
|
|
169
|
+
if (!allValues || !version)
|
|
170
|
+
return "";
|
|
171
|
+
const targetIndex = version.index;
|
|
172
|
+
for (const val of allValues) {
|
|
173
|
+
const index = val.version.index;
|
|
174
|
+
if (targetIndex < index) {
|
|
175
|
+
return val.oldName;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return "";
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* @returns get old type if applicable.
|
|
182
|
+
*/
|
|
183
|
+
export function getTypeBeforeVersion(p, t, v) {
|
|
184
|
+
const target = toVersion(p, t, v);
|
|
185
|
+
const map = getTypeChangedFrom(p, t);
|
|
186
|
+
if (!map || !target)
|
|
187
|
+
return undefined;
|
|
188
|
+
for (const [key, val] of map) {
|
|
189
|
+
if (target.index < key.index) {
|
|
190
|
+
return val;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* @returns get old type if applicable.
|
|
197
|
+
*/
|
|
198
|
+
export function getReturnTypeBeforeVersion(p, t, v) {
|
|
199
|
+
const target = toVersion(p, t, v);
|
|
200
|
+
const map = getReturnTypeChangedFrom(p, t);
|
|
201
|
+
if (!map || !target)
|
|
202
|
+
return "";
|
|
203
|
+
for (const [key, val] of map) {
|
|
204
|
+
if (target.index < key.index) {
|
|
205
|
+
return val;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return "";
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* @deprecated since version 0.39.0.
|
|
212
|
+
* @returns version when the given type was added if applicable.
|
|
213
|
+
*/
|
|
214
|
+
export function getAddedOn(p, t) {
|
|
215
|
+
var _a;
|
|
216
|
+
reportDeprecated(p, "Deprecated: getAddedOn is deprecated. Use getAddedOnVersions.", t);
|
|
217
|
+
return (_a = p.stateMap(addedOnKey).get(t)) === null || _a === void 0 ? void 0 : _a[0];
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated since version 0.39.0.
|
|
221
|
+
* @returns version when the given type was removed if applicable.
|
|
222
|
+
*/
|
|
223
|
+
export function getRemovedOn(p, t) {
|
|
224
|
+
var _a;
|
|
225
|
+
reportDeprecated(p, "Deprecated: getRemovedOn is deprecated. Use getRemovedOnVersions.", t);
|
|
226
|
+
return (_a = p.stateMap(removedOnKey).get(t)) === null || _a === void 0 ? void 0 : _a[0];
|
|
227
|
+
}
|
|
228
|
+
export function getAddedOnVersions(p, t) {
|
|
229
|
+
return p.stateMap(addedOnKey).get(t);
|
|
230
|
+
}
|
|
231
|
+
export function getRemovedOnVersions(p, t) {
|
|
232
|
+
return p.stateMap(removedOnKey).get(t);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* @returns version when the given type was made optional if applicable.
|
|
236
|
+
*/
|
|
237
|
+
export function getMadeOptionalOn(p, t) {
|
|
238
|
+
return p.stateMap(madeOptionalKey).get(t);
|
|
239
|
+
}
|
|
240
|
+
export class VersionMap {
|
|
241
|
+
constructor(namespace, enumType) {
|
|
242
|
+
var _a, _b;
|
|
243
|
+
this.map = new Map();
|
|
244
|
+
let index = 0;
|
|
245
|
+
for (const member of enumType.members.values()) {
|
|
246
|
+
this.map.set(member, {
|
|
247
|
+
name: member.name,
|
|
248
|
+
value: (_b = (_a = member.value) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : member.name,
|
|
249
|
+
enumMember: member,
|
|
250
|
+
index,
|
|
251
|
+
namespace,
|
|
252
|
+
});
|
|
253
|
+
index++;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
getVersionForEnumMember(member) {
|
|
257
|
+
return this.map.get(member);
|
|
258
|
+
}
|
|
259
|
+
getVersions() {
|
|
260
|
+
return [...this.map.values()];
|
|
261
|
+
}
|
|
262
|
+
get size() {
|
|
263
|
+
return this.map.size;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export function $versioned(context, t, versions) {
|
|
267
|
+
context.program.stateMap(versionsKey).set(t, new VersionMap(t, versions));
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get the version map of the namespace.
|
|
271
|
+
*/
|
|
272
|
+
export function getVersion(program, namespace) {
|
|
273
|
+
return program.stateMap(versionsKey).get(namespace);
|
|
274
|
+
}
|
|
275
|
+
export function findVersionedNamespace(program, namespace) {
|
|
276
|
+
let current = namespace;
|
|
277
|
+
while (current) {
|
|
278
|
+
if (program.stateMap(versionsKey).has(current)) {
|
|
279
|
+
return current;
|
|
280
|
+
}
|
|
281
|
+
current = current.namespace;
|
|
282
|
+
}
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
export function $useDependency(context, target, ...versionRecords) {
|
|
286
|
+
var _a;
|
|
287
|
+
const versions = [];
|
|
288
|
+
// ensure only valid versions are passed in
|
|
289
|
+
for (const record of versionRecords) {
|
|
290
|
+
const ver = checkIsVersion(context.program, record, context.getArgumentTarget(0));
|
|
291
|
+
if (ver) {
|
|
292
|
+
versions.push(ver);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (target.kind === "Namespace") {
|
|
296
|
+
let state = context.program.stateMap(useDependencyNamespaceKey).get(target);
|
|
297
|
+
if (!state) {
|
|
298
|
+
state = versions;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
state.push(...versions);
|
|
302
|
+
}
|
|
303
|
+
context.program.stateMap(useDependencyNamespaceKey).set(target, state);
|
|
304
|
+
}
|
|
305
|
+
else if (target.kind === "EnumMember") {
|
|
306
|
+
const targetEnum = target.enum;
|
|
307
|
+
let state = context.program.stateMap(useDependencyEnumKey).get(targetEnum);
|
|
308
|
+
if (!state) {
|
|
309
|
+
state = new Map();
|
|
310
|
+
}
|
|
311
|
+
// get any existing versions and combine them
|
|
312
|
+
const currentVersions = (_a = state.get(target)) !== null && _a !== void 0 ? _a : [];
|
|
313
|
+
currentVersions.push(...versions);
|
|
314
|
+
state.set(target, currentVersions);
|
|
315
|
+
context.program.stateMap(useDependencyEnumKey).set(targetEnum, state);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
export function getUseDependencies(program, target, searchEnum = true) {
|
|
319
|
+
var _a;
|
|
320
|
+
const result = new Map();
|
|
321
|
+
if (target.kind === "Namespace") {
|
|
322
|
+
let current = target;
|
|
323
|
+
while (current) {
|
|
324
|
+
const data = program.stateMap(useDependencyNamespaceKey).get(current);
|
|
325
|
+
if (!data) {
|
|
326
|
+
// See if the namspace has a version enum
|
|
327
|
+
if (searchEnum) {
|
|
328
|
+
const versions = (_a = getVersion(program, current)) === null || _a === void 0 ? void 0 : _a.getVersions();
|
|
329
|
+
if (versions === null || versions === void 0 ? void 0 : versions.length) {
|
|
330
|
+
const enumDeps = getUseDependencies(program, versions[0].enumMember.enum);
|
|
331
|
+
if (enumDeps) {
|
|
332
|
+
return enumDeps;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
current = current.namespace;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
for (const v of data) {
|
|
340
|
+
result.set(v.namespace, v);
|
|
341
|
+
}
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
else if (target.kind === "Enum") {
|
|
348
|
+
const data = program.stateMap(useDependencyEnumKey).get(target);
|
|
349
|
+
if (!data) {
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
const resolved = resolveVersionDependency(program, data);
|
|
353
|
+
if (resolved instanceof Map) {
|
|
354
|
+
for (const [enumVer, value] of resolved) {
|
|
355
|
+
for (const val of value) {
|
|
356
|
+
const targetNamespace = val.enumMember.enum.namespace;
|
|
357
|
+
if (!targetNamespace) {
|
|
358
|
+
reportDiagnostic(program, {
|
|
359
|
+
code: "version-not-found",
|
|
360
|
+
target: val.enumMember.enum,
|
|
361
|
+
format: { version: val.enumMember.name, enumName: val.enumMember.enum.name },
|
|
362
|
+
});
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
let subMap = result.get(targetNamespace);
|
|
366
|
+
if (subMap) {
|
|
367
|
+
subMap.set(enumVer, val);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
subMap = new Map([[enumVer, val]]);
|
|
371
|
+
}
|
|
372
|
+
result.set(targetNamespace, subMap);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
export function $versionedDependency(context, referenceNamespace, versionRecord) {
|
|
380
|
+
const { program } = context;
|
|
381
|
+
reportDeprecated(program, "@versionedDependency is deprecated. Use @useDependency instead.", context.decoratorTarget);
|
|
382
|
+
let state = program.stateMap(versionDependencyKey).get(referenceNamespace);
|
|
383
|
+
if (!state) {
|
|
384
|
+
state = new Map();
|
|
385
|
+
context.program.stateMap(versionDependencyKey).set(referenceNamespace, state);
|
|
386
|
+
}
|
|
387
|
+
if (versionRecord.kind === "EnumMember") {
|
|
388
|
+
const v = checkIsVersion(program, versionRecord, context.getArgumentTarget(0));
|
|
389
|
+
if (v) {
|
|
390
|
+
state.set(v.namespace, v);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
let targetNamespace;
|
|
395
|
+
const versionMap = new Map();
|
|
396
|
+
for (const entry of versionRecord.values) {
|
|
397
|
+
if (entry.kind !== "Tuple") {
|
|
398
|
+
reportDiagnostic(context.program, { code: "versioned-dependency-tuple", target: entry });
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const [sourceMember, targetMember] = entry.values;
|
|
402
|
+
if (sourceMember === undefined || sourceMember.kind !== "EnumMember") {
|
|
403
|
+
reportDiagnostic(context.program, {
|
|
404
|
+
code: "versioned-dependency-tuple-enum-member",
|
|
405
|
+
target: sourceMember !== null && sourceMember !== void 0 ? sourceMember : entry,
|
|
406
|
+
});
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (targetMember === undefined || targetMember.kind !== "EnumMember") {
|
|
410
|
+
reportDiagnostic(context.program, {
|
|
411
|
+
code: "versioned-dependency-tuple-enum-member",
|
|
412
|
+
target: targetMember !== null && targetMember !== void 0 ? targetMember : entry,
|
|
413
|
+
});
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
const targetVersion = checkIsVersion(program, targetMember, targetMember);
|
|
417
|
+
if (!targetVersion) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (targetNamespace === undefined) {
|
|
421
|
+
targetNamespace = targetVersion.namespace;
|
|
422
|
+
}
|
|
423
|
+
else if (targetNamespace !== targetVersion.namespace) {
|
|
424
|
+
reportDiagnostic(context.program, {
|
|
425
|
+
code: "versioned-dependency-same-namespace",
|
|
426
|
+
format: {
|
|
427
|
+
namespace1: getNamespaceFullName(targetNamespace),
|
|
428
|
+
namespace2: getNamespaceFullName(targetVersion.namespace),
|
|
429
|
+
},
|
|
430
|
+
target: targetMember,
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
versionMap.set(sourceMember, targetVersion);
|
|
435
|
+
}
|
|
436
|
+
if (targetNamespace) {
|
|
437
|
+
state.set(targetNamespace, versionMap);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function findVersionDependencyForNamespace(program, namespace) {
|
|
442
|
+
let current = namespace;
|
|
443
|
+
while (current) {
|
|
444
|
+
const data = program.stateMap(versionDependencyKey).get(current);
|
|
445
|
+
if (data) {
|
|
446
|
+
return data;
|
|
447
|
+
}
|
|
448
|
+
current = current.namespace;
|
|
449
|
+
}
|
|
450
|
+
return undefined;
|
|
451
|
+
}
|
|
452
|
+
export function getVersionDependencies(program, namespace) {
|
|
453
|
+
const useDeps = getUseDependencies(program, namespace);
|
|
454
|
+
if (useDeps) {
|
|
455
|
+
return useDeps;
|
|
456
|
+
}
|
|
457
|
+
const data = findVersionDependencyForNamespace(program, namespace);
|
|
458
|
+
if (data === undefined) {
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
461
|
+
const result = new Map();
|
|
462
|
+
for (const [key, value] of data) {
|
|
463
|
+
result.set(key, resolveVersionDependency(program, value));
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
function resolveVersionDependency(program, data) {
|
|
468
|
+
if (!(data instanceof Map)) {
|
|
469
|
+
return data;
|
|
470
|
+
}
|
|
471
|
+
const mapping = new Map();
|
|
472
|
+
for (const [key, value] of data) {
|
|
473
|
+
const sourceVersion = getVersionForEnumMember(program, key);
|
|
474
|
+
if (sourceVersion !== undefined) {
|
|
475
|
+
mapping.set(sourceVersion, value);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return mapping;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Resolve the version to use for all namespace for each of the root namespace versions.
|
|
482
|
+
* @param program
|
|
483
|
+
* @param rootNs Root namespace.
|
|
484
|
+
*/
|
|
485
|
+
export function resolveVersions(program, rootNs) {
|
|
486
|
+
var _a;
|
|
487
|
+
const versions = getVersion(program, rootNs);
|
|
488
|
+
const dependencies = (_a = getVersionDependencies(program, rootNs)) !== null && _a !== void 0 ? _a : new Map();
|
|
489
|
+
if (!versions) {
|
|
490
|
+
if (dependencies.size === 0) {
|
|
491
|
+
return [{ rootVersion: undefined, versions: new Map() }];
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
const map = new Map();
|
|
495
|
+
for (const [dependencyNs, version] of dependencies) {
|
|
496
|
+
if (version instanceof Map) {
|
|
497
|
+
const rootNsName = getNamespaceFullName(rootNs);
|
|
498
|
+
const dependencyNsName = getNamespaceFullName(dependencyNs);
|
|
499
|
+
throw new Error(`Unexpected error: Namespace ${rootNsName} version dependency to ${dependencyNsName} should be a picked version.`);
|
|
500
|
+
}
|
|
501
|
+
map.set(dependencyNs, version);
|
|
502
|
+
}
|
|
503
|
+
return [{ rootVersion: undefined, versions: map }];
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
return versions.getVersions().map((version) => {
|
|
508
|
+
const resolution = {
|
|
509
|
+
rootVersion: version,
|
|
510
|
+
versions: new Map(),
|
|
511
|
+
};
|
|
512
|
+
resolution.versions.set(rootNs, version);
|
|
513
|
+
for (const [dependencyNs, versionMap] of dependencies) {
|
|
514
|
+
if (!(versionMap instanceof Map)) {
|
|
515
|
+
const rootNsName = getNamespaceFullName(rootNs);
|
|
516
|
+
const dependencyNsName = getNamespaceFullName(dependencyNs);
|
|
517
|
+
throw new Error(`Unexpected error: Namespace ${rootNsName} version dependency to ${dependencyNsName} should be a mapping of version.`);
|
|
518
|
+
}
|
|
519
|
+
resolution.versions.set(dependencyNs, versionMap.get(version));
|
|
520
|
+
}
|
|
521
|
+
return resolution;
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
528
|
+
export function indexVersions(program, versions) {
|
|
529
|
+
const versionKey = program.checker.createType({
|
|
530
|
+
kind: "Object",
|
|
531
|
+
properties: {},
|
|
532
|
+
});
|
|
533
|
+
program.stateMap(key).set(versionKey, versions);
|
|
534
|
+
return versionKey;
|
|
535
|
+
}
|
|
536
|
+
function getVersionForNamespace(program, versionKey, namespaceType) {
|
|
537
|
+
var _a;
|
|
538
|
+
return (_a = program.stateMap(key).get(versionKey)) === null || _a === void 0 ? void 0 : _a.get(namespaceType);
|
|
539
|
+
}
|
|
540
|
+
const key = createStateSymbol("version-index");
|
|
541
|
+
export function buildVersionProjections(program, rootNs) {
|
|
542
|
+
const resolutions = resolveVersions(program, rootNs);
|
|
543
|
+
return resolutions.map((resolution) => {
|
|
544
|
+
var _a;
|
|
545
|
+
if (resolution.versions.size === 0) {
|
|
546
|
+
return { version: undefined, projections: [] };
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
const versionKey = indexVersions(program, resolution.versions);
|
|
550
|
+
return {
|
|
551
|
+
version: (_a = resolution.rootVersion) === null || _a === void 0 ? void 0 : _a.value,
|
|
552
|
+
projections: [
|
|
553
|
+
{
|
|
554
|
+
projectionName: "v",
|
|
555
|
+
arguments: [versionKey],
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
const versionCache = new WeakMap();
|
|
563
|
+
function cacheVersion(key, versions) {
|
|
564
|
+
versionCache.set(key, versions);
|
|
565
|
+
return versions;
|
|
566
|
+
}
|
|
567
|
+
export function getVersionsForEnum(program, en) {
|
|
568
|
+
const namespace = en.namespace;
|
|
569
|
+
if (namespace === undefined) {
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
const nsVersion = getVersion(program, namespace);
|
|
573
|
+
if (nsVersion === undefined) {
|
|
574
|
+
return [];
|
|
575
|
+
}
|
|
576
|
+
return [namespace, nsVersion];
|
|
577
|
+
}
|
|
578
|
+
export function getVersions(p, t) {
|
|
579
|
+
if (versionCache.has(t)) {
|
|
580
|
+
return versionCache.get(t);
|
|
581
|
+
}
|
|
582
|
+
if (t.kind === "Namespace") {
|
|
583
|
+
const nsVersion = getVersion(p, t);
|
|
584
|
+
if (nsVersion !== undefined) {
|
|
585
|
+
return cacheVersion(t, [t, nsVersion]);
|
|
586
|
+
}
|
|
587
|
+
else if (t.namespace) {
|
|
588
|
+
return cacheVersion(t, getVersions(p, t.namespace));
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
return cacheVersion(t, [t, undefined]);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
else if (t.kind === "Operation" ||
|
|
595
|
+
t.kind === "Interface" ||
|
|
596
|
+
t.kind === "Model" ||
|
|
597
|
+
t.kind === "Union" ||
|
|
598
|
+
t.kind === "Enum") {
|
|
599
|
+
if (t.namespace) {
|
|
600
|
+
return cacheVersion(t, getVersions(p, t.namespace) || []);
|
|
601
|
+
}
|
|
602
|
+
else if (t.kind === "Operation" && t.interface) {
|
|
603
|
+
return cacheVersion(t, getVersions(p, t.interface) || []);
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
return cacheVersion(t, []);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else if (t.kind === "ModelProperty") {
|
|
610
|
+
if (t.sourceProperty) {
|
|
611
|
+
return getVersions(p, t.sourceProperty);
|
|
612
|
+
}
|
|
613
|
+
else if (t.model) {
|
|
614
|
+
return getVersions(p, t.model);
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
return cacheVersion(t, []);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else if (t.kind === "EnumMember") {
|
|
621
|
+
return cacheVersion(t, getVersions(p, t.enum) || []);
|
|
622
|
+
}
|
|
623
|
+
else if (t.kind === "UnionVariant") {
|
|
624
|
+
return cacheVersion(t, getVersions(p, t.union) || []);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
return cacheVersion(t, []);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// these decorators take a `versionSource` parameter because not all types can walk up to
|
|
631
|
+
// the containing namespace. Model properties, for example.
|
|
632
|
+
/**
|
|
633
|
+
* @deprecated since version 0.39.0. Use existsAtVersion instead.
|
|
634
|
+
* @param p
|
|
635
|
+
* @param type
|
|
636
|
+
* @param version
|
|
637
|
+
* @returns
|
|
638
|
+
*/
|
|
639
|
+
export function addedAfter(p, type, version) {
|
|
640
|
+
reportDeprecated(p, "Deprecated: addedAfter is deprecated. Use existsAtVersion instead.", type);
|
|
641
|
+
const appliesAt = appliesAtVersion(getAddedOn, p, type, version);
|
|
642
|
+
return appliesAt === null ? false : !appliesAt;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* @deprecated since version 0.39.0. Use existsAtVersion instead.
|
|
646
|
+
* @param p
|
|
647
|
+
* @param type
|
|
648
|
+
* @param version
|
|
649
|
+
* @returns
|
|
650
|
+
*/
|
|
651
|
+
export function removedOnOrBefore(p, type, version) {
|
|
652
|
+
reportDeprecated(p, "Deprecated: removedOnOrBefore is deprecated. Use existsAtVersion instead.", type);
|
|
653
|
+
const appliesAt = appliesAtVersion(getRemovedOn, p, type, version);
|
|
654
|
+
return appliesAt === null ? false : appliesAt;
|
|
655
|
+
}
|
|
656
|
+
function getAllVersions(p, t) {
|
|
657
|
+
var _a;
|
|
658
|
+
const [namespace, _] = getVersions(p, t);
|
|
659
|
+
if (namespace === undefined)
|
|
660
|
+
return undefined;
|
|
661
|
+
return (_a = getVersion(p, namespace)) === null || _a === void 0 ? void 0 : _a.getVersions();
|
|
662
|
+
}
|
|
663
|
+
export var Availability;
|
|
664
|
+
(function (Availability) {
|
|
665
|
+
Availability["Unavailable"] = "Unavailable";
|
|
666
|
+
Availability["Added"] = "Added";
|
|
667
|
+
Availability["Available"] = "Available";
|
|
668
|
+
Availability["Removed"] = "Removed";
|
|
669
|
+
})(Availability = Availability || (Availability = {}));
|
|
670
|
+
/**
|
|
671
|
+
* Returns a map of version names and whether a given type is available in that version
|
|
672
|
+
* @param program TypeSpec program
|
|
673
|
+
* @param type Type to get the availability map for
|
|
674
|
+
* @returns the availability map for the type, if applicable
|
|
675
|
+
*/
|
|
676
|
+
export function getAvailabilityMap(program, type) {
|
|
677
|
+
var _a, _b;
|
|
678
|
+
const avail = new Map();
|
|
679
|
+
const allVersions = getAllVersions(program, type);
|
|
680
|
+
// if unversioned then everything exists
|
|
681
|
+
if (allVersions === undefined)
|
|
682
|
+
return undefined;
|
|
683
|
+
const added = (_a = getAddedOnVersions(program, type)) !== null && _a !== void 0 ? _a : [];
|
|
684
|
+
const removed = (_b = getRemovedOnVersions(program, type)) !== null && _b !== void 0 ? _b : [];
|
|
685
|
+
// if there's absolutely no versioning information, return undefined
|
|
686
|
+
// contextually, this might mean it inherits its versioning info from a parent
|
|
687
|
+
// or that it is treated as unversioned
|
|
688
|
+
if (!added.length && !removed.length)
|
|
689
|
+
return undefined;
|
|
690
|
+
// implicitly, all versioned things are assumed to have been added at
|
|
691
|
+
// v1 if not specified
|
|
692
|
+
if (!added.length) {
|
|
693
|
+
added.push(allVersions[0]);
|
|
694
|
+
}
|
|
695
|
+
// something isn't available by default
|
|
696
|
+
let isAvail = false;
|
|
697
|
+
for (const ver of allVersions) {
|
|
698
|
+
const add = added.find((x) => x.index === ver.index);
|
|
699
|
+
const rem = removed.find((x) => x.index === ver.index);
|
|
700
|
+
if (rem) {
|
|
701
|
+
isAvail = false;
|
|
702
|
+
avail.set(ver.name, Availability.Removed);
|
|
703
|
+
}
|
|
704
|
+
else if (add) {
|
|
705
|
+
isAvail = true;
|
|
706
|
+
avail.set(ver.name, Availability.Added);
|
|
707
|
+
}
|
|
708
|
+
else if (isAvail) {
|
|
709
|
+
avail.set(ver.name, Availability.Available);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
avail.set(ver.name, Availability.Unavailable);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return avail;
|
|
716
|
+
}
|
|
717
|
+
export function existsAtVersion(p, type, versionKey) {
|
|
718
|
+
const version = toVersion(p, type, versionKey);
|
|
719
|
+
// if unversioned then everything exists
|
|
720
|
+
if (version === undefined)
|
|
721
|
+
return true;
|
|
722
|
+
const availability = getAvailabilityMap(p, type);
|
|
723
|
+
if (!availability)
|
|
724
|
+
return true;
|
|
725
|
+
const isAvail = availability.get(version.name);
|
|
726
|
+
return [Availability.Added, Availability.Available].includes(isAvail);
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* @deprecated since version 0.39.0. Use hasDifferentNameAtVersion instead.
|
|
730
|
+
* @param p
|
|
731
|
+
* @param type
|
|
732
|
+
* @param version
|
|
733
|
+
* @returns
|
|
734
|
+
*/
|
|
735
|
+
export function renamedAfter(p, type, version) {
|
|
736
|
+
reportDeprecated(p, "Deprecated: renamedAfter is deprecated. Use hasDifferentNameAtVersion instead.", type);
|
|
737
|
+
const appliesAt = appliesAtVersion(getRenamedFromVersion, p, type, version);
|
|
738
|
+
return appliesAt === null ? false : !appliesAt;
|
|
739
|
+
}
|
|
740
|
+
export function hasDifferentNameAtVersion(p, type, version) {
|
|
741
|
+
return getNameAtVersion(p, type, version) !== "";
|
|
742
|
+
}
|
|
743
|
+
export function madeOptionalAfter(p, type, version) {
|
|
744
|
+
const appliesAt = appliesAtVersion(getMadeOptionalOn, p, type, version);
|
|
745
|
+
return appliesAt === null ? false : !appliesAt;
|
|
746
|
+
}
|
|
747
|
+
export function hasDifferentTypeAtVersion(p, type, version) {
|
|
748
|
+
return getTypeBeforeVersion(p, type, version) !== undefined;
|
|
749
|
+
}
|
|
750
|
+
export function hasDifferentReturnTypeAtVersion(p, type, version) {
|
|
751
|
+
return getReturnTypeBeforeVersion(p, type, version) !== "";
|
|
752
|
+
}
|
|
753
|
+
export function getVersionForEnumMember(program, member) {
|
|
754
|
+
const parentEnum = member.enum;
|
|
755
|
+
if (!parentEnum) {
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
const [, versions] = getVersionsForEnum(program, parentEnum);
|
|
759
|
+
return versions === null || versions === void 0 ? void 0 : versions.getVersionForEnumMember(member);
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* returns either null, which means unversioned, or true or false depending
|
|
763
|
+
* on whether the change is active or not at that particular version
|
|
764
|
+
*/
|
|
765
|
+
function appliesAtVersion(getMetadataFn, p, type, versionKey) {
|
|
766
|
+
const version = toVersion(p, type, versionKey);
|
|
767
|
+
if (version === undefined) {
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
const appliedOnVersion = getMetadataFn(p, type);
|
|
771
|
+
if (appliedOnVersion === undefined) {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
const appliedOnVersionIndex = appliedOnVersion.index;
|
|
775
|
+
if (appliedOnVersionIndex === -1)
|
|
776
|
+
return null;
|
|
777
|
+
const testVersionIndex = version.index;
|
|
778
|
+
if (testVersionIndex === -1)
|
|
779
|
+
return null;
|
|
780
|
+
return testVersionIndex >= appliedOnVersionIndex;
|
|
781
|
+
}
|
|
782
|
+
//# sourceMappingURL=versioning.js.map
|