@fluidframework/container-runtime 1.0.1 → 1.1.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.
Files changed (58) hide show
  1. package/dist/connectionTelemetry.d.ts +19 -0
  2. package/dist/connectionTelemetry.d.ts.map +1 -1
  3. package/dist/connectionTelemetry.js +21 -21
  4. package/dist/connectionTelemetry.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +13 -1
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +99 -11
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStore.d.ts.map +1 -1
  10. package/dist/dataStore.js +14 -3
  11. package/dist/dataStore.js.map +1 -1
  12. package/dist/dataStoreRegistry.d.ts +0 -4
  13. package/dist/dataStoreRegistry.d.ts.map +1 -1
  14. package/dist/dataStoreRegistry.js +12 -1
  15. package/dist/dataStoreRegistry.js.map +1 -1
  16. package/dist/dataStores.d.ts.map +1 -1
  17. package/dist/dataStores.js +4 -4
  18. package/dist/dataStores.js.map +1 -1
  19. package/dist/garbageCollection.d.ts +20 -24
  20. package/dist/garbageCollection.d.ts.map +1 -1
  21. package/dist/garbageCollection.js +40 -117
  22. package/dist/garbageCollection.js.map +1 -1
  23. package/dist/packageVersion.d.ts +1 -1
  24. package/dist/packageVersion.js +1 -1
  25. package/dist/packageVersion.js.map +1 -1
  26. package/lib/connectionTelemetry.d.ts +19 -0
  27. package/lib/connectionTelemetry.d.ts.map +1 -1
  28. package/lib/connectionTelemetry.js +21 -21
  29. package/lib/connectionTelemetry.js.map +1 -1
  30. package/lib/containerRuntime.d.ts +13 -1
  31. package/lib/containerRuntime.d.ts.map +1 -1
  32. package/lib/containerRuntime.js +101 -13
  33. package/lib/containerRuntime.js.map +1 -1
  34. package/lib/dataStore.d.ts.map +1 -1
  35. package/lib/dataStore.js +15 -4
  36. package/lib/dataStore.js.map +1 -1
  37. package/lib/dataStoreRegistry.d.ts +0 -4
  38. package/lib/dataStoreRegistry.d.ts.map +1 -1
  39. package/lib/dataStoreRegistry.js +12 -1
  40. package/lib/dataStoreRegistry.js.map +1 -1
  41. package/lib/dataStores.d.ts.map +1 -1
  42. package/lib/dataStores.js +5 -5
  43. package/lib/dataStores.js.map +1 -1
  44. package/lib/garbageCollection.d.ts +20 -24
  45. package/lib/garbageCollection.d.ts.map +1 -1
  46. package/lib/garbageCollection.js +39 -115
  47. package/lib/garbageCollection.js.map +1 -1
  48. package/lib/packageVersion.d.ts +1 -1
  49. package/lib/packageVersion.js +1 -1
  50. package/lib/packageVersion.js.map +1 -1
  51. package/package.json +20 -48
  52. package/src/connectionTelemetry.ts +58 -37
  53. package/src/containerRuntime.ts +119 -26
  54. package/src/dataStore.ts +21 -4
  55. package/src/dataStoreRegistry.ts +8 -1
  56. package/src/dataStores.ts +6 -5
  57. package/src/garbageCollection.ts +66 -158
  58. package/src/packageVersion.ts +1 -1
@@ -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
- provider: IGarbageCollectionRuntime,
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 = false;
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
- protected constructor(
392
- private readonly runtime: IGarbageCollectionRuntime,
393
- private readonly gcOptions: IGCRuntimeOptions,
394
- /** For a given node path, returns the node's package path. */
395
- private readonly getNodePackagePath: (nodePath: string) => readonly string[] | undefined,
396
- /** Returns the timestamp of the last summary generated for this container. */
397
- private readonly getLastSummaryTimestampMs: () => number | undefined,
398
- baseSnapshot: ISnapshotTree | undefined,
399
- readAndParseBlob: ReadAndParseBlob,
400
- baseLogger: ITelemetryLogger,
401
- existing: boolean,
402
- metadata: IContainerRuntimeMetadata | undefined,
403
- private readonly isSummarizerClient: boolean = true,
404
- ) {
405
- this.mc = loggerToMonitoringContext(
406
- ChildLogger.create(baseLogger, "GarbageCollector", { all: { completedGCRuns: () => this.completedRuns } }),
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
- const minimumVersion = this.mc.config.getString(trackGCStateMinimumVersionKey);
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
- * Enable resetting initial state once the following issue is resolved:
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
- // If `writeDataAtRoot` setting is true, write the GC data into the root of the summary tree. We do this so that
516
- // the roll out can be staged. Once its rolled out everywhere, we will start writing at root by default.
517
- this._writeDataAtRoot = this.mc.config.getBoolean(writeAtRootKey) ?? this.gcOptions.writeDataAtRoot === true;
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
- // The following log will be enabled once this issue is resolved:
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, ["/"], logger);
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
- * `this.unreferencedNodesState` has the previous unreferenced state of all nodes. `referenced` flag passed
1159
- * here is current state of the give node. Check if the reference state of the changed.
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: `/${pkg.join("/")}`, tag: TelemetryDataTag.PackageData },
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, 0x2f8 /* Expected real numbers in minimum version! */);
1349
- return Number.parseInt(value, 10);
1350
- });
1351
- assert(minimumValues.length === 3, 0x2f9 /* 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, 0x2fa /* Expected version to match semver rules! */);
1356
- const currentValues = currentValuesString.slice(0, 3).map((value) => {
1357
- assert(isNaN(+value) === false, 0x2fb /* 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
- }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "1.0.1";
9
+ export const pkgVersion = "1.1.0";