-
Notifications
You must be signed in to change notification settings - Fork 569
Expand file tree
/
Copy pathlayerCompat.ts
More file actions
177 lines (165 loc) · 6.3 KB
/
layerCompat.ts
File metadata and controls
177 lines (165 loc) · 6.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/
/**
* The different Fluid layers in a client.
* @internal
*/
export type FluidLayer = "loader" | "driver" | "runtime" | "dataStore";
/**
* The policy for compatibility windows that a layer uses to validate compatibility with another layer.
* The other layer must be within this window relative to the layer to be compatible.
* Note that the policy is defined in terms of months, but the actual compatibility check is done using generations.
* Generations are updated every 1+ months depending on the release cadence so they are mostly 1-to-1 with months,
* but they are always higher than months since releases are not exactly every month.
*
* IMPORTANT: When changing these policy values, update the documentation in "Layer Compatibility Policy" section of
* LayerCompatibility.md at the root of the repository.
*
* @internal
*/
export const LayerCompatibilityPolicyWindowMonths = {
/**
* Driver is compatible with Loader versions up to 12 months (or generations) older.
*/
DriverLoader: 12,
/**
* Loader is compatible with Driver versions up to 12 months (or generations) older.
*/
LoaderDriver: 12,
/**
* Runtime is compatible with Loader versions up to 12 months (or generations) older.
*/
RuntimeLoader: 12,
/**
* Loader is compatible with Runtime versions up to 3 months (or generations) older.
*/
LoaderRuntime: 3,
/**
* Runtime is compatible with DataStore versions up to 3 months (or generations) older.
*/
RuntimeDataStore: 3,
/**
* DataStore is compatible with Runtime versions up to 3 months (or generations) older.
*/
DataStoreRuntime: 3,
} as const;
/**
* Result of a layer compatibility check - whether a layer is compatible with another layer.
* @internal
*/
export type LayerCompatCheckResult =
| { readonly isCompatible: true }
| {
readonly isCompatible: false;
/**
* Whether the generation of the layer is compatible with the other layer.
*/
readonly isGenerationCompatible: boolean;
/**
* The features that are required by the layer but are not supported by the other layer. This will only
* be set if there are unsupported features.
*/
readonly unsupportedFeatures: readonly string[] | undefined;
};
/**
* @internal
*/
export const ILayerCompatDetails: keyof IProvideLayerCompatDetails = "ILayerCompatDetails";
/**
* @internal
*/
export interface IProvideLayerCompatDetails {
readonly ILayerCompatDetails: ILayerCompatDetails;
}
/**
* This interface is used to communicate the compatibility details of a layer to another layer.
* @internal
*/
export interface ILayerCompatDetails extends Partial<IProvideLayerCompatDetails> {
/**
* A list of features supported by the layer at a particular layer boundary. This is used to check if these
* set of features satisfy the requirements of another layer.
*/
readonly supportedFeatures: ReadonlySet<string>;
/**
* The generation of the layer. The other layer at the layer boundary uses this to check if this satisfies
* the minimum generation it requires to be compatible.
*
* @remarks Generation is updated on a regular cadence, say, monthly. This will allow us to determine how
* far apart two layers are in terms of time and whether they are compatible.
* For example, say generation is updated every month and the compatibility window between layer1 and layer2 is
* 6 months. Now, if layer1 is at generation 1 and layer2 is at generation 5, then they are 4 months apart and are
* compatible. But if layer1 is at generation 1 and layer2 is at generation 8, then they are 7 months apart and
* are not compatible.
*/
readonly generation: number;
/**
* The package version of the layer. When an incompatibility is detected, this is used to provide more context
* on what the versions of the incompatible layers are.
*/
readonly pkgVersion: string;
}
/**
* This is the default compat details for a layer when it doesn't provide any compat details. This is used for
* backwards compatibility to allow older layers to work before compatibility enforcement was introduced.
* @internal
*/
export const defaultLayerCompatDetails: ILayerCompatDetails = {
supportedFeatures: new Set(),
generation: 0, // 0 is reserved for layers before compatibility enforcement was introduced.
pkgVersion: "unknown",
};
/**
* The requirements that a layer needs another layer to support for them to be compatible.
* @internal
*/
export interface ILayerCompatSupportRequirements {
/**
* The minimum supported generation the other layer needs to be at.
*/
readonly minSupportedGeneration: number;
/**
* The features that the other layer needs to support.
*/
readonly requiredFeatures: readonly string[];
}
/**
* Checks compatibility of a layer (layer1) with another layer (layer2).
* @param compatSupportRequirementsLayer1 - The requirements from layer1 that layer2 needs to meet.
* @param compatDetailsLayer2 - The compatibility details of the layer2. If this is undefined, then the
* default compatibility details are used for backwards compatibility.
* @returns The result of the compatibility check indicating whether layer2 is compatible with layer1.
*
* @internal
*/
export function checkLayerCompatibility(
compatSupportRequirementsLayer1: ILayerCompatSupportRequirements,
compatDetailsLayer2: ILayerCompatDetails | undefined,
): LayerCompatCheckResult {
const compatDetailsLayer2ToUse = compatDetailsLayer2 ?? defaultLayerCompatDetails;
let isGenerationCompatible = true;
const unsupportedFeatures: string[] = [];
// If layer2's generation is less than the required minimum supported generation of layer1,
// then it is not compatible.
if (
compatDetailsLayer2ToUse.generation <
compatSupportRequirementsLayer1.minSupportedGeneration
) {
isGenerationCompatible = false;
}
// All features required by layer1 must be supported by layer2 to be compatible.
for (const feature of compatSupportRequirementsLayer1.requiredFeatures) {
if (!compatDetailsLayer2ToUse.supportedFeatures.has(feature)) {
unsupportedFeatures.push(feature);
}
}
return isGenerationCompatible && unsupportedFeatures.length === 0
? { isCompatible: true }
: {
isCompatible: false,
isGenerationCompatible,
unsupportedFeatures: unsupportedFeatures.length > 0 ? unsupportedFeatures : undefined,
};
}