@fluidframework/container-runtime 1.0.0 → 1.1.0-76254
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/dist/connectionTelemetry.d.ts +19 -0
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +21 -21
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +13 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +99 -11
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +14 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +0 -4
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +12 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +4 -4
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +20 -24
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +40 -117
- package/dist/garbageCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +19 -0
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +21 -21
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +13 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +101 -13
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +15 -4
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +0 -4
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +12 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +5 -5
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +20 -24
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +39 -115
- package/lib/garbageCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +20 -48
- package/src/connectionTelemetry.ts +58 -37
- package/src/containerRuntime.ts +119 -26
- package/src/dataStore.ts +21 -4
- package/src/dataStoreRegistry.ts +8 -1
- package/src/dataStores.ts +6 -5
- package/src/garbageCollection.ts +66 -158
- package/src/packageVersion.ts +1 -1
package/src/garbageCollection.ts
CHANGED
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
|
|
43
43
|
import { IGCRuntimeOptions, RuntimeHeaders } from "./containerRuntime";
|
|
44
44
|
import { getSummaryForDatastores } from "./dataStores";
|
|
45
|
-
import { pkgVersion } from "./packageVersion";
|
|
46
45
|
import {
|
|
47
46
|
getGCVersion,
|
|
48
47
|
GCVersion,
|
|
@@ -73,12 +72,8 @@ const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
|
|
|
73
72
|
export const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
74
73
|
// Feature gate key to disable expiring session after a set period of time, even if expiry value is present
|
|
75
74
|
export const disableSessionExpiryKey = "Fluid.GarbageCollection.DisableSessionExpiry";
|
|
76
|
-
// Feature gate key to log error messages if GC reference validation fails.
|
|
77
|
-
export const logUnknownOutboundReferencesKey = "Fluid.GarbageCollection.LogUnknownOutboundReferences";
|
|
78
75
|
// Feature gate key to write the gc blob as a handle if the data is the same.
|
|
79
76
|
export const trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
|
|
80
|
-
// Feature gate key to limit which versions can write the gc blob as a handle if the data is the same.
|
|
81
|
-
export const trackGCStateMinimumVersionKey = "Fluid.GarbageCollection.TrackGCState.MinVersion";
|
|
82
77
|
|
|
83
78
|
const defaultInactiveTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
84
79
|
export const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
@@ -187,6 +182,20 @@ export interface IGarbageCollector {
|
|
|
187
182
|
dispose(): void;
|
|
188
183
|
}
|
|
189
184
|
|
|
185
|
+
/** Parameters necessary for creating a GarbageCollector. */
|
|
186
|
+
export interface IGarbageCollectorCreateParams {
|
|
187
|
+
readonly runtime: IGarbageCollectionRuntime;
|
|
188
|
+
readonly gcOptions: IGCRuntimeOptions;
|
|
189
|
+
readonly baseLogger: ITelemetryLogger;
|
|
190
|
+
readonly existing: boolean;
|
|
191
|
+
readonly metadata: IContainerRuntimeMetadata | undefined;
|
|
192
|
+
readonly baseSnapshot: ISnapshotTree | undefined;
|
|
193
|
+
readonly isSummarizerClient: boolean;
|
|
194
|
+
readonly getNodePackagePath: (nodePath: string) => readonly string[] | undefined;
|
|
195
|
+
readonly getLastSummaryTimestampMs: () => number | undefined;
|
|
196
|
+
readonly readAndParseBlob: ReadAndParseBlob;
|
|
197
|
+
}
|
|
198
|
+
|
|
190
199
|
/**
|
|
191
200
|
* Helper class that tracks the state of an unreferenced node such as the time it was unreferenced. It also sets
|
|
192
201
|
* the node's state to inactive if it remains unreferenced for a given amount of time (inactiveTimeoutMs).
|
|
@@ -258,30 +267,8 @@ class UnreferencedStateTracker {
|
|
|
258
267
|
* NodeId = "dds1" NodeId = "dds2"
|
|
259
268
|
*/
|
|
260
269
|
export class GarbageCollector implements IGarbageCollector {
|
|
261
|
-
public static create(
|
|
262
|
-
|
|
263
|
-
gcOptions: IGCRuntimeOptions,
|
|
264
|
-
getNodePackagePath: (nodePath: string) => readonly string[] | undefined,
|
|
265
|
-
getLastSummaryTimestampMs: () => number | undefined,
|
|
266
|
-
baseSnapshot: ISnapshotTree | undefined,
|
|
267
|
-
readAndParseBlob: ReadAndParseBlob,
|
|
268
|
-
baseLogger: ITelemetryLogger,
|
|
269
|
-
existing: boolean,
|
|
270
|
-
metadata: IContainerRuntimeMetadata | undefined,
|
|
271
|
-
isSummarizerClient: boolean,
|
|
272
|
-
): IGarbageCollector {
|
|
273
|
-
return new GarbageCollector(
|
|
274
|
-
provider,
|
|
275
|
-
gcOptions,
|
|
276
|
-
getNodePackagePath,
|
|
277
|
-
getLastSummaryTimestampMs,
|
|
278
|
-
baseSnapshot,
|
|
279
|
-
readAndParseBlob,
|
|
280
|
-
baseLogger,
|
|
281
|
-
existing,
|
|
282
|
-
metadata,
|
|
283
|
-
isSummarizerClient,
|
|
284
|
-
);
|
|
270
|
+
public static create(createParams: IGarbageCollectorCreateParams): IGarbageCollector {
|
|
271
|
+
return new GarbageCollector(createParams);
|
|
285
272
|
}
|
|
286
273
|
|
|
287
274
|
/**
|
|
@@ -332,7 +319,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
332
319
|
/**
|
|
333
320
|
* Tells whether the GC data should be written to the root of the summary tree.
|
|
334
321
|
*/
|
|
335
|
-
private _writeDataAtRoot: boolean =
|
|
322
|
+
private _writeDataAtRoot: boolean = true;
|
|
336
323
|
public get writeDataAtRoot(): boolean {
|
|
337
324
|
return this._writeDataAtRoot;
|
|
338
325
|
}
|
|
@@ -388,23 +375,29 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
388
375
|
// The number of times GC has successfully completed on this instance of GarbageCollector.
|
|
389
376
|
private completedRuns = 0;
|
|
390
377
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
this.
|
|
406
|
-
|
|
407
|
-
|
|
378
|
+
private readonly runtime: IGarbageCollectionRuntime;
|
|
379
|
+
private readonly gcOptions: IGCRuntimeOptions;
|
|
380
|
+
private readonly isSummarizerClient: boolean;
|
|
381
|
+
|
|
382
|
+
/** For a given node path, returns the node's package path. */
|
|
383
|
+
private readonly getNodePackagePath: (nodePath: string) => readonly string[] | undefined;
|
|
384
|
+
/** Returns the timestamp of the last summary generated for this container. */
|
|
385
|
+
private readonly getLastSummaryTimestampMs: () => number | undefined;
|
|
386
|
+
|
|
387
|
+
protected constructor(createParams: IGarbageCollectorCreateParams) {
|
|
388
|
+
this.runtime = createParams.runtime;
|
|
389
|
+
this.isSummarizerClient = createParams.isSummarizerClient;
|
|
390
|
+
this.gcOptions = createParams.gcOptions;
|
|
391
|
+
this.getNodePackagePath = createParams.getNodePackagePath;
|
|
392
|
+
this.getLastSummaryTimestampMs = createParams.getLastSummaryTimestampMs;
|
|
393
|
+
|
|
394
|
+
const baseSnapshot = createParams.baseSnapshot;
|
|
395
|
+
const metadata = createParams.metadata;
|
|
396
|
+
const readAndParseBlob = createParams.readAndParseBlob;
|
|
397
|
+
|
|
398
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(
|
|
399
|
+
createParams.baseLogger, "GarbageCollector", { all: { completedGCRuns: () => this.completedRuns } },
|
|
400
|
+
));
|
|
408
401
|
|
|
409
402
|
let prevSummaryGCVersion: number | undefined;
|
|
410
403
|
|
|
@@ -415,7 +408,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
415
408
|
* 3. Whether GC session expiry is enabled or not.
|
|
416
409
|
* For existing containers, we get this information from the metadata blob of its summary.
|
|
417
410
|
*/
|
|
418
|
-
if (existing) {
|
|
411
|
+
if (createParams.existing) {
|
|
419
412
|
prevSummaryGCVersion = getGCVersion(metadata);
|
|
420
413
|
// Existing documents which did not have metadata blob or had GC disabled have version as 0. For all
|
|
421
414
|
// other existing documents, GC is enabled.
|
|
@@ -425,15 +418,15 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
425
418
|
} else {
|
|
426
419
|
// Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this
|
|
427
420
|
// scenario but explicitly failing makes it clearer and promotes correct usage.
|
|
428
|
-
if (gcOptions.sweepAllowed && gcOptions.gcAllowed === false) {
|
|
421
|
+
if (this.gcOptions.sweepAllowed && this.gcOptions.gcAllowed === false) {
|
|
429
422
|
throw new UsageError("GC sweep phase cannot be enabled without enabling GC mark phase");
|
|
430
423
|
}
|
|
431
424
|
|
|
432
425
|
// For new documents, GC is enabled by default. It can be explicitly disabled by setting the gcAllowed
|
|
433
426
|
// flag in GC options to false.
|
|
434
|
-
this.gcEnabled = gcOptions.gcAllowed !== false;
|
|
427
|
+
this.gcEnabled = this.gcOptions.gcAllowed !== false;
|
|
435
428
|
// The sweep phase has to be explicitly enabled by setting the sweepAllowed flag in GC options to true.
|
|
436
|
-
this.sweepEnabled = gcOptions.sweepAllowed === true;
|
|
429
|
+
this.sweepEnabled = this.gcOptions.sweepAllowed === true;
|
|
437
430
|
|
|
438
431
|
// Set the Session Expiry only if the flag is enabled or the test option is set.
|
|
439
432
|
if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
|
|
@@ -475,13 +468,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
475
468
|
// GC must be enabled for the document.
|
|
476
469
|
this.gcEnabled
|
|
477
470
|
// GC must not be disabled via GC options.
|
|
478
|
-
&& !gcOptions.disableGC
|
|
471
|
+
&& !this.gcOptions.disableGC
|
|
479
472
|
);
|
|
480
473
|
|
|
481
|
-
|
|
482
|
-
const shouldTrackStateForVersion = meetsMinimumVersionRequirement(pkgVersion, minimumVersion);
|
|
483
|
-
|
|
484
|
-
this.trackGCState = this.mc.config.getBoolean(trackGCStateKey) === true && shouldTrackStateForVersion;
|
|
474
|
+
this.trackGCState = this.mc.config.getBoolean(trackGCStateKey) === true;
|
|
485
475
|
|
|
486
476
|
/**
|
|
487
477
|
* Whether sweep should run or not. The following conditions have to be met to run sweep:
|
|
@@ -500,21 +490,17 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
500
490
|
defaultInactiveTimeoutMs;
|
|
501
491
|
|
|
502
492
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
503
|
-
this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? gcOptions.runGCInTestMode === true;
|
|
493
|
+
this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? this.gcOptions.runGCInTestMode === true;
|
|
504
494
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
* https://github.com/microsoft/FluidFramework/issues/8878.
|
|
508
|
-
* Currently, the GC tree is not written at root, so we don't know if the base snapshot contains GC tree or not.
|
|
509
|
-
*/
|
|
510
|
-
// The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't contain
|
|
511
|
-
// GC tree and GC is enabled.
|
|
512
|
-
// const gcTreePresent = baseSnapshot?.trees[gcTreeKey] !== undefined;
|
|
513
|
-
// this.initialStateNeedsReset = gcTreePresent ? !this.shouldRunGC : this.shouldRunGC;
|
|
495
|
+
// GC state is written into root of the summary tree by default. Can be overridden via feature flag for now.
|
|
496
|
+
this._writeDataAtRoot = this.mc.config.getBoolean(writeAtRootKey) ?? true;
|
|
514
497
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
498
|
+
if (this._writeDataAtRoot) {
|
|
499
|
+
// The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't
|
|
500
|
+
// contain GC tree and GC is enabled.
|
|
501
|
+
const gcTreePresent = baseSnapshot?.trees[gcTreeKey] !== undefined;
|
|
502
|
+
this.initialStateNeedsReset = gcTreePresent !== this.shouldRunGC;
|
|
503
|
+
}
|
|
518
504
|
|
|
519
505
|
// Get the GC state from the GC blob in the base snapshot. Use LazyPromise because we only want to do
|
|
520
506
|
// this once since it involves fetching blobs from storage which is expensive.
|
|
@@ -627,11 +613,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
627
613
|
// Run GC on the nodes in the base summary to get the routes used in each node in the container.
|
|
628
614
|
// This is an optimization for space (vs performance) wherein we don't need to store the used routes of
|
|
629
615
|
// each node in the summary.
|
|
630
|
-
const usedRoutes = runGarbageCollection(
|
|
631
|
-
gcNodes,
|
|
632
|
-
["/"],
|
|
633
|
-
this.mc.logger,
|
|
634
|
-
).referencedNodeIds;
|
|
616
|
+
const usedRoutes = runGarbageCollection(gcNodes, ["/"]).referencedNodeIds;
|
|
635
617
|
|
|
636
618
|
const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });
|
|
637
619
|
// Currently, the nodes may write the GC data. So, we need to update it's base GC details with the
|
|
@@ -658,7 +640,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
658
640
|
testMode: this.testMode,
|
|
659
641
|
sessionExpiry: this.sessionExpiryTimeoutMs,
|
|
660
642
|
inactiveTimeout: this.inactiveTimeoutMs,
|
|
661
|
-
existing,
|
|
643
|
+
existing: createParams.existing,
|
|
662
644
|
...this.gcOptions,
|
|
663
645
|
});
|
|
664
646
|
if (this.isSummarizerClient) {
|
|
@@ -712,11 +694,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
712
694
|
|
|
713
695
|
// Get the runtime's GC data and run GC on the reference graph in it.
|
|
714
696
|
const gcData = await this.runtime.getGCData(fullGC);
|
|
715
|
-
const gcResult = runGarbageCollection(
|
|
716
|
-
gcData.gcNodes,
|
|
717
|
-
["/"],
|
|
718
|
-
logger,
|
|
719
|
-
);
|
|
697
|
+
const gcResult = runGarbageCollection(gcData.gcNodes, ["/"]);
|
|
720
698
|
const gcStats = this.generateStatsAndLogEvents(gcResult, logger);
|
|
721
699
|
|
|
722
700
|
// Update the state since the last GC run. There can be nodes that were referenced between the last and
|
|
@@ -829,10 +807,6 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
829
807
|
result: RefreshSummaryResult,
|
|
830
808
|
readAndParseBlob: ReadAndParseBlob,
|
|
831
809
|
): Promise<void> {
|
|
832
|
-
// After a summary is successfully submitted and ack'd by this client, the GC state should have been reset in
|
|
833
|
-
// the summary and doesn't need to be reset anymore.
|
|
834
|
-
this.initialStateNeedsReset = false;
|
|
835
|
-
|
|
836
810
|
if (!this.shouldRunGC || !result.latestSummaryUpdated) {
|
|
837
811
|
return;
|
|
838
812
|
}
|
|
@@ -841,6 +815,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
841
815
|
// Basically, it was written in the current GC version.
|
|
842
816
|
if (result.wasSummaryTracked) {
|
|
843
817
|
this.latestSummaryGCVersion = this.currentGCVersion;
|
|
818
|
+
this.initialStateNeedsReset = false;
|
|
844
819
|
if (this.trackGCState) {
|
|
845
820
|
this.latestSerializedSummaryState = this.pendingSerializedSummaryState;
|
|
846
821
|
this.pendingSerializedSummaryState = undefined;
|
|
@@ -1007,10 +982,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1007
982
|
this.newReferencesSinceLastRun,
|
|
1008
983
|
);
|
|
1009
984
|
|
|
1010
|
-
|
|
1011
|
-
// https://github.com/microsoft/FluidFramework/issues/8878.
|
|
1012
|
-
if (this.mc.config.getBoolean(logUnknownOutboundReferencesKey) === true
|
|
1013
|
-
&& missingExplicitReferences.length > 0) {
|
|
985
|
+
if (this.writeDataAtRoot && missingExplicitReferences.length > 0) {
|
|
1014
986
|
missingExplicitReferences.forEach((missingExplicitReference) => {
|
|
1015
987
|
const event: ITelemetryPerformanceEvent = {
|
|
1016
988
|
eventName: "gcUnknownOutboundReferences",
|
|
@@ -1056,7 +1028,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1056
1028
|
* unreferenced, stop tracking them and remove from unreferenced list.
|
|
1057
1029
|
* Some of these nodes may be unreferenced now and if so, the current run will add unreferenced state for them.
|
|
1058
1030
|
*/
|
|
1059
|
-
const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, ["/"]
|
|
1031
|
+
const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, ["/"]);
|
|
1060
1032
|
for (const nodeId of gcResult.referencedNodeIds) {
|
|
1061
1033
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
1062
1034
|
if (nodeStateTracker !== undefined) {
|
|
@@ -1154,11 +1126,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1154
1126
|
|
|
1155
1127
|
const updateNodeStats = (nodeId: string, referenced: boolean) => {
|
|
1156
1128
|
gcStats.nodeCount++;
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
const stateUpdated = this.unreferencedNodesState.has(nodeId) ? referenced : !referenced;
|
|
1129
|
+
// If there is no previous GC data, every node's state is generated and is considered as updated.
|
|
1130
|
+
// Otherwise, find out if any node went from referenced to unreferenced or vice-versa.
|
|
1131
|
+
const stateUpdated = this.previousGCDataFromLastRun === undefined ||
|
|
1132
|
+
this.unreferencedNodesState.has(nodeId) === referenced;
|
|
1162
1133
|
if (stateUpdated) {
|
|
1163
1134
|
gcStats.updatedNodeCount++;
|
|
1164
1135
|
}
|
|
@@ -1251,7 +1222,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1251
1222
|
if (pkg !== undefined) {
|
|
1252
1223
|
this.mc.logger.sendErrorEvent({
|
|
1253
1224
|
...event,
|
|
1254
|
-
pkg: { value:
|
|
1225
|
+
pkg: { value: pkg.join("/"), tag: TelemetryDataTag.PackageData },
|
|
1255
1226
|
});
|
|
1256
1227
|
} else {
|
|
1257
1228
|
this.pendingEventsQueue.push(event);
|
|
@@ -1320,66 +1291,3 @@ function setLongTimeout(
|
|
|
1320
1291
|
}
|
|
1321
1292
|
setTimerFn(timer);
|
|
1322
1293
|
}
|
|
1323
|
-
|
|
1324
|
-
/**
|
|
1325
|
-
* meetsMinimumVersionRequirement is used determining if a feature version should be run. This is similar to feature
|
|
1326
|
-
* flags. The advantage of this is that if we ship a bug in version 0.1.1 and fix it in version 0.2.1. We can keep this
|
|
1327
|
-
* feature disabled for version 0.1.1 and enabled for 0.2.1. Older versions will run without the feature and new
|
|
1328
|
-
* versions will run with the feature.
|
|
1329
|
-
* @param currentVersion - the total time the timeout needs to last in ms
|
|
1330
|
-
* @param minimumVersion - the function to execute when the timer ends
|
|
1331
|
-
*/
|
|
1332
|
-
function meetsMinimumVersionRequirement(currentVersion: string, minimumVersion: string | undefined) {
|
|
1333
|
-
return minimumVersion === undefined || semverCompare(currentVersion, minimumVersion) >= 0;
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
/**
|
|
1337
|
-
* Compare semver versions.
|
|
1338
|
-
* @param currentVersion - assumed to be any valid semver version
|
|
1339
|
-
* @param minimumVersion - must be [major].[minor].[patch], where major, minor, and patch are all numbers
|
|
1340
|
-
* as it complicates the algorithm if we allow comparisons against minimum pre-release versions.
|
|
1341
|
-
* @returns
|
|
1342
|
-
* 0 if the currentVersion equals the minimumVersion
|
|
1343
|
-
* 1 if the currentVersion is greater than the minimumVersion
|
|
1344
|
-
* -1 if the minimumVersion is greater than the currentVersion
|
|
1345
|
-
*/
|
|
1346
|
-
export function semverCompare(currentVersion: string, minimumVersion: string): number {
|
|
1347
|
-
const minimumValues = minimumVersion.split(".").map((value): number => {
|
|
1348
|
-
assert(isNaN(+value) === false, "Expected real numbers in minimum version!");
|
|
1349
|
-
return Number.parseInt(value, 10);
|
|
1350
|
-
});
|
|
1351
|
-
assert(minimumValues.length === 3, "Expected minimumVersion to be [major].[minor].[patch]");
|
|
1352
|
-
const [minMajor, minMinor, minPatch] = minimumValues;
|
|
1353
|
-
|
|
1354
|
-
const currentValuesString = currentVersion.split(/\W/);
|
|
1355
|
-
assert(currentValuesString.length >= 3, "Expected version to match semver rules!");
|
|
1356
|
-
const currentValues = currentValuesString.slice(0, 3).map((value) => {
|
|
1357
|
-
assert(isNaN(+value) === false, "Expected real numbers in minimum version!");
|
|
1358
|
-
return Number.parseInt(value, 10);
|
|
1359
|
-
});
|
|
1360
|
-
const [cMajor, cMinor, cPatch] = currentValues;
|
|
1361
|
-
|
|
1362
|
-
if (cMajor > minMajor) {
|
|
1363
|
-
return 1;
|
|
1364
|
-
} else if (minMajor > cMajor) {
|
|
1365
|
-
return -1;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
if (cMinor > minMinor) {
|
|
1369
|
-
return 1;
|
|
1370
|
-
} else if (minMinor > cMinor) {
|
|
1371
|
-
return -1;
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
if (cPatch > minPatch) {
|
|
1375
|
-
return 1;
|
|
1376
|
-
} else if (minPatch > cPatch) {
|
|
1377
|
-
return -1;
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
if (currentValuesString.length === 3) {
|
|
1381
|
-
return 0;
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
return -1;
|
|
1385
|
-
}
|
package/src/packageVersion.ts
CHANGED