@dev-blinq/bvt-playwright-js 1.0.0-dev.4.staging.135.1 → 1.0.0-dev.4.staging.146.1

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/index.d.mts CHANGED
@@ -276,6 +276,14 @@ declare const elementAssertionSchema: z.ZodDiscriminatedUnion<[
276
276
  z.ZodObject<{
277
277
  type: z.ZodLiteral<"toHaveProperty">;
278
278
  name: z.ZodString;
279
+ operator: z.ZodOptional<z.ZodEnum<{
280
+ equals: "equals";
281
+ notEquals: "notEquals";
282
+ contains: "contains";
283
+ lessThan: "lessThan";
284
+ greaterThan: "greaterThan";
285
+ exists: "exists";
286
+ }>>;
279
287
  value: z.ZodOptional<z.ZodDiscriminatedUnion<[
280
288
  z.ZodObject<{
281
289
  type: z.ZodLiteral<"string">;
@@ -1336,6 +1344,7 @@ declare const CommandSchema: z.ZodDiscriminatedUnion<[
1336
1344
  ], "type">;
1337
1345
  target: z.ZodObject<{
1338
1346
  name: z.ZodString;
1347
+ isElementHidden: z.ZodOptional<z.ZodBoolean>;
1339
1348
  uniqueSelectors: z.ZodArray<z.ZodDiscriminatedUnion<[
1340
1349
  z.ZodObject<{
1341
1350
  type: z.ZodLiteral<"pw.selectorString">;
@@ -2011,6 +2020,14 @@ declare const CommandSchema: z.ZodDiscriminatedUnion<[
2011
2020
  z.ZodObject<{
2012
2021
  type: z.ZodLiteral<"toHaveProperty">;
2013
2022
  name: z.ZodString;
2023
+ operator: z.ZodOptional<z.ZodEnum<{
2024
+ equals: "equals";
2025
+ notEquals: "notEquals";
2026
+ contains: "contains";
2027
+ lessThan: "lessThan";
2028
+ greaterThan: "greaterThan";
2029
+ exists: "exists";
2030
+ }>>;
2014
2031
  value: z.ZodOptional<z.ZodDiscriminatedUnion<[
2015
2032
  z.ZodObject<{
2016
2033
  type: z.ZodLiteral<"string">;
@@ -2096,6 +2113,7 @@ declare const CommandSchema: z.ZodDiscriminatedUnion<[
2096
2113
  ], "type">;
2097
2114
  target: z.ZodObject<{
2098
2115
  name: z.ZodString;
2116
+ isElementHidden: z.ZodOptional<z.ZodBoolean>;
2099
2117
  uniqueSelectors: z.ZodArray<z.ZodDiscriminatedUnion<[
2100
2118
  z.ZodObject<{
2101
2119
  type: z.ZodLiteral<"pw.selectorString">;
@@ -2740,6 +2758,7 @@ declare const CommandSchema: z.ZodDiscriminatedUnion<[
2740
2758
  ], "type">;
2741
2759
  target: z.ZodObject<{
2742
2760
  name: z.ZodString;
2761
+ isElementHidden: z.ZodOptional<z.ZodBoolean>;
2743
2762
  uniqueSelectors: z.ZodArray<z.ZodDiscriminatedUnion<[
2744
2763
  z.ZodObject<{
2745
2764
  type: z.ZodLiteral<"pw.selectorString">;
@@ -4335,6 +4354,7 @@ declare const StepDefinitionSchema: z.ZodObject<{
4335
4354
  ], "type">;
4336
4355
  target: z.ZodObject<{
4337
4356
  name: z.ZodString;
4357
+ isElementHidden: z.ZodOptional<z.ZodBoolean>;
4338
4358
  uniqueSelectors: z.ZodArray<z.ZodDiscriminatedUnion<[
4339
4359
  z.ZodObject<{
4340
4360
  type: z.ZodLiteral<"pw.selectorString">;
@@ -5010,6 +5030,14 @@ declare const StepDefinitionSchema: z.ZodObject<{
5010
5030
  z.ZodObject<{
5011
5031
  type: z.ZodLiteral<"toHaveProperty">;
5012
5032
  name: z.ZodString;
5033
+ operator: z.ZodOptional<z.ZodEnum<{
5034
+ equals: "equals";
5035
+ notEquals: "notEquals";
5036
+ contains: "contains";
5037
+ lessThan: "lessThan";
5038
+ greaterThan: "greaterThan";
5039
+ exists: "exists";
5040
+ }>>;
5013
5041
  value: z.ZodOptional<z.ZodDiscriminatedUnion<[
5014
5042
  z.ZodObject<{
5015
5043
  type: z.ZodLiteral<"string">;
@@ -5095,6 +5123,7 @@ declare const StepDefinitionSchema: z.ZodObject<{
5095
5123
  ], "type">;
5096
5124
  target: z.ZodObject<{
5097
5125
  name: z.ZodString;
5126
+ isElementHidden: z.ZodOptional<z.ZodBoolean>;
5098
5127
  uniqueSelectors: z.ZodArray<z.ZodDiscriminatedUnion<[
5099
5128
  z.ZodObject<{
5100
5129
  type: z.ZodLiteral<"pw.selectorString">;
@@ -5739,6 +5768,7 @@ declare const StepDefinitionSchema: z.ZodObject<{
5739
5768
  ], "type">;
5740
5769
  target: z.ZodObject<{
5741
5770
  name: z.ZodString;
5771
+ isElementHidden: z.ZodOptional<z.ZodBoolean>;
5742
5772
  uniqueSelectors: z.ZodArray<z.ZodDiscriminatedUnion<[
5743
5773
  z.ZodObject<{
5744
5774
  type: z.ZodLiteral<"pw.selectorString">;
@@ -8793,6 +8823,9 @@ type PWSelectorDetails = {
8793
8823
  score: number;
8794
8824
  elements?: Element[];
8795
8825
  };
8826
+ type SelectorGenerationOptions = Partial<InternalOptions> & {
8827
+ visibleOnly?: boolean;
8828
+ };
8796
8829
  declare class SelectorGenerator {
8797
8830
  private injectedScript;
8798
8831
  private _pw;
@@ -8805,7 +8838,7 @@ declare class SelectorGenerator {
8805
8838
  private pushCandidate;
8806
8839
  getMatchingElements(selector: string, options?: {
8807
8840
  visibleOnly?: boolean;
8808
- root?: Element;
8841
+ root?: Element | Document;
8809
8842
  prefix?: string;
8810
8843
  }): Element[];
8811
8844
  private addNearestTextToSelectors;
@@ -8813,13 +8846,14 @@ declare class SelectorGenerator {
8813
8846
  buildPWNoTextCandidates(element: Element, options?: InternalOptions): PWSelectorDetails[];
8814
8847
  computeUnique(element: Element, locatorGenerator: (element: Element, options?: InternalOptions & {
8815
8848
  isTargetNode: boolean;
8816
- }) => PWSelectorDetails[], strategy: "text" | "css", options?: InternalOptions): Generator<UniqueSelector>;
8849
+ }) => PWSelectorDetails[], strategy: "text" | "css", options?: SelectorGenerationOptions): Generator<UniqueSelector>;
8817
8850
  computeUnique2(element: Element, locatorGenerator: (element: Element, options?: InternalOptions & {
8818
8851
  isTargetNode: boolean;
8819
- }) => PWSelectorDetails[], strategy: "text" | "css", options?: InternalOptions): Generator<UniqueSelector>;
8820
- getUniquePWSelectors(element: Element, options?: InternalOptions): Generator<UniqueSelector, undefined, unknown>;
8852
+ }) => PWSelectorDetails[], strategy: "text" | "css", options?: SelectorGenerationOptions): Generator<UniqueSelector>;
8853
+ getUniquePWSelectors(element: Element, options?: SelectorGenerationOptions): Generator<UniqueSelector, undefined, unknown>;
8821
8854
  getCSSChainSelector(element: Element, options: {
8822
8855
  root?: Element | Document;
8856
+ visibleOnly?: boolean;
8823
8857
  }): string;
8824
8858
  getUniqueSelectors(element: Element, options?: InternalOptions & {
8825
8859
  timeout?: number;
@@ -8849,6 +8883,7 @@ type ElementActionEvent = {
8849
8883
  bvtId: string;
8850
8884
  details: ElementDetails;
8851
8885
  uniqueSelectors: UniqueSelector[];
8886
+ isElementHidden?: boolean;
8852
8887
  };
8853
8888
  statistics: {
8854
8889
  handleEventDurationMs: string;
@@ -9099,6 +9134,9 @@ type UnboxedTuple<T extends unknown[]> = {
9099
9134
  [K in keyof T]: Unboxed<T[K]>;
9100
9135
  };
9101
9136
  type LocatorScope = Frame | FrameLocator;
9137
+ type LocatorVisibilityOptions = {
9138
+ isElementHidden?: boolean;
9139
+ };
9102
9140
  type GlobalThis = {
9103
9141
  __bvt: {
9104
9142
  BrowserObserver: typeof BrowserObserver;
@@ -9349,7 +9387,7 @@ declare class Tester {
9349
9387
  isFrameObject(frameScope: LocatorScope): frameScope is Frame;
9350
9388
  getFrameObjectFromFrameScope(frameScope: FrameLocator | Frame): Promise<Frame>;
9351
9389
  getFrame(scope: Page, frameElementDescriptors: ElementCommand["target"]["frames"]): Promise<LocatorScope>;
9352
- getLocator(scope: LocatorScope, uniqueSelector: UniqueSelector): {
9390
+ getLocator(scope: LocatorScope, uniqueSelector: UniqueSelector, options?: LocatorVisibilityOptions): {
9353
9391
  target: Locator;
9354
9392
  };
9355
9393
  executeElementAction(locator: Locator, action: ElementAction): Promise<void>;
package/index.mjs CHANGED
@@ -5162,6 +5162,17 @@ const primitiveOrRegex = discriminatedUnion("type", [
5162
5162
  }).strict()
5163
5163
  ]);
5164
5164
 
5165
+ //#endregion
5166
+ //#region ../../core/schemas/src/assertions/assertion-operators.ts
5167
+ const ASSERTION_OPERATORS = [
5168
+ "equals",
5169
+ "notEquals",
5170
+ "contains",
5171
+ "lessThan",
5172
+ "greaterThan",
5173
+ "exists"
5174
+ ];
5175
+
5165
5176
  //#endregion
5166
5177
  //#region ../../core/schemas/src/api/api.schema.ts
5167
5178
  const numberAssertion = object({
@@ -5393,6 +5404,7 @@ const elementAssertionSchema = discriminatedUnion("type", [
5393
5404
  object({
5394
5405
  type: literal$1("toHaveProperty"),
5395
5406
  name: string(),
5407
+ operator: _enum(ASSERTION_OPERATORS).optional(),
5396
5408
  value: primitiveOrRegex.optional(),
5397
5409
  options: object({ timeout: number().optional() }).optional()
5398
5410
  }),
@@ -6550,6 +6562,7 @@ const RichElementTargetDetailsSchema = object({
6550
6562
  }).passthrough();
6551
6563
  const elementTargetSchema = object({
6552
6564
  name: string(),
6565
+ isElementHidden: boolean().optional(),
6553
6566
  uniqueSelectors: array(UniqueSelectorSchema),
6554
6567
  frames: array(object({
6555
6568
  name: string(),
@@ -6918,6 +6931,23 @@ const ProjectBrowserLaunchConfigurationSchema = object({
6918
6931
  }).strict();
6919
6932
  const ProjectGitCodegenFormats = ["playwright", "gherkin"];
6920
6933
  const ProjectGitCodegenFormatSchema = _enum(ProjectGitCodegenFormats).default("playwright");
6934
+ /**
6935
+ * Personalisation inputs for the analytics dashboard.
6936
+ *
6937
+ * These values drive the "Budget Saved", "Time Saved", and
6938
+ * "AI Work Time Distribution" widgets. All hours are in human-hours
6939
+ * (the unit the modal collects). The defaults match the legacy
6940
+ * Metabase queries' `$ifNull` fallbacks — projects that never
6941
+ * customise these still get sensible numbers on day one.
6942
+ */
6943
+ const DashboardPersonalizationSchema = object({
6944
+ hourlyRateForTestEngineer: number().nonnegative().default(50),
6945
+ avgTimeToAutomateScenario: number().nonnegative().default(4),
6946
+ avgTimeToAutomateScenarioUsingBlinqIO: number().nonnegative().default(.25),
6947
+ avgTimeToMaintainScenario: number().nonnegative().default(2),
6948
+ avgTimeToAnalyzeFailedScenario: number().nonnegative().default(2)
6949
+ }).strict();
6950
+ const DEFAULT_DASHBOARD_PERSONALIZATION = DashboardPersonalizationSchema.parse({});
6921
6951
  const ProjectSettingsSchema = object({
6922
6952
  _id: EntityIdSchema,
6923
6953
  projectId: EntityIdSchema,
@@ -6928,11 +6958,13 @@ const ProjectSettingsSchema = object({
6928
6958
  width: 1280,
6929
6959
  height: 900
6930
6960
  } }
6931
- })
6961
+ }),
6962
+ dashboardPersonalization: DashboardPersonalizationSchema.default(DEFAULT_DASHBOARD_PERSONALIZATION)
6932
6963
  }).strict();
6933
6964
  const ProjectSettingsUpdatableFieldsSchema = ProjectSettingsSchema.omit({
6934
6965
  _id: true,
6935
- projectId: true
6966
+ projectId: true,
6967
+ dashboardPersonalization: true
6936
6968
  });
6937
6969
  const DEFAULT_PROJECT_SETTINGS = ProjectSettingsUpdatableFieldsSchema.parse({});
6938
6970
  const GetProjectSettingsInputSchema = object({ projectId: EntityIdSchema }).strict();
@@ -27257,7 +27289,7 @@ var Tester = class {
27257
27289
  if (!frameElementDescriptors || frameElementDescriptors.length === 0) return scope.mainFrame();
27258
27290
  let currentFrame = scope.mainFrame();
27259
27291
  for (const frameDescriptor of frameElementDescriptors) for (const selectorInfo of frameDescriptor.uniqueSelectors) {
27260
- const { target } = this.getLocator(currentFrame, selectorInfo);
27292
+ const { target } = this.getLocator(currentFrame, selectorInfo, { isElementHidden: false });
27261
27293
  try {
27262
27294
  if (!await target.elementHandle()) throw new Error(`Could not find element for frame selector: ${JSON.stringify(selectorInfo)}`);
27263
27295
  const selector = target._selector;
@@ -27269,9 +27301,14 @@ var Tester = class {
27269
27301
  }
27270
27302
  return currentFrame;
27271
27303
  }
27272
- getLocator(scope, uniqueSelector) {
27304
+ getLocator(scope, uniqueSelector, options) {
27273
27305
  switch (uniqueSelector.type) {
27274
- case "pw.selectorString": return { target: scope.locator(uniqueSelector.selectorString + ` >> visible=true`) };
27306
+ case "pw.selectorString": {
27307
+ const visibleSuffix = options?.isElementHidden === true ? "" : ` >> visible=true`;
27308
+ const finalSelector = uniqueSelector.selectorString + visibleSuffix;
27309
+ this.obs.logger.info(`getLocator(pw.selectorString): isElementHidden=${options?.isElementHidden}, visibleAppended=${visibleSuffix !== ""}, finalSelector=${finalSelector}`);
27310
+ return { target: scope.locator(finalSelector) };
27311
+ }
27275
27312
  case "bvt.selectorObject": throw new Error(`Selector type "bvt.selectorObject" is not yet supported in getLocator`);
27276
27313
  default:
27277
27314
  const _exhaustiveCheck = uniqueSelector;
@@ -27390,15 +27427,82 @@ var Tester = class {
27390
27427
  else await asserter.toHaveAttribute(assertion.name, value, assertion.options);
27391
27428
  break;
27392
27429
  }
27393
- case "toHaveProperty":
27394
- this.obs.logger.info(`Checking property "${assertion.name}"...`);
27395
- if (assertion.value === void 0) await asserter.toHaveJSProperty(assertion.name, assertion.options);
27396
- else {
27397
- let value = getValueFromPrimitiveOrRegex(assertion.value);
27398
- this.obs.logger.info(`Expected property value: "${value}" (timeout: ${assertion.options?.timeout ?? "default"}ms)`);
27399
- await asserter.toHaveJSProperty(assertion.name, value, assertion.options);
27430
+ case "toHaveProperty": {
27431
+ const operator = assertion.operator ?? "equals";
27432
+ this.obs.logger.info(`Checking property "${assertion.name}" [operator: ${operator}]...`);
27433
+ switch (operator) {
27434
+ case "equals":
27435
+ if (assertion.value === void 0) await asserter.toHaveJSProperty(assertion.name, assertion.options);
27436
+ else {
27437
+ const value = getValueFromPrimitiveOrRegex(assertion.value);
27438
+ this.obs.logger.info(`Expected: "${value}" (timeout: ${assertion.options?.timeout ?? "default"}ms)`);
27439
+ await asserter.toHaveJSProperty(assertion.name, value, assertion.options);
27440
+ }
27441
+ break;
27442
+ case "notEquals":
27443
+ if (assertion.value !== void 0) {
27444
+ const value = getValueFromPrimitiveOrRegex(assertion.value);
27445
+ this.obs.logger.info(`Expected NOT: "${value}"`);
27446
+ await expect(locator).not.toHaveJSProperty(assertion.name, value, assertion.options);
27447
+ }
27448
+ break;
27449
+ case "contains":
27450
+ if (assertion.value !== void 0) {
27451
+ const raw = getValueFromPrimitiveOrRegex(assertion.value);
27452
+ const stringValue = typeof raw === "string" ? raw : String(raw);
27453
+ this.obs.logger.info(`Expected to contain: "${stringValue}"`);
27454
+ const propName = assertion.name;
27455
+ const isTextProp = propName === "textContent" || propName === "innerText";
27456
+ const isHtmlAttr = [
27457
+ "href",
27458
+ "src",
27459
+ "class",
27460
+ "id",
27461
+ "placeholder",
27462
+ "title",
27463
+ "alt",
27464
+ "name",
27465
+ "type",
27466
+ "action",
27467
+ "target",
27468
+ "rel",
27469
+ "value"
27470
+ ].includes(propName) || propName.startsWith("data-") || propName.startsWith("aria-");
27471
+ if (isTextProp) await expect(locator).toContainText(stringValue, assertion.options);
27472
+ else if (isHtmlAttr) {
27473
+ const attrPattern = raw instanceof RegExp ? raw : new RegExp(stringValue.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
27474
+ await expect(locator).toHaveAttribute(propName, attrPattern, assertion.options);
27475
+ } else {
27476
+ const actual = await locator.evaluate((el, name) => String(el[name] ?? ""), propName);
27477
+ if (!actual.includes(stringValue)) throw new Error(`Property "${propName}" expected to contain "${stringValue}", but got "${actual}"`);
27478
+ }
27479
+ }
27480
+ break;
27481
+ case "lessThan":
27482
+ if (assertion.value !== void 0) {
27483
+ const threshold = Number(getValueFromPrimitiveOrRegex(assertion.value));
27484
+ const actual = Number(await locator.evaluate((el, name) => el[name], assertion.name));
27485
+ this.obs.logger.info(`Expected "${assertion.name}" (${actual}) < ${threshold}`);
27486
+ if (!(actual < threshold)) throw new Error(`Property "${assertion.name}" expected to be less than ${threshold}, but got ${actual}`);
27487
+ }
27488
+ break;
27489
+ case "greaterThan":
27490
+ if (assertion.value !== void 0) {
27491
+ const threshold = Number(getValueFromPrimitiveOrRegex(assertion.value));
27492
+ const actual = Number(await locator.evaluate((el, name) => el[name], assertion.name));
27493
+ this.obs.logger.info(`Expected "${assertion.name}" (${actual}) > ${threshold}`);
27494
+ if (!(actual > threshold)) throw new Error(`Property "${assertion.name}" expected to be greater than ${threshold}, but got ${actual}`);
27495
+ }
27496
+ break;
27497
+ case "exists": {
27498
+ const exists = await locator.evaluate((el, name) => el[name] !== void 0, assertion.name);
27499
+ this.obs.logger.info(`Expected property "${assertion.name}" to exist: ${exists}`);
27500
+ if (!exists) throw new Error(`Property "${assertion.name}" does not exist on element`);
27501
+ break;
27502
+ }
27400
27503
  }
27401
27504
  break;
27505
+ }
27402
27506
  case "toHaveValue": {
27403
27507
  const value = getValueFromStringOrRegex(assertion.value);
27404
27508
  await asserter.toHaveValue(value, assertion.options);
@@ -27585,7 +27689,7 @@ var Tester = class {
27585
27689
  const selectorInfo = target.uniqueSelectors[chosenSelectorIndex];
27586
27690
  if (!selectorInfo) throw new Error(`No selector found for target "${target.name}" at chosenSelectorIndex ${chosenSelectorIndex}. Available selectors: ${JSON.stringify(target.uniqueSelectors)}`);
27587
27691
  const frame = await this.getFrame(input.page, target.frames);
27588
- const locator = this.getLocator(frame, selectorInfo);
27692
+ const locator = this.getLocator(frame, selectorInfo, target);
27589
27693
  try {
27590
27694
  const dataWithDefaultTimeout = {
27591
27695
  ...data,
@@ -27606,7 +27710,7 @@ var Tester = class {
27606
27710
  for (let i = 0; i < target.uniqueSelectors.length; i++) {
27607
27711
  if (i === chosenSelectorIndex) continue;
27608
27712
  const selectorInfo = target.uniqueSelectors[i];
27609
- const locator = this.getLocator(frame, selectorInfo);
27713
+ const locator = this.getLocator(frame, selectorInfo, target);
27610
27714
  try {
27611
27715
  this.obs.logger.info(`Retrying action "${data.type}" using next selector: ${JSON.stringify(selectorInfo)}`);
27612
27716
  const dataWithModifiedTimeout = {
@@ -27637,7 +27741,7 @@ var Tester = class {
27637
27741
  const chosenSelectorIndex = target.chosenSelectorIndex ?? 0;
27638
27742
  const selectorInfo = target.uniqueSelectors[chosenSelectorIndex];
27639
27743
  const frame = await this.getFrame(input.page, target.frames);
27640
- const locator = this.getLocator(frame, selectorInfo);
27744
+ const locator = this.getLocator(frame, selectorInfo, target);
27641
27745
  try {
27642
27746
  this.obs.logger.info(`Executing assertion "${data.type}" using selector: ${JSON.stringify(selectorInfo)}`);
27643
27747
  const dataWithDefaultTimeout = {
@@ -27657,7 +27761,7 @@ var Tester = class {
27657
27761
  for (let i = 0; i < target.uniqueSelectors.length; i++) {
27658
27762
  if (i === chosenSelectorIndex) continue;
27659
27763
  const selectorInfo = target.uniqueSelectors[i];
27660
- const locator = this.getLocator(frame, selectorInfo);
27764
+ const locator = this.getLocator(frame, selectorInfo, target);
27661
27765
  const dataWithModifiedTimeout = {
27662
27766
  ...data,
27663
27767
  options: {
@@ -27690,7 +27794,7 @@ var Tester = class {
27690
27794
  const selectorInfo = target.uniqueSelectors[chosenSelectorIndex];
27691
27795
  if (!selectorInfo) throw new Error(`No selector found for target "${target.name}" at chosenSelectorIndex ${chosenSelectorIndex}. Available selectors: ${JSON.stringify(target.uniqueSelectors)}`);
27692
27796
  const frame = await this.getFrame(input.page, target.frames);
27693
- const locator = this.getLocator(frame, selectorInfo);
27797
+ const locator = this.getLocator(frame, selectorInfo, target);
27694
27798
  try {
27695
27799
  this.obs.logger.info(`Executing extraction "${extract.type}" using selector: ${JSON.stringify(selectorInfo)}`);
27696
27800
  await this.executeElementExtraction(locator.target, extract, storageDetails);
@@ -27703,7 +27807,7 @@ var Tester = class {
27703
27807
  for (let i = 0; i < target.uniqueSelectors.length; i++) {
27704
27808
  if (i === chosenSelectorIndex) continue;
27705
27809
  const fallbackSelectorInfo = target.uniqueSelectors[i];
27706
- const fallbackLocator = this.getLocator(frame, fallbackSelectorInfo);
27810
+ const fallbackLocator = this.getLocator(frame, fallbackSelectorInfo, target);
27707
27811
  try {
27708
27812
  this.obs.logger.info(`Retrying extraction "${extract.type}" using next selector: ${JSON.stringify(fallbackSelectorInfo)}`);
27709
27813
  await this.executeElementExtraction(fallbackLocator.target, extract, storageDetails);