@dev-blinq/bvt-playwright-js 1.0.0-dev.4.latest.66.1 → 1.0.0-dev.4.staging.100.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 +8 -0
- package/index.mjs +135 -20
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/index.d.mts
CHANGED
|
@@ -7409,6 +7409,7 @@ type ExecutionStatusUpdateEventData = {
|
|
|
7409
7409
|
stepDefinitionId: string;
|
|
7410
7410
|
completedAt?: ExecutionEventTimestamp;
|
|
7411
7411
|
result: ExecResult;
|
|
7412
|
+
resolvedChosenSelectorIndex?: number;
|
|
7412
7413
|
recovery?: RecoveryMetadata;
|
|
7413
7414
|
} | RecoveryStatusEvent;
|
|
7414
7415
|
type ExecutionCompletedEventData = {
|
|
@@ -7897,6 +7898,7 @@ declare const ExecutionResultSchema: z.ZodObject<{
|
|
|
7897
7898
|
data: z.ZodOptional<z.ZodUnknown>;
|
|
7898
7899
|
}, z.core.$strict>>>>;
|
|
7899
7900
|
duration: z.ZodNumber;
|
|
7901
|
+
screenshotBefore: z.ZodOptional<z.ZodString>;
|
|
7900
7902
|
screenshotAfter: z.ZodOptional<z.ZodString>;
|
|
7901
7903
|
mutationSignals: z.ZodOptional<z.ZodObject<{
|
|
7902
7904
|
httpMutations: z.ZodArray<z.ZodObject<{
|
|
@@ -8212,11 +8214,13 @@ type CustomCodeRuntimeOptions = {
|
|
|
8212
8214
|
readonly driver?: CustomCodeDriver;
|
|
8213
8215
|
readonly policy?: CustomCodePolicy;
|
|
8214
8216
|
readonly logger?: AgentLoggerInput;
|
|
8217
|
+
readonly pgHostBridge?: boolean;
|
|
8215
8218
|
};
|
|
8216
8219
|
declare class CustomCodeRuntime {
|
|
8217
8220
|
private readonly driver;
|
|
8218
8221
|
private readonly policy;
|
|
8219
8222
|
private readonly logger?;
|
|
8223
|
+
private readonly pgHostBridge;
|
|
8220
8224
|
constructor(options?: CustomCodeRuntimeOptions);
|
|
8221
8225
|
execute(input: ExecuteCustomCodeInput): Promise<CustomCodeExecutionResult>;
|
|
8222
8226
|
}
|
|
@@ -8909,6 +8913,7 @@ declare class BrowserObserver {
|
|
|
8909
8913
|
private rrwebBufferedBytes;
|
|
8910
8914
|
private rrwebCommandAnchors;
|
|
8911
8915
|
static instance: BrowserObserver | null;
|
|
8916
|
+
private writeConsole;
|
|
8912
8917
|
private getRrwebChunkEventCountThreshold;
|
|
8913
8918
|
private getRrwebChunkByteThreshold;
|
|
8914
8919
|
private getRrwebMaskProfile;
|
|
@@ -9314,6 +9319,7 @@ declare class Tester {
|
|
|
9314
9319
|
result?: unknown;
|
|
9315
9320
|
error?: string | undefined;
|
|
9316
9321
|
userMessage?: string | undefined;
|
|
9322
|
+
screenshotBefore?: string | undefined;
|
|
9317
9323
|
screenshotAfter?: string | undefined;
|
|
9318
9324
|
mutationSignals?: {
|
|
9319
9325
|
httpMutations: {
|
|
@@ -9358,6 +9364,8 @@ declare class Tester {
|
|
|
9358
9364
|
headers: Record<string, string>;
|
|
9359
9365
|
bodyText: string;
|
|
9360
9366
|
bodyJson?: unknown;
|
|
9367
|
+
} | {
|
|
9368
|
+
resolvedChosenSelectorIndex: number;
|
|
9361
9369
|
} | undefined>;
|
|
9362
9370
|
/**
|
|
9363
9371
|
* Runtime test-data accessor bound to the tester's current data provider and
|
package/index.mjs
CHANGED
|
@@ -6202,6 +6202,15 @@ const AiRecoveredScenarioVersionSchema = object({
|
|
|
6202
6202
|
supersededAt: date().optional()
|
|
6203
6203
|
}).strict();
|
|
6204
6204
|
|
|
6205
|
+
//#endregion
|
|
6206
|
+
//#region ../../core/schemas/src/custom-code-step-template/custom-code-step-template.schema.ts
|
|
6207
|
+
const CustomCodeStepTemplateSchema = object({
|
|
6208
|
+
_id: EntityIdSchema,
|
|
6209
|
+
projectId: EntityIdSchema,
|
|
6210
|
+
name: string().min(1).max(500),
|
|
6211
|
+
code: string().min(1).max(1e5)
|
|
6212
|
+
}).strict();
|
|
6213
|
+
|
|
6205
6214
|
//#endregion
|
|
6206
6215
|
//#region ../../core/schemas/src/workspace/workspace.schema.ts
|
|
6207
6216
|
const EnvironmentSchema = object({
|
|
@@ -9803,6 +9812,7 @@ const FieldKindSchema = _enum([
|
|
|
9803
9812
|
"number",
|
|
9804
9813
|
"date",
|
|
9805
9814
|
"select",
|
|
9815
|
+
"custom-select",
|
|
9806
9816
|
"multiselect",
|
|
9807
9817
|
"checkbox",
|
|
9808
9818
|
"radio",
|
|
@@ -9829,11 +9839,17 @@ const FillFormBatchResultEntrySchema = object({
|
|
|
9829
9839
|
"error"
|
|
9830
9840
|
]),
|
|
9831
9841
|
error: string().optional(),
|
|
9832
|
-
actualValue: string().optional()
|
|
9842
|
+
actualValue: string().optional(),
|
|
9843
|
+
signal: object({
|
|
9844
|
+
filled: boolean(),
|
|
9845
|
+
matchesConfirmField: boolean().optional(),
|
|
9846
|
+
valueSatisfiesIntent: boolean().optional()
|
|
9847
|
+
}).strict().optional()
|
|
9833
9848
|
}).strict();
|
|
9834
9849
|
const FillFormBatchResultSchema = object({
|
|
9835
9850
|
results: array(FillFormBatchResultEntrySchema),
|
|
9836
|
-
domGrew: boolean()
|
|
9851
|
+
domGrew: boolean(),
|
|
9852
|
+
fieldTimingsMs: array(number().nonnegative()).optional()
|
|
9837
9853
|
}).strict();
|
|
9838
9854
|
|
|
9839
9855
|
//#endregion
|
|
@@ -9885,13 +9901,31 @@ const GenerateParameterizedStepNameRequestSchema = object({
|
|
|
9885
9901
|
scenarioId: string().min(1).optional(),
|
|
9886
9902
|
projectId: string().min(1).optional()
|
|
9887
9903
|
});
|
|
9904
|
+
const AiChatFormFieldSnapshotSchema = object({
|
|
9905
|
+
tag: string().min(1),
|
|
9906
|
+
type: string().nullable(),
|
|
9907
|
+
id: string().nullable(),
|
|
9908
|
+
name: string().nullable(),
|
|
9909
|
+
ariaLabel: string().nullable(),
|
|
9910
|
+
placeholder: string().nullable(),
|
|
9911
|
+
required: boolean(),
|
|
9912
|
+
disabled: boolean(),
|
|
9913
|
+
readOnly: boolean().default(false),
|
|
9914
|
+
label: string().nullable(),
|
|
9915
|
+
selector: string().nullable(),
|
|
9916
|
+
options: array(string()).optional(),
|
|
9917
|
+
formIndex: number().int().nonnegative().nullable().optional(),
|
|
9918
|
+
formName: string().nullable().optional()
|
|
9919
|
+
}).strict();
|
|
9888
9920
|
const AiChatPageContextSchema = object({
|
|
9889
9921
|
url: string().url(),
|
|
9890
9922
|
title: string().min(1),
|
|
9891
9923
|
accessibilityTree: string().min(1),
|
|
9892
9924
|
visibleText: string().min(1).optional(),
|
|
9893
9925
|
domSnapshot: string().min(1).optional(),
|
|
9894
|
-
screenshotBase64: string().min(1).optional()
|
|
9926
|
+
screenshotBase64: string().min(1).optional(),
|
|
9927
|
+
formFields: array(AiChatFormFieldSnapshotSchema).optional(),
|
|
9928
|
+
formFieldsCapped: boolean().optional()
|
|
9895
9929
|
}).strict();
|
|
9896
9930
|
const ValidationComplexitySchema = _enum([
|
|
9897
9931
|
"simple",
|
|
@@ -9930,6 +9964,7 @@ const ExecutionResultSchema = object({
|
|
|
9930
9964
|
logs: array(string()),
|
|
9931
9965
|
checkpoints: array(CheckpointSchema).optional().default([]),
|
|
9932
9966
|
duration: number().nonnegative(),
|
|
9967
|
+
screenshotBefore: string().optional(),
|
|
9933
9968
|
screenshotAfter: string().optional(),
|
|
9934
9969
|
mutationSignals: SegmentMutationSignalsSchema.optional()
|
|
9935
9970
|
}).strict();
|
|
@@ -20618,17 +20653,40 @@ async function executePlaywrightCode({ page, code, context, timeoutMs = PLAYWRIG
|
|
|
20618
20653
|
}
|
|
20619
20654
|
|
|
20620
20655
|
//#endregion
|
|
20621
|
-
//#region ../../shared/observability/src/
|
|
20622
|
-
function
|
|
20656
|
+
//#region ../../shared/observability/src/log-message.ts
|
|
20657
|
+
function resolveLogMessageClass(controls) {
|
|
20658
|
+
if (controls.isMilestone) return "MILESTONE";
|
|
20659
|
+
if (controls.severity === "critical") return "CRITICAL_ERR";
|
|
20660
|
+
if (controls.severity === "error") return "ERR";
|
|
20661
|
+
return controls.messageClass;
|
|
20662
|
+
}
|
|
20663
|
+
function buildClassifiedEventMessage(controls) {
|
|
20664
|
+
if (!controls.eventName) return;
|
|
20665
|
+
const resolvedClass = resolveLogMessageClass(controls);
|
|
20666
|
+
if (!resolvedClass) return;
|
|
20667
|
+
return `[${resolvedClass}][${controls.eventName}]`;
|
|
20668
|
+
}
|
|
20669
|
+
function extractStructuredLogMessageControls(metadata) {
|
|
20623
20670
|
if (!metadata) return {};
|
|
20624
20671
|
const rawEventName = metadata.eventName;
|
|
20625
|
-
|
|
20626
|
-
const
|
|
20672
|
+
const eventName = typeof rawEventName === "string" && rawEventName.trim().length > 0 ? rawEventName : void 0;
|
|
20673
|
+
const rawMessageClass = metadata.messageClass;
|
|
20674
|
+
const messageClass = rawMessageClass === "USER_EVENT" || rawMessageClass === "INFRA_EVENT" || rawMessageClass === "MILESTONE" || rawMessageClass === "ERR" || rawMessageClass === "CRITICAL_ERR" ? rawMessageClass : void 0;
|
|
20675
|
+
const rawSeverity = metadata.severity;
|
|
20676
|
+
const severity = rawSeverity === "error" || rawSeverity === "critical" ? rawSeverity : void 0;
|
|
20677
|
+
const isMilestone = metadata.isMilestone === true;
|
|
20678
|
+
const { eventName: _eventName, messageClass: _messageClass, severity: _severity, isMilestone: _isMilestone, ...restMetadata } = metadata;
|
|
20627
20679
|
return {
|
|
20628
|
-
eventName:
|
|
20629
|
-
|
|
20680
|
+
...eventName ? { eventName } : {},
|
|
20681
|
+
...messageClass ? { messageClass } : {},
|
|
20682
|
+
...severity ? { severity } : {},
|
|
20683
|
+
...isMilestone ? { isMilestone } : {},
|
|
20684
|
+
...Object.keys(restMetadata).length > 0 ? { sanitizedMetadata: restMetadata } : {}
|
|
20630
20685
|
};
|
|
20631
20686
|
}
|
|
20687
|
+
|
|
20688
|
+
//#endregion
|
|
20689
|
+
//#region ../../shared/observability/src/logger.ts
|
|
20632
20690
|
var Logger = class Logger {
|
|
20633
20691
|
constructor(transports, defaultContext = { system: true }) {
|
|
20634
20692
|
this.transports = transports;
|
|
@@ -20646,15 +20704,48 @@ var Logger = class Logger {
|
|
|
20646
20704
|
error(message, metadata, context) {
|
|
20647
20705
|
this.emit("error", message, metadata, context);
|
|
20648
20706
|
}
|
|
20707
|
+
userEvent(eventName, metadata, context) {
|
|
20708
|
+
this.emit("info", eventName, {
|
|
20709
|
+
eventName,
|
|
20710
|
+
messageClass: "USER_EVENT",
|
|
20711
|
+
...metadata
|
|
20712
|
+
}, context);
|
|
20713
|
+
}
|
|
20714
|
+
infraEvent(eventName, metadata, context) {
|
|
20715
|
+
this.emit("info", eventName, {
|
|
20716
|
+
eventName,
|
|
20717
|
+
messageClass: "INFRA_EVENT",
|
|
20718
|
+
...metadata
|
|
20719
|
+
}, context);
|
|
20720
|
+
}
|
|
20721
|
+
milestone(eventName, metadata, context) {
|
|
20722
|
+
this.emit("info", eventName, {
|
|
20723
|
+
eventName,
|
|
20724
|
+
isMilestone: true,
|
|
20725
|
+
...metadata
|
|
20726
|
+
}, context);
|
|
20727
|
+
}
|
|
20728
|
+
errorEvent(eventName, metadata, context, options) {
|
|
20729
|
+
this.emit("error", eventName, {
|
|
20730
|
+
eventName,
|
|
20731
|
+
severity: options?.critical ? "critical" : "error",
|
|
20732
|
+
...metadata
|
|
20733
|
+
}, context);
|
|
20734
|
+
}
|
|
20649
20735
|
/** Returns a new Logger instance bound to the given context. */
|
|
20650
20736
|
withContext(context) {
|
|
20651
20737
|
return new Logger(this.transports, context);
|
|
20652
20738
|
}
|
|
20653
20739
|
emit(level, message, metadata, context) {
|
|
20654
|
-
const { eventName, sanitizedMetadata } =
|
|
20740
|
+
const { eventName, messageClass, severity, isMilestone, sanitizedMetadata } = extractStructuredLogMessageControls(metadata);
|
|
20655
20741
|
const entry = {
|
|
20656
20742
|
level,
|
|
20657
|
-
message
|
|
20743
|
+
message: buildClassifiedEventMessage({
|
|
20744
|
+
eventName,
|
|
20745
|
+
messageClass,
|
|
20746
|
+
severity,
|
|
20747
|
+
isMilestone
|
|
20748
|
+
}) ?? message,
|
|
20658
20749
|
...eventName ? { eventName } : {},
|
|
20659
20750
|
context: context ?? this.defaultContext,
|
|
20660
20751
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -21089,6 +21180,11 @@ async function restoreSessionStorageState(page, value) {
|
|
|
21089
21180
|
|
|
21090
21181
|
//#endregion
|
|
21091
21182
|
//#region ../../core/bvt-agent/src/agent/tester.ts
|
|
21183
|
+
function getResolvedChosenSelectorIndex(result) {
|
|
21184
|
+
if (!result || typeof result !== "object") return;
|
|
21185
|
+
const candidate = result.resolvedChosenSelectorIndex;
|
|
21186
|
+
return Number.isInteger(candidate) ? candidate : void 0;
|
|
21187
|
+
}
|
|
21092
21188
|
const browserTypesMap = {
|
|
21093
21189
|
chromium,
|
|
21094
21190
|
firefox,
|
|
@@ -21247,7 +21343,7 @@ var Tester = class {
|
|
|
21247
21343
|
startedAt: /* @__PURE__ */ new Date()
|
|
21248
21344
|
};
|
|
21249
21345
|
}
|
|
21250
|
-
async onCommandPass(command, stepDefinitionId, session,
|
|
21346
|
+
async onCommandPass(command, stepDefinitionId, session, metadata) {
|
|
21251
21347
|
this.obs.logger.log(`Command executed successfully: ${command}`);
|
|
21252
21348
|
if (session?.type === "run") {
|
|
21253
21349
|
this.obs.logger.log(`Ending Playwright tracing group for command: ${command}`);
|
|
@@ -21264,7 +21360,8 @@ var Tester = class {
|
|
|
21264
21360
|
stepDefinitionId,
|
|
21265
21361
|
completedAt,
|
|
21266
21362
|
result: { type: "success" },
|
|
21267
|
-
|
|
21363
|
+
...typeof metadata?.resolvedChosenSelectorIndex === "number" ? { resolvedChosenSelectorIndex: metadata.resolvedChosenSelectorIndex } : {},
|
|
21364
|
+
...metadata?.recovery ? { recovery: metadata.recovery } : {}
|
|
21268
21365
|
};
|
|
21269
21366
|
}
|
|
21270
21367
|
async onCommandFail(command, stepDefinitionId, error, session, recovery) {
|
|
@@ -21557,7 +21654,7 @@ var Tester = class {
|
|
|
21557
21654
|
});
|
|
21558
21655
|
if ((command.type === "custom" || command.type === "custom.code") && commandResult && isFailedCustomCommandResult(commandResult)) throw new Error(commandResult.error || `Custom command "${command._id}" failed.`);
|
|
21559
21656
|
this.obs.logger.log(`Finished executing command: ${command}`);
|
|
21560
|
-
yield await this.onCommandPass(command, stepDefinitionId, session);
|
|
21657
|
+
yield await this.onCommandPass(command, stepDefinitionId, session, { resolvedChosenSelectorIndex: getResolvedChosenSelectorIndex(commandResult) });
|
|
21561
21658
|
} catch (error) {
|
|
21562
21659
|
const executionError = this.toExecutionError(error);
|
|
21563
21660
|
let terminalExecutionError = executionError;
|
|
@@ -21676,7 +21773,7 @@ var Tester = class {
|
|
|
21676
21773
|
};
|
|
21677
21774
|
emittedTerminalRecoveryStatus = true;
|
|
21678
21775
|
this.obs.logger.log(`Recovered command ${command._id} with whole-step repair`);
|
|
21679
|
-
yield await this.onCommandPass(command, stepDefinitionId, session, recoveryMeta);
|
|
21776
|
+
yield await this.onCommandPass(command, stepDefinitionId, session, { recovery: recoveryMeta });
|
|
21680
21777
|
commandIndex = recorderStep.definition.commands.length - 1;
|
|
21681
21778
|
continue;
|
|
21682
21779
|
} catch (retryError) {
|
|
@@ -21794,7 +21891,7 @@ var Tester = class {
|
|
|
21794
21891
|
};
|
|
21795
21892
|
emittedTerminalRecoveryStatus = true;
|
|
21796
21893
|
this.obs.logger.log(`Recovered command ${command._id} with step repair`);
|
|
21797
|
-
yield await this.onCommandPass(command, stepDefinitionId, session, recoveryMeta);
|
|
21894
|
+
yield await this.onCommandPass(command, stepDefinitionId, session, { recovery: recoveryMeta });
|
|
21798
21895
|
if (decision.stepRepairPlan.type === "command-operations" || decision.stepRepairPlan.preserveSuffix) commandIndex = recorderStep.definition.commands.length - 1;
|
|
21799
21896
|
continue;
|
|
21800
21897
|
} catch (retryError) {
|
|
@@ -22382,7 +22479,7 @@ var Tester = class {
|
|
|
22382
22479
|
};
|
|
22383
22480
|
await this.executeElementAction(locator.target, dataWithModifiedTimeout);
|
|
22384
22481
|
this.obs.logger.info(`Action "${data.type}" completed successfully on target "${target.name}" with fallback selector`);
|
|
22385
|
-
return;
|
|
22482
|
+
return { resolvedChosenSelectorIndex: i };
|
|
22386
22483
|
} catch (error) {
|
|
22387
22484
|
this.obs.logger.warn(`Action "${data.type}" failed on fallback selector "${JSON.stringify(selectorInfo, null, 2)}". Trying next selector if available...`, error);
|
|
22388
22485
|
errors.push(this.toExecutionError(error));
|
|
@@ -22432,7 +22529,7 @@ var Tester = class {
|
|
|
22432
22529
|
this.obs.logger.info(`Retrying assertion "${data.type}" using next selector: ${JSON.stringify(selectorInfo)}`);
|
|
22433
22530
|
await this.executeElementAssertion(locator.target, dataWithModifiedTimeout, isNegativeAssertion);
|
|
22434
22531
|
this.obs.logger.info(`Assertion "${data.type}" passed on target "${target.name}" with fallback selector`);
|
|
22435
|
-
return;
|
|
22532
|
+
return { resolvedChosenSelectorIndex: i };
|
|
22436
22533
|
} catch (error) {
|
|
22437
22534
|
this.obs.logger.warn(`Assertion "${data.type}" failed on fallback selector "${JSON.stringify(selectorInfo, null, 2)}". Trying next selector if available...`, error);
|
|
22438
22535
|
errors.push(this.toExecutionError(error));
|
|
@@ -22471,7 +22568,7 @@ var Tester = class {
|
|
|
22471
22568
|
this.obs.logger.info(`Retrying extraction "${extract.type}" using next selector: ${JSON.stringify(fallbackSelectorInfo)}`);
|
|
22472
22569
|
await this.executeElementExtraction(fallbackLocator.target, extract, storageDetails);
|
|
22473
22570
|
this.obs.logger.info(`Extraction "${extract.type}" completed successfully on target "${target.name}" with fallback selector`);
|
|
22474
|
-
return;
|
|
22571
|
+
return { resolvedChosenSelectorIndex: i };
|
|
22475
22572
|
} catch (error) {
|
|
22476
22573
|
this.obs.logger.warn(`Extraction "${extract.type}" failed on fallback selector "${JSON.stringify(fallbackSelectorInfo, null, 2)}". Trying next selector if available...`, error);
|
|
22477
22574
|
errors.push(this.toExecutionError(error));
|
|
@@ -27734,7 +27831,25 @@ var DateEvaluator = class {
|
|
|
27734
27831
|
const upperBound = rest ? parseDate(`in ${rest}`, now, { forwardDate: true }) ?? new Date(now.getTime() + 720 * 60 * 60 * 1e3) : new Date(now.getTime() + 720 * 60 * 60 * 1e3);
|
|
27735
27832
|
return new Date(now.getTime() + Math.random() * (upperBound.getTime() - now.getTime()));
|
|
27736
27833
|
}
|
|
27737
|
-
|
|
27834
|
+
const monthDay = this.parseMonthWithDay(expr, now);
|
|
27835
|
+
if (monthDay) return monthDay;
|
|
27836
|
+
return parseDate(this.normalizeExpression(expr), now, { forwardDate: true });
|
|
27837
|
+
}
|
|
27838
|
+
normalizeExpression(expr) {
|
|
27839
|
+
return expr.replace(/\b(next|last|previous|this)\s+week\s+(mon|tues?|wed(?:nes)?|thurs?|fri|sat(?:ur)?|sun)(day)?\b/gi, (_match, qualifier, weekdayStem, daySuffix) => `${weekdayStem}${daySuffix ?? ""} ${qualifier} week`);
|
|
27840
|
+
}
|
|
27841
|
+
parseMonthWithDay(expr, now) {
|
|
27842
|
+
const trimmed = expr.trim();
|
|
27843
|
+
const qualifierFirst = trimmed.match(/^(next|last|previous|this)\s+month\s+(\d{1,2})(?:st|nd|rd|th)?$/i);
|
|
27844
|
+
const dayFirst = trimmed.match(/^(\d{1,2})(?:st|nd|rd|th)?\s+(?:of\s+)?(next|last|previous|this)\s+month$/i);
|
|
27845
|
+
const match = qualifierFirst ?? dayFirst;
|
|
27846
|
+
if (!match) return null;
|
|
27847
|
+
const qualifier = (qualifierFirst ? match[1] : match[2]).toLowerCase();
|
|
27848
|
+
const day = parseInt(qualifierFirst ? match[2] : match[1], 10);
|
|
27849
|
+
const monthOffset = qualifier === "next" ? 1 : qualifier === "last" || qualifier === "previous" ? -1 : 0;
|
|
27850
|
+
const target = new Date(now.getFullYear(), now.getMonth() + monthOffset, day, now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds());
|
|
27851
|
+
if (target.getDate() !== day) return null;
|
|
27852
|
+
return target;
|
|
27738
27853
|
}
|
|
27739
27854
|
formatDate(date, format) {
|
|
27740
27855
|
if (format === "iso") return date.toISOString();
|